TeamManager.h 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  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. // TODO: optimize the list of teams and team members to be O(1). Store in hashes, use linked lists to get ordered traversal
  11. /// \file TeamManager.h
  12. /// \brief Automates networking and list management for teams
  13. /// \details TeamManager provides support for teams. A team is a list of team members.
  14. /// Teams contain properties including the number of team members per team, whether or not tagged teams must have equal numbers of members, and if a team is locked or not to certain entry conditions
  15. /// Team members contain properties including which teams they are on and which teams they want to join if a team is not immediately joinable
  16. /// Advanced functionality includes the ability for a team member to be on multiple teams simultaneously, the ability to swap teams with other members, and the ability to resize the number of members supported per team
  17. ///
  18. #include "NativeFeatureIncludes.h"
  19. #if _RAKNET_SUPPORT_TeamManager==1
  20. #ifndef __TEAM_MANAGER_H
  21. #define __TEAM_MANAGER_H
  22. #include "PluginInterface2.h"
  23. #include "RakMemoryOverride.h"
  24. #include "NativeTypes.h"
  25. #include "DS_List.h"
  26. #include "RakNetTypes.h"
  27. #include "DS_Hash.h"
  28. #include "DS_OrderedList.h"
  29. namespace RakNet
  30. {
  31. /// Forward declarations
  32. class RakPeerInterface;
  33. /// \defgroup TEAM_MANAGER_GROUP TeamManager
  34. /// \brief Automates networking and list management for teams
  35. /// \details When used with ReplicaManager3 and FullyConnectedMesh2, provides a complete solution to managing a distributed list of teams and team member objects with support for host migration.
  36. /// \ingroup PLUGINS_GROUP
  37. /// \ingroup TEAM_MANAGER_GROUP
  38. /// \brief A subcategory of not being on a team. For example, 0 may mean no team for a player, while 1 may mean no team for a spectator. Defined by the user.
  39. typedef unsigned char NoTeamId;
  40. /// \ingroup TEAM_MANAGER_GROUP
  41. /// Used for multiple worlds.
  42. typedef uint8_t WorldId;
  43. /// \ingroup TEAM_MANAGER_GROUP
  44. /// Maximum number of members on one team. Use 65535 for unlimited.
  45. typedef uint16_t TeamMemberLimit;
  46. /// Allow members to join this team when they specify TeamSelection::JOIN_ANY_AVAILABLE_TEAM
  47. #define ALLOW_JOIN_ANY_AVAILABLE_TEAM (1<<0)
  48. /// Allow members to join this team when they specify TeamSelection::JOIN_SPECIFIC_TEAM
  49. #define ALLOW_JOIN_SPECIFIC_TEAM (1<<1)
  50. /// Allow the host to put members on this team when rebalancing with TM_World::SetBalanceTeams()
  51. #define ALLOW_JOIN_REBALANCING (1<<2)
  52. // Bitwise combination of ALLOW_JOIN_ANY_AVAILABLE_TEAM, ALLOW_JOIN_SPECIFIC_TEAM, ALLOW_JOIN_REBALANCING
  53. typedef uint8_t JoinPermissions;
  54. // Forward declarations
  55. class TM_Team;
  56. class TM_TeamMember;
  57. class TM_World;
  58. class TeamManager;
  59. /// \ingroup TEAM_MANAGER_GROUP
  60. enum JoinTeamType
  61. {
  62. /// Attempt to join the first available team.
  63. JOIN_ANY_AVAILABLE_TEAM,
  64. /// Attempt to join a specific team, previously added with TM_World::ReferenceTeam()
  65. JOIN_SPECIFIC_TEAM,
  66. /// No team. Always succeeds.
  67. JOIN_NO_TEAM
  68. };
  69. /// \ingroup TEAM_MANAGER_GROUP
  70. enum TMTopology
  71. {
  72. // Each system will send all messages to all participants
  73. TM_PEER_TO_PEER,
  74. // The host will relay incoming messages to all participants
  75. TM_CLIENT_SERVER,
  76. };
  77. /// \brief Parameter to TM_World::ReferenceTeamMember()
  78. /// \details Use TeamSelection::AnyAvailable(), TeamSelection::SpecificTeam(), or TeamSelection::NoTeam()
  79. /// \ingroup TEAM_MANAGER_GROUP
  80. struct TeamSelection
  81. {
  82. TeamSelection();
  83. TeamSelection(JoinTeamType itt);
  84. TeamSelection(JoinTeamType itt, TM_Team *param);
  85. TeamSelection(JoinTeamType itt, NoTeamId param);
  86. JoinTeamType joinTeamType;
  87. union
  88. {
  89. TM_Team *specificTeamToJoin;
  90. NoTeamId noTeamSubcategory;
  91. } teamParameter;
  92. /// \brief Join any team that has available slots and is tagged with ALLOW_JOIN_ANY_AVAILABLE_TEAM
  93. /// \details ID_TEAM_BALANCER_TEAM_ASSIGNED, ID_TEAM_BALANCER_REQUESTED_TEAM_FULL, or ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED will be returned to all systems.
  94. static TeamSelection AnyAvailable(void);
  95. /// \brief Join a specific team if it has available slots, and is tagged with JOIN_SPECIFIC_TEAMS
  96. /// \details ID_TEAM_BALANCER_TEAM_ASSIGNED, ID_TEAM_BALANCER_REQUESTED_TEAM_FULL, or ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED will be returned to all systems.
  97. /// \param[in] specificTeamToJoin Which team to attempt to join.
  98. static TeamSelection SpecificTeam(TM_Team *specificTeamToJoin);
  99. /// \brief Do not join a team, or leave all current teams.
  100. /// \details This always succeeds. ID_TEAM_BALANCER_TEAM_ASSIGNED will be returned to all systems.
  101. /// \param[in] noTeamSubcategory Even when not on a team, you can internally identify a subcategory of not being on a team, such as AI or spectator.
  102. static TeamSelection NoTeam(NoTeamId noTeamSubcategory);
  103. };
  104. /// \brief A member of one or more teams.
  105. /// \details Contains data and operations on data to manage which team your game's team members are on.
  106. /// Best used as a composite member of your "User" or "Player" class(es).
  107. /// When using with ReplicaManager3, call TM_TeamMember::ReferenceTeamMember() in Replica3::DeserializeConstruction() and TM_TeamMember::DeserializeConstruction() in Replica3::PostDeserializeConstruction()
  108. /// There is otherwise no need to manually serialize the class, as operations are networked internally.
  109. /// \ingroup TEAM_MANAGER_GROUP
  110. class RAK_DLL_EXPORT TM_TeamMember
  111. {
  112. public:
  113. // GetInstance() and DestroyInstance(instance*)
  114. STATIC_FACTORY_DECLARATIONS(TM_TeamMember)
  115. TM_TeamMember();
  116. virtual ~TM_TeamMember();
  117. /// \brief Request to join any team, a specific team, or to leave all teams
  118. /// \details Function will return false on invalid operations, such as joining a team you are already on.
  119. /// Will also fail with TeamSelection::JOIN_ANY_AVAILABLE_TEAM if you are currently on a team.
  120. /// On success, every system will get ID_TEAM_BALANCER_TEAM_ASSIGNED. Use TeamManager::DecomposeTeamAssigned() to get details of which team member the message refers to.
  121. /// On failure, all systems will get ID_TEAM_BALANCER_REQUESTED_TEAM_FULL or ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED. Use TeamManager::DecomposeTeamFull() and TeamManager::DecomposeTeamLocked() to get details of which team member the message refers to.
  122. /// \note Joining a specific team with this function may result in being on more than one team at once, even if you call the function while locally only on one team. If your game depends on only being on one team at a team, use RequestTeamSwitch() instead with the parameter teamToLeave set to 0
  123. /// \param[in] TeamSelection::AnyAvailable(), TeamSelection::SpecificTeam(), or TeamSelection::NoTeam()
  124. /// \return false On invalid or unnecessary operation. Otherwise returns true
  125. bool RequestTeam(TeamSelection teamSelection);
  126. /// \brief Similar to RequestTeam with TeamSelection::SpecificTeam(), but leave a team simultaneously when the desired team is joinable
  127. /// \param[in] teamToJoin Which team to join
  128. /// \param[in] teamToLeave If 0, means leave all current teams. Otherwise, leave the specified team.
  129. /// \return false On invalid or unnecessary operation. Otherwise returns true
  130. bool RequestTeamSwitch(TM_Team *teamToJoin, TM_Team *teamToLeave);
  131. /// \brief Returns the first requested team in the list of requested teams, if you have a requested team at all.
  132. /// \return TeamSelection::SpecificTeam(), TeamSelection::NoTeam(), or TeamSelection::AnyAvailable()
  133. TeamSelection GetRequestedTeam(void) const;
  134. /// \brief Returns pending calls to RequestTeam() when using TeamSelection::JOIN_SPECIFIC_TEAM
  135. /// \param[out] All pending requested teams
  136. void GetRequestedSpecificTeams(DataStructures::List<TM_Team*> &requestedTeams) const;
  137. /// \brief Returns if the specified team is in the list of pending requested teams
  138. /// \param[in] The team we are checking
  139. /// \return Did we request to join this specific team?
  140. bool HasRequestedTeam(TM_Team *team) const;
  141. /// \brief Returns the index of \a team in the requested teams list
  142. /// \param[in] The team we are checking
  143. /// \return -1 if we did not requested to join this team. Otherwise the index.
  144. unsigned int GetRequestedTeamIndex(TM_Team *team) const;
  145. /// \return The number of teams that would be returned by a call to GetRequestedSpecificTeams()
  146. unsigned int GetRequestedTeamCount(void) const;
  147. /// \brief Cancels a request to join a specific team.
  148. /// \details Useful if you got ID_TEAM_BALANCER_REQUESTED_TEAM_FULL or ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED and changed your mind about joining the team.
  149. /// \note This is not guaranteed to work due to latency. To clarify, If the host switches your team at the same time you call CancelRequestTeam() you may still get ID_TEAM_BALANCER_TEAM_ASSIGNED for the team you tried to cancel.
  150. /// \param[in] specificTeamToCancel Which team to no longer join. Use 0 for all.
  151. /// \return false On invalid or unnecessary operation. Otherwise returns true
  152. bool CancelTeamRequest(TM_Team *specificTeamToCancel);
  153. /// \brief Leave a team
  154. /// \details Leaves a team that you are on. Always succeeds provided you are on that team
  155. /// Generates ID_TEAM_BALANCER_TEAM_ASSIGNED on all systems on success.
  156. /// If you leave the last team you are on, \a noTeamSubcategory is set as well.
  157. /// \param[in] team Which team to leave
  158. /// \param[in] _noTeamSubcategory If the team member has been removed from all teams, which subcategory of NoTeamId to set them to
  159. /// \return false On invalid or unnecessary operation. Otherwise returns true
  160. bool LeaveTeam(TM_Team* team, NoTeamId _noTeamSubcategory);
  161. /// \brief Leave all teams
  162. /// \Details Leaves all teams you are on, and sets \a noTeamSubcategory
  163. /// \note This is the same as and just calls RequestTeam(TeamSelection::NoTeam(noTeamSubcategory));
  164. /// \return false On invalid or unnecessary operation. Otherwise returns true
  165. bool LeaveAllTeams(NoTeamId noTeamSubcategory);
  166. /// \return Get the first team we are on, or 0 if we are not on a team.
  167. TM_Team* GetCurrentTeam(void) const;
  168. /// \return How many teams we are on
  169. unsigned int GetCurrentTeamCount(void) const;
  170. /// \return Returns one of the teams in the current team list, up to GetCurrentTeamCount()
  171. TM_Team* GetCurrentTeamByIndex(unsigned int index);
  172. /// \param[out] Get all teams we are on, as a list
  173. void GetCurrentTeams(DataStructures::List<TM_Team*> &_teams) const;
  174. /// For each team member, when you get ID_TEAM_BALANCER_TEAM_ASSIGNED for that member, the team list is saved.
  175. /// Use this function to get that list, for example to determine which teams we just left or joined
  176. /// \param[out] _teams The previous list of teams we were on
  177. void GetLastTeams(DataStructures::List<TM_Team*> &_teams) const;
  178. /// \param[in] The team we are checking
  179. /// \return Are we on this team?
  180. bool IsOnTeam(TM_Team *team) const;
  181. /// \return The teamMemberID parameter passed to TM_World::ReferenceTeamMember()
  182. NetworkID GetNetworkID(void) const;
  183. /// \return The TM_World instance that was used when calling TM_World::ReferenceTeamMember()
  184. TM_World* GetTM_World(void) const;
  185. /// \brief Serializes the current state of this object
  186. /// \details To replicate a TM_TeamMember on another system, first instantiate the object using your own code, or a system such as ReplicaManager3.
  187. /// Next, call SerializeConstruction() from whichever system owns the team member
  188. /// Last, call DeserializeConstruction() on the newly created TM_TeamMember
  189. /// \note You must instantiate and deserialize all TM_Team instances that the team member refers to before calling DesrializeConstruction(). ReplicaManager3::PostSerializeConstruction() and ReplicaManager3::PostDeserializeConstruction() will ensure this.
  190. /// \param[out] constructionBitstream This object serialized to a BitStream
  191. void SerializeConstruction(BitStream *constructionBitstream);
  192. /// \brief Deserializes the current state of this object
  193. /// \details See SerializeConstruction for more details()
  194. /// \note DeserializeConstruction also calls ReferenceTeamMember on the passed \a teamManager instance, there is no need to do so yourself
  195. /// \param[in] teamManager TeamManager instance
  196. /// \param[in] constructionBitstream This object serialized to a BitStream
  197. bool DeserializeConstruction(TeamManager *teamManager, BitStream *constructionBitstream);
  198. /// \param[in] o Stores a void* for your own use. If using composition, this is useful to store a pointer to the containing object.
  199. void SetOwner(void *o);
  200. /// \return Whatever was passed to SetOwner()
  201. void *GetOwner(void) const;
  202. /// \return If not on a team, returns the current NoTeamId value
  203. NoTeamId GetNoTeamId(void) const;
  204. /// Return world->GetTeamMemberIndex(this)
  205. unsigned int GetWorldIndex(void) const;
  206. /// \internal
  207. static unsigned long ToUint32( const NetworkID &g );
  208. /// \internal
  209. struct RequestedTeam
  210. {
  211. RakNet::Time whenRequested;
  212. unsigned int requestIndex;
  213. TM_Team *requested;
  214. bool isTeamSwitch;
  215. TM_Team *teamToLeave;
  216. };
  217. protected:
  218. NetworkID networkId;
  219. TM_World* world;
  220. // Teams we are a member of. We can be on more than one team, but not on the same team more than once
  221. DataStructures::List<TM_Team*> teams;
  222. // If teams is empty, which subcategory of noTeam we are on
  223. NoTeamId noTeamSubcategory;
  224. // Teams we have requested to join. Mutually exclusive with teams we are already on. Cannot request the same team more than once.
  225. DataStructures::List<RequestedTeam> teamsRequested;
  226. // If teamsRequested is not empty, we want to join a specific team
  227. // If teamsRequested is empty, then joinTeamType is either JOIN_NO_TEAM or JOIN_ANY_AVAILABLE_TEAM
  228. JoinTeamType joinTeamType;
  229. // Set by StoreLastTeams()
  230. DataStructures::List<TM_Team*> lastTeams;
  231. RakNet::Time whenJoinAnyRequested;
  232. unsigned int joinAnyRequestIndex;
  233. void *owner;
  234. // Remove from all requested and current teams.
  235. void UpdateListsToNoTeam(NoTeamId nti);
  236. bool JoinAnyTeamCheck(void) const;
  237. bool JoinSpecificTeamCheck(TM_Team *specificTeamToJoin, bool ignoreRequested) const;
  238. bool SwitchSpecificTeamCheck(TM_Team *teamToJoin, TM_Team *teamToLeave, bool ignoreRequested) const;
  239. bool LeaveTeamCheck(TM_Team *team) const;
  240. void UpdateTeamsRequestedToAny(void);
  241. void UpdateTeamsRequestedToNone(void);
  242. void AddToRequestedTeams(TM_Team *teamToJoin);
  243. void AddToRequestedTeams(TM_Team *teamToJoin, TM_Team *teamToLeave);
  244. bool RemoveFromRequestedTeams(TM_Team *team);
  245. void AddToTeamList(TM_Team *team);
  246. void RemoveFromSpecificTeamInternal(TM_Team *team);
  247. void RemoveFromAllTeamsInternal(void);
  248. void StoreLastTeams(void);
  249. friend class TM_World;
  250. friend class TM_Team;
  251. friend class TeamManager;
  252. };
  253. /// \brief A team, containing a list of TM_TeamMember instances
  254. /// \details Contains lists of TM_TeamMember instances
  255. /// Best used as a composite member of your "Team" or "PlayerList" class(es).
  256. /// When using with ReplicaManager3, call TM_Team::ReferenceTeam() in Replica3::DeserializeConstruction() and TM_Team::DeserializeConstruction() in Replica3::PostDeserializeConstruction()
  257. /// There is otherwise no need to manually serialize the class, as operations are networked internally.
  258. /// \ingroup TEAM_MANAGER_GROUP
  259. class RAK_DLL_EXPORT TM_Team
  260. {
  261. public:
  262. // GetInstance() and DestroyInstance(instance*)
  263. STATIC_FACTORY_DECLARATIONS(TM_Team)
  264. TM_Team();
  265. virtual ~TM_Team();
  266. /// \brief Set the maximum number of members that can join this team.
  267. /// Defaults to 65535
  268. /// Setting the limit lower than the existing number of members kicks members out, and assigns noTeamSubcategory to them if they have no other team to go to
  269. /// Setting the limit higher allows members to join in. If a member has a pending request to join this team, they join automatically and ID_TEAM_BALANCER_TEAM_ASSIGNED will be returned for those members.
  270. /// \param[in] _teamMemberLimit The new limit
  271. /// \param[in] noTeamSubcategory Which noTeamSubcategory to assign to members that now have no team.
  272. /// \return false On invalid or unnecessary operation. Otherwise returns true
  273. bool SetMemberLimit(TeamMemberLimit _teamMemberLimit, NoTeamId noTeamSubcategory);
  274. /// \return If team balancing is on, the most members that can be on this team that would not either unbalance it or exceed the value passed to SetMemberLimit(). If team balancing is off, the same as GetMemberLimitSetting()
  275. TeamMemberLimit GetMemberLimit(void) const;
  276. /// \return What was passed to SetMemberLimit() or the default
  277. TeamMemberLimit GetMemberLimitSetting(void) const;
  278. /// \brief Who can join this team under what conditions, while the team is not full
  279. /// To not allow new joins, pass 0
  280. /// To allow all new joins under any circumstances, bitwise-OR all permission defines.
  281. /// For an invite-only team, use ALLOW_JOIN_SPECIFIC_TEAM only and only allow the requester to call TM_TeamMember::RequestTeam() upon invitiation through your game code.
  282. /// Defaults to allow all
  283. /// \param[in] _joinPermissions Bitwise combination of ALLOW_JOIN_ANY_AVAILABLE_TEAM, ALLOW_JOIN_SPECIFIC_TEAM, ALLOW_JOIN_REBALANCING
  284. /// \return false On invalid or unnecessary operation. Otherwise returns true
  285. bool SetJoinPermissions(JoinPermissions _joinPermissions);
  286. /// \return Whatever was passed to SetJoinPermissions(), or the default.
  287. JoinPermissions GetJoinPermissions(void) const;
  288. /// \brief Removes a member from a team he or she is on
  289. /// \details Identical to teamMember->LeaveTeam(this, noTeamSubcategory); See TeamMember::LeaveTeam() for details.
  290. /// \param[in] teamMember Which team member to remove
  291. /// \param[in] noTeamSubcategory If the team member has been removed from all teams, which subcategory of NoTeamId to set them to
  292. void LeaveTeam(TM_TeamMember* teamMember, NoTeamId noTeamSubcategory);
  293. /// \return What was passed as the \a applyBalancing parameter TM_World::ReferenceTeam() when this team was added.
  294. bool GetBalancingApplies(void) const;
  295. /// \param[out] All team members of this team
  296. void GetTeamMembers(DataStructures::List<TM_TeamMember*> &_teamMembers) const;
  297. /// \return The number of team members on this team
  298. unsigned int GetTeamMembersCount(void) const;
  299. /// \return A team member on this team. Members are stored in the order they are added
  300. /// \param[in] index A value between 0 and GetTeamMembersCount()
  301. TM_TeamMember *GetTeamMemberByIndex(unsigned int index) const;
  302. /// \return The teamID parameter passed to TM_World::ReferenceTeam()
  303. NetworkID GetNetworkID(void) const;
  304. /// \return The TM_World instance that was used when calling TM_World::ReferenceTeamMember()
  305. TM_World* GetTM_World(void) const;
  306. /// \brief Used by the host to serialize the initial state of this object to a new system
  307. /// \details On the host, when sending existing objects to a new system, call SerializeConstruction() on each of those objects to serialize creation state.
  308. /// Creating the actual Team and TeamMember objects should be handled by your game code, or a system such as ReplicaManager3
  309. void SerializeConstruction(BitStream *constructionBitstream);
  310. /// \brief Used by non-host systems to read the bitStream written by SerializeConstruction()
  311. /// \details On non-host systems, after creating existing objects, call DeserializeConstruction() to read and setup that object
  312. /// Creating the actual Team and TeamMember objects should be handled by your game code, or a system such as ReplicaManager3
  313. bool DeserializeConstruction(TeamManager *teamManager, BitStream *constructionBitstream);
  314. /// \param[in] o Stores a void* for your own use. If using composition, this is useful to store a pointer to the containing object.
  315. void SetOwner(void *o);
  316. /// \return Whatever was passed to SetOwner()
  317. void *GetOwner(void) const;
  318. /// Return world->GetTeamIndex(this)
  319. unsigned int GetWorldIndex(void) const;
  320. /// \internal
  321. static unsigned long ToUint32( const NetworkID &g );
  322. protected:
  323. NetworkID ID;
  324. TM_World* world;
  325. // Which members are on this team. The same member cannot be on the same team more than once
  326. DataStructures::List<TM_TeamMember*> teamMembers;
  327. // Permissions on who can join this team
  328. JoinPermissions joinPermissions;
  329. // Whether or not to consider this team when balancing teams
  330. bool balancingApplies;
  331. TeamMemberLimit teamMemberLimit;
  332. void *owner;
  333. // Remove input from list teamMembers
  334. void RemoveFromTeamMemberList(TM_TeamMember *teamMember);
  335. // Find the member index that wants to join the indicated team, is only on one team, and wants to leave that team
  336. unsigned int GetMemberWithRequestedSingleTeamSwitch(TM_Team *team);
  337. friend class TM_World;
  338. friend class TM_TeamMember;
  339. friend class TeamManager;
  340. };
  341. /// \brief Stores a list of teams which may be enforcing a balanced number of members
  342. /// \details Each TM_World instance is independent of other TM_World world instances. This enables you to host multiple games on a single computer.
  343. /// Not currently supported to have the same TM_Team or TM_TeamMember in more than one world at a time, but easily added on request.
  344. /// \ingroup TEAM_MANAGER_GROUP
  345. class TM_World
  346. {
  347. public:
  348. TM_World();
  349. virtual ~TM_World();
  350. /// \return Returns the plugin that created this TM_World instance
  351. TeamManager *GetTeamManager(void) const;
  352. /// \brief Add a new system to send team and team member updates to.
  353. /// \param[in] rakNetGUID GUID of the system you are adding. See Packet::rakNetGUID or RakPeerInterface::GetGUIDFromSystemAddress()
  354. void AddParticipant(RakNetGUID rakNetGUID);
  355. /// \brief Remove a system that was previously added with AddParticipant()
  356. /// \details Systems that disconnect are removed automatically
  357. /// \param[in] rakNetGUID GUID of the system you are removing. See Packet::rakNetGUID or RakPeerInterface::GetGUIDFromSystemAddress()
  358. void RemoveParticipant(RakNetGUID rakNetGUID);
  359. /// \brief If true, all new connections are added to this world using AddParticipant()
  360. /// \details Defaults to true
  361. /// \param[in] autoAdd Setting to set
  362. void SetAutoManageConnections(bool autoAdd);
  363. /// Get the participants added with AddParticipant()
  364. /// \param[out] participantList Participants added with AddParticipant();
  365. void GetParticipantList(DataStructures::List<RakNetGUID> &participantList);
  366. /// \brief Register a TM_Team object with this system.
  367. /// \details Your game should contain instances of TM_Team, for example by using composition with your game's Team or PlayerList class
  368. /// Tell TeamManager about these instances using ReferenceTeam().
  369. /// \note The destrutor of TM_Team calls DereferenceTeam() automatically.
  370. /// \param[in] team The instance you are registering
  371. /// \param[in] networkId Identifies this instance. This value is independent of values used by NetworkIDManager. You can use the same value as the object that contains this instance.
  372. /// \param[in] applyBalancing Whether or not to include this team for balancing when calling SetBalanceTeams().
  373. void ReferenceTeam(TM_Team *team, NetworkID networkId, bool applyBalancing);
  374. /// \brief Unregisters the associated TM_Team object with this system.
  375. /// Call when a TM_Team instance is no longer needed
  376. /// \param[in] team Which team instance to unregister
  377. /// \param[in] noTeamSubcategory All players on this team are kicked off. If these players then have no team, they are set to this no team category.
  378. void DereferenceTeam(TM_Team *team, NoTeamId noTeamSubcategory);
  379. /// \return Number of teams uniquely added with ReferenceTeam()
  380. unsigned int GetTeamCount(void) const;
  381. /// \param[in] index A value between 0 and GetTeamCount()
  382. /// \return Returns whatever was passed to \a team in the function ReferenceTeam() in the order it was called.
  383. TM_Team *GetTeamByIndex(unsigned int index) const;
  384. /// \param[in] teamId Value passed to ReferenceTeam()
  385. /// \return Returns whatever was passed to \a team in the function ReferenceTeam() with this NetworkID.
  386. TM_Team *GetTeamByNetworkID(NetworkID teamId);
  387. /// \brief Inverse of GetTeamByIndex()
  388. /// \param[in] team Which taem
  389. /// \return The index of the specified team, or -1 if not found
  390. unsigned int GetTeamIndex(const TM_Team *team) const;
  391. /// \brief Register a TM_TeamMember object with this system.
  392. /// \details Your game should contain instances of TM_TeamMember, for example by using composition with your game's User or Player classes
  393. /// Tell TeamManager about these instances using ReferenceTeamMember().
  394. /// \note The destrutor of TM_TeamMember calls DereferenceTeamMember() automatically.
  395. /// \param[in] teamMember The instance you are registering
  396. /// \param[in] networkId Identifies this instance. This value is independent of values used by NetworkIDManager. You can use the same value as the object that contains this instance
  397. void ReferenceTeamMember(TM_TeamMember *teamMember, NetworkID networkId);
  398. /// \brief Unregisters the associated TM_TeamMember object with this system.
  399. /// Call when a TM_TeamMember instance is no longer needed
  400. /// \note This is called by the destructor of TM_TeamMember automatically, so you do not normally need to call this function
  401. void DereferenceTeamMember(TM_TeamMember *teamMember);
  402. /// \return Number of team members uniquely added with ReferenceTeamMember()
  403. unsigned int GetTeamMemberCount(void) const;
  404. /// \param[in] index A value between 0 and GetTeamMemberCount()
  405. /// \return Returns whatever was passed to \a team in the function ReferenceTeamMember() in the order it was called.
  406. TM_TeamMember *GetTeamMemberByIndex(unsigned int index) const;
  407. /// \param[in] index A value between 0 and GetTeamMemberCount()
  408. /// \return Returns whatever was passed to \a teamMemberID in the function ReferenceTeamMember() in the order it was called.
  409. NetworkID GetTeamMemberIDByIndex(unsigned int index) const;
  410. /// \param[in] teamId Value passed to ReferenceTeamMember()
  411. /// \return Returns Returns whatever was passed to \a team in the function ReferenceTeamMember() with this NetworkID
  412. TM_TeamMember *GetTeamMemberByNetworkID(NetworkID teamMemberId);
  413. /// \brief Inverse of GetTeamMemberByIndex()
  414. /// \param[in] team Which team member
  415. /// \return The index of the specified team member, or -1 if not found
  416. unsigned int GetTeamMemberIndex(const TM_TeamMember *teamMember) const;
  417. /// \brief Force or stop forcing teams to be balanced.
  418. /// \details For each team added with ReferenceTeam() and \a applyBalancing set to true, players on unbalanced teams will be redistributed
  419. /// While active, players can only join balanced teams if doing so would not cause that team to become unbalanced.
  420. /// If a player on the desired team also wants to switch, then both players will switch simultaneously. Otherwise, ID_TEAM_BALANCER_REQUESTED_TEAM_FULL will be returned to the requester and switching will occur when possible.
  421. /// If balanceTeams is true and later set to false, players waiting on ID_TEAM_BALANCER_REQUESTED_TEAM_FULL will be able to join the desired team immediately provided it is not full.
  422. /// \param[in] balanceTeams Whether to activate or deactivate team balancing.
  423. /// \param[in] noTeamSubcategory If a player is kicked off a team and is no longer on any team, his or her noTeamSubcategory is set to this value
  424. bool SetBalanceTeams(bool balanceTeams, NoTeamId noTeamSubcategory);
  425. /// \return \a balanceTeams parameter of SetBalanceTeams(), or the default
  426. bool GetBalanceTeams(void) const;
  427. /// \brief Set the host that will perform balancing calculations and send notifications
  428. /// \details Operations that can cause conflicts due to latency, such as joining teams, are operated on by the host. The result is sent to all systems added with AddParticipant()
  429. /// For a client/server game, call SetHost() with the server's RakNetGUID value on all systems (including the server itself). If you call TeamManager::SetTopology(TM_CLIENT_SERVER), the server will also relay messages between participants.
  430. /// For a peer to peer game, call SetHost() on the same peer when host migration occurs. Use TeamManager::SetTopology(TM_PEER_TO_PEER) in this case.
  431. /// \note If using FullyConnectedMesh2, SetHost() is called automatically when ID_FCM2_NEW_HOST is returned.
  432. /// \param[in] _hostGuid The host, which is the system that will serialize and resolve team disputes and calculate team balancing.
  433. void SetHost(RakNetGUID _hostGuid);
  434. /// \return Returns the current host, or UNASSIGNED_RAKNET_GUID if unknown
  435. RakNetGUID GetHost(void) const;
  436. /// \return The \a worldId passed to TeamManagr::AddWorld()
  437. WorldId GetWorldId(void) const;
  438. /// \brief Clear all memory and reset everything.
  439. /// \details It is up to the user to deallocate pointers passed to ReferenceTeamMember() or ReferenceTeam(), if so desired.
  440. void Clear(void);
  441. /// \internal
  442. struct JoinRequestHelper
  443. {
  444. RakNet::Time whenRequestMade;
  445. unsigned int teamMemberIndex;
  446. unsigned int indexIntoTeamsRequested;
  447. unsigned int requestIndex;
  448. };
  449. /// \internal
  450. static int JoinRequestHelperComp(const TM_World::JoinRequestHelper &key, const TM_World::JoinRequestHelper &data);
  451. protected:
  452. virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason );
  453. virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming);
  454. // Teams with too many members have those members go to other teams.
  455. void EnforceTeamBalance(NoTeamId noTeamSubcategory);
  456. void KickExcessMembers(NoTeamId noTeamSubcategory);
  457. void FillRequestedSlots(void);
  458. unsigned int GetAvailableTeamIndexWithFewestMembers(TeamMemberLimit secondaryLimit, JoinPermissions joinPermissions);
  459. void GetSortedJoinRequests(DataStructures::OrderedList<JoinRequestHelper, JoinRequestHelper, JoinRequestHelperComp> &joinRequests);
  460. // Send a message to all participants
  461. void BroadcastToParticipants(RakNet::BitStream *bsOut, RakNetGUID exclusionGuid);
  462. void BroadcastToParticipants(unsigned char *data, const int length, RakNetGUID exclusionGuid);
  463. // 1. If can join a team:
  464. // A. teamMember->UpdateTeamsRequestedToNone();
  465. // B. teamMember->AddToTeamList()
  466. // C. Return new team
  467. // 2. Else return 0
  468. TM_Team* JoinAnyTeam(TM_TeamMember *teamMember, int *resultCode);
  469. int JoinSpecificTeam(TM_TeamMember *teamMember, TM_Team *team, bool isTeamSwitch, TM_Team *teamToLeave, DataStructures::List<TM_Team*> &teamsWeAreLeaving);
  470. TeamMemberLimit GetBalancedTeamLimit(void) const;
  471. // For fast lookup. Shares pointers with list teams
  472. DataStructures::Hash<NetworkID, TM_Team*, 256, TM_Team::ToUint32> teamsHash;
  473. // For fast lookup. Shares pointers with list teamMembers
  474. DataStructures::Hash<NetworkID, TM_TeamMember*, 256, TM_TeamMember::ToUint32> teamMembersHash;
  475. TeamManager *teamManager;
  476. DataStructures::List<RakNetGUID> participants;
  477. DataStructures::List<TM_Team*> teams;
  478. DataStructures::List<TM_TeamMember*> teamMembers;
  479. bool balanceTeamsIsActive;
  480. RakNetGUID hostGuid;
  481. WorldId worldId;
  482. bool autoAddParticipants;
  483. int teamRequestIndex;
  484. friend class TeamManager;
  485. friend class TM_TeamMember;
  486. friend class TM_Team;
  487. };
  488. /// \brief Automates networking and list management for teams
  489. /// \details TeamManager provides support for teams. A team is a list of team members.
  490. /// Teams contain properties including the number of team members per team, whether or not tagged teams must have equal numbers of members, and if a team is locked or not to certain entry conditions
  491. /// Team members contain properties including which teams they are on and which teams they want to join if a team is not immediately joinable
  492. /// Advanced functionality includes the ability for a team member to be on multiple teams simultaneously, the ability to swap teams with other members, and the ability to resize the number of members supported per team
  493. /// The architecture is designed for easy integration with ReplicaManager3
  494. ///
  495. /// Usage:<BR>
  496. /// 1. Define your game classes to represent teams and team members. Your game classes should hold game-specific information such as team name and color.<BR>
  497. /// 2. Have those game classes contain a corresponding TM_Team or TM_TeamMember instance. Operations on teams will be performed by those instances. Use SetOwner() to refer to the parent object when using composition.<BR>
  498. /// 3. Call TeamManager::SetTopology() for client/server or peer to peer.<BR>
  499. /// 4. Call AddWorld() to instantiate a TM_World object which will contain references to your TM_TeamMember and TM_Team instances.<BR>
  500. /// 5. When you instantiate a TM_TeamMember or TM_Team object, call ReferenceTeam() and ReferenceTeamMember() for each corresponding object<BR>
  501. /// 6. When sending world state to a new connection, for example in ReplicaManager3::SerializeConstruction(), call TM_SerializeConstruction() on the corresponding TM_TeamMember and TM_Team objects. TM_Team instances on the new connection must be created before TM_TeamMember instances.<BR>
  502. /// 7. Call TM_DeserializeConstruction() on your new corresponding TM_TeamMember and TM_Team instances.<BR>
  503. /// 8. Execute team operations. ID_TEAM_BALANCER_REQUESTED_TEAM_FULL, ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED, ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED, and ID_TEAM_BALANCER_TEAM_ASSIGNED are returned to all systems when the corresponding event occurs for a team member.<BR>
  504. /// 9. As the peer to peer session host changes, call SetHost() (Not necessary if using FullyConnectedMesh2). If using client/server, you must set the host<BR>
  505. /// \note This replaces TeamBalancer. You cannot use TeamBalancer and TeamManager at the same time.
  506. /// \ingroup TEAM_MANAGER_GROUP
  507. class RAK_DLL_EXPORT TeamManager : public PluginInterface2
  508. {
  509. public:
  510. // GetInstance() and DestroyInstance(instance*)
  511. STATIC_FACTORY_DECLARATIONS(TeamManager)
  512. TeamManager();
  513. virtual ~TeamManager();
  514. /// \brief Allocate a world to hold a list of teams and players for that team.
  515. /// Use the returned TM_World object for actual team functionality.
  516. /// \note The world is tracked by TeamManager and deallocated by calling Clear()
  517. /// \param[in] worldId Arbitrary user-defined id of the world to create. Each world instance must have a unique id.
  518. TM_World* AddWorld(WorldId worldId);
  519. /// \brief Deallocate a world created with AddWorld()
  520. /// \param[in] worldId The world to deallocate
  521. void RemoveWorld(WorldId worldId);
  522. /// \return Returns the number of worlds created with AddWorld()
  523. unsigned int GetWorldCount(void) const;
  524. /// \param[in] index A value beteween 0 and GetWorldCount()-1 inclusive.
  525. /// \return Returns a world created with AddWorld()
  526. TM_World* GetWorldAtIndex(unsigned int index) const;
  527. /// \param[in] worldId \a worldId value passed to AddWorld()
  528. /// \return Returns a world created with AddWorld(), or 0 if no such \a worldId
  529. TM_World* GetWorldWithId(WorldId worldId) const;
  530. /// \brief When auto managing connections, call TM_World::AddParticipant() on all worlds for all new connections automatically
  531. /// Defaults to true
  532. /// \note You probably want this set to false if using multiple worlds
  533. /// \param[in] autoAdd Automatically call TM_World::AddParticipant() all worlds each new connection. Defaults to true.
  534. void SetAutoManageConnections(bool autoAdd);
  535. /// \brief If \a _topology is set to TM_CLIENT_SERVER, the host will relay messages to participants.
  536. /// \details If topology is set to TM_PEER_TO_PEER, the host assumes the original message source was connected to all other participants and does not relay messages.
  537. /// \note If TM_PEER_TO_PEER, this plugin will listen for ID_FCM2_NEW_HOST and call SetHost() on all worlds automatically
  538. /// \note Defaults to TM_PEER_TO_PEER
  539. /// \param[in] _topology Topology to use
  540. void SetTopology(TMTopology _topology);
  541. /// \brief When you get ID_TEAM_BALANCER_REQUESTED_TEAM_FULL, pass the packet to this function to read out parameters
  542. /// \param[in] A packet where packet->data[0]==ID_TEAM_BALANCER_REQUESTED_TEAM_FULL
  543. /// \return true on success, false on read error
  544. void DecomposeTeamFull(Packet *packet,
  545. TM_World **world, TM_TeamMember **teamMember, TM_Team **team,
  546. uint16_t &currentMembers, uint16_t &memberLimitIncludingBalancing, bool &balancingIsActive, JoinPermissions &joinPermissions);
  547. /// \brief When you get ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED, pass the packet to this function to read out parameters
  548. /// \param[in] A packet where packet->data[0]==ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED
  549. /// \return true on success, false on read error
  550. void DecomposeTeamLocked(Packet *packet,
  551. TM_World **world, TM_TeamMember **teamMember, TM_Team **team,
  552. uint16_t &currentMembers, uint16_t &memberLimitIncludingBalancing, bool &balancingIsActive, JoinPermissions &joinPermissions);
  553. /// \brief Clear all memory and reset everything.
  554. /// \details Deallocates TM_World instances. It is up to the user to deallocate pointers passed to ReferenceTeamMember() or ReferenceTeam(), if so desired.
  555. void Clear(void);
  556. /// \brief Reads out the world and teamMember from ID_TEAM_BALANCER_TEAM_ASSIGNED
  557. /// \note You can get the current and prior team list from the teamMember itself
  558. /// \param[in] A packet where packet->data[0]==ID_TEAM_BALANCER_TEAM_ASSIGNED
  559. /// \param[out] world Set to the world this \a teamMember is on. 0 on bad lookup.
  560. /// \param[out] teamMember Set to the teamMember affected. 0 on bad lookup.
  561. void DecodeTeamAssigned(Packet *packet, TM_World **world, TM_TeamMember **teamMember);
  562. // \brief Reads out the world and teamMember from ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED
  563. /// \note You can get the requested team list from the teamMember itself
  564. /// \param[in] A packet where packet->data[0]==ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED
  565. /// \param[out] world Set to the world this \a teamMember is on. 0 on bad lookup.
  566. /// \param[out] teamMember Set to the teamMember affected. 0 on bad lookup.
  567. /// \param[out] teamCancelled Set to the team that was cancelled. 0 for all teams.
  568. void DecodeTeamCancelled(Packet *packet, TM_World **world, TM_TeamMember **teamMember, TM_Team **teamCancelled);
  569. protected:
  570. virtual void Update(void);
  571. virtual PluginReceiveResult OnReceive(Packet *packet);
  572. virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason );
  573. virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming);
  574. void Send( const RakNet::BitStream * bitStream, const AddressOrGUID systemIdentifier, bool broadcast );
  575. void EncodeTeamFullOrLocked(RakNet::BitStream *bitStream, TM_TeamMember *teamMember, TM_Team *team);
  576. void DecomposeTeamFullOrLocked(RakNet::BitStream *bsIn, TM_World **world, TM_TeamMember **teamMember, TM_Team **team,
  577. uint16_t &currentMembers, uint16_t &memberLimitIncludingBalancing, bool &balancingIsActive, JoinPermissions &joinPermissions);
  578. void ProcessTeamAssigned(RakNet::BitStream *bsIn);
  579. void EncodeTeamAssigned(RakNet::BitStream *bitStream, TM_TeamMember *teamMember);
  580. void RemoveFromTeamsRequestedAndAddTeam(TM_TeamMember *teamMember, TM_Team *team, bool isTeamSwitch, TM_Team *teamToLeave);
  581. void PushTeamAssigned(TM_TeamMember *teamMember);
  582. void PushBitStream(RakNet::BitStream *bitStream);
  583. void OnUpdateListsToNoTeam(Packet *packet, TM_World *world);
  584. void OnUpdateTeamsRequestedToAny(Packet *packet, TM_World *world);
  585. void OnJoinAnyTeam(Packet *packet, TM_World *world);
  586. void OnJoinRequestedTeam(Packet *packet, TM_World *world);
  587. void OnUpdateTeamsRequestedToNoneAndAddTeam(Packet *packet, TM_World *world);
  588. void OnRemoveFromTeamsRequestedAndAddTeam(Packet *packet, TM_World *world);
  589. void OnAddToRequestedTeams(Packet *packet, TM_World *world);
  590. bool OnRemoveFromRequestedTeams(Packet *packet, TM_World *world);
  591. void OnLeaveTeam(Packet *packet, TM_World *world);
  592. void OnSetMemberLimit(Packet *packet, TM_World *world);
  593. void OnSetJoinPermissions(Packet *packet, TM_World *world);
  594. void OnSetBalanceTeams(Packet *packet, TM_World *world);
  595. void OnSetBalanceTeamsInitial(Packet *packet, TM_World *world);
  596. void EncodeTeamFull(RakNet::BitStream *bitStream, TM_TeamMember *teamMember, TM_Team *team);
  597. void EncodeTeamLocked(RakNet::BitStream *bitStream, TM_TeamMember *teamMember, TM_Team *team);
  598. /// \brief When you get ID_TEAM_BALANCER_TEAM_ASSIGNED, pass the packet to this function to read out parameters
  599. /// \param[in] A packet where packet->data[0]==ID_TEAM_BALANCER_TEAM_ASSIGNED
  600. /// \return true on success, false on read error
  601. void DecodeTeamAssigned(RakNet::BitStream *bsIn, TM_World **world, TM_TeamMember **teamMember, NoTeamId &noTeamSubcategory,
  602. JoinTeamType &joinTeamType, DataStructures::List<TM_Team *> &newTeam,
  603. DataStructures::List<TM_Team *> &teamsLeft, DataStructures::List<TM_Team *> &teamsJoined);
  604. // O(1) lookup for a given world. If I need more worlds, change this to a hash or ordered list
  605. TM_World *worldsArray[255];
  606. // All allocated worlds for linear traversal
  607. DataStructures::List<TM_World*> worldsList;
  608. bool autoAddParticipants;
  609. TMTopology topology;
  610. friend class TM_TeamMember;
  611. friend class TM_World;
  612. friend class TM_Team;
  613. };
  614. } // namespace RakNet
  615. #endif // __TEAM_MANAGER_H
  616. #endif // _RAKNET_SUPPORT_*
粤ICP备19079148号