为什么这个网络游戏同步算法一段时间以后就不同步了?

暗黑Zero 2015-05-08 06:20:14
我最近正在编写一款简单的网络游戏,其中服务器部分使用IOCP(完成端口)写的,然后由于基本上是在局域网内进行的游戏,所以核心的控制逻辑是这样的:

每一帧客户端都和服务器端通信,把自己这一帧中接收到的玩家按键发送给服务器,在服务器回送消息之前等待;
服务器收集齐客户端的消息,然后编在一起,发送给四个客户端(一局游戏是4个人);
四个客户端拿到消息后,按照所发送的消息更新状态。

发送的消息当中包括:每个客户端上一帧到这一帧所经历的时间,以及玩家的按键事件。

这个算法不知为何在一段时间以后就会让四个客户端的游戏失去同步……
而且失去同步的时候,似乎永远是正在操控的那个人的游戏和另外三个不一样,另外三个是一样的,并且似乎就是在移动的过程中不同步的。

想了半天也没想出哪儿错了,于是想问一问……

以下是代码:
先是服务器的代码,服务器是多线程的,每收到一个消息就会开一个线程。

while(game_host[now_roomnum].Ready(now_playernum)) //如果上一个消息还没有取走,等待
{
Sleep(1);
}

game_host[now_roomnum].SetMessage(now_playernum, recv_msg->msg);
game_host[now_roomnum].SetReady(now_playernum, true);
game_host[now_roomnum].SetUsed(now_playernum, false);
while(!game_host[now_roomnum].AllReady())//等待本游戏中的所有人都发来消息
{
Sleep(1);
}

string all_msg = game_host[now_roomnum].GetAllMessage();
game_host[now_roomnum].SetUsed(now_playernum, true);
while(!game_host[now_roomnum].AllUsed())//等待本游戏内的所有人都获取了消息
{
Sleep(1);
}
game_host[now_roomnum].SetReady(now_playernum, false);//标记该消息已经无效了

strcpy_s(ret.msg, all_msg.c_str());


接着是客户端的每帧的更新函数,每两帧之间玩家的操作会存在一个队列中

GameState CGame::Update(float game_time)
{
CMessage msg = MakeMessage(game_time); //将这两帧之间的时间以及操作队列做成一个消息
CMessage recv = p_res_manager->m_Client._SendMessage(msg);//发送消息,并在服务器返回消息前等待
stringstream input(recv.msg);
int i;

rest_time -= game_time;
float game_times[MAX_PLAYER+1]={0};

//analyze recv operations
for(i=1; i<=MAX_PLAYER; i++)
{
int n;
input>>n;
input>>game_times[i];//解析消息,先取出消息个数n,然后取出第i个玩家这两帧间经过的时间game_times[i]
for(int oper_i = 1; oper_i <= n; oper_i++)//然后取出这两帧间的n个操作并改变状态
{
int now_event;
UINT nchar;
input>>now_event>>nchar;

if(now_event == int(Event::KEY_UP))
HandleKeyUpInUpdate(i, nchar);
else //if(now_event == int(Event::KEY_DOWN))
HandleKeyDownInUpdate(i, nchar);
}
}

//update player,根据game_times[i]来更新玩家状态,基本的方法就是s[i] = v[i] *game_times[i]
for(i=1; i<=MAX_PLAYER; i++)
{
player[i].Update(game_times[i]); // 用第i个玩家的这两帧的更新时间来更新玩家状态
……………………
}


谢谢大家!
...全文
210 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
xiaohuh421 2015-05-14
  • 打赏
  • 举报
回复
对于实时性较高的移动数据是不能缓存的. 每次都要去取最新的. 比如在2号玩家的视野内, 有1,3,4号玩家, 那么给2号玩家的移动数据, 就应该每次取到1,3,4的最新坐标, 再发给2号玩家. 而不是看取没有取过.
暗黑Zero 2015-05-14
  • 打赏
  • 举报
回复
引用 6 楼 xiaohuh421 的回复:
对于实时性较高的移动数据是不能缓存的. 每次都要去取最新的. 比如在2号玩家的视野内, 有1,3,4号玩家, 那么给2号玩家的移动数据, 就应该每次取到1,3,4的最新坐标, 再发给2号玩家. 而不是看取没有取过.
嗯,现在其实实现的就是这个意思……
暗黑Zero 2015-05-13
  • 打赏
  • 举报
回复
引用 4 楼 xiaohuh421 的回复:
while(game_host[now_roomnum].Ready(now_playernum)) //如果上一个消息还没有取走,等待 问题就在于这里. 对于移动事件, 服务器端永远只能处理最后一个. 否则客户端上已经移动了100次, 而服务器上可以还在发送第50次. 能同步才怪. 对于移动这样的实际事件应该是发来一个, 就清除掉前面的, 执行最新的. 当然, 在服务器卡(也就是延迟高)的情况下, 在其它客户端看来, 你可能就在瞬移, 或者跳帧.
服务器并不是只能处理最后一个,因为服务器是基于IOCP实现的,所以每个消息都会开一个线程处理,所有的移动消息处理都是并行的; 不同步的真正原因在于,如果来的四个人的移动消息顺序分别是1 3 4 2号玩家,然后这四个人全都填充并读取完了,现在他们都在game_host[now_roomnum].SetReady(now_playernum, false); 这句话上,然后2号线程先返回消息并结束了,这个时候2号客户端继续了下一帧,又发来一个消息,现在2号消息一看之前的2号被读过了,于是进来,这个时候1 3 4的ready还是true,于是2号就误以为四个人的新消息都齐了,于是他自己又给自己返回了一次,是这个原因导致的不同步……
xiaohuh421 2015-05-13
  • 打赏
  • 举报
回复
while(game_host[now_roomnum].Ready(now_playernum)) //如果上一个消息还没有取走,等待 问题就在于这里. 对于移动事件, 服务器端永远只能处理最后一个. 否则客户端上已经移动了100次, 而服务器上可以还在发送第50次. 能同步才怪. 对于移动这样的实际事件应该是发来一个, 就清除掉前面的, 执行最新的. 当然, 在服务器卡(也就是延迟高)的情况下, 在其它客户端看来, 你可能就在瞬移, 或者跳帧.
暗黑Zero 2015-05-12
  • 打赏
  • 举报
回复
引用 2 楼 wxhxj0268 的回复:
画面也要通过网络传送,负担也忒重了吧?
没有通过网络传送画面啊,传送的只是玩家的操作……
笨笨仔 2015-05-12
  • 打赏
  • 举报
回复
画面也要通过网络传送,负担也忒重了吧?
暗黑Zero 2015-05-12
  • 打赏
  • 举报
回复
我后来发现不同步的原因在于两点,第一是Sleep(1)这个睡眠时间太长,1ms的时间够线程进行很多操作了; 第二是game_host[now_roomnum].SetReady(now_playernum, false);这句话,如果这句话执行的话,后面一次新进来的同样player_num的消息就会覆盖前一次的,并且如果其他线程还没有把自己的ready set成false的话,就会立刻再发送一次,是这样造成的不同步……

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧