一日一点RakNet(20)--NetworkIDObject

来源:互联网 发布:希特勒南极 知乎 编辑:程序博客网 时间:2024/04/26 14:44
 

网络ID对象

 

NetworkIDObject 和NetworkIDManager类允许使用普通的ID查询指针

 

       NetworkIDOjbect类是一个可选类,可以将自己的类从这个类派生,那么你的类就自动赋值标识数字(NetworkID)。这种方法对于多玩家游戏特别有用,否则你必须有自己的方法动态的访问远端系统上分配的对象。

       在RakNet 4中,NetworkID是8字节长的全局唯一数字,随机选择。旧版本的RakNet要求中心授权者(服务器)赋值NetworkIDs。这种方法已经废弃了,因为如果游戏是端到到(p2p)的形式,或者有多个分布式的服务器存在,那么编程人员创建这个ID号就非常困难。如果客户端要创建一个对象,也要增加额外的很多工作。因为客户端必须先赋值一个临时的ID,然后从服务器请求真实的NetworkID,然后才可以将它赋值给这个客户端创建的对象。

 

NetworkIDObject类提供了如下的函数:

       SetNetworkIDManager( NetworkIDManager * manager)

       NetworkIDManager保存了一个NetworkID的列表用于查询。因此,这就要求你在调用GetNetworkID()或SetNetworkID() 之前调用SetNetworkIDManager()。List不能简单设置为静态的,原因是你或许想要多个NetworkIDManager,例如如果你想要启动多个游戏,它们之间没有任何的交互,如果设置为静态,就会出现错误。

      

       NetworkID GetNetworkID(void)

       如果 SetNetworkID() 在前面调用了,这个函数就会返回NetworkID值。否则它为对象生成一个新的,推测是唯一的NetworkID。本质上说,对象只有在调用了GetNetworkID()时才会真正为这个对象赋值一个NetworkID。在客户端/服务器应用下,如果所有的对象依旧是由服务器创建的,那么就不需要客户端生成NetworkID。

      

       SetNetworkID( NetworkID id)

       给对象赋值一个NetworkID。用到这个方法的例子就是服务器创建新的游戏对象,广播对象的数据到客户端。客户端会创建一个相同时间的类,读取在用户消息中编码的NetworkID,在同一个对象上调用SetNetworkID()方法。

       NetworkIDManager类仅仅有一个用户函数:

       template < class returnType>

       returnType GET_OBJECT_FROM_ID(NetworkID x);

      

       这是一个模板函数,因此你可以如下一样写代码:

       Solider * solider = networkIDManager.GET_OBJECT_FROM_ID<Solider *>(networkId);

 

       如下是一个将指针存储到类的一个例子,重新检索出来,使用Assert确保有效工作(不出现错误):

       class Solider : public NetworkIDObject{}

       int main(void)

       {

              NetworkIDManager networkIDManager;

              Solider * solider = new Solider;

              solider->SetNetworkIDManager(&networkIDManager);

              NetworkID soliderNetworkID = solider->GetNetworkID();

              Assert(networkIDManager.GET_OBJECT_FROM_ID<Solider ->(soliderNetworkID)== solider);

       }

       如下是一个例子,使用系统创建一个远端系统上的对象,将同一个ID值赋给两者:

       Server:

       void CreateSoldier(void)
       {

              Soldier *soldier = new Soldier;

              soldier->SetNetworkIDManager(&networkIDManager);

              RakNet::BitStream bsOut;

              bsOut.Write((MessageID)ID_CREATE_SOLDIER);

              bsOut.Write(soldier->GetNetworkID());

              rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,UNASSIGNED_SYSTEM_ADDRESS,true);

       }

 

       Client:

       Packet *packet = rakPeerInterface->Receive();

       if (packet->data[0]==ID_CREATE_SOLDIER)

       {

              RakNet::BitStream bsIn(packet->data, packet->length, false);

              bsIn.IgnoreBytes(sizeof(MessageID));

              NetworkID soldierNetworkID;

              bsIn.Read(soldierNetworkID);

              Soldier *soldier = new Soldier;

              soldier->SetNetworkIDManager(&networkIDManager);

              soldier->SetNetworkID(soldierNetworkID);

       }

 

静态对象:

       有时候对象并不是动态创建的,而是在所有的系统上都已经存在了,所有的系统提前都知道了。例如,如果你在标记地图上有一个获取物,带有三个标记,这三个标记或许是硬编码进关卡设计中的,因此当游戏加载关卡信息的时候,这些数据就直接加载进了游戏。这些是静态对象,NetworkIDManager也是可以访问的。使得这些对象从NetworkIDObject对象派生出来,如同创建动态对象一样,调用SetNetworkIDManager。然后只需要给你的对象赋值一个唯一的ID即可。ID是什么并不重要,只要它是唯一的就好,那么你可以给flag1赋值ID 0,flag2 赋值 ID 1 以及给flag 3 赋值ID 2。这都没有问题。

       // 所有的NetworkID在这里增加

       enum StaticNetworkIDs
       {

              CTF_FLAG_1,

              CTF_FLAG_2,

              CTF_FLAG_3,

       };

 

       class Flag : public NetworkIDObject
       {
              // 关卡设计者给标记命名flag1, flag2, 或flag3都可以,

              // 地图在其他系统也是以相似方式加载

              Flag(std::string flagName, NetworkIDManager *networkIDManager) {

              SetNetworkIDManager(networkIDManager);

 

              if (flagName=="flag1")

                     SetNetworkID(CTF_FLAG_1);

              else if (flagName=="flag2")

                     SetNetworkID(CTF_FLAG_2);

              else if (flagName=="flag3")

                     SetNetworkID(CTF_FLAG_3);

       };

 

By 北洋小郭

转载请注明出处,切勿用于商业。谢谢!

 

原创粉丝点击