TeamBalancer.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  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. #include "NativeFeatureIncludes.h"
  11. #if _RAKNET_SUPPORT_TeamBalancer==1
  12. #include "TeamBalancer.h"
  13. #include "BitStream.h"
  14. #include "MessageIdentifiers.h"
  15. #include "RakPeerInterface.h"
  16. #include "Rand.h"
  17. using namespace RakNet;
  18. enum TeamBalancerOperations
  19. {
  20. ID_STATUS_UPDATE_TO_NEW_HOST,
  21. ID_CANCEL_TEAM_REQUEST,
  22. ID_REQUEST_ANY_TEAM,
  23. ID_REQUEST_SPECIFIC_TEAM
  24. };
  25. STATIC_FACTORY_DEFINITIONS(TeamBalancer,TeamBalancer);
  26. TeamBalancer::TeamBalancer()
  27. {
  28. defaultAssigmentAlgorithm=SMALLEST_TEAM;
  29. forceTeamsToBeEven=false;
  30. lockTeams=false;
  31. hostGuid=UNASSIGNED_RAKNET_GUID;
  32. }
  33. TeamBalancer::~TeamBalancer()
  34. {
  35. }
  36. void TeamBalancer::SetTeamSizeLimit(TeamId team, unsigned short limit)
  37. {
  38. teamLimits.Replace(limit,0,team,_FILE_AND_LINE_);
  39. if (teamLimits.Size() > teamMemberCounts.Size())
  40. teamMemberCounts.Replace(0,0,teamLimits.Size()-1,_FILE_AND_LINE_);
  41. }
  42. void TeamBalancer::SetDefaultAssignmentAlgorithm(DefaultAssigmentAlgorithm daa)
  43. {
  44. // Just update the default. Currently active teams are not affected.
  45. defaultAssigmentAlgorithm=daa;
  46. }
  47. void TeamBalancer::SetForceEvenTeams(bool force)
  48. {
  49. // Set flag to indicate that teams should be even.
  50. forceTeamsToBeEven=force;
  51. // If teams are locked, just return.
  52. if (lockTeams==true)
  53. return;
  54. if (forceTeamsToBeEven==true)
  55. {
  56. // Run the even team algorithm
  57. EvenTeams();
  58. }
  59. }
  60. void TeamBalancer::SetLockTeams(bool lock)
  61. {
  62. if (lock==lockTeams)
  63. return;
  64. // Set flag to indicate that teams can no longer be changed.
  65. lockTeams=lock;
  66. // If lock is false, and teams were set to be forced as even, then run through the even team algorithm
  67. if (lockTeams==false)
  68. {
  69. // Process even swaps
  70. TeamId i,j;
  71. for (i=0; i < teamMembers.Size(); i++)
  72. {
  73. if (teamMembers[i].requestedTeam!=UNASSIGNED_TEAM_ID)
  74. {
  75. for (j=i+1; j < teamMembers.Size(); j++)
  76. {
  77. if (teamMembers[j].requestedTeam==teamMembers[i].currentTeam &&
  78. teamMembers[i].requestedTeam==teamMembers[j].currentTeam)
  79. {
  80. SwapTeamMembersByRequest(i,j);
  81. NotifyTeamAssigment(i);
  82. NotifyTeamAssigment(j);
  83. }
  84. }
  85. }
  86. }
  87. if (forceTeamsToBeEven==true)
  88. {
  89. EvenTeams();
  90. }
  91. else
  92. {
  93. // Process requested team changes
  94. // Process movement while not full
  95. for (i=0; i < teamMembers.Size(); i++)
  96. {
  97. TeamId requestedTeam = teamMembers[i].requestedTeam;
  98. if (requestedTeam!=UNASSIGNED_TEAM_ID)
  99. {
  100. if (teamMemberCounts[requestedTeam]<teamLimits[requestedTeam])
  101. {
  102. SwitchMemberTeam(i,requestedTeam);
  103. NotifyTeamAssigment(i);
  104. }
  105. }
  106. }
  107. }
  108. }
  109. }
  110. void TeamBalancer::RequestSpecificTeam(NetworkID memberId, TeamId desiredTeam)
  111. {
  112. bool foundMatch=false;
  113. for (unsigned int i=0; i < myTeamMembers.Size(); i++)
  114. {
  115. if (myTeamMembers[i].memberId==memberId)
  116. {
  117. foundMatch=true;
  118. if (myTeamMembers[i].requestedTeam==desiredTeam && myTeamMembers[i].currentTeam==desiredTeam)
  119. {
  120. return;
  121. }
  122. else
  123. {
  124. myTeamMembers[i].requestedTeam=desiredTeam;
  125. }
  126. }
  127. }
  128. if (foundMatch==false)
  129. {
  130. MyTeamMembers mtm;
  131. mtm.currentTeam=UNASSIGNED_TEAM_ID;
  132. mtm.memberId=memberId;
  133. mtm.requestedTeam=desiredTeam;
  134. myTeamMembers.Push(mtm, _FILE_AND_LINE_);
  135. }
  136. // Send desiredTeam to the current host.
  137. // Also flag that we have requested a team, and record desiredTeam in case the host changes and it needs to be resent.
  138. BitStream bsOut;
  139. bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL);
  140. bsOut.Write((MessageID)ID_REQUEST_SPECIFIC_TEAM);
  141. bsOut.Write(memberId);
  142. bsOut.Write(desiredTeam);
  143. rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false);
  144. }
  145. void TeamBalancer::CancelRequestSpecificTeam(NetworkID memberId)
  146. {
  147. for (unsigned int i=0; i < myTeamMembers.Size(); i++)
  148. {
  149. if (myTeamMembers[i].memberId==memberId)
  150. {
  151. myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID;
  152. // Send packet to the host to remove our request flag.
  153. BitStream bsOut;
  154. bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL);
  155. bsOut.Write((MessageID)ID_CANCEL_TEAM_REQUEST);
  156. bsOut.Write(memberId);
  157. rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false);
  158. return;
  159. }
  160. }
  161. }
  162. void TeamBalancer::RequestAnyTeam(NetworkID memberId)
  163. {
  164. bool foundMatch=false;
  165. for (unsigned int i=0; i < myTeamMembers.Size(); i++)
  166. {
  167. if (myTeamMembers[i].memberId==memberId)
  168. {
  169. foundMatch=true;
  170. if (myTeamMembers[i].currentTeam!=UNASSIGNED_TEAM_ID)
  171. return;
  172. else
  173. myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID;
  174. break;
  175. }
  176. }
  177. if (foundMatch==false)
  178. {
  179. MyTeamMembers mtm;
  180. mtm.currentTeam=UNASSIGNED_TEAM_ID;
  181. mtm.memberId=memberId;
  182. mtm.requestedTeam=UNASSIGNED_TEAM_ID;
  183. myTeamMembers.Push(mtm, _FILE_AND_LINE_);
  184. }
  185. // Else send to the current host that we need a team.
  186. BitStream bsOut;
  187. bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL);
  188. bsOut.Write((MessageID)ID_REQUEST_ANY_TEAM);
  189. bsOut.Write(memberId);
  190. rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false);
  191. }
  192. TeamId TeamBalancer::GetMyTeam(NetworkID memberId) const
  193. {
  194. // Return team returned by last ID_TEAM_BALANCER_TEAM_ASSIGNED packet
  195. for (unsigned int i=0; i < myTeamMembers.Size(); i++)
  196. {
  197. if (myTeamMembers[i].memberId==memberId)
  198. {
  199. return myTeamMembers[i].currentTeam;
  200. }
  201. }
  202. return UNASSIGNED_TEAM_ID;
  203. }
  204. void TeamBalancer::DeleteMember(NetworkID memberId)
  205. {
  206. for (unsigned int i=0; i < myTeamMembers.Size(); i++)
  207. {
  208. if (myTeamMembers[i].memberId==memberId)
  209. {
  210. myTeamMembers.RemoveAtIndexFast(i);
  211. break;
  212. }
  213. }
  214. for (unsigned int i=0; i < teamMembers.Size(); i++)
  215. {
  216. if (teamMembers[i].memberId==memberId)
  217. {
  218. RemoveTeamMember(i);
  219. break;
  220. }
  221. }
  222. }
  223. PluginReceiveResult TeamBalancer::OnReceive(Packet *packet)
  224. {
  225. switch (packet->data[0])
  226. {
  227. case ID_FCM2_NEW_HOST:
  228. {
  229. hostGuid=packet->guid;
  230. if (myTeamMembers.Size()>0)
  231. {
  232. BitStream bsOut;
  233. bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL);
  234. bsOut.Write((MessageID)ID_STATUS_UPDATE_TO_NEW_HOST);
  235. bsOut.WriteCasted<uint8_t>(myTeamMembers.Size());
  236. for (unsigned int i=0; i < myTeamMembers.Size(); i++)
  237. {
  238. bsOut.Write(myTeamMembers[i].memberId);
  239. bsOut.Write(myTeamMembers[i].currentTeam);
  240. bsOut.Write(myTeamMembers[i].requestedTeam);
  241. }
  242. rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false);
  243. }
  244. }
  245. break;
  246. case ID_TEAM_BALANCER_INTERNAL:
  247. {
  248. if (packet->length>=2)
  249. {
  250. switch (packet->data[1])
  251. {
  252. case ID_STATUS_UPDATE_TO_NEW_HOST:
  253. OnStatusUpdateToNewHost(packet);
  254. break;
  255. case ID_CANCEL_TEAM_REQUEST:
  256. OnCancelTeamRequest(packet);
  257. break;
  258. case ID_REQUEST_ANY_TEAM:
  259. OnRequestAnyTeam(packet);
  260. break;
  261. case ID_REQUEST_SPECIFIC_TEAM:
  262. OnRequestSpecificTeam(packet);
  263. break;
  264. }
  265. }
  266. }
  267. return RR_STOP_PROCESSING_AND_DEALLOCATE;
  268. case ID_TEAM_BALANCER_TEAM_ASSIGNED:
  269. {
  270. return OnTeamAssigned(packet);
  271. }
  272. case ID_TEAM_BALANCER_REQUESTED_TEAM_FULL:
  273. {
  274. return OnRequestedTeamChangePending(packet);
  275. }
  276. case ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED:
  277. {
  278. return OnTeamsLocked(packet);
  279. }
  280. }
  281. // Got RequestSpecificTeam
  282. // If teams are locked
  283. // - If this user already has a team, return ID_TEAM_BALANCER_TEAMS_LOCKED
  284. // - This user does not already have a team. Assign a team as if the user called RequestAnyTeam(), with a preference for the requested team. Return ID_TEAM_BALANCER_TEAM_ASSIGNED once the team has been assigned.
  285. // If teams are not locked
  286. // - If even team balancing is on, only assign this user if this would not cause teams to be unbalanced. If teams WOULD be unbalanced, then flag this user as wanting to join this team. Return ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING
  287. // - If the destination team is full, flag this user as wanting to join this team. Return ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING
  288. // - Else, join this team. Return ID_TEAM_BALANCER_TEAM_ASSIGNED
  289. // Got RequestAnyTeam
  290. // Put user on a team following the algorithm. No team is set as preferred.
  291. return RR_CONTINUE_PROCESSING;
  292. }
  293. void TeamBalancer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
  294. {
  295. (void) systemAddress;
  296. (void) lostConnectionReason;
  297. RemoveByGuid(rakNetGUID);
  298. }
  299. void TeamBalancer::OnAttach(void)
  300. {
  301. hostGuid = rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS);
  302. }
  303. void TeamBalancer::RemoveByGuid(RakNetGUID rakNetGUID)
  304. {
  305. // If we are the host, and the closed connection has a team, and teams are not locked:
  306. if (WeAreHost())
  307. {
  308. unsigned int droppedMemberIndex=0;
  309. while (droppedMemberIndex < teamMembers.Size())
  310. {
  311. if (teamMembers[droppedMemberIndex].memberGuid==rakNetGUID)
  312. {
  313. TeamId droppedTeam = teamMembers[droppedMemberIndex].currentTeam;
  314. RemoveTeamMember(droppedMemberIndex);
  315. if (lockTeams==false)
  316. {
  317. if (forceTeamsToBeEven)
  318. {
  319. // - teams were forced to be even, then run the even team algorithm
  320. EvenTeams();
  321. }
  322. else
  323. {
  324. // - teams were NOT forced to be even, and the team the dropped player on was full, then move users wanting to join that team (if any)
  325. if (teamMemberCounts[ droppedTeam ]==teamLimits[ droppedTeam ]-1)
  326. {
  327. MoveMemberThatWantsToJoinTeam(droppedTeam);
  328. }
  329. }
  330. }
  331. }
  332. else
  333. {
  334. droppedMemberIndex++;
  335. }
  336. }
  337. }
  338. }
  339. void TeamBalancer::OnStatusUpdateToNewHost(Packet *packet)
  340. {
  341. if (WeAreHost()==false)
  342. return;
  343. BitStream bsIn(packet->data,packet->length,false);
  344. bsIn.IgnoreBytes(2);
  345. uint8_t requestedTeamChangeListSize;
  346. bsIn.Read(requestedTeamChangeListSize);
  347. TeamMember tm;
  348. tm.memberGuid=packet->guid;
  349. for (uint8_t i=0; i < requestedTeamChangeListSize; i++)
  350. {
  351. bsIn.Read(tm.memberId);
  352. bsIn.Read(tm.currentTeam);
  353. bsIn.Read(tm.requestedTeam);
  354. if (tm.currentTeam!=UNASSIGNED_TEAM_ID && tm.currentTeam>teamLimits.Size())
  355. {
  356. RakAssert("Current team out of range in TeamBalancer::OnStatusUpdateToNewHost" && 0);
  357. return;
  358. }
  359. if (tm.requestedTeam!=UNASSIGNED_TEAM_ID && tm.requestedTeam>teamLimits.Size())
  360. {
  361. RakAssert("Requested team out of range in TeamBalancer::OnStatusUpdateToNewHost" && 0);
  362. return;
  363. }
  364. if (tm.currentTeam==UNASSIGNED_TEAM_ID && tm.requestedTeam==UNASSIGNED_TEAM_ID)
  365. return;
  366. unsigned int memberIndex = GetMemberIndex(tm.memberId, packet->guid);
  367. if (memberIndex==(unsigned int) -1)
  368. {
  369. // Add this system (by GUID) to the list of members if he is not already there
  370. // Also update his requested team flag.
  371. // Do not process balancing on requested teams, since we don't necessarily have all data from all systems yet and hopefully the state during the host migration was stable.
  372. if (tm.currentTeam==UNASSIGNED_TEAM_ID)
  373. {
  374. // Assign a default team, then add team member
  375. if (tm.requestedTeam==UNASSIGNED_TEAM_ID)
  376. {
  377. // Assign a default team
  378. tm.currentTeam=GetNextDefaultTeam();
  379. }
  380. else
  381. {
  382. // Assign to requested team if possible. Otherwise, assign to a default team
  383. if (TeamWouldBeOverpopulatedOnAddition(tm.requestedTeam, teamMembers.Size())==false)
  384. {
  385. tm.currentTeam=tm.requestedTeam;
  386. }
  387. else
  388. {
  389. tm.currentTeam=GetNextDefaultTeam();
  390. }
  391. }
  392. }
  393. if (tm.currentTeam==UNASSIGNED_TEAM_ID)
  394. {
  395. RakAssert("Too many members asking for teams!" && 0);
  396. return;
  397. }
  398. NotifyTeamAssigment(AddTeamMember(tm));
  399. }
  400. }
  401. }
  402. void TeamBalancer::OnCancelTeamRequest(Packet *packet)
  403. {
  404. if (WeAreHost()==false)
  405. return;
  406. BitStream bsIn(packet->data,packet->length,false);
  407. bsIn.IgnoreBytes(2);
  408. NetworkID memberId;
  409. bsIn.Read(memberId);
  410. unsigned int memberIndex = GetMemberIndex(memberId, packet->guid);
  411. if (memberIndex!=(unsigned int)-1)
  412. teamMembers[memberIndex].requestedTeam=UNASSIGNED_TEAM_ID;
  413. }
  414. void TeamBalancer::OnRequestAnyTeam(Packet *packet)
  415. {
  416. if (WeAreHost()==false)
  417. return;
  418. BitStream bsIn(packet->data,packet->length,false);
  419. bsIn.IgnoreBytes(2);
  420. NetworkID memberId;
  421. bsIn.Read(memberId);
  422. unsigned int memberIndex = GetMemberIndex(memberId, packet->guid);
  423. if (memberIndex==(unsigned int)-1)
  424. {
  425. TeamMember tm;
  426. tm.currentTeam=GetNextDefaultTeam();
  427. tm.requestedTeam=UNASSIGNED_TEAM_ID;
  428. tm.memberGuid=packet->guid;
  429. tm.memberId=memberId;
  430. if (tm.currentTeam==UNASSIGNED_TEAM_ID)
  431. {
  432. RakAssert("Too many members asking for teams!" && 0);
  433. return;
  434. }
  435. NotifyTeamAssigment(AddTeamMember(tm));
  436. }
  437. }
  438. void TeamBalancer::OnRequestSpecificTeam(Packet *packet)
  439. {
  440. if (WeAreHost()==false)
  441. return;
  442. BitStream bsIn(packet->data,packet->length,false);
  443. bsIn.IgnoreBytes(2);
  444. TeamMember tm;
  445. bsIn.Read(tm.memberId);
  446. bsIn.Read(tm.requestedTeam);
  447. unsigned int memberIndex = GetMemberIndex(tm.memberId, packet->guid);
  448. if (tm.requestedTeam==UNASSIGNED_TEAM_ID)
  449. {
  450. NotifyNoTeam(tm.memberId, packet->guid);
  451. if (memberIndex != (unsigned int) -1)
  452. RemoveTeamMember(memberIndex);
  453. return;
  454. }
  455. if (tm.requestedTeam>teamLimits.Size())
  456. {
  457. RakAssert("Requested team out of range in TeamBalancer::OnRequestSpecificTeam" && 0);
  458. return;
  459. }
  460. if (memberIndex==(unsigned int) -1)
  461. {
  462. tm.memberGuid=packet->guid;
  463. // Assign to requested team if possible. Otherwise, assign to a default team
  464. if (TeamWouldBeOverpopulatedOnAddition(tm.requestedTeam, teamMembers.Size())==false)
  465. {
  466. tm.currentTeam=tm.requestedTeam;
  467. tm.requestedTeam=UNASSIGNED_TEAM_ID;
  468. }
  469. else
  470. {
  471. tm.currentTeam=GetNextDefaultTeam();
  472. }
  473. if (tm.currentTeam==UNASSIGNED_TEAM_ID)
  474. {
  475. RakAssert("Too many members asking for teams!" && 0);
  476. return;
  477. }
  478. NotifyTeamAssigment(AddTeamMember(tm));
  479. }
  480. else
  481. {
  482. teamMembers[memberIndex].requestedTeam=tm.requestedTeam;
  483. TeamId oldTeamThisUserWasOn = teamMembers[memberIndex].currentTeam;
  484. if (lockTeams)
  485. {
  486. NotifyTeamsLocked(packet->guid, tm.requestedTeam);
  487. return;
  488. }
  489. // Assign to requested team if possible. Otherwise, assign to a default team
  490. if (TeamsWouldBeEvenOnSwitch(tm.requestedTeam,oldTeamThisUserWasOn)==true)
  491. {
  492. SwitchMemberTeam(memberIndex,tm.requestedTeam);
  493. NotifyTeamAssigment(memberIndex);
  494. }
  495. else
  496. {
  497. // If someone wants to join this user's old team, and we want to join their team, they can swap
  498. unsigned int swappableMemberIndex;
  499. for (swappableMemberIndex=0; swappableMemberIndex < teamMembers.Size(); swappableMemberIndex++)
  500. {
  501. if (teamMembers[swappableMemberIndex].currentTeam==tm.requestedTeam && teamMembers[swappableMemberIndex].requestedTeam==oldTeamThisUserWasOn)
  502. break;
  503. }
  504. if (swappableMemberIndex!=teamMembers.Size())
  505. {
  506. SwapTeamMembersByRequest(memberIndex,swappableMemberIndex);
  507. NotifyTeamAssigment(memberIndex);
  508. NotifyTeamAssigment(swappableMemberIndex);
  509. }
  510. else
  511. {
  512. // Full or would not be even
  513. NotifyTeamSwitchPending(packet->guid, tm.requestedTeam, tm.memberId);
  514. }
  515. }
  516. }
  517. }
  518. unsigned int TeamBalancer::GetMemberIndex(NetworkID memberId, RakNetGUID guid) const
  519. {
  520. for (unsigned int i=0; i < teamMembers.Size(); i++)
  521. {
  522. if (teamMembers[i].memberGuid==guid && teamMembers[i].memberId==memberId)
  523. return i;
  524. }
  525. return (unsigned int) -1;
  526. }
  527. unsigned int TeamBalancer::AddTeamMember(const TeamMember &tm)
  528. {
  529. if (tm.currentTeam>teamLimits.Size())
  530. {
  531. RakAssert("TeamBalancer::AddTeamMember team index out of bounds" && 0);
  532. return (unsigned int) -1;
  533. }
  534. RakAssert(tm.currentTeam!=UNASSIGNED_TEAM_ID);
  535. teamMembers.Push(tm,_FILE_AND_LINE_);
  536. if (teamMemberCounts.Size()<tm.currentTeam)
  537. teamMemberCounts.Replace(1,0,tm.currentTeam,_FILE_AND_LINE_);
  538. else
  539. teamMemberCounts[tm.currentTeam]=teamMemberCounts[tm.currentTeam]+1;
  540. return teamMembers.Size()-1;
  541. }
  542. void TeamBalancer::RemoveTeamMember(unsigned int index)
  543. {
  544. RakAssert( teamMemberCounts[ teamMembers[index].currentTeam ] != 0);
  545. teamMemberCounts[ teamMembers[index].currentTeam ]=teamMemberCounts[ teamMembers[index].currentTeam ]-1;
  546. teamMembers.RemoveAtIndexFast(index);
  547. }
  548. void TeamBalancer::GetMinMaxTeamMembers(int &minMembersOnASingleTeam, int &maxMembersOnASingleTeam)
  549. {
  550. minMembersOnASingleTeam = teamMembers.Size()/teamLimits.Size();
  551. if ((teamMembers.Size() % teamLimits.Size()) == 0)
  552. maxMembersOnASingleTeam = minMembersOnASingleTeam;
  553. else
  554. maxMembersOnASingleTeam = minMembersOnASingleTeam+1;
  555. }
  556. void TeamBalancer::EvenTeams(void)
  557. {
  558. // Ensure all teams are even. If not, pick players at random from overpopulated teams, and move to underpopulated teams.
  559. int minMembersOnASingleTeam;
  560. int maxMembersOnASingleTeam;
  561. GetMinMaxTeamMembers(minMembersOnASingleTeam,maxMembersOnASingleTeam);
  562. // First select among players that have requested to switch teams, if any, before choosing players that did not want to switch teams.
  563. // Players that are moved should be notified of ID_TEAM_BALANCER_TEAM_ASSIGNED
  564. DataStructures::List<TeamId> overpopulatedTeams;
  565. TeamId teamMemberCountsIndex;
  566. unsigned int memberIndexToSwitch;
  567. for (teamMemberCountsIndex=0; teamMemberCountsIndex<teamMemberCounts.Size(); teamMemberCountsIndex++)
  568. {
  569. while (teamMemberCounts[teamMemberCountsIndex]<minMembersOnASingleTeam && teamMemberCounts[teamMemberCountsIndex]<teamLimits[teamMemberCountsIndex])
  570. {
  571. GetOverpopulatedTeams(overpopulatedTeams,maxMembersOnASingleTeam);
  572. RakAssert(overpopulatedTeams.Size()>0);
  573. memberIndexToSwitch=GetMemberIndexToSwitchTeams(overpopulatedTeams,teamMemberCountsIndex);
  574. RakAssert(memberIndexToSwitch!=(unsigned int)-1);
  575. SwitchMemberTeam(memberIndexToSwitch,teamMemberCountsIndex);
  576. // Tell this member he switched teams
  577. NotifyTeamAssigment(memberIndexToSwitch);
  578. }
  579. }
  580. }
  581. unsigned int TeamBalancer::GetMemberIndexToSwitchTeams(const DataStructures::List<TeamId> &sourceTeamNumbers, TeamId targetTeamNumber)
  582. {
  583. DataStructures::List<unsigned int> preferredSwapIndices;
  584. DataStructures::List<unsigned int> potentialSwapIndices;
  585. unsigned int i,j;
  586. for (j=0; j < sourceTeamNumbers.Size(); j++)
  587. {
  588. RakAssert(sourceTeamNumbers[j]!=targetTeamNumber);
  589. for (i=0; i < teamMembers.Size(); i++)
  590. {
  591. if (teamMembers[i].currentTeam==sourceTeamNumbers[j])
  592. {
  593. if (teamMembers[i].requestedTeam==targetTeamNumber)
  594. preferredSwapIndices.Push(i,_FILE_AND_LINE_);
  595. else
  596. potentialSwapIndices.Push(i,_FILE_AND_LINE_);
  597. }
  598. }
  599. }
  600. if (preferredSwapIndices.Size()>0)
  601. {
  602. return preferredSwapIndices[ randomMT() % preferredSwapIndices.Size() ];
  603. }
  604. else if (potentialSwapIndices.Size()>0)
  605. {
  606. return potentialSwapIndices[ randomMT() % potentialSwapIndices.Size() ];
  607. }
  608. else
  609. {
  610. return (unsigned int) -1;
  611. }
  612. }
  613. void TeamBalancer::SwitchMemberTeam(unsigned int teamMemberIndex, TeamId destinationTeam)
  614. {
  615. teamMemberCounts[ teamMembers[teamMemberIndex].currentTeam ]=teamMemberCounts[ teamMembers[teamMemberIndex].currentTeam ]-1;
  616. teamMemberCounts[ destinationTeam ]=teamMemberCounts[ destinationTeam ]+1;
  617. teamMembers[teamMemberIndex].currentTeam=destinationTeam;
  618. if (teamMembers[teamMemberIndex].requestedTeam==destinationTeam)
  619. teamMembers[teamMemberIndex].requestedTeam=UNASSIGNED_TEAM_ID;
  620. }
  621. void TeamBalancer::GetOverpopulatedTeams(DataStructures::List<TeamId> &overpopulatedTeams, int maxTeamSize)
  622. {
  623. overpopulatedTeams.Clear(true,_FILE_AND_LINE_);
  624. for (TeamId i=0; i < teamMemberCounts.Size(); i++)
  625. {
  626. if (teamMemberCounts[i]>=maxTeamSize)
  627. overpopulatedTeams.Push(i,_FILE_AND_LINE_);
  628. }
  629. }
  630. void TeamBalancer::NotifyTeamAssigment(unsigned int teamMemberIndex)
  631. {
  632. RakAssert(teamMemberIndex < teamMembers.Size());
  633. if (teamMemberIndex>=teamMembers.Size())
  634. return;
  635. BitStream bsOut;
  636. bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAM_ASSIGNED);
  637. bsOut.Write(teamMembers[teamMemberIndex].currentTeam);
  638. bsOut.Write(teamMembers[teamMemberIndex].memberId);
  639. rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,teamMembers[teamMemberIndex].memberGuid,false);
  640. }
  641. bool TeamBalancer::WeAreHost(void) const
  642. {
  643. return hostGuid==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS);
  644. }
  645. PluginReceiveResult TeamBalancer::OnTeamAssigned(Packet *packet)
  646. {
  647. if (packet->guid!=hostGuid)
  648. return RR_STOP_PROCESSING_AND_DEALLOCATE;
  649. BitStream bsIn(packet->data,packet->length,false);
  650. bsIn.IgnoreBytes(1);
  651. MyTeamMembers mtm;
  652. bsIn.Read(mtm.currentTeam);
  653. bsIn.Read(mtm.memberId);
  654. mtm.requestedTeam=UNASSIGNED_TEAM_ID;
  655. bool foundMatch=false;
  656. for (unsigned int i=0; i < myTeamMembers.Size(); i++)
  657. {
  658. if (myTeamMembers[i].memberId==mtm.memberId)
  659. {
  660. foundMatch=true;
  661. if (myTeamMembers[i].requestedTeam==mtm.currentTeam)
  662. myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID;
  663. myTeamMembers[i].currentTeam=mtm.currentTeam;
  664. break;
  665. }
  666. }
  667. if (foundMatch==false)
  668. {
  669. return RR_STOP_PROCESSING_AND_DEALLOCATE;
  670. }
  671. return RR_CONTINUE_PROCESSING;
  672. }
  673. PluginReceiveResult TeamBalancer::OnRequestedTeamChangePending(Packet *packet)
  674. {
  675. if (packet->guid!=hostGuid)
  676. return RR_STOP_PROCESSING_AND_DEALLOCATE;
  677. return RR_CONTINUE_PROCESSING;
  678. }
  679. PluginReceiveResult TeamBalancer::OnTeamsLocked(Packet *packet)
  680. {
  681. if (packet->guid!=hostGuid)
  682. return RR_STOP_PROCESSING_AND_DEALLOCATE;
  683. return RR_CONTINUE_PROCESSING;
  684. }
  685. TeamId TeamBalancer::GetNextDefaultTeam(void)
  686. {
  687. // Accounting for team balancing and team limits, get the team a player should be placed on
  688. switch (defaultAssigmentAlgorithm)
  689. {
  690. case SMALLEST_TEAM:
  691. {
  692. return GetSmallestNonFullTeam();
  693. }
  694. case FILL_IN_ORDER:
  695. {
  696. return GetFirstNonFullTeam();
  697. }
  698. default:
  699. {
  700. RakAssert("TeamBalancer::GetNextDefaultTeam unknown algorithm enumeration" && 0);
  701. return UNASSIGNED_TEAM_ID;
  702. }
  703. }
  704. }
  705. bool TeamBalancer::TeamWouldBeOverpopulatedOnAddition(TeamId teamId, unsigned int teamMemberSize)
  706. {
  707. // Accounting for team balancing and team limits, would this team be overpopulated if a member was added to it?
  708. if (teamMemberCounts[teamId]>=teamLimits[teamId])
  709. {
  710. return true;
  711. }
  712. if (forceTeamsToBeEven)
  713. {
  714. int allowedLimit = teamMemberSize/teamLimits.Size() + 1;
  715. return teamMemberCounts[teamId]>=allowedLimit;
  716. }
  717. return false;
  718. }
  719. bool TeamBalancer::TeamWouldBeUnderpopulatedOnLeave(TeamId teamId, unsigned int teamMemberSize)
  720. {
  721. if (forceTeamsToBeEven)
  722. {
  723. unsigned int minMembersOnASingleTeam = (teamMemberSize-1)/teamLimits.Size();
  724. return teamMemberCounts[teamId]<=minMembersOnASingleTeam;
  725. }
  726. return false;
  727. }
  728. TeamId TeamBalancer::GetSmallestNonFullTeam(void) const
  729. {
  730. TeamId idx;
  731. unsigned long smallestTeamCount=MAX_UNSIGNED_LONG;
  732. TeamId smallestTeamIndex = UNASSIGNED_TEAM_ID;
  733. for (idx=0; idx < teamMemberCounts.Size(); idx++)
  734. {
  735. if (teamMemberCounts[idx]<smallestTeamCount && teamMemberCounts[idx]<teamLimits[idx])
  736. {
  737. smallestTeamCount=teamMemberCounts[idx];
  738. smallestTeamIndex=idx;
  739. }
  740. }
  741. return smallestTeamIndex;
  742. }
  743. TeamId TeamBalancer::GetFirstNonFullTeam(void) const
  744. {
  745. TeamId idx;
  746. for (idx=0; idx < teamMemberCounts.Size(); idx++)
  747. {
  748. if (teamMemberCounts[idx]<teamLimits[idx])
  749. {
  750. return idx;
  751. }
  752. }
  753. return UNASSIGNED_TEAM_ID;
  754. }
  755. void TeamBalancer::MoveMemberThatWantsToJoinTeam(TeamId teamId)
  756. {
  757. RakAssert(forceTeamsToBeEven==false && lockTeams==false);
  758. do
  759. {
  760. teamId = MoveMemberThatWantsToJoinTeamInternal(teamId);
  761. } while (teamId!=UNASSIGNED_TEAM_ID);
  762. }
  763. TeamId TeamBalancer::MoveMemberThatWantsToJoinTeamInternal(TeamId teamId)
  764. {
  765. DataStructures::List<TeamId> membersThatWantToJoinTheTeam;
  766. for (TeamId i=0; i < teamMembers.Size(); i++)
  767. {
  768. if (teamMembers[i].requestedTeam==teamId)
  769. membersThatWantToJoinTheTeam.Push(i,_FILE_AND_LINE_);
  770. }
  771. if (membersThatWantToJoinTheTeam.Size()>0)
  772. {
  773. TeamId oldTeam;
  774. unsigned int swappedMemberIndex = membersThatWantToJoinTheTeam[ randomMT() % membersThatWantToJoinTheTeam.Size() ];
  775. oldTeam=teamMembers[swappedMemberIndex].currentTeam;
  776. SwitchMemberTeam(swappedMemberIndex,teamId);
  777. NotifyTeamAssigment(swappedMemberIndex);
  778. return oldTeam;
  779. }
  780. return UNASSIGNED_TEAM_ID;
  781. }
  782. void TeamBalancer::NotifyTeamsLocked(RakNetGUID target, TeamId requestedTeam)
  783. {
  784. BitStream bsOut;
  785. bsOut.Write((MessageID)ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED);
  786. bsOut.Write(requestedTeam);
  787. rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false);
  788. }
  789. void TeamBalancer::NotifyTeamSwitchPending(RakNetGUID target, TeamId requestedTeam, NetworkID memberId)
  790. {
  791. BitStream bsOut;
  792. bsOut.Write((MessageID)ID_TEAM_BALANCER_REQUESTED_TEAM_FULL);
  793. bsOut.Write(requestedTeam);
  794. bsOut.Write(memberId);
  795. rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false);
  796. }
  797. void TeamBalancer::SwapTeamMembersByRequest(unsigned int memberIndex1, unsigned int memberIndex2)
  798. {
  799. TeamId index1Team = teamMembers[memberIndex1].currentTeam;
  800. teamMembers[memberIndex1].currentTeam=teamMembers[memberIndex2].currentTeam;
  801. teamMembers[memberIndex2].currentTeam=index1Team;
  802. teamMembers[memberIndex1].requestedTeam=UNASSIGNED_TEAM_ID;
  803. teamMembers[memberIndex2].requestedTeam=UNASSIGNED_TEAM_ID;
  804. }
  805. void TeamBalancer::NotifyNoTeam(NetworkID memberId, RakNetGUID target)
  806. {
  807. BitStream bsOut;
  808. bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAM_ASSIGNED);
  809. bsOut.Write((unsigned char)UNASSIGNED_TEAM_ID);
  810. bsOut.Write(memberId);
  811. rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false);
  812. }
  813. bool TeamBalancer::TeamsWouldBeEvenOnSwitch(TeamId t1, TeamId t2)
  814. {
  815. RakAssert(teamMembers.Size()!=0);
  816. return TeamWouldBeOverpopulatedOnAddition(t1, teamMembers.Size()-1)==false &&
  817. TeamWouldBeUnderpopulatedOnLeave(t2, teamMembers.Size()-1)==false;
  818. }
  819. #endif // _RAKNET_SUPPORT_*
粤ICP备19079148号