VariableDeltaSerializer.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. * Copyright (c) 2014, Oculus VR, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. #ifndef __VARIABLE_DELTA_SERIALIZER_H
  11. #define __VARIABLE_DELTA_SERIALIZER_H
  12. #include "VariableListDeltaTracker.h"
  13. #include "DS_MemoryPool.h"
  14. #include "NativeTypes.h"
  15. #include "BitStream.h"
  16. #include "PacketPriority.h"
  17. #include "DS_OrderedList.h"
  18. namespace RakNet
  19. {
  20. /// \brief Class to compare memory values of variables in a current state to a prior state
  21. /// Results of the comparisons will be written to a bitStream, such that only changed variables get written<BR>
  22. /// Can be used with ReplicaManager3 to Serialize a Replica3 per-variable, rather than comparing the entire object against itself<BR>
  23. /// Usage:<BR>
  24. ///<BR>
  25. /// 1. Call BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize(). In the case of Replica3, this would be in the Serialize() call<BR>
  26. /// 2. For each variable of the type in step 1, call Serialize(). The same variables must be serialized every tick()<BR>
  27. /// 3. Call EndSerialize()<BR>
  28. /// 4. Repeat step 1 for each of the other categories of how to send varaibles<BR>
  29. ///<BR>
  30. /// On the receiver:<BR>
  31. ///<BR>
  32. /// 1. Call BeginDeserialize(). In the case of Replica3, this would be in the Deserialize() call<BR>
  33. /// 2. Call DeserializeVariable() for each variable, in the same order as was Serialized()<BR>
  34. /// 3. Call EndSerialize()<BR>
  35. /// \sa The ReplicaManager3 sample
  36. class RAK_DLL_EXPORT VariableDeltaSerializer
  37. {
  38. protected:
  39. struct RemoteSystemVariableHistory;
  40. struct ChangedVariablesList;
  41. public:
  42. VariableDeltaSerializer();
  43. ~VariableDeltaSerializer();
  44. struct SerializationContext
  45. {
  46. SerializationContext();
  47. ~SerializationContext();
  48. RakNetGUID guid;
  49. BitStream *bitStream;
  50. uint32_t rakPeerSendReceipt;
  51. RemoteSystemVariableHistory *variableHistory;
  52. RemoteSystemVariableHistory *variableHistoryIdentical;
  53. RemoteSystemVariableHistory *variableHistoryUnique;
  54. ChangedVariablesList *changedVariables;
  55. uint32_t sendReceipt;
  56. PacketReliability serializationMode;
  57. bool anyVariablesWritten;
  58. bool newSystemSend; // Force send all, do not record
  59. };
  60. struct DeserializationContext
  61. {
  62. BitStream *bitStream;
  63. };
  64. /// \brief Call before doing one or more SerializeVariable calls when the data will be sent UNRELIABLE_WITH_ACK_RECEIPT
  65. /// The last value of each variable will be saved per remote system. Additionally, a history of \a _sendReceipts is stored to determine what to resend on packetloss.
  66. /// When variables are lost, they will be flagged dirty and always resent to the system that lost it
  67. /// Disadvantages: Every variable for every remote system is copied internally, in addition to a history list of what variables changed for which \a _sendReceipt. Very memory and CPU intensive for multiple connections.
  68. /// Advantages: When data needs to be resent by RakNet, RakNet can only resend the value it currently has. This allows the application to control the resend, sending the most recent value of the variable. The end result is that bandwidth is used more efficiently because old data is never sent.
  69. /// \pre Upon getting ID_SND_RECEIPT_LOSS or ID_SND_RECEIPT_ACKED call OnMessageReceipt()
  70. /// \pre AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() must be called for new and lost connections
  71. /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function.
  72. /// \param[in] _guid Which system we are sending to
  73. /// \param[in] _bitSteam Which bitStream to write to
  74. /// \param[in] _sendReceipt Returned from RakPeer::IncrementNextSendReceipt() and passed to the Send() or SendLists() function. Identifies this update for ID_SND_RECEIPT_LOSS and ID_SND_RECEIPT_ACKED
  75. void BeginUnreliableAckedSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream, uint32_t _sendReceipt);
  76. /// \brief Call before doing one or more SerializeVariable calls for data that may be sent differently to every remote system (such as an invisibility flag that only teammates can see)
  77. /// The last value of each variable will be saved per remote system.
  78. /// Unlike BeginUnreliableAckedSerialize(), send receipts are not necessary
  79. /// Disadvantages: Every variable for every remote system is copied internally. Very memory and CPU intensive for multiple connections.
  80. /// Advantages: When data is sent differently depending on the recipient, this system can make things easier to use and is as efficient as it can be.
  81. /// \pre AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() must be called for new and lost connections
  82. /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function.
  83. /// \param[in] _guid Which system we are sending to
  84. /// \param[in] _bitSteam Which bitStream to write to
  85. void BeginUniqueSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream);
  86. /// \brief Call before doing one or more SerializeVariable calls for data that is sent with the same value to every remote system (such as health, position, etc.)
  87. /// This is the most common type of serialization, and also the most efficient
  88. /// Disadvantages: A copy of every variable still needs to be held, although only once
  89. /// Advantages: After the first serialization, the last serialized bitStream will be used for subsequent sends
  90. /// \pre Call OnPreSerializeTick() before doing any calls to BeginIdenticalSerialize() for each of your objects, once per game tick
  91. /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function.
  92. /// \param[in] _isFirstSerializeToThisSystem Pass true if this is the first time ever serializing to this system (the initial download). This way all variables will be written, rather than checking against prior sent values.
  93. /// \param[in] _bitSteam Which bitStream to write to
  94. void BeginIdenticalSerialize(SerializationContext *context, bool _isFirstSerializeToThisSystem, BitStream *_bitStream);
  95. /// \brief Call after BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize(), then after calling SerializeVariable() one or more times
  96. /// \param[in] context Same context pointer passed to BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize()
  97. void EndSerialize(SerializationContext *context);
  98. /// \brief Call when you receive the BitStream written by SerializeVariable(), before calling DeserializeVariable()
  99. /// \param[in] context Holds the context of this group of deserialize calls. This can be a stack object just passed to the function.
  100. /// \param[in] _bitStream Pass the bitStream originally passed to and written to by serialize calls
  101. void BeginDeserialize(DeserializationContext *context, BitStream *_bitStream);
  102. /// \param[in] context Same context pointer passed to BeginDeserialize()
  103. void EndDeserialize(DeserializationContext *context);
  104. /// BeginUnreliableAckedSerialize() and BeginUniqueSerialize() require knowledge of when connections are added and dropped
  105. /// Call AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() to notify the system of these events
  106. /// \param[in] _guid Which system we are sending to
  107. void AddRemoteSystemVariableHistory(RakNetGUID guid);
  108. /// BeginUnreliableAckedSerialize() and BeginUniqueSerialize() require knowledge of when connections are added and dropped
  109. /// Call AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() to notify the system of these events
  110. /// \param[in] _guid Which system we are sending to
  111. void RemoveRemoteSystemVariableHistory(RakNetGUID guid);
  112. /// BeginIdenticalSerialize() requires knowledge of when serialization has started for an object across multiple systems
  113. /// This way it can setup the flag to do new comparisons against the last sent values, rather than just resending the last sent bitStream
  114. /// For Replica3, overload and call this from Replica3::OnUserReplicaPreSerializeTick()
  115. void OnPreSerializeTick(void);
  116. /// Call when getting ID_SND_RECEIPT_LOSS or ID_SND_RECEIPT_ACKED for a particular system
  117. /// Example:
  118. ///
  119. /// uint32_t msgNumber;
  120. /// memcpy(&msgNumber, packet->data+1, 4);
  121. /// DataStructures::List<Replica3*> replicaListOut;
  122. /// replicaManager.GetReplicasCreatedByMe(replicaListOut);
  123. /// unsigned int idx;
  124. /// for (idx=0; idx < replicaListOut.GetSize(); idx++)
  125. /// {
  126. /// ((SampleReplica*)replicaListOut[idx])->NotifyReplicaOfMessageDeliveryStatus(packet->guid,msgNumber, packet->data[0]==ID_SND_RECEIPT_ACKED);
  127. /// }
  128. ///
  129. /// \param[in] guid Which system we are sending to
  130. /// \param[in] receiptId Encoded in bytes 1-4 inclusive of ID_SND_RECEIPT_LOSS and ID_SND_RECEIPT_ACKED
  131. /// \param[in] messageArrived True for ID_SND_RECEIPT_ACKED, false otherwise
  132. void OnMessageReceipt(RakNetGUID guid, uint32_t receiptId, bool messageArrived);
  133. /// Call to Serialize a variable
  134. /// Will write to the bitSteam passed to \a context true, variableValue if the variable has changed or has never been written. Otherwise will write false.
  135. /// \pre You have called BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize()
  136. /// \pre Will also require calling OnPreSerializeTick() if using BeginIdenticalSerialize()
  137. /// \note Be sure to call EndSerialize() after finishing all serializations
  138. /// \param[in] context Same context pointer passed to BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize()
  139. /// \param[in] variable A variable to write to the bitStream passed to \a context
  140. template <class VarType>
  141. void SerializeVariable(SerializationContext *context, const VarType &variable)
  142. {
  143. if (context->newSystemSend)
  144. {
  145. if (context->variableHistory->variableListDeltaTracker.IsPastEndOfList()==false)
  146. {
  147. // previously sent data to another system
  148. context->bitStream->Write(true);
  149. context->bitStream->Write(variable);
  150. context->anyVariablesWritten=true;
  151. }
  152. else
  153. {
  154. // never sent data to another system
  155. context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream);
  156. context->anyVariablesWritten=true;
  157. }
  158. }
  159. else if (context->serializationMode==UNRELIABLE_WITH_ACK_RECEIPT)
  160. {
  161. context->anyVariablesWritten|=
  162. context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream, context->changedVariables->bitField, context->changedVariables->bitWriteIndex++);
  163. }
  164. else
  165. {
  166. if (context->variableHistoryIdentical)
  167. {
  168. // Identical serialization to a number of systems
  169. if (didComparisonThisTick==false)
  170. context->anyVariablesWritten|=
  171. context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream);
  172. // Else bitstream is written to at the end
  173. }
  174. else
  175. {
  176. // Per-system serialization
  177. context->anyVariablesWritten|=
  178. context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream);
  179. }
  180. }
  181. }
  182. /// Call to deserialize into a variable
  183. /// \pre You have called BeginDeserialize()
  184. /// \note Be sure to call EndDeserialize() after finishing all deserializations
  185. /// \param[in] context Same context pointer passed to BeginDeserialize()
  186. /// \param[in] variable A variable to write to the bitStream passed to \a context
  187. template <class VarType>
  188. bool DeserializeVariable(DeserializationContext *context, VarType &variable)
  189. {
  190. return VariableListDeltaTracker::ReadVarFromBitstream(variable, context->bitStream);
  191. }
  192. protected:
  193. // For a given send receipt from RakPeer::Send() track which variables we updated
  194. // That way if that send does not arrive (ID_SND_RECEIPT_LOSS) we can mark those variables as dirty to resend them with current values
  195. struct ChangedVariablesList
  196. {
  197. uint32_t sendReceipt;
  198. unsigned short bitWriteIndex;
  199. unsigned char bitField[56];
  200. };
  201. // static int Replica2ObjectComp( const uint32_t &key, ChangedVariablesList* const &data );
  202. static int UpdatedVariablesListPtrComp( const uint32_t &key, ChangedVariablesList* const &data );
  203. // For each remote system, track the last values of variables we sent to them, and the history of what values changed per call to Send()
  204. // Every serialize if a variable changes from its last value, send it out again
  205. // Also if a send does not arrive (ID_SND_RECEIPT_LOSS) we use updatedVariablesHistory to mark those variables as dirty, to resend them unreliably with the current values
  206. struct RemoteSystemVariableHistory
  207. {
  208. RakNetGUID guid;
  209. VariableListDeltaTracker variableListDeltaTracker;
  210. DataStructures::OrderedList<uint32_t,ChangedVariablesList*,VariableDeltaSerializer::UpdatedVariablesListPtrComp> updatedVariablesHistory;
  211. };
  212. /// A list of RemoteSystemVariableHistory indexed by guid, one per connection that we serialize to
  213. /// List is added to when SerializeConstruction is called, and removed from when SerializeDestruction is called, or when a given connection is dropped
  214. DataStructures::List<RemoteSystemVariableHistory*> remoteSystemVariableHistoryList;
  215. // Because the ChangedVariablesList is created every serialize and destroyed every receipt I use a pool to avoid fragmentation
  216. DataStructures::MemoryPool<ChangedVariablesList> updatedVariablesMemoryPool;
  217. bool didComparisonThisTick;
  218. RakNet::BitStream identicalSerializationBs;
  219. void FreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId);
  220. void DirtyAndFreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId);
  221. unsigned int GetVarsWrittenPerRemoteSystemListIndex(RakNetGUID guid);
  222. void RemoveRemoteSystemVariableHistory(void);
  223. RemoteSystemVariableHistory* GetRemoteSystemVariableHistory(RakNetGUID guid);
  224. ChangedVariablesList *AllocChangedVariablesList(void);
  225. void FreeChangedVariablesList(ChangedVariablesList *changedVariables);
  226. void StoreChangedVariablesList(RemoteSystemVariableHistory *variableHistory, ChangedVariablesList *changedVariables, uint32_t sendReceipt);
  227. RemoteSystemVariableHistory *StartVariableHistoryWrite(RakNetGUID guid);
  228. unsigned int GetRemoteSystemHistoryListIndex(RakNetGUID guid);
  229. };
  230. }
  231. #endif
粤ICP备19079148号