FullyConnectedMesh2.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  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. /// \file FullyConnectedMesh2.h
  11. /// \brief Fully connected mesh plugin, revision 2.
  12. /// \details This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about.
  13. ///
  14. #include "NativeFeatureIncludes.h"
  15. #if _RAKNET_SUPPORT_FullyConnectedMesh2==1
  16. #ifndef __FULLY_CONNECTED_MESH_2_H
  17. #define __FULLY_CONNECTED_MESH_2_H
  18. #include "PluginInterface2.h"
  19. #include "RakMemoryOverride.h"
  20. #include "NativeTypes.h"
  21. #include "DS_List.h"
  22. #include "RakString.h"
  23. #include "BitStream.h"
  24. typedef int64_t FCM2Guid;
  25. namespace RakNet
  26. {
  27. /// Forward declarations
  28. class RakPeerInterface;
  29. /// \brief Fully connected mesh plugin, revision 2
  30. /// \details This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about.<BR>
  31. /// It will also calculate which system has been running longest, to find out who should be host, if you need one system to act as a host
  32. /// \pre You must also install the ConnectionGraph2 plugin in order to use SetConnectOnNewRemoteConnection()
  33. /// \ingroup FULLY_CONNECTED_MESH_GROUP
  34. class RAK_DLL_EXPORT FullyConnectedMesh2 : public PluginInterface2
  35. {
  36. public:
  37. // GetInstance() and DestroyInstance(instance*)
  38. STATIC_FACTORY_DECLARATIONS(FullyConnectedMesh2)
  39. FullyConnectedMesh2();
  40. virtual ~FullyConnectedMesh2();
  41. /// When the message ID_REMOTE_NEW_INCOMING_CONNECTION arrives, we try to connect to that system
  42. /// If \a attemptConnection is false, you can manually connect to all systems listed in ID_REMOTE_NEW_INCOMING_CONNECTION with ConnectToRemoteNewIncomingConnections()
  43. /// \note This will not work on any console. It will also not work if NAT punchthrough is needed. Generally, this should be false and you should connect manually. It is here for legacy reasons.
  44. /// \param[in] attemptConnection If true, we try to connect to any systems we are notified about with ID_REMOTE_NEW_INCOMING_CONNECTION, which comes from the ConnectionGraph2 plugin. Defaults to true.
  45. /// \param[in] pw The password to use to connect with. Only used if \a attemptConnection is true
  46. void SetConnectOnNewRemoteConnection(bool attemptConnection, RakNet::RakString pw);
  47. /// \brief The connected host is whichever system we are connected to that has been running the longest.
  48. /// \details Will return UNASSIGNED_RAKNET_GUID if we are not connected to anyone, or if we are connected and are calculating the host
  49. /// If includeCalculating is true, will return the estimated calculated host as long as the calculation is nearly complete
  50. /// includeCalculating should be true if you are taking action based on another system becoming host, because not all host calculations may complete at the exact same time
  51. /// \sa ConnectionGraph2::GetLowestAveragePingSystem() . If you need one system in the peer to peer group to relay data, have the host call this function after host migration, and use that system
  52. /// \return System address of whichever system is host.
  53. RakNetGUID GetConnectedHost(void) const;
  54. SystemAddress GetConnectedHostAddr(void) const;
  55. /// \return System address of whichever system is host. Always returns something, even though it may be our own system.
  56. RakNetGUID GetHostSystem(void) const;
  57. /// \return If our system is host
  58. bool IsHostSystem(void) const;
  59. /// Get the list of connected systems, from oldest connected to newest
  60. /// This is also the order that the hosts will be chosen in
  61. void GetHostOrder(DataStructures::List<RakNetGUID> &hostList);
  62. /// \param[in] includeCalculating If true, and we are currently calculating a new host, return the new host if the calculation is nearly complete
  63. /// \return If our system is host
  64. bool IsConnectedHost(void) const;
  65. /// \brief Automatically add new connections to the fully connected mesh.
  66. /// Each remote system that you want to check should be added as a participant, either through SetAutoparticipateConnections() or by calling this function
  67. /// \details Defaults to true.
  68. /// \param[in] b As stated
  69. void SetAutoparticipateConnections(bool b);
  70. /// Clear our own host order, and recalculate as if we had just reconnected
  71. /// Call this to reset the running time of the host just before joining/creating a game room for networking
  72. void ResetHostCalculation(void);
  73. /// \brief if SetAutoparticipateConnections() is called with false, then you need to use AddParticipant before these systems will be added to the mesh
  74. /// FullyConnectedMesh2 will track who is the who host among a fully connected mesh of participants
  75. /// Each remote system that you want to check should be added as a participant, either through SetAutoparticipateConnections() or by calling this function
  76. /// \param[in] participant The new participant
  77. /// \param[in] userContext Static data to be passed around with each participant, which can be queried with GetParticipantData().
  78. /// \sa StartVerifiedJoin()
  79. void AddParticipant(RakNetGUID rakNetGuid);
  80. /// Get the participants added with AddParticipant()
  81. /// \param[out] participantList Participants added with AddParticipant();
  82. void GetParticipantList(DataStructures::List<RakNetGUID> &participantList);
  83. /// \brief Returns if a participant is in the participant list
  84. /// \param[in] RakNetGUID of the participant to query
  85. /// \return True if in the list
  86. bool HasParticipant(RakNetGUID participantGuid);
  87. /// \brief Reads userData written with SetMyContext()
  88. /// \param[in] RakNetGUID of the participant to query
  89. /// \param[out] userContext Pointer to BitStream to be written to
  90. /// \return True if data was written
  91. // bool GetParticipantContext(RakNetGUID participantGuid, BitStream *userContext);
  92. /// Set data for other systems to read with GetParticipantContext
  93. /// \param[in] userContext Pointer to BitStream to be read from
  94. // void SetMyContext(BitStream *userContext);
  95. /// Connect to all systems from ID_REMOTE_NEW_INCOMING_CONNECTION
  96. /// You can call this if SetConnectOnNewRemoteConnection is false
  97. /// \param[in] packet The packet containing ID_REMOTE_NEW_INCOMING_CONNECTION
  98. /// \param[in] connectionPassword Password passed to RakPeerInterface::Connect()
  99. /// \param[in] connectionPasswordLength Password length passed to RakPeerInterface::Connect()
  100. void ConnectToRemoteNewIncomingConnections(Packet *packet);
  101. /// \brief Clear all memory and reset everything
  102. void Clear(void);
  103. unsigned int GetParticipantCount(void) const;
  104. void GetParticipantCount(unsigned int *participantListSize) const;
  105. /// In the simple case of forming a peer to peer mesh:
  106. ///
  107. /// 1. AddParticipant() is called on the host whenever you get a new connection
  108. /// 2. The host sends all participants to the new client
  109. /// 3. The client connects to the participant list
  110. ///
  111. /// However, the above steps assumes connections to all systems in the mesh always complete.
  112. /// When there is a risk of failure, such as if relying on NATPunchthroughClient, you may not want to call AddParticipant() until are connections have completed to all other particpants
  113. /// StartVerifiedJoin() can manage the overhead of the negotiation involved so the programmer only has to deal with overall success or failure
  114. ///
  115. /// Processing:
  116. /// 1. Send the RakNetGUID and SystemAddress values of GetParticipantList() to the client with ID_FCM2_VERIFIED_JOIN_START
  117. /// 2. The client, on ID_FCM2_VERIFIED_JOIN_START, can execute NatPunchthroughClient::OpenNAT() (optional), followed by RakPeerInterface::Connect() if punchthrough success, for each system returned from GetVerifiedJoinRequiredProcessingList()
  118. /// 3. After all participants in step 2 have connected, failed to connect, or failed NatPunchthrough, the client automatically sends the results to the server.
  119. /// 4. The server compares the results of the operations in step 2 with the values from GetParticpantList().
  120. /// 4A. If the client failed to connect to a current participant, return ID_FCM2_VERIFIED_JOIN_FAILED to the client. CloseConnection() is automatically called on the client for the failed participants.
  121. /// 4B. If AddParticipant() was called between steps 1 and 4, go back to step 1, transmitting new participants.
  122. /// 4C. If the client successfully connected to all participants, the server gets ID_FCM2_VERIFIED_JOIN_CAPABLE. The server programmer, on the same frame, should execute RespondOnVerifiedJoinCapable() to either accept or reject the client.
  123. /// 5. If the client got ID_FCM2_VERIFIED_JOIN_ACCEPTED, AddParticipant() is automatically called for each system in the mesh.
  124. /// 6. If the client got ID_FCM2_VERIFIED_JOIN_REJECTED, CloseConnection() is automatically called for each system in the mesh. The connection is NOT automatically closed to the original host that sent StartVerifiedJoin().
  125. /// 7. If the client's connection to the server was lost before getting ID_FCM2_VERIFIED_JOIN_ACCEPTED or ID_FCM2_VERIFIED_JOIN_REJECTED, return to the programmer ID_FCM2_VERIFIED_JOIN_FAILED and call RakPeerInterface::CloseConnection()
  126. ///
  127. /// \brief Notify the client of GetParticipantList() in order to connect to each of those systems until the mesh has been completed
  128. /// \param[in] client The system to send ID_FCM2_VERIFIED_JOIN_START to
  129. virtual void StartVerifiedJoin(RakNetGUID client);
  130. /// \brief On ID_FCM2_VERIFIED_JOIN_CAPABLE , accept or reject the new connection
  131. /// \code
  132. /// fullyConnectedMesh->RespondOnVerifiedJoinCapable(packet, true, 0);
  133. /// \endcode
  134. /// \param[in] packet The system that sent ID_FCM2_VERIFIED_JOIN_CAPABLE. Based on \accept, ID_FCM2_VERIFIED_JOIN_ACCEPTED or ID_FCM2_VERIFIED_JOIN_REJECTED will be sent in reply
  135. /// \param[in] accept True to accept, and thereby automatically call AddParticipant() on all systems on the mesh. False to reject, and call CloseConnection() to all mesh systems on the target
  136. /// \param[in] additionalData Any additional data you want to add to the ID_FCM2_VERIFIED_JOIN_ACCEPTED or ID_FCM2_VERIFIED_JOIN_REJECTED messages
  137. /// \sa WriteVJCUserData()
  138. virtual void RespondOnVerifiedJoinCapable(Packet *packet, bool accept, BitStream *additionalData);
  139. /// \brief On ID_FCM2_VERIFIED_JOIN_START, read the SystemAddress and RakNetGUID values of each system to connect to
  140. /// \code
  141. /// DataStructures::List<SystemAddress> addresses;
  142. /// DataStructures::List<RakNetGUID> guids;
  143. /// fullyConnectedMesh->GetVerifiedJoinRequiredProcessingList(packet->guid, addresses, guids);
  144. /// for (unsigned int i=0; i < addresses.Size(); i++)
  145. /// rakPeer[i]->Connect(addresses[i].ToString(false), addresses[i].GetPort(), 0, 0);
  146. /// \endcode
  147. /// \param[in] host Which system sent ID_FCM2_VERIFIED_JOIN_START
  148. /// \param[out] addresses SystemAddress values of systems to connect to. List has the same number and order as \a guids
  149. /// \param[out] guids RakNetGUID values of systems to connect to. List has the same number and order as \a guids
  150. /// \param[out] userData What was written with WriteVJSUserData
  151. virtual void GetVerifiedJoinRequiredProcessingList(RakNetGUID host,
  152. DataStructures::List<SystemAddress> &addresses,
  153. DataStructures::List<RakNetGUID> &guids,
  154. DataStructures::List<BitStream*> &userData);
  155. /// \brief On ID_FCM2_VERIFIED_JOIN_ACCEPTED, read additional data passed to RespondOnVerifiedJoinCapable()
  156. /// \code
  157. /// bool thisSystemAccepted;
  158. /// DataStructures::List<RakNetGUID> systemsAccepted;
  159. /// RakNet::BitStream additionalData;
  160. /// fullyConnectedMesh->GetVerifiedJoinAcceptedAdditionalData(packet, &thisSystemAccepted, systemsAccepted, &additionalData);
  161. /// \endcode
  162. /// \param[in] packet Packet containing the ID_FCM2_VERIFIED_JOIN_ACCEPTED message
  163. /// \param[out] thisSystemAccepted If true, it was this instance of RakPeerInterface that was accepted. If false, this is notification for another system
  164. /// \param[out] systemsAccepted Which system(s) were added with AddParticipant(). If \a thisSystemAccepted is false, this list will only have length 1
  165. /// \param[out] additionalData \a additionalData parameter passed to RespondOnVerifiedJoinCapable()
  166. virtual void GetVerifiedJoinAcceptedAdditionalData(Packet *packet, bool *thisSystemAccepted, DataStructures::List<RakNetGUID> &systemsAccepted, BitStream *additionalData);
  167. /// \brief On ID_FCM2_VERIFIED_JOIN_REJECTED, read additional data passed to RespondOnVerifiedJoinCapable()
  168. /// \details This does not automatically close the connection. The following code will do so:
  169. /// \code
  170. /// rakPeer[i]->CloseConnection(packet->guid, true);
  171. /// \endcode
  172. /// \param[in] packet Packet containing the ID_FCM2_VERIFIED_JOIN_REJECTED message
  173. /// \param[out] additionalData \a additionalData parameter passed to RespondOnVerifiedJoinCapable().
  174. virtual void GetVerifiedJoinRejectedAdditionalData(Packet *packet, BitStream *additionalData);
  175. /// Override to write data when ID_FCM2_VERIFIED_JOIN_CAPABLE is sent
  176. virtual void WriteVJCUserData(RakNet::BitStream *bsOut) {(void) bsOut;}
  177. /// Use to read data written from WriteVJCUserData()
  178. /// \code
  179. /// RakNet::BitStream bsIn(packet->data,packet->length,false);
  180. /// FullyConnectedMesh2::SkipToVJCUserData(&bsIn);
  181. /// // Your code here
  182. static void SkipToVJCUserData(RakNet::BitStream *bsIn);
  183. /// Write custom user data to be sent with ID_FCM2_VERIFIED_JOIN_START, per user
  184. /// \param[out] bsOut Write your data here, if any. Has to match what is read by ReadVJSUserData
  185. /// \param[in] userGuid The RakNetGuid of the user you are writing for
  186. /// \param[in] userContext The data set with SetMyContext() for that system. May be empty. To properly write userContext, you will need to first write userContext->GetNumberOfBitsUsed(), followed by bsOut->Write(userContext);
  187. //virtual void WriteVJSUserData(RakNet::BitStream *bsOut, RakNetGUID userGuid, BitStream *userContext) {(void) bsOut; (void) userGuid; (void) userContext;}
  188. virtual void WriteVJSUserData(RakNet::BitStream *bsOut, RakNetGUID userGuid) {(void) bsOut; (void) userGuid;}
  189. /// \internal
  190. RakNet::TimeUS GetElapsedRuntime(void);
  191. /// \internal
  192. virtual PluginReceiveResult OnReceive(Packet *packet);
  193. /// \internal
  194. virtual void OnRakPeerStartup(void);
  195. /// \internal
  196. virtual void OnAttach(void);
  197. /// \internal
  198. virtual void OnRakPeerShutdown(void);
  199. /// \internal
  200. virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason );
  201. /// \internal
  202. virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming);
  203. /// \internal
  204. virtual void OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason);
  205. /// \internal
  206. struct FCM2Participant
  207. {
  208. FCM2Participant() {}
  209. FCM2Participant(const FCM2Guid &_fcm2Guid, const RakNetGUID &_rakNetGuid) : fcm2Guid(_fcm2Guid), rakNetGuid(_rakNetGuid) {}
  210. // Low half is a random number.
  211. // High half is the order we connected in (totalConnectionCount)
  212. FCM2Guid fcm2Guid;
  213. RakNetGUID rakNetGuid;
  214. // BitStream userContext;
  215. };
  216. enum JoinInProgressState
  217. {
  218. JIPS_PROCESSING,
  219. JIPS_FAILED,
  220. JIPS_CONNECTED,
  221. JIPS_UNNECESSARY,
  222. };
  223. struct VerifiedJoinInProgressMember
  224. {
  225. SystemAddress systemAddress;
  226. RakNetGUID guid;
  227. JoinInProgressState joinInProgressState;
  228. BitStream *userData;
  229. bool workingFlag;
  230. };
  231. /// \internal
  232. struct VerifiedJoinInProgress
  233. {
  234. RakNetGUID requester;
  235. DataStructures::List<VerifiedJoinInProgressMember> vjipMembers;
  236. //bool sentResults;
  237. };
  238. /// \internal for debugging
  239. unsigned int GetTotalConnectionCount(void) const;
  240. protected:
  241. void PushNewHost(const RakNetGUID &guid, RakNetGUID oldHost);
  242. void SendOurFCMGuid(SystemAddress addr);
  243. void SendFCMGuidRequest(RakNetGUID rakNetGuid);
  244. void SendConnectionCountResponse(SystemAddress addr, unsigned int responseTotalConnectionCount);
  245. void OnRequestFCMGuid(Packet *packet);
  246. //void OnUpdateUserContext(Packet *packet);
  247. void OnRespondConnectionCount(Packet *packet);
  248. void OnInformFCMGuid(Packet *packet);
  249. void OnUpdateMinTotalConnectionCount(Packet *packet);
  250. void AssignOurFCMGuid(void);
  251. void CalculateHost(RakNetGUID *rakNetGuid, FCM2Guid *fcm2Guid);
  252. // bool AddParticipantInternal( RakNetGUID rakNetGuid, FCM2Guid theirFCMGuid, BitStream *userContext );
  253. bool AddParticipantInternal( RakNetGUID rakNetGuid, FCM2Guid theirFCMGuid );
  254. void CalculateAndPushHost(void);
  255. bool ParticipantListComplete(void);
  256. void IncrementTotalConnectionCount(unsigned int i);
  257. PluginReceiveResult OnVerifiedJoinStart(Packet *packet);
  258. PluginReceiveResult OnVerifiedJoinCapable(Packet *packet);
  259. virtual void OnVerifiedJoinFailed(RakNetGUID hostGuid, bool callCloseConnection);
  260. virtual void OnVerifiedJoinAccepted(Packet *packet);
  261. virtual void OnVerifiedJoinRejected(Packet *packet);
  262. unsigned int GetJoinsInProgressIndex(RakNetGUID requester) const;
  263. void UpdateVerifiedJoinInProgressMember(const AddressOrGUID systemIdentifier, RakNetGUID guidToAssign, JoinInProgressState newState);
  264. bool ProcessVerifiedJoinInProgressIfCompleted(VerifiedJoinInProgress *vjip);
  265. void ReadVerifiedJoinInProgressMember(RakNet::BitStream *bsIn, VerifiedJoinInProgressMember *vjipm);
  266. unsigned int GetVerifiedJoinInProgressMemberIndex(const AddressOrGUID systemIdentifier, VerifiedJoinInProgress *vjip);
  267. void DecomposeJoinCapable(Packet *packet, VerifiedJoinInProgress *vjip);
  268. void WriteVerifiedJoinCapable(RakNet::BitStream *bsOut, VerifiedJoinInProgress *vjip);
  269. void CategorizeVJIP(VerifiedJoinInProgress *vjip,
  270. DataStructures::List<RakNetGUID> &participatingMembersOnClientSucceeded,
  271. DataStructures::List<RakNetGUID> &participatingMembersOnClientFailed,
  272. DataStructures::List<RakNetGUID> &participatingMembersNotOnClient,
  273. DataStructures::List<RakNetGUID> &clientMembersNotParticipatingSucceeded,
  274. DataStructures::List<RakNetGUID> &clientMembersNotParticipatingFailed);
  275. // Used to track how long RakNet has been running. This is so we know who has been running longest
  276. RakNet::TimeUS startupTime;
  277. // Option for SetAutoparticipateConnections
  278. bool autoParticipateConnections;
  279. // totalConnectionCount is roughly maintained across all systems, and increments by 1 each time a new system connects to the mesh
  280. // It is always kept at the highest known value
  281. // It is used as the high 4 bytes for new FCMGuids. This causes newer values of FCM2Guid to be higher than lower values. The lowest value is the host.
  282. unsigned int totalConnectionCount;
  283. // Our own ourFCMGuid. Starts at unassigned (0). Assigned once we send ID_FCM2_REQUEST_FCMGUID and get back ID_FCM2_RESPOND_CONNECTION_COUNT
  284. FCM2Guid ourFCMGuid;
  285. /// List of systems we know the FCM2Guid for
  286. DataStructures::List<FCM2Participant*> fcm2ParticipantList;
  287. RakNetGUID lastPushedHost;
  288. // Optimization: Store last calculated host in these variables.
  289. RakNetGUID hostRakNetGuid;
  290. FCM2Guid hostFCM2Guid;
  291. RakNet::RakString connectionPassword;
  292. bool connectOnNewRemoteConnections;
  293. DataStructures::List<VerifiedJoinInProgress*> joinsInProgress;
  294. BitStream myContext;
  295. };
  296. } // namespace RakNet
  297. /*
  298. Startup()
  299. ourFCMGuid=unknown
  300. totalConnectionCount=0
  301. Set startupTime
  302. AddParticipant()
  303. if (sender by guid is a participant)
  304. return;
  305. AddParticipantInternal(guid);
  306. if (ourFCMGuid==unknown)
  307. Send to that system a request for their fcmGuid, totalConnectionCount. Inform startupTime.
  308. else
  309. Send to that system a request for their fcmGuid. Inform total connection count, our fcmGuid
  310. OnRequestGuid()
  311. if (sender by guid is not a participant)
  312. {
  313. // They added us as a participant, but we didn't add them. This can be caused by lag where both participants are not added at the same time.
  314. // It doesn't affect the outcome as long as we still process the data
  315. AddParticipantInternal(guid);
  316. }
  317. if (ourFCMGuid==unknown)
  318. {
  319. if (includedStartupTime)
  320. {
  321. // Nobody has a fcmGuid
  322. if (their startup time is greater than our startup time)
  323. ReplyConnectionCount(1);
  324. else
  325. ReplyConnectionCount(2);
  326. }
  327. else
  328. {
  329. // They have a fcmGuid, we do not
  330. SetMaxTotalConnectionCount(remoteCount);
  331. AssignTheirGuid()
  332. GenerateOurGuid();
  333. SendOurGuid(all);
  334. }
  335. }
  336. else
  337. {
  338. if (includedStartupTime)
  339. {
  340. // We have a fcmGuid they do not
  341. ReplyConnectionCount(totalConnectionCount+1);
  342. SendOurGuid(sender);
  343. }
  344. else
  345. {
  346. // We both have fcmGuids
  347. SetMaxTotalConnectionCount(remoteCount);
  348. AssignTheirGuid();
  349. SendOurGuid(sender);
  350. }
  351. }
  352. OnReplyConnectionCount()
  353. SetMaxTotalConnectionCount(remoteCount);
  354. GenerateOurGuid();
  355. SendOurGuid(allParticipants);
  356. OnReceiveTheirGuid()
  357. AssignTheirGuid()
  358. */
  359. #endif
  360. #endif // _RAKNET_SUPPORT_*
粤ICP备19079148号