2D网络游戏开发(网络篇)(六)


 


作者:akinggw


 


在前面的章节中,我们实现了一个简单的聊天室。今天,我们仍然要围绕这个主题,但采取别的方法,这个方法很有用,应该说是整个网络引擎的关键,它就是――RPC(Remote Procedure Calls),翻译成中文就可以理解成”远程功能调用”。


通常情况下,你发送一个信息,你必须实现下面的四个步骤:


 


1.        建一个数据包来存储你的数据;


2.        必须建立一个函数来实现数据包的编码和发送;


3.        建立一个数据包识别函数,用来识别你的数据包,以便于你调用哪个函数来处理它;


4. 最后 建立一个函数来解码你的数据包并且处理它。


  


以上就是我们在编写网络程序要做的四个步骤。


但raknet已经为你做好了这一切,那就是RPC,有了它,你只用实现两个步骤,这样你就有更多的时间集中到游戏上。


 


1.        将你的数据编码;


2.        然后调用远程系统的一个相应函数来处理它.


 


RPC在你的游戏中实现的过程如下:


 


A.       告诉网络系统允许使用RPC调用函数


当然,你不需要RPC调用系统中的任何一个函数,这样势必为你带来很多的麻烦。你只需要几个用RPC参数定义的函数就行了,你可以照着下面的例子来定义你的RPC函数。


C 
 函数 
 
void MyFunction(RPCParameters *rpcParameters) {}
//  
 客户端指针 
 
RakClient *rakClient;
// 
 注册成为 
 RPC
REGISTER_AS_REMOTE_PROCEDURE_CALL(rakClient, MyFunction);

C++ 
 静态函数 
 
static void MyClass::MyFunction(RPCParameters *rpcParameters) {}
//  
 客户端指针 
 
RakClient *rakClient;
// 
 注册成为 
 RPC
REGISTER_AS_REMOTE_PROCEDURE_CALL(rakClient, MyClass::MyFunction);

C++  
 成员函数 
 
class MyClass : public NetworkIDGenerator {
void __cdecl func1(RPCParameters *rpcParms);
};
// 
 客户端指针 
 
RakClient *rakClient;
// 
 注册成为 
 RPC
REGISTER_CLASS_MEMBER_RPC(rakClient, MyClass, func1)

服务器的注册同上一样。


 


B.给你的数据编码


你的RPC方法能够处理一个有长度的字符串或比特流,这就等同于将数据打包。


 


C.调用RPC函数进行处理


 


D.在远程系统上相应的函数将对你的数据进行处理


 


以上就是RPC的处理过程。


下面,我们来看一下RPC的参数和结构:


 


char * input; 来自于远程系统的数据;


unsigned int numberOfBitsOfData; 我们接收的数据的大小;


PlayerID sender; 哪一个系统调用这个RPC;


RakPeerInterface * recipient; rakpeer中的哪一个实例将得到这次调用;


Bool hasTimestamp; 如果为真,那么输入的开始4个字节表示时间;


RakNet::BitStream * replyToSender 用相应的数据流回应发送者.


 


下面,我们来具体地看一下代码:


char message1[300];
 
 
 
void PrintMessage(RPCParameters *rpcParameters)
 
{
 
printf("%sn",rpcParameters->input);
 
    sprintf(message1,"%s",rpcParameters->input);       
 
    
 
           if(rakServerInterface)
 
        {
 
      rakServerInterface->RPC("PrintMessage", message1, (strlen(message1)+1)*8, HIGH_PRIORITY, RELIABLE_ORDERED, 0,rpcParameters->sender, true, false, UNASSIGNED_NETWORK_ID,0);        
 
      }
 
}


下面,我们来具体地讲解这个函数。


Message1用于存储得到的信息。首先,我们打印我们得到的信息,然后判断我们是否是服务器,如果是,那么就调用客户端的RPC.


这里这个RPC函数原型如下:


bool RakServer::RPC ( char * uniqueID, 
 
 const char * data, 
 
 unsigned int bitLength, 
 
 PacketPriority priority, 
 
 PacketReliability reliability, 
 
 char orderingChannel, 
 
 PlayerID playerId, 
 
 bool broadcast, 
 
 bool shiftTimestamp, 
 
 ObjectID objectID, 
 
 RakNet::BitStream * replyFromTarget 
 
 ) [virtual, inherited]


第一个参数,我们注册的RPC函数名;


第二个参数,我们要发送的数据;


第三个参数,发送的数据的大小;


第四个参数,数据包的安全级别,和send函数一样;


第五个参数,数据包的可靠性,和send函数一样;


第六个参数,和send函数一样;


第七个参数,接收者ID;


第八个参数,是否广播;


第九个参数,与时间有关,以后讲解;


第十个参数,如果是静态函数,直接设置成 UNASSIGNED_OBJECT_ID


第十一个参数,保留。


 


if (rakServerInterface)
 
{
 
        // 服务器运行在端口60000处 
 
       rakServerInterface->Start(32, 0, 0, 60000);
 
 
        REGISTER_STATIC_RPC(rakServerInterface, PrintMessage); 

 
 
 
}
 
else
 
{
 
        // 运行客户端 
 
输入服务器IP地址:n");
 
       gets(str);
 
        // 127.0.0.1 designates the feedback loop so we can test on one computer
 
        if (str[0]==0)
 
               strcpy(str, "127.0.0.1");
 
       rakClientInterface->Connect(str, 60000, 0, 0, 0);          
 
       REGISTER_STATIC_RPC(rakClientInterface, PrintMessage);
 
 
 
}
 
 
 
在服务器或客户端注册RPC。
 
gets(message);
 
         
 
         if(rakServerInterface)
 
          {
 
           rakServerInterface->RPC("PrintMessage", message, (strlen(message)+1)*8, HIGH_PRIORITY, RELIABLE_ORDERED, 0,UNASSIGNED_PLAYER_ID , true, false, UNASSIGNED_NETWORK_ID,0);          }
 
         else 
 
          {
 
           rakClientInterface->RPC("PrintMessage", message, (strlen(message)+1)*8, HIGH_PRIORITY, RELIABLE_ORDERED, 0, false, UNASSIGNED_NETWORK_ID,0);
 
          }


得到信息,然后调用RPC。