1 #include "RPC3.h"
2 #include "RakPeerInterface.h"
3 #include "RakNetworkFactory.h"
4 #include <stdio.h>
5 #include "Kbhit.h"
6 #include <string.h>
7 #include <stdlib.h>
8 #include "BitStream.h"
9 #include "MessageIdentifiers.h"
10 #include "StringCompressor.h"
11 #include "RakSleep.h"
12 #include "NetworkIDObject.h"
13 #include "NetworkIDManager.h"
14 #include "GetTime.h"
15
16 // This has to be a pointer, because it uses UNASSIGNED_NETWORK_ID, initialized globally
17 RakNet::RPC3 *rpc3Inst;
18
19 struct NormalizedVector
20 {
21 public:
NormalizedVectorNormalizedVector22 NormalizedVector() {x=1.0f; y=0.0f; z=0.0f;}
23 float x,y,z;
24 };
25
26 // Specialize the << and >> operator to serialize each element of NormalizedVector individually
27 // This allows us to endian swap each parameter (which could not otherwise happen) and also send the data in a compressed form
operator <<(RakNet::BitStream & out,NormalizedVector & in)28 RakNet::BitStream& operator<<(RakNet::BitStream& out, NormalizedVector& in)
29 {
30 out.WriteNormVector(in.x,in.y,in.z);
31 return out;
32 }
operator >>(RakNet::BitStream & in,NormalizedVector & out)33 RakNet::BitStream& operator>>(RakNet::BitStream& in, NormalizedVector& out)
34 {
35 bool success = in.ReadNormVector(out.x,out.y,out.z);
36 assert(success);
37 return in;
38 }
39
40 class C;
41 class D;
42
43 class A {
A()44 public: A() {a=1;} int a;};
45 class B {
46 public:
B()47 B() {b=2;} int b;
48 virtual void ClassMemberFunc(A *a1, A &a2, C *c1, D *d1, RakNet::BitStream *bs1, RakNet::BitStream &bs2, RakNet::RPC3 *rpc3Inst);
49 };
50 class C : public A, public B, public NetworkIDObject {
51 public:
C()52 C() {c=3;} int c;
53 virtual void ClassMemberFunc(A *a1, A &a2, C *c1, D *d1, RakNet::BitStream *bs1, RakNet::BitStream &bs2, RakNet::RPC3 *rpc3Inst);
54 void ClassMemberFunc2(RakNet::RPC3 *rpc3Inst);
TestSlot(void)55 virtual void TestSlot(void) {printf("C::TestSlot\n");}
56 };
57
58 class D : public B, public NetworkIDObject {
59 public:
D()60 D() {for (int i=0; i < 10; i++) tenBytes[i]=i;}
61 char tenBytes[10];
Verify(void)62 bool Verify(void) {for (int i=0; i < 10; i++) if (tenBytes[i]!=i) return false; return true;}
TestSlot(void)63 virtual void TestSlot(void) {printf("D::TestSlot\n");}
64 };
65
66 // The number of parameters for a C++ function is limited by BOOST_FUSION_INVOKE_MAX_ARITY-1 found in boost/fusion/functional/invocation/limits.hpp
67 // I define this in RPC3_Boost.h to be 10. The default is 6.
68 // rpcFromNetwork is automatically filled in from the RPC class. Pass 0 when calling locally. Will be set to the plugin instance when this function is called from the remote system
ClassMemberFunc(A * a1,A & a2,C * c1,D * d1,RakNet::BitStream * bs1,RakNet::BitStream & bs2,RakNet::RPC3 * rpcFromNetwork)69 void B::ClassMemberFunc(A *a1, A &a2, C *c1, D *d1, RakNet::BitStream *bs1, RakNet::BitStream &bs2, RakNet::RPC3 *rpcFromNetwork){
70 if (rpcFromNetwork==0)
71 printf("\nB::ClassMemberFunc called locally\n");
72 else
73 printf("\nB::ClassMemberFunc called from %s\n", rpcFromNetwork->GetLastSenderAddress().ToString());
74 printf("a1=%i a2=%i c1=%i\n", a1->a, a2.a, c1->c);
75 printf("d1::Verify=%i\n", d1->Verify());
76 RakNet::RakString rs1, rs2;
77 bs1->Read(rs1);
78 bs2.Read(rs2);
79 printf("rs1=%s\n", rs1.C_String());
80 printf("rs2=%s\n", rs2.C_String());
81 printf("rpc3Inst=%p\n", rpc3Inst);
82 }
83
84 // C and D derive from networkIDObject, so cannot be passed as references. A pointer is required to do the object lookup
ClassMemberFunc(A * a1,A & a2,C * c1,D * d1,RakNet::BitStream * bs1,RakNet::BitStream & bs2,RakNet::RPC3 * rpcFromNetwork)85 void C::ClassMemberFunc(A *a1, A &a2, C *c1, D *d1, RakNet::BitStream *bs1, RakNet::BitStream &bs2, RakNet::RPC3 *rpcFromNetwork) {
86 printf("\nC::ClassMemberFunc\n");
87 B::ClassMemberFunc(a1,a2,c1,d1,bs1,bs2,rpcFromNetwork);
88
89 if (rpcFromNetwork==0)
90 {
91 // The RakNet::RPC3 * parameter can be passed to Call() if you want to - it is skipped and not serialized or deserialized so it doesn't matter.
92 // The point of it is so when this function is called on the remote system, it is set to the instance of the plugin so you can query network parameters from the caller
93 //
94 // By default, pointers to objects that derive from NetworkIDObject (classes C and D), only transmit the NetworkID of the object.
95 // If you also want to dereference the pointer and serialize the object itself, use RakNet::_RPC3::Deref(myVariable)
96 // In this case, parameters that both derive from NetworkIDObject and are pointers are the variables c1 and d1
97 // c1 will only transmit c1->GetNetworkID() (default behavior)
98 // d1 will transmit d1->GetNetworkID() and also bitStream << (*d1) (contents of the pointer)
99 //
100 rpc3Inst->CallCPP("&C::ClassMemberFunc", GetNetworkID(), a1,a2,c1,RakNet::_RPC3::Deref(d1),bs1,bs2,rpcFromNetwork);
101 }
102 }
103
ClassMemberFunc2(RakNet::RPC3 * rpcFromNetwork)104 void C::ClassMemberFunc2(RakNet::RPC3 *rpcFromNetwork) {
105 printf("\nC::ClassMemberFunc2\n");
106
107 if (rpcFromNetwork==0)
108 {
109 // The RakNet::RPC3 * parameter can be passed to Call() if you want to - it is skipped and not serialized or deserialized so it doesn't matter.
110 // The point of it is so when this function is called on the remote system, it is set to the instance of the plugin so you can query network parameters from the caller
111 //
112 // By default, pointers to objects that derive from NetworkIDObject (classes C and D), only transmit the NetworkID of the object.
113 // If you also want to dereference the pointer and serialize the object itself, use RakNet::_RPC3::Deref(myVariable)
114 // In this case, parameters that both derive from NetworkIDObject and are pointers are the variables c1 and d1
115 // c1 will only transmit c1->GetNetworkID() (default behavior)
116 // d1 will transmit d1->GetNetworkID() and also bitStream << (*d1) (contents of the pointer)
117 //
118 rpc3Inst->CallCPP("&C::ClassMemberFunc2", GetNetworkID(), rpcFromNetwork);
119 }
120 }
121
122 // The number of parameters for a C function is limited by BOOST_FUSION_INVOKE_MAX_ARITY found in boost/fusion/functional/invocation/limits.hpp
123 // I define this in RPC3_Boost.h to be 9. The default is 6.
CFunc(RakNet::RakString rakString,int intArray[10],C * c1,const char * str,NormalizedVector * nv1,NormalizedVector & nv2,RakNet::RPC3 * rpcFromNetwork)124 void CFunc(RakNet::RakString rakString, int intArray[10], C *c1, const char *str, NormalizedVector *nv1, NormalizedVector &nv2, RakNet::RPC3 *rpcFromNetwork )
125 {
126 // We pass 0 to the rpcFromNetwork when calling this function locally. When it is called by the RPC3 system, it is set to the address of the plugin
127 if (rpcFromNetwork==0)
128 printf("\nCFunc called locally\n");
129 else
130 printf("\nCFunc called from %s\n", rpcFromNetwork->GetLastSenderAddress().ToString());
131 printf("rakString=%s\n", rakString.C_String());
132 printf("intArray = ");
133 for (int i=0; i < 10; i++)
134 printf("%i ", intArray[i]);
135 printf("\n");
136 printf("c1=%i\n", c1->c);
137 printf("str=%s\n", str);
138 printf("nv1->x=%f nv1->y=%f nv1->z=%f\n", nv1->x, nv1->y, nv1->z);
139 printf("nv2.x=%f nv2.y=%f nv2.z=%f\n", nv2.x, nv2.y, nv2.z);
140 printf("rpc3Inst=%p\n", rpc3Inst);
141
142 // the parameter "int intArray[10]" is actually a pointer due to the design of C and C++
143 // The RakNet::_RPC3::PtrToArray() function will tell the RPC3 system that this is actually an array of n elements
144 // Each element will be endian swapped appropriately
145 if (rpcFromNetwork==0)
146 rpc3Inst->CallC("CFunc", rakString,RakNet::_RPC3::PtrToArray(10,intArray),c1,str,nv1,nv2,rpcFromNetwork);
147 }
main(void)148 int main(void)
149 {
150 printf("Demonstration of the RPC3 plugin.\n");
151 printf("It is similar to Raknet's RPC system, but automatically\n");
152 printf("serializes and deserializes the parameters to the function call\n");
153 printf("Difficulty: Intermediate\n\n");
154
155 DataStructures::OrderedList<int,int> ol;
156 ol.Insert(3,3,false,__FILE__,__LINE__);
157 ol.Insert(4,4,false,__FILE__,__LINE__);
158 ol.Insert(5,5,false,__FILE__,__LINE__);
159 ol.Insert(4,4,false,__FILE__,__LINE__);
160 bool objectExists;
161 int idx = ol.GetIndexFromKey(4,&objectExists);
162
163 RakPeerInterface *rakPeer;
164 SystemAddress tempAddr = UNASSIGNED_SYSTEM_ADDRESS;
165 A a;
166 B b;
167 C c;
168 D d;
169
170 NormalizedVector normalizedVector;
171 RakNetTime stage2=0;
172 NetworkIDManager networkIDManager;
173 networkIDManager.SetIsNetworkIDAuthority(true);
174 rpc3Inst = new RakNet::RPC3;
175 rpc3Inst->SetNetworkIDManager(&networkIDManager);
176 NetworkID idZero, idOne;
177 idZero.localSystemAddress=0;
178 idOne.localSystemAddress=1;
179 rpc3Inst->SetNetworkIDManager(&networkIDManager);
180 c.SetNetworkIDManager(&networkIDManager);
181 d.SetNetworkIDManager(&networkIDManager);
182 c.SetNetworkID(idZero);
183 d.SetNetworkID(idOne);
184
185 // Register a regular C function, and a class member function
186 // Unlike AutoRPC, you don't have to specify if it is C or C++, or the number of parameters
187 RPC3_REGISTER_FUNCTION(rpc3Inst, CFunc);
188 // Note the & operator as the macro and RPC3::RegisterFunction takes a class pointer
189 RPC3_REGISTER_FUNCTION(rpc3Inst, &C::ClassMemberFunc);
190 RPC3_REGISTER_FUNCTION(rpc3Inst, &C::ClassMemberFunc2);
191
192 // All slots are called when a signal is sent. This is true whether the slot is local or remote
193 rpc3Inst->RegisterSlot("TestSlot",&C::TestSlot, c.GetNetworkID(), 0);
194 rpc3Inst->RegisterSlot("TestSlot",&D::TestSlot, d.GetNetworkID(), 0);
195
196 printf("(S)erver or (C)lient?: ");
197 bool isServer;
198 char str[256];
199 gets(str);
200 if (str[0]=='s' || str[0]=='S')
201 isServer=true;
202 else
203 isServer=false;
204
205 rakPeer = RakNetworkFactory::GetRakPeerInterface();
206 if (isServer)
207 {
208 SocketDescriptor socketDescriptor(50000,0);
209 rakPeer->Startup(10, 30, &socketDescriptor, 1);
210 rakPeer->SetMaximumIncomingConnections(10);
211 printf("Server started.\n");
212 }
213 else
214 {
215 SocketDescriptor socketDescriptor(0,0);
216 rakPeer->Startup(1, 30, &socketDescriptor, 1);
217
218 // Send out a LAN broadcast to find other instances on the same computer
219 rakPeer->Ping( "255.255.255.255", 50000, true, 0 );
220
221 printf("Client started. Will automatically connect to running servers.\n");
222 }
223 rakPeer->AttachPlugin(rpc3Inst);
224
225 Packet *p;
226 while (1)
227 {
228 for (p=rakPeer->Receive(); p; rakPeer->DeallocatePacket(p), p=rakPeer->Receive())
229 {
230 switch (p->data[0])
231 {
232 case ID_DISCONNECTION_NOTIFICATION:
233 printf("ID_DISCONNECTION_NOTIFICATION\n");
234 break;
235 case ID_ALREADY_CONNECTED:
236 printf("ID_ALREADY_CONNECTED\n");
237 break;
238 case ID_CONNECTION_ATTEMPT_FAILED:
239 printf("Connection attempt failed\n");
240 break;
241 case ID_NO_FREE_INCOMING_CONNECTIONS:
242 printf("ID_NO_FREE_INCOMING_CONNECTIONS\n");
243 break;
244 case ID_PONG:
245 // Found the server
246 rakPeer->Connect(p->systemAddress.ToString(false),p->systemAddress.port,0,0,0);
247 break;
248 case ID_CONNECTION_REQUEST_ACCEPTED:
249 // This tells the client they have connected
250 printf("ID_CONNECTION_REQUEST_ACCEPTED\n");
251 break;
252 case ID_NEW_INCOMING_CONNECTION:
253 {
254 RakNet::BitStream testBitStream1, testBitStream2;
255 testBitStream1.Write("Hello World 1");
256 testBitStream2.Write("Hello World 2");
257 c.ClassMemberFunc(&a,a,&c,&d,&testBitStream1,testBitStream2,0);
258 c.ClassMemberFunc2(0);
259 RakNet::RakString rs("RakString test");
260 int intArray[10];
261 for (int i=0; i < sizeof(intArray)/sizeof(int); i++)
262 intArray[i]=i;
263 CFunc(rs, intArray,&c,"Test string",&normalizedVector,normalizedVector,0);
264 stage2=RakNet::GetTime()+500;
265 break;
266 }
267 case ID_RPC_REMOTE_ERROR:
268 {
269 // Recipient system returned an error
270 switch (p->data[1])
271 {
272 case RakNet::RPC_ERROR_NETWORK_ID_MANAGER_UNAVAILABLE:
273 printf("RPC_ERROR_NETWORK_ID_MANAGER_UNAVAILABLE\n");
274 break;
275 case RakNet::RPC_ERROR_OBJECT_DOES_NOT_EXIST:
276 printf("RPC_ERROR_OBJECT_DOES_NOT_EXIST\n");
277 break;
278 case RakNet::RPC_ERROR_FUNCTION_INDEX_OUT_OF_RANGE:
279 printf("RPC_ERROR_FUNCTION_INDEX_OUT_OF_RANGE\n");
280 break;
281 case RakNet::RPC_ERROR_FUNCTION_NOT_REGISTERED:
282 printf("RPC_ERROR_FUNCTION_NOT_REGISTERED\n");
283 break;
284 case RakNet::RPC_ERROR_FUNCTION_NO_LONGER_REGISTERED:
285 printf("RPC_ERROR_FUNCTION_NO_LONGER_REGISTERED\n");
286 break;
287 case RakNet::RPC_ERROR_CALLING_CPP_AS_C:
288 printf("RPC_ERROR_CALLING_CPP_AS_C\n");
289 break;
290 case RakNet::RPC_ERROR_CALLING_C_AS_CPP:
291 printf("RPC_ERROR_CALLING_C_AS_CPP\n");
292 break;
293 }
294 printf("Function: %s", p->data+2);
295 }
296 }
297 }
298
299 if (stage2 && stage2 < RakNet::GetTime())
300 {
301 stage2=0;
302
303 RakNet::BitStream testBitStream1, testBitStream2;
304 testBitStream1.Write("Hello World 1 (2)");
305 testBitStream2.Write("Hello World 2 (2)");
306 c.ClassMemberFunc(&a,a,&c,&d,&testBitStream1,testBitStream2,0);
307 RakNet::RakString rs("RakString test (2)");
308 int intArray[10];
309 for (int i=0; i < sizeof(intArray)/sizeof(int); i++)
310 intArray[i]=i;
311 CFunc(rs, intArray,&c,"Test string (2)",&normalizedVector,normalizedVector,0);
312 rpc3Inst->Signal("TestSlot");
313 }
314
315 RakSleep(0);
316 }
317
318 rakPeer->Shutdown(100,0);
319 RakNetworkFactory::DestroyRakPeerInterface(rakPeer);
320 delete rpc3Inst;
321
322 return 1;
323 }
324