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