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