Transfer coding of our RPG game
原文链接 https://liutaihua.github.io/2015/04/08/Transfer-coding-of-our-RPG-game.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
首先, 单个socket是面向玩家的, 以此展开的会有,这个玩家,也就是说这个socket,会拥有一个gameWorld, 以及在玩家login之前, 所有玩家都面向的gameHolder, Holer处理登陆, 已登陆玩家数据网络层将推送给Player类处理。 transfer和gameHoler, gameWorld, Player之间,各使用3个线程安全的数据队列m_DataQueue. m_DataQueue的类型是 FastQueue m_DataQueue; 它的原型是: {{% highlight c++ %}} template class FastQueue { struct node { T element; node * next; };
node * last;
node * first;
LOCK m_lock;
public:
FastQueue()
{
last = 0;
first = 0;
}
~FastQueue()
{
Clear();
}
void Push(T elem)
{
}
T Pop()
{
}
// 一些其他方法, 这里略去
bool HasItems()
{
bool ret;
m_lock.Acquire();
ret = (first != 0);
m_lock.Release();
return ret;
}
}; {{% endhighlight %}}
玩家和server的socket建立后, 放入socketManager: g_SocketsManager->AddServerSocket(this); socket的数据epoll事件通知,下次再看, 先略过。 基本概念就是socket会在收到数据的时候。
gameHoler, gameWorld, Player在初始化的时候生成各自的m_DataQueue. 登陆的过程大体是这样子:
- client send LOGIN REQ
socket收到,分析是属于LOGIN REQ发给gameHoler->m_DataQueue:
case LOGIN: ((GameHolder*)m_pGameHolder)->m_DataQueue.Push(pck);
gameHodler上如何收到这个数据请求并开始处理的呢,gameHoler每秒20帧的速度循环update, 它会在自己的update里检查自己的m_DataQueue队列, 并处理。
gameHoler按需要为玩家分配gameWorldId, 或新建world, 根据玩家连接上来的sessionid, 从g_SocketsManager获取玩家的socket连接, 设置这个连接的几个相关property: {{% highlight c++ %}} GameWorld* tempWD=GetPlayerWorld(sessionId,TableId);
BoostSC->IsLogin=true; BoostSC->m_Player=pNewPlayer; BoostSC->m_GameWorld=tempWD; {{% endhighlight %}}
- 之后的请求进入Player类处理,socket会在收到数据REQ时, 判断类型,并判断是否 BoostSC->IsLogin:
{{% highlight c++ %}}
if (IsLogin)
{
pck = new ByteBuffer(sizeof(uint32)2+asize);
*pck<< m_handle;
*pck<< msgId;
pck ->append((char)pData,asize);
if (m_Player!=NULL)
((Player*)m_Player)->m_DataQueue.Push(pck);
else
delete pck;
}
{{% endhighlight %}}
同样的, Player也会在自己的Update里检查m_DataQueue队列, 处理客户端的REQ, 不过Player的这个做法是在自己的叫UpdateData方法里, 这个UpdateDat并不是Player在Update里调用, 而是gameWorld在自己的Update里, 遍历world里的所有玩家, 逐个调用Player的UpdateData: {{% highlight c++ %}} WorldObject tempObj; for (auto itor = m_PlayerNetMap.begin(); itor != m_PlayerNetMap.end(); ++itor) { Player p = itor->second; if (p) p->UpdateData(); }
// ... 下面是Player的UpdateData方法 void Player::UpdateData() {
uint32 sessionId;
uint32 MsgId;
uint32 asize;
char Data[MAX_PACK_LEN];
ByteBuffer * pbuffer=NULL;
try
{
for (auto i=0;i<10;i++)
{
pbuffer=m_DataQueue.Pop();
if (pbuffer)
{
memset(Data,0,sizeof(Data));
*pbuffer>>sessionId;
*pbuffer>>MsgId;
asize=pbuffer->size()-pbuffer->rpos();
pbuffer->read((uint8 *)Data,asize);
delete pbuffer;
OnRecvWorld(sessionId,MsgId,Data);
}
else
return;
}
}
{{% endhighlight %}} Player将Data从m_DataQueue取出, 进入OnRecvWorld处理流程, 诸如客户端点击玩家走动, 施放技能,拾取物品, 完成任务等REQ。