我有一个游戏,2个玩家通过使用mysql存储游戏状态的websockets相互玩游戏。玩家同时提交动作,服务器应做出相应的响应:
场景:Player1提交转弯。服务器检查数据库以查看最近的转弯是什么,并且:
如果两位玩家都提交了该回合的动作,则会创建一个 使用Player1的动作输入进行新的转变。
如果最近一次转弯没有Player1的动作,它会 更新数据库中的操作,然后执行游戏。
如果最近一次转弯没有为player2输入,但它确实有一个输入为player1,它应该只更新数据库,然后等待player2做出回应。
当Player1提交他的回合时,问题出现了,玩家2提交他们的回合,然后两者几乎同时选择火力。这将导致2轮插入数据库,然后游戏不知道两个玩家都提交了他们的转弯(因为每个玩家认为他们已经创建了一个新的转弯)。
这是转牌表:
我不确定如何在不暴露这种竞争条件的情况下继续进行。除主键之外的任何列都不是唯一的,因此我无法检查这种方式,并且如果有某种方法可以阻止对现有GameId / TurnNumber组合的多次插入,则服务器需要知道以防止默认执行。
该项目使用ASP.NET MVC4和SignalR作为websocket协议,如果这有帮助的话。
编辑:
我知道在GameId和TurnNumber之间的表上创建一个复合唯一索引,但这不会向调用者指示存在插入冲突。
以下是一个示例(非常简化的)执行代码:
var turns = new List<Turn>();
var query = "select * from Turn where GameId = @id order by TurnNumber asc";
using (var conn = new MySqlConnection())
{
using(var cmd = new MySqlCommand(conn, query))
{
cmd.Parameters.AddWithValue("@id", gameId);
try
{
using(var reader = cmd.ExecuteReader())
{
while(reader.read())
{
turns.Add(//Get Reader Data to make turn);
}
}
}
//Handle exceptions
}
}
var lastTurn = turns[turns.Count - 1];
//This turn is old
if(TurnIsOld(lastTurn))
{
//Create new turn and insert
return;
}
//Player 1 has not taken an action
else if(Player1NoAction(turn))
{
//Update the turn
ExecuteGame(turns);
}
//Thus player 2 must have not taken an action
else
{
//Update player 1 action
return;
}
问题在于,当玩家1和玩家2在与其他玩家的if条件块尚未完成执行的大致相同的时间做出轮到他们的决定时,他们都可以执行第一个if条件。