1 // Demonstrates ReplicaManager 3: A system to automatically create, destroy, and serialize objects
2
3 #include "StringTable.h"
4 #include "RakPeerInterface.h"
5 #include "RakNetworkFactory.h"
6 #include <stdio.h>
7 #include "Kbhit.h"
8 #include <string.h>
9 #include "BitStream.h"
10 #include "MessageIdentifiers.h"
11 #include "ReplicaManager3.h"
12 #include "NetworkIDManager.h"
13 #include "RakSleep.h"
14 #include "FormatString.h"
15 #include "RakString.h"
16 #include "GetTime.h"
17 #include "SocketLayer.h"
18 #include "Getche.h"
19 #include "Rand.h"
20 #include "VariableDeltaSerializer.h"
21
22 enum
23 {
24 CLIENT,
25 SERVER,
26 P2P
27 } topology;
28
29 // ReplicaManager3 is in the namespace RakNet
30 using namespace RakNet;
31
32 struct SampleReplica : public Replica3
33 {
SampleReplicaSampleReplica34 SampleReplica() {var1Unreliable=0; var2Unreliable=0; var3Reliable=0; var4Reliable=0;}
~SampleReplicaSampleReplica35 ~SampleReplica() {}
36 virtual RakNet::RakString GetName(void) const=0;
WriteAllocationIDSampleReplica37 virtual void WriteAllocationID(RakNet::BitStream *allocationIdBitstream) const {
38 allocationIdBitstream->Write(GetName());
39 }
PrintStringInBitstreamSampleReplica40 void PrintStringInBitstream(RakNet::BitStream *bs)
41 {
42 if (bs->GetNumberOfBitsUsed()==0)
43 return;
44 RakNet::RakString rakString;
45 bs->Read(rakString);
46 printf("Receive: %s\n", rakString.C_String());
47 }
SerializeConstructionSampleReplica48 virtual void SerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {
49
50 // variableDeltaSerializer is a helper class that tracks what variables were sent to what remote system
51 // This call adds another remote system to track
52 variableDeltaSerializer.AddRemoteSystemVariableHistory(destinationConnection->GetRakNetGUID());
53
54 constructionBitstream->Write(GetName() + RakNet::RakString(" SerializeConstruction"));
55 }
DeserializeConstructionSampleReplica56 virtual bool DeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {
57 PrintStringInBitstream(constructionBitstream);
58 return true;
59 }
SerializeDestructionSampleReplica60 virtual void SerializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *destinationConnection) {
61
62 // variableDeltaSerializer is a helper class that tracks what variables were sent to what remote system
63 // This call removes a remote system
64 variableDeltaSerializer.RemoveRemoteSystemVariableHistory(destinationConnection->GetRakNetGUID());
65
66 destructionBitstream->Write(GetName() + RakNet::RakString(" SerializeDestruction"));
67
68 }
DeserializeDestructionSampleReplica69 virtual bool DeserializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *sourceConnection) {
70 PrintStringInBitstream(destructionBitstream);
71 return true;
72 }
DeallocReplicaSampleReplica73 virtual void DeallocReplica(RakNet::Connection_RM3 *sourceConnection) {
74 delete this;
75 }
76
77 /// Overloaded Replica3 function
OnUserReplicaPreSerializeTickSampleReplica78 virtual void OnUserReplicaPreSerializeTick(void)
79 {
80 /// Required by VariableDeltaSerializer::BeginIdenticalSerialize()
81 variableDeltaSerializer.OnPreSerializeTick();
82 }
83
SerializeSampleReplica84 virtual RM3SerializationResult Serialize(SerializeParameters *serializeParameters) {
85
86 VariableDeltaSerializer::SerializationContext serializationContext;
87
88 // Put all variables to be sent unreliably on the same channel, then specify the send type for that channel
89 serializeParameters->pro[0].reliability=UNRELIABLE_WITH_ACK_RECEIPT;
90 // Sending unreliably with an ack receipt requires the receipt number, and that you inform the system of ID_SND_RECEIPT_ACKED and ID_SND_RECEIPT_LOSS
91 serializeParameters->pro[0].sendReceipt=replicaManager->GetRakPeerInterface()->IncrementNextSendReceipt();
92 serializeParameters->messageTimestamp=RakNet::GetTime();
93
94 // Begin writing all variables to be sent UNRELIABLE_WITH_ACK_RECEIPT
95 variableDeltaSerializer.BeginUnreliableAckedSerialize(
96 &serializationContext,
97 serializeParameters->destinationConnection->GetRakNetGUID(),
98 &serializeParameters->outputBitstream[0],
99 serializeParameters->pro[0].sendReceipt
100 );
101 // Write each variable
102 variableDeltaSerializer.SerializeVariable(&serializationContext, var1Unreliable);
103 // Write each variable
104 variableDeltaSerializer.SerializeVariable(&serializationContext, var2Unreliable);
105 // Tell the system this is the last variable to be written
106 variableDeltaSerializer.EndSerialize(&serializationContext);
107
108 // All variables to be sent using a different mode go on different channels
109 serializeParameters->pro[1].reliability=RELIABLE_ORDERED;
110
111 // Same as above, all variables to be sent with a particular reliability are sent in a batch
112 // We use BeginIdenticalSerialize instead of BeginSerialize because the reliable variables have the same values sent to all systems. This is memory-saving optimization
113 variableDeltaSerializer.BeginIdenticalSerialize(
114 &serializationContext,
115 serializeParameters->whenLastSerialized==0,
116 &serializeParameters->outputBitstream[1]
117 );
118 variableDeltaSerializer.SerializeVariable(&serializationContext, var3Reliable);
119 variableDeltaSerializer.SerializeVariable(&serializationContext, var4Reliable);
120 variableDeltaSerializer.EndSerialize(&serializationContext);
121
122 // This return type makes is to ReplicaManager3 itself does not do a memory compare. we entirely control serialization ourselves here.
123 // Use RM3SR_SERIALIZED_ALWAYS instead of RM3SR_SERIALIZED_ALWAYS_IDENTICALLY to support sending different data to different system, which is needed when using unreliable and dirty variable resends
124 return RM3SR_SERIALIZED_ALWAYS;
125 }
DeserializeSampleReplica126 virtual void Deserialize(RakNet::DeserializeParameters *deserializeParameters) {
127
128 VariableDeltaSerializer::DeserializationContext deserializationContext;
129
130 // Deserialization is written similar to serialization
131 // Note that the Serialize() call above uses two different reliability types. This results in two separate Send calls
132 // So Deserialize is potentially called twice from a single Serialize
133 variableDeltaSerializer.BeginDeserialize(&deserializationContext, &deserializeParameters->serializationBitstream[0]);
134 if (variableDeltaSerializer.DeserializeVariable(&deserializationContext, var1Unreliable))
135 printf("var1Unreliable changed to %i\n", var1Unreliable);
136 if (variableDeltaSerializer.DeserializeVariable(&deserializationContext, var2Unreliable))
137 printf("var2Unreliable changed to %i\n", var2Unreliable);
138 variableDeltaSerializer.EndDeserialize(&deserializationContext);
139
140 variableDeltaSerializer.BeginDeserialize(&deserializationContext, &deserializeParameters->serializationBitstream[1]);
141 if (variableDeltaSerializer.DeserializeVariable(&deserializationContext, var3Reliable))
142 printf("var3Reliable changed to %i\n", var3Reliable);
143 if (variableDeltaSerializer.DeserializeVariable(&deserializationContext, var4Reliable))
144 printf("var4Reliable changed to %i\n", var4Reliable);
145 variableDeltaSerializer.EndDeserialize(&deserializationContext);
146 }
147
SerializeConstructionRequestAcceptedSampleReplica148 virtual void SerializeConstructionRequestAccepted(RakNet::BitStream *serializationBitstream, RakNet::Connection_RM3 *requestingConnection) {
149 serializationBitstream->Write(GetName() + RakNet::RakString(" SerializeConstructionRequestAccepted"));
150 }
DeserializeConstructionRequestAcceptedSampleReplica151 virtual void DeserializeConstructionRequestAccepted(RakNet::BitStream *serializationBitstream, RakNet::Connection_RM3 *acceptingConnection) {
152 PrintStringInBitstream(serializationBitstream);
153 }
SerializeConstructionRequestRejectedSampleReplica154 virtual void SerializeConstructionRequestRejected(RakNet::BitStream *serializationBitstream, RakNet::Connection_RM3 *requestingConnection) {
155 serializationBitstream->Write(GetName() + RakNet::RakString(" SerializeConstructionRequestRejected"));
156 }
DeserializeConstructionRequestRejectedSampleReplica157 virtual void DeserializeConstructionRequestRejected(RakNet::BitStream *serializationBitstream, RakNet::Connection_RM3 *rejectingConnection) {
158 PrintStringInBitstream(serializationBitstream);
159 }
160
OnPoppedConnectionSampleReplica161 void OnPoppedConnection(RakNet::Connection_RM3 *droppedConnection)
162 {
163 // Same as in SerializeDestruction(), no longer track this system
164 variableDeltaSerializer.RemoveRemoteSystemVariableHistory(droppedConnection->GetRakNetGUID());
165 }
NotifyReplicaOfMessageDeliveryStatusSampleReplica166 void NotifyReplicaOfMessageDeliveryStatus(RakNetGUID guid, uint32_t receiptId, bool messageArrived)
167 {
168 // When using UNRELIABLE_WITH_ACK_RECEIPT, the system tracks which variables were updated with which sends
169 // So it is then necessary to inform the system of messages arriving or lost
170 // Lost messages will flag each variable sent in that update as dirty, meaning the next Serialize() call will resend them with the current values
171 variableDeltaSerializer.OnMessageReceipt(guid,receiptId,messageArrived);
172 }
RandomizeVariablesSampleReplica173 void RandomizeVariables(void)
174 {
175 if (randomMT()%2)
176 {
177 var1Unreliable=randomMT();
178 printf("var1Unreliable changed to %i\n", var1Unreliable);
179 }
180 if (randomMT()%2)
181 {
182 var2Unreliable=randomMT();
183 printf("var2Unreliable changed to %i\n", var2Unreliable);
184 }
185 if (randomMT()%2)
186 {
187 var3Reliable=randomMT();
188 printf("var3Reliable changed to %i\n", var3Reliable);
189 }
190 if (randomMT()%2)
191 {
192 var4Reliable=randomMT();
193 printf("var4Reliable changed to %i\n", var4Reliable);
194 }
195 }
196
197 // Demonstrate per-variable synchronization
198 // We manually test each variable to the last synchronized value and only send those values that change
199 int var1Unreliable,var2Unreliable,var3Reliable,var4Reliable;
200
201 // Class to save and compare the states of variables this Serialize() to the last Serialize()
202 // If the value is different, true is written to the bitStream, followed by the value. Otherwise false is written.
203 // It also tracks which variables changed which Serialize() call, so if an unreliable message was lost (ID_SND_RECEIPT_LOSS) those variables are flagged 'dirty' and resent
204 VariableDeltaSerializer variableDeltaSerializer;
205 };
206
207 struct ClientCreatible_ClientSerialized : public SampleReplica {
GetNameClientCreatible_ClientSerialized208 virtual RakNet::RakString GetName(void) const {return RakNet::RakString("ClientCreatible_ClientSerialized");}
SerializeClientCreatible_ClientSerialized209 virtual RM3SerializationResult Serialize(SerializeParameters *serializeParameters)
210 {
211 return SampleReplica::Serialize(serializeParameters);
212 }
QueryConstructionClientCreatible_ClientSerialized213 virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {
214 return QueryConstruction_ClientConstruction(destinationConnection);
215 }
QueryRemoteConstructionClientCreatible_ClientSerialized216 virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection) {
217 return QueryRemoteConstruction_ClientConstruction(sourceConnection);
218 }
219
QuerySerializationClientCreatible_ClientSerialized220 virtual RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection) {
221 return QuerySerialization_ClientSerializable(destinationConnection);
222 }
QueryActionOnPopConnectionClientCreatible_ClientSerialized223 virtual RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const {
224 return QueryActionOnPopConnection_Client(droppedConnection);
225 }
226 };
227 struct ServerCreated_ClientSerialized : public SampleReplica {
GetNameServerCreated_ClientSerialized228 virtual RakNet::RakString GetName(void) const {return RakNet::RakString("ServerCreated_ClientSerialized");}
SerializeServerCreated_ClientSerialized229 virtual RM3SerializationResult Serialize(SerializeParameters *serializeParameters)
230 {
231 return SampleReplica::Serialize(serializeParameters);
232 }
QueryConstructionServerCreated_ClientSerialized233 virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {
234 return QueryConstruction_ServerConstruction(destinationConnection);
235 }
QueryRemoteConstructionServerCreated_ClientSerialized236 virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection) {
237 return QueryRemoteConstruction_ServerConstruction(sourceConnection);
238 }
QuerySerializationServerCreated_ClientSerialized239 virtual RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection) {
240 return QuerySerialization_ClientSerializable(destinationConnection);
241 }
QueryActionOnPopConnectionServerCreated_ClientSerialized242 virtual RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const {
243 return QueryActionOnPopConnection_Server(droppedConnection);
244 }
245 };
246 struct ClientCreatible_ServerSerialized : public SampleReplica {
GetNameClientCreatible_ServerSerialized247 virtual RakNet::RakString GetName(void) const {return RakNet::RakString("ClientCreatible_ServerSerialized");}
SerializeClientCreatible_ServerSerialized248 virtual RM3SerializationResult Serialize(SerializeParameters *serializeParameters)
249 {
250 if (topology==CLIENT)
251 return RM3SR_DO_NOT_SERIALIZE;
252 return SampleReplica::Serialize(serializeParameters);
253 }
QueryConstructionClientCreatible_ServerSerialized254 virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {
255 return QueryConstruction_ClientConstruction(destinationConnection);
256 }
QueryRemoteConstructionClientCreatible_ServerSerialized257 virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection) {
258 return QueryRemoteConstruction_ClientConstruction(sourceConnection);
259 }
QuerySerializationClientCreatible_ServerSerialized260 virtual RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection) {
261 return QuerySerialization_ServerSerializable(destinationConnection);
262 }
QueryActionOnPopConnectionClientCreatible_ServerSerialized263 virtual RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const {
264 return QueryActionOnPopConnection_Client(droppedConnection);
265 }
266 };
267 struct ServerCreated_ServerSerialized : public SampleReplica {
GetNameServerCreated_ServerSerialized268 virtual RakNet::RakString GetName(void) const {return RakNet::RakString("ServerCreated_ServerSerialized");}
SerializeServerCreated_ServerSerialized269 virtual RM3SerializationResult Serialize(SerializeParameters *serializeParameters)
270 {
271 if (topology==CLIENT)
272 return RM3SR_DO_NOT_SERIALIZE;
273
274 return SampleReplica::Serialize(serializeParameters);
275 }
QueryConstructionServerCreated_ServerSerialized276 virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {
277 return QueryConstruction_ServerConstruction(destinationConnection);
278 }
QueryRemoteConstructionServerCreated_ServerSerialized279 virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection) {
280 return QueryRemoteConstruction_ServerConstruction(sourceConnection);
281 }
QuerySerializationServerCreated_ServerSerialized282 virtual RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection) {
283 return QuerySerialization_ServerSerializable(destinationConnection);
284 }
QueryActionOnPopConnectionServerCreated_ServerSerialized285 virtual RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const {
286 return QueryActionOnPopConnection_Server(droppedConnection);
287 }
288 };
289 struct P2PReplica : public SampleReplica {
GetNameP2PReplica290 virtual RakNet::RakString GetName(void) const {return RakNet::RakString("P2PReplica");}
QueryConstructionP2PReplica291 virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {
292 return QueryConstruction_PeerToPeer(destinationConnection);
293 }
QueryRemoteConstructionP2PReplica294 virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection) {
295 return QueryRemoteConstruction_PeerToPeer(sourceConnection);
296 }
QuerySerializationP2PReplica297 virtual RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection) {
298 return QuerySerialization_PeerToPeer(destinationConnection);
299 }
QueryActionOnPopConnectionP2PReplica300 virtual RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const {
301 return QueryActionOnPopConnection_PeerToPeer(droppedConnection);
302 }
303 };
304
305 class SampleConnection : public Connection_RM3 {
306 public:
SampleConnection(SystemAddress _systemAddress,RakNetGUID _guid)307 SampleConnection(SystemAddress _systemAddress, RakNetGUID _guid) : Connection_RM3(_systemAddress, _guid) {}
~SampleConnection()308 virtual ~SampleConnection() {}
309
310 // See documentation - Makes all messages between ID_REPLICA_MANAGER_DOWNLOAD_STARTED and ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE arrive in one tick
QueryGroupDownloadMessages(void) const311 bool QueryGroupDownloadMessages(void) const {return true;}
312
AllocReplica(RakNet::BitStream * allocationId,ReplicaManager3 * replicaManager3)313 virtual Replica3 *AllocReplica(RakNet::BitStream *allocationId, ReplicaManager3 *replicaManager3)
314 {
315 RakNet::RakString typeName;
316 allocationId->Read(typeName);
317 if (typeName=="ClientCreatible_ClientSerialized") return new ClientCreatible_ClientSerialized;
318 if (typeName=="ServerCreated_ClientSerialized") return new ServerCreated_ClientSerialized;
319 if (typeName=="ClientCreatible_ServerSerialized") return new ClientCreatible_ServerSerialized;
320 if (typeName=="ServerCreated_ServerSerialized") return new ServerCreated_ServerSerialized;
321 if (typeName=="P2PReplica") return new P2PReplica;
322 return 0;
323 }
324 protected:
325 };
326
327 class ReplicaManager3Sample : public ReplicaManager3
328 {
AllocConnection(SystemAddress systemAddress,RakNetGUID rakNetGUID) const329 virtual Connection_RM3* AllocConnection(SystemAddress systemAddress, RakNetGUID rakNetGUID) const {
330 return new SampleConnection(systemAddress,rakNetGUID);
331 }
DeallocConnection(Connection_RM3 * connection) const332 virtual void DeallocConnection(Connection_RM3 *connection) const {
333 delete connection;
334 }
335 };
336
main(void)337 int main(void)
338 {
339 char ch;
340 SocketDescriptor sd;
341 char ip[128];
342 static const int SERVER_PORT=12345;
343
344 // ReplicaManager3 requires NetworkIDManager to lookup pointers from numbers.
345 NetworkIDManager networkIdManager;
346 // Each application has one instance of RakPeerInterface
347 RakPeerInterface *rakPeer;
348 // The system that performs most of our functionality for this demo
349 ReplicaManager3Sample replicaManager;
350
351
352 printf("Demonstration of ReplicaManager3.\n");
353 printf("1. Demonstrates creating objects created by the server and client.\n");
354 printf("2. Demonstrates automatic serialization data members\n");
355 printf("Difficulty: Intermediate\n\n");
356
357 printf("Start as (c)lient, (s)erver, (p)eer? ");
358 ch=getche();
359
360 rakPeer = RakNetworkFactory::GetRakPeerInterface();
361 if (ch=='c' || ch=='C')
362 {
363 topology=CLIENT;
364 sd.port=0;
365 }
366 else if (ch=='s' || ch=='S')
367 {
368 topology=SERVER;
369 sd.port=SERVER_PORT;
370 }
371 else
372 {
373 topology=P2P;
374 sd.port=SERVER_PORT;
375 while (SocketLayer::IsPortInUse(sd.port)==true)
376 sd.port++;
377 }
378
379 // ObjectMemberRPC, AutoRPC for objects, and ReplicaManager3 require that you call SetNetworkIDManager()
380 rakPeer->SetNetworkIDManager(&networkIdManager);
381 // The network ID authority is the system that creates the common numerical identifier used to lookup pointers.
382 // For client/server this is the server
383 // For peer to peer this would be true on every system, and NETWORK_ID_SUPPORTS_PEER_TO_PEER should be defined in RakNetDefines.h
384 networkIdManager.SetIsNetworkIDAuthority(topology==SERVER||topology==P2P);
385 // Start RakNet, up to 32 connections if the server
386 rakPeer->Startup(32,100,&sd,1);
387 rakPeer->AttachPlugin(&replicaManager);
388 rakPeer->SetMaximumIncomingConnections(32);
389
390 printf("\n");
391 if (topology==CLIENT)
392 {
393 printf("Enter server IP: ");
394 gets(ip);
395 if (ip[0]==0)
396 strcpy(ip, "127.0.0.1");
397 rakPeer->Connect(ip,SERVER_PORT,0,0,0);
398 printf("Connecting...\n");
399 }
400
401 printf("Commands:\n(Q)uit\n'C'reate objects\n'R'andomly change variables in my objects\n'D'estroy my objects\n");
402
403 // Enter infinite loop to run the system
404 Packet *packet;
405 bool quit=false;
406 while (!quit)
407 {
408 for (packet = rakPeer->Receive(); packet; rakPeer->DeallocatePacket(packet), packet = rakPeer->Receive())
409 {
410 switch (packet->data[0])
411 {
412 case ID_CONNECTION_ATTEMPT_FAILED:
413 printf("ID_CONNECTION_ATTEMPT_FAILED\n");
414 quit=true;
415 break;
416 case ID_NO_FREE_INCOMING_CONNECTIONS:
417 printf("ID_NO_FREE_INCOMING_CONNECTIONS\n");
418 quit=true;
419 break;
420 case ID_CONNECTION_REQUEST_ACCEPTED:
421 printf("ID_CONNECTION_REQUEST_ACCEPTED\n");
422 break;
423 case ID_NEW_INCOMING_CONNECTION:
424 printf("ID_NEW_INCOMING_CONNECTION from %s\n", packet->systemAddress.ToString());
425 break;
426 case ID_DISCONNECTION_NOTIFICATION:
427 printf("ID_DISCONNECTION_NOTIFICATION\n");
428 break;
429 case ID_CONNECTION_LOST:
430 printf("ID_CONNECTION_LOST\n");
431 break;
432 case ID_ADVERTISE_SYSTEM:
433 // The conditional is needed because ID_ADVERTISE_SYSTEM may be from a system we are connected to, but replying on a different address.
434 if (rakPeer->GetSystemAddressFromGuid(packet->guid)==UNASSIGNED_SYSTEM_ADDRESS)
435 {
436 printf("Connecting to %s\n", packet->systemAddress.ToString(true));
437 rakPeer->Connect(packet->systemAddress.ToString(false), packet->systemAddress.port,0,0);
438 }
439 break;
440 case ID_SND_RECEIPT_LOSS:
441 case ID_SND_RECEIPT_ACKED:
442 {
443 uint32_t msgNumber;
444 memcpy(&msgNumber, packet->data+1, 4);
445
446 DataStructures::Multilist<ML_STACK, Replica3*> replicaListOut;
447 replicaManager.GetReplicasCreatedByMe(replicaListOut);
448 DataStructures::DefaultIndexType idx;
449 for (idx=0; idx < replicaListOut.GetSize(); idx++)
450 {
451 ((SampleReplica*)replicaListOut[idx])->NotifyReplicaOfMessageDeliveryStatus(packet->guid,msgNumber, packet->data[0]==ID_SND_RECEIPT_ACKED);
452 }
453 }
454 break;
455 }
456 }
457
458 if (kbhit())
459 {
460 ch=getch();
461 if (ch=='q' || ch=='Q')
462 {
463 printf("Quitting.\n");
464 quit=true;
465 }
466 if (ch=='c' || ch=='C')
467 {
468 printf("Objects created.\n");
469 if (topology==SERVER||topology==CLIENT)
470 {
471 replicaManager.Reference(new ClientCreatible_ClientSerialized);
472 replicaManager.Reference(new ServerCreated_ClientSerialized);
473 replicaManager.Reference(new ClientCreatible_ServerSerialized);
474 replicaManager.Reference(new ServerCreated_ServerSerialized);
475 }
476 else
477 {
478 // for (int i=0; i < 20; i++)
479 replicaManager.Reference(new P2PReplica);
480 }
481 }
482 if (ch=='r' || ch=='R')
483 {
484 DataStructures::Multilist<ML_STACK, Replica3*> replicaListOut;
485 replicaManager.GetReplicasCreatedByMe(replicaListOut);
486 DataStructures::DefaultIndexType idx;
487 for (idx=0; idx < replicaListOut.GetSize(); idx++)
488 {
489 ((SampleReplica*)replicaListOut[idx])->RandomizeVariables();
490 }
491 }
492 if (ch=='d' || ch=='D')
493 {
494 printf("My objects destroyed.\n");
495 DataStructures::Multilist<ML_STACK, Replica3*> replicaListOut;
496 // The reason for ClearPointers is that in the sample, I don't track which objects have and have not been allocated at the application level. So ClearPointers will call delete on every object in the returned list, which is every object that the application has created. Another way to put it is
497 // A. Send a packet to tell other systems to delete these objects
498 // B. Delete these objects on my own system
499 replicaManager.GetReplicasCreatedByMe(replicaListOut);
500 replicaManager.BroadcastDestructionList(replicaListOut, UNASSIGNED_SYSTEM_ADDRESS);
501 replicaListOut.ClearPointers( true, __FILE__, __LINE__ );
502 }
503
504 }
505
506 RakSleep(30);
507 for (int i=0; i < 32; i++)
508 {
509 if (rakPeer->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS,0).port!=SERVER_PORT+i)
510 rakPeer->AdvertiseSystem("255.255.255.255", SERVER_PORT+i, 0,0,0);
511 }
512 }
513
514 rakPeer->Shutdown(100,0);
515 RakNetworkFactory::DestroyRakPeerInterface(rakPeer);
516 }
517