00001 #include "NativeFeatureIncludes.h"
00002 #if _RAKNET_SUPPORT_TeamBalancer==1
00003
00004 #include "TeamBalancer.h"
00005 #include "BitStream.h"
00006 #include "MessageIdentifiers.h"
00007 #include "RakPeerInterface.h"
00008 #include "Rand.h"
00009
00010 using namespace RakNet;
00011
00012 enum TeamBalancerOperations
00013 {
00014 ID_STATUS_UPDATE_TO_NEW_HOST,
00015 ID_CANCEL_TEAM_REQUEST,
00016 ID_REQUEST_ANY_TEAM,
00017 ID_REQUEST_SPECIFIC_TEAM
00018 };
00019
00020 TeamBalancer::TeamBalancer()
00021 {
00022 hostGuid=UNASSIGNED_RAKNET_GUID;
00023 currentTeam=UNASSIGNED_TEAM_ID;
00024 requestedTeam=UNASSIGNED_TEAM_ID;
00025 defaultAssigmentAlgorithm=SMALLEST_TEAM;
00026 forceTeamsToBeEven=false;
00027 lockTeams=false;
00028 expectingToReceiveTeamNumber=false;
00029 allowHostMigration=true;
00030 }
00031 TeamBalancer::~TeamBalancer()
00032 {
00033
00034 }
00035 void TeamBalancer::SetHostGuid(RakNetGUID _hostGuid)
00036 {
00037
00038 if (hostGuid==_hostGuid)
00039 return;
00040
00041 hostGuid=_hostGuid;
00042
00043
00044 if (expectingToReceiveTeamNumber==false && currentTeam==UNASSIGNED_TEAM_ID)
00045 return;
00046
00047
00048 BitStream bsOut;
00049 bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL);
00050 bsOut.Write((MessageID)ID_STATUS_UPDATE_TO_NEW_HOST);
00051 bsOut.Write(currentTeam);
00052 bsOut.Write(requestedTeam);
00053 rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,_hostGuid,false);
00054 }
00055 void TeamBalancer::SetTeamSizeLimits(const DataStructures::List<unsigned short> &_teamLimits)
00056 {
00057
00058 teamLimits=_teamLimits;
00059
00060 teamMemberCounts.Clear(true,__FILE__,__LINE__);
00061 if (_teamLimits.Size()>0)
00062 teamMemberCounts.Replace(0,0,_teamLimits.Size()-1,__FILE__,__LINE__);
00063 }
00064 void TeamBalancer::SetTeamSizeLimits(unsigned short *values, int valuesLength)
00065 {
00066 RakAssert(valuesLength>0);
00067 teamMemberCounts.Clear(true,__FILE__,__LINE__);
00068 for (int i=0; i < valuesLength; i++)
00069 teamMemberCounts.Push(values[i],__FILE__,__LINE__);
00070 }
00071 void TeamBalancer::SetDefaultAssignmentAlgorithm(DefaultAssigmentAlgorithm daa)
00072 {
00073
00074 defaultAssigmentAlgorithm=daa;
00075 }
00076 void TeamBalancer::SetForceEvenTeams(bool force)
00077 {
00078
00079 forceTeamsToBeEven=force;
00080
00081
00082 if (lockTeams==true)
00083 return;
00084
00085 if (forceTeamsToBeEven==true)
00086 {
00087
00088 EvenTeams();
00089 }
00090 }
00091 void TeamBalancer::SetLockTeams(bool lock)
00092 {
00093 if (lock==lockTeams)
00094 return;
00095
00096
00097 lockTeams=lock;
00098
00099
00100 if (lockTeams==false)
00101 {
00102
00103 TeamId i,j;
00104 for (i=0; i < teamMembers.Size(); i++)
00105 {
00106 if (teamMembers[i].requestedTeam!=UNASSIGNED_TEAM_ID)
00107 {
00108 for (j=i+1; j < teamMembers.Size(); j++)
00109 {
00110 if (teamMembers[j].requestedTeam==teamMembers[i].currentTeam &&
00111 teamMembers[i].requestedTeam==teamMembers[j].currentTeam)
00112 {
00113 SwapTeamMembersByRequest(i,j);
00114 NotifyTeamAssigment(i);
00115 NotifyTeamAssigment(j);
00116 }
00117 }
00118 }
00119 }
00120
00121 if (forceTeamsToBeEven==true)
00122 {
00123 EvenTeams();
00124 }
00125 else
00126 {
00127
00128
00129 for (i=0; i < teamMembers.Size(); i++)
00130 {
00131 TeamId requestedTeam = teamMembers[i].requestedTeam;
00132 if (requestedTeam!=UNASSIGNED_TEAM_ID)
00133 {
00134 if (teamMemberCounts[requestedTeam]<teamLimits[requestedTeam])
00135 {
00136 SwitchMemberTeam(i,requestedTeam);
00137 NotifyTeamAssigment(i);
00138 }
00139 }
00140 }
00141 }
00142 }
00143 }
00144 bool TeamBalancer::RequestSpecificTeam(TeamId desiredTeam)
00145 {
00146
00147
00148 BitStream bsOut;
00149 bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL);
00150 bsOut.Write((MessageID)ID_REQUEST_SPECIFIC_TEAM);
00151 bsOut.Write(desiredTeam);
00152 rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false);
00153
00154 if (desiredTeam!=UNASSIGNED_TEAM_ID)
00155 expectingToReceiveTeamNumber=true;
00156
00157 return true;
00158 }
00159 void TeamBalancer::CancelRequestSpecificTeam(void)
00160 {
00161
00162 requestedTeam=UNASSIGNED_TEAM_ID;
00163
00164
00165 BitStream bsOut;
00166 bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL);
00167 bsOut.Write((MessageID)ID_CANCEL_TEAM_REQUEST);
00168 rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false);
00169
00170 expectingToReceiveTeamNumber=false;
00171 }
00172 void TeamBalancer::RequestAnyTeam(void)
00173 {
00174
00175 if (GetMyTeam()!=UNASSIGNED_TEAM_ID)
00176 return;
00177
00178
00179 BitStream bsOut;
00180 bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL);
00181 bsOut.Write((MessageID)ID_REQUEST_ANY_TEAM);
00182 rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false);
00183
00184 expectingToReceiveTeamNumber=true;
00185 }
00186 TeamId TeamBalancer::GetMyTeam(void) const
00187 {
00188
00189
00190 return currentTeam;
00191 }
00192 PluginReceiveResult TeamBalancer::OnReceive(Packet *packet)
00193 {
00194 switch (packet->data[0])
00195 {
00196 case ID_TEAM_BALANCER_INTERNAL:
00197 {
00198 if (packet->length>=2)
00199 {
00200 switch (packet->data[1])
00201 {
00202 case ID_STATUS_UPDATE_TO_NEW_HOST:
00203 OnStatusUpdateToNewHost(packet);
00204 break;
00205 case ID_CANCEL_TEAM_REQUEST:
00206 OnCancelTeamRequest(packet);
00207 break;
00208 case ID_REQUEST_ANY_TEAM:
00209 OnRequestAnyTeam(packet);
00210 break;
00211 case ID_REQUEST_SPECIFIC_TEAM:
00212 OnRequestSpecificTeam(packet);
00213 break;
00214 }
00215 }
00216 }
00217 return RR_STOP_PROCESSING_AND_DEALLOCATE;
00218
00219 case ID_TEAM_BALANCER_TEAM_ASSIGNED:
00220 {
00221 return OnTeamAssigned(packet);
00222 }
00223
00224 case ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING:
00225 {
00226 return OnRequestedTeamChangePending(packet);
00227 }
00228
00229 case ID_TEAM_BALANCER_TEAMS_LOCKED:
00230 {
00231 return OnTeamsLocked(packet);
00232 }
00233 }
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247 return RR_CONTINUE_PROCESSING;
00248 }
00249 void TeamBalancer::OnClosedConnection(SystemAddress systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
00250 {
00251 (void) systemAddress;
00252 (void) lostConnectionReason;
00253
00254 RemoveByGuid(rakNetGUID);
00255 }
00256 void TeamBalancer::RemoveByGuid(RakNetGUID rakNetGUID)
00257 {
00258
00259 if (WeAreHost())
00260 {
00261 unsigned int droppedMemberIndex = GetMemberIndex(rakNetGUID);
00262 if (droppedMemberIndex!=(unsigned int)-1)
00263 {
00264 TeamId droppedTeam = teamMembers[droppedMemberIndex].currentTeam;
00265 RemoveTeamMember(droppedMemberIndex);
00266 if (lockTeams==false)
00267 {
00268 if (forceTeamsToBeEven)
00269 {
00270
00271 EvenTeams();
00272 }
00273 else
00274 {
00275
00276 if (teamMemberCounts[ droppedTeam ]==teamLimits[ droppedTeam ]-1)
00277 {
00278 MoveMemberThatWantsToJoinTeam(droppedTeam);
00279 }
00280 }
00281 }
00282 }
00283 }
00284 }
00285 void TeamBalancer::OnStatusUpdateToNewHost(Packet *packet)
00286 {
00287 if (WeAreHost()==false)
00288 return;
00289
00290 if (allowHostMigration==false)
00291 return;
00292
00293 BitStream bsIn(packet->data,packet->length,false);
00294 bsIn.IgnoreBytes(2);
00295 TeamMember tm;
00296 bsIn.Read(tm.currentTeam);
00297 bsIn.Read(tm.requestedTeam);
00298
00299 if (tm.currentTeam!=UNASSIGNED_TEAM_ID && tm.currentTeam>teamLimits.Size())
00300 {
00301 RakAssert("Current team out of range in TeamBalancer::OnStatusUpdateToNewHost" && 0);
00302 return;
00303 }
00304
00305 if (tm.requestedTeam!=UNASSIGNED_TEAM_ID && tm.requestedTeam>teamLimits.Size())
00306 {
00307 RakAssert("Requested team out of range in TeamBalancer::OnStatusUpdateToNewHost" && 0);
00308 return;
00309 }
00310
00311 unsigned int memberIndex = GetMemberIndex(packet->guid);
00312 if (memberIndex==(unsigned int) -1)
00313 {
00314 tm.memberGuid=packet->guid;
00315
00316
00317
00318
00319 if (tm.currentTeam==UNASSIGNED_TEAM_ID)
00320 {
00321
00322 if (tm.requestedTeam==UNASSIGNED_TEAM_ID)
00323 {
00324
00325 tm.currentTeam=GetNextDefaultTeam();
00326 }
00327 else
00328 {
00329
00330 if (TeamWouldBeOverpopulatedOnAddition(tm.requestedTeam, teamMembers.Size())==false)
00331 {
00332 tm.currentTeam=tm.requestedTeam;
00333 }
00334 else
00335 {
00336 tm.currentTeam=GetNextDefaultTeam();
00337 }
00338 }
00339 }
00340
00341 if (tm.currentTeam==UNASSIGNED_TEAM_ID)
00342 {
00343 RakAssert("Too many members asking for teams!" && 0);
00344 return;
00345 }
00346 NotifyTeamAssigment(AddTeamMember(tm));
00347 }
00348 }
00349 void TeamBalancer::OnCancelTeamRequest(Packet *packet)
00350 {
00351 if (WeAreHost()==false)
00352 return;
00353
00354 unsigned int memberIndex = GetMemberIndex(packet->guid);
00355 if (memberIndex!=(unsigned int)-1)
00356 teamMembers[memberIndex].requestedTeam=UNASSIGNED_TEAM_ID;
00357 }
00358 void TeamBalancer::OnRequestAnyTeam(Packet *packet)
00359 {
00360 if (WeAreHost()==false)
00361 return;
00362
00363 unsigned int memberIndex = GetMemberIndex(packet->guid);
00364 if (memberIndex==(unsigned int)-1)
00365 {
00366 TeamMember tm;
00367 tm.currentTeam=GetNextDefaultTeam();
00368 tm.requestedTeam=UNASSIGNED_TEAM_ID;
00369 tm.memberGuid=packet->guid;
00370 if (tm.currentTeam==UNASSIGNED_TEAM_ID)
00371 {
00372 RakAssert("Too many members asking for teams!" && 0);
00373 return;
00374 }
00375 NotifyTeamAssigment(AddTeamMember(tm));
00376 }
00377 }
00378 void TeamBalancer::OnRequestSpecificTeam(Packet *packet)
00379 {
00380 if (WeAreHost()==false)
00381 return;
00382
00383 BitStream bsIn(packet->data,packet->length,false);
00384 bsIn.IgnoreBytes(2);
00385 TeamMember tm;
00386 bsIn.Read(tm.requestedTeam);
00387
00388 if (tm.requestedTeam==UNASSIGNED_TEAM_ID)
00389 {
00390 RemoveByGuid(packet->guid);
00391 NotifyNoTeam(packet->guid);
00392 return;
00393 }
00394
00395 if (tm.requestedTeam>teamLimits.Size())
00396 {
00397 RakAssert("Requested team out of range in TeamBalancer::OnRequestSpecificTeam" && 0);
00398 return;
00399 }
00400 unsigned int memberIndex = GetMemberIndex(packet->guid);
00401 if (memberIndex==(unsigned int) -1)
00402 {
00403 tm.memberGuid=packet->guid;
00404
00405
00406 if (TeamWouldBeOverpopulatedOnAddition(tm.requestedTeam, teamMembers.Size())==false)
00407 tm.currentTeam=tm.requestedTeam;
00408 else
00409 tm.currentTeam=GetNextDefaultTeam();
00410 if (tm.currentTeam==UNASSIGNED_TEAM_ID)
00411 {
00412 RakAssert("Too many members asking for teams!" && 0);
00413 return;
00414 }
00415 NotifyTeamAssigment(AddTeamMember(tm));
00416 }
00417 else
00418 {
00419 teamMembers[memberIndex].requestedTeam=tm.requestedTeam;
00420 TeamId oldTeamThisUserWasOn = teamMembers[memberIndex].currentTeam;
00421
00422 if (lockTeams)
00423 {
00424 NotifyTeamsLocked(packet->guid, tm.requestedTeam);
00425 return;
00426 }
00427
00428
00429 if (TeamsWouldBeEvenOnSwitch(tm.requestedTeam,oldTeamThisUserWasOn)==true)
00430 {
00431 SwitchMemberTeam(memberIndex,tm.requestedTeam);
00432 NotifyTeamAssigment(memberIndex);
00433 }
00434 else
00435 {
00436
00437 unsigned int swappableMemberIndex;
00438 for (swappableMemberIndex=0; swappableMemberIndex < teamMembers.Size(); swappableMemberIndex++)
00439 {
00440 if (teamMembers[swappableMemberIndex].currentTeam==tm.requestedTeam && teamMembers[swappableMemberIndex].requestedTeam==oldTeamThisUserWasOn)
00441 break;
00442 }
00443
00444 if (swappableMemberIndex!=teamMembers.Size())
00445 {
00446 SwapTeamMembersByRequest(memberIndex,swappableMemberIndex);
00447 NotifyTeamAssigment(memberIndex);
00448 NotifyTeamAssigment(swappableMemberIndex);
00449 }
00450 else
00451 {
00452
00453 NotifyTeamSwitchPending(packet->guid, tm.requestedTeam);
00454 }
00455 }
00456 }
00457 }
00458 unsigned int TeamBalancer::GetMemberIndex(RakNetGUID guid)
00459 {
00460 for (unsigned int i=0; i < teamMembers.Size(); i++)
00461 {
00462 if (teamMembers[i].memberGuid==guid)
00463 return i;
00464 }
00465 return (unsigned int) -1;
00466 }
00467 unsigned int TeamBalancer::AddTeamMember(const TeamMember &tm)
00468 {
00469 if (tm.currentTeam>teamLimits.Size())
00470 {
00471 RakAssert("TeamBalancer::AddTeamMember team index out of bounds" && 0);
00472 return (unsigned int) -1;
00473 }
00474
00475 RakAssert(tm.currentTeam!=UNASSIGNED_TEAM_ID);
00476
00477 teamMembers.Push(tm,__FILE__,__LINE__);
00478 if (teamMemberCounts.Size()<tm.currentTeam)
00479 teamMemberCounts.Replace(1,0,tm.currentTeam,__FILE__,__LINE__);
00480 else
00481 teamMemberCounts[tm.currentTeam]=teamMemberCounts[tm.currentTeam]+1;
00482 return teamMembers.Size()-1;
00483 }
00484 void TeamBalancer::RemoveTeamMember(unsigned int index)
00485 {
00486 teamMemberCounts[ teamMembers[index].currentTeam ]=teamMemberCounts[ teamMembers[index].currentTeam ]-1;
00487 teamMembers.RemoveAtIndexFast(index);
00488 }
00489 void TeamBalancer::GetMinMaxTeamMembers(int &minMembersOnASingleTeam, int &maxMembersOnASingleTeam)
00490 {
00491 minMembersOnASingleTeam = teamMembers.Size()/teamLimits.Size();
00492 if ((teamMembers.Size() % teamLimits.Size()) == 0)
00493 maxMembersOnASingleTeam = minMembersOnASingleTeam;
00494 else
00495 maxMembersOnASingleTeam = minMembersOnASingleTeam+1;
00496 }
00497 void TeamBalancer::EvenTeams(void)
00498 {
00499
00500 int minMembersOnASingleTeam;
00501 int maxMembersOnASingleTeam;
00502 GetMinMaxTeamMembers(minMembersOnASingleTeam,maxMembersOnASingleTeam);
00503
00504
00505
00506 DataStructures::List<TeamId> overpopulatedTeams;
00507 TeamId teamMemberCountsIndex;
00508 unsigned int memberIndexToSwitch;
00509 for (teamMemberCountsIndex=0; teamMemberCountsIndex<teamMemberCounts.Size(); teamMemberCountsIndex++)
00510 {
00511 while (teamMemberCounts[teamMemberCountsIndex]<minMembersOnASingleTeam && teamMemberCounts[teamMemberCountsIndex]<teamLimits[teamMemberCountsIndex])
00512 {
00513 GetOverpopulatedTeams(overpopulatedTeams,maxMembersOnASingleTeam);
00514 RakAssert(overpopulatedTeams.Size()>0);
00515 memberIndexToSwitch=GetMemberIndexToSwitchTeams(overpopulatedTeams,teamMemberCountsIndex);
00516 RakAssert(memberIndexToSwitch!=(unsigned int)-1);
00517 SwitchMemberTeam(memberIndexToSwitch,teamMemberCountsIndex);
00518
00519 NotifyTeamAssigment(memberIndexToSwitch);
00520 }
00521 }
00522 }
00523 unsigned int TeamBalancer::GetMemberIndexToSwitchTeams(const DataStructures::List<TeamId> &sourceTeamNumbers, TeamId targetTeamNumber)
00524 {
00525 DataStructures::List<unsigned int> preferredSwapIndices;
00526 DataStructures::List<unsigned int> potentialSwapIndices;
00527 unsigned int i,j;
00528 for (j=0; j < sourceTeamNumbers.Size(); j++)
00529 {
00530 RakAssert(sourceTeamNumbers[j]!=targetTeamNumber);
00531 for (i=0; i < teamMembers.Size(); i++)
00532 {
00533 if (teamMembers[i].currentTeam==sourceTeamNumbers[j])
00534 {
00535 if (teamMembers[i].requestedTeam==targetTeamNumber)
00536 preferredSwapIndices.Push(i,__FILE__,__LINE__);
00537 else
00538 potentialSwapIndices.Push(i,__FILE__,__LINE__);
00539 }
00540 }
00541 }
00542
00543 if (preferredSwapIndices.Size()>0)
00544 {
00545 return preferredSwapIndices[ randomMT() % preferredSwapIndices.Size() ];
00546 }
00547 else if (potentialSwapIndices.Size()>0)
00548 {
00549 return potentialSwapIndices[ randomMT() % potentialSwapIndices.Size() ];
00550 }
00551 else
00552 {
00553 return (unsigned int) -1;
00554 }
00555 }
00556 void TeamBalancer::SwitchMemberTeam(unsigned int teamMemberIndex, TeamId destinationTeam)
00557 {
00558 teamMemberCounts[ teamMembers[teamMemberIndex].currentTeam ]=teamMemberCounts[ teamMembers[teamMemberIndex].currentTeam ]-1;
00559 teamMemberCounts[ destinationTeam ]=teamMemberCounts[ destinationTeam ]+1;
00560 teamMembers[teamMemberIndex].currentTeam=destinationTeam;
00561 if (teamMembers[teamMemberIndex].requestedTeam==destinationTeam)
00562 teamMembers[teamMemberIndex].requestedTeam=UNASSIGNED_TEAM_ID;
00563 }
00564 void TeamBalancer::GetOverpopulatedTeams(DataStructures::List<TeamId> &overpopulatedTeams, int maxTeamSize)
00565 {
00566 overpopulatedTeams.Clear(true,__FILE__,__LINE__);
00567 for (TeamId i=0; i < teamMemberCounts.Size(); i++)
00568 {
00569 if (teamMemberCounts[i]>=maxTeamSize)
00570 overpopulatedTeams.Push(i,__FILE__,__LINE__);
00571 }
00572 }
00573 void TeamBalancer::NotifyTeamAssigment(unsigned int teamMemberIndex)
00574 {
00575 RakAssert(teamMemberIndex < teamMembers.Size());
00576 if (teamMemberIndex>=teamMembers.Size())
00577 return;
00578
00579 BitStream bsOut;
00580 bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAM_ASSIGNED);
00581 bsOut.Write(teamMembers[teamMemberIndex].currentTeam);
00582 rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,teamMembers[teamMemberIndex].memberGuid,false);
00583 }
00584 bool TeamBalancer::WeAreHost(void) const
00585 {
00586 return hostGuid==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS);
00587 }
00588 PluginReceiveResult TeamBalancer::OnTeamAssigned(Packet *packet)
00589 {
00590 if (packet->guid!=hostGuid)
00591 return RR_STOP_PROCESSING_AND_DEALLOCATE;
00592
00593 BitStream bsIn(packet->data,packet->length,false);
00594 bsIn.IgnoreBytes(1);
00595 bsIn.Read(currentTeam);
00596 if (currentTeam==UNASSIGNED_TEAM_ID)
00597 requestedTeam=UNASSIGNED_TEAM_ID;
00598
00599 expectingToReceiveTeamNumber=false;
00600
00601 return RR_CONTINUE_PROCESSING;
00602 }
00603 PluginReceiveResult TeamBalancer::OnRequestedTeamChangePending(Packet *packet)
00604 {
00605 if (packet->guid!=hostGuid)
00606 return RR_STOP_PROCESSING_AND_DEALLOCATE;
00607
00608 expectingToReceiveTeamNumber=false;
00609
00610 return RR_CONTINUE_PROCESSING;
00611 }
00612 PluginReceiveResult TeamBalancer::OnTeamsLocked(Packet *packet)
00613 {
00614 if (packet->guid!=hostGuid)
00615 return RR_STOP_PROCESSING_AND_DEALLOCATE;
00616
00617 expectingToReceiveTeamNumber=false;
00618
00619 return RR_CONTINUE_PROCESSING;
00620 }
00621 TeamId TeamBalancer::GetNextDefaultTeam(void)
00622 {
00623
00624 switch (defaultAssigmentAlgorithm)
00625 {
00626 case SMALLEST_TEAM:
00627 {
00628 return GetSmallestNonFullTeam();
00629 }
00630
00631 case FILL_IN_ORDER:
00632 {
00633 return GetFirstNonFullTeam();
00634 }
00635
00636 default:
00637 {
00638 RakAssert("TeamBalancer::GetNextDefaultTeam unknown algorithm enumeration" && 0);
00639 return UNASSIGNED_TEAM_ID;
00640 }
00641 }
00642 }
00643 bool TeamBalancer::TeamWouldBeOverpopulatedOnAddition(TeamId teamId, unsigned int teamMemberSize)
00644 {
00645
00646 if (teamMemberCounts[teamId]>=teamLimits[teamId])
00647 {
00648 return true;
00649 }
00650
00651 if (forceTeamsToBeEven)
00652 {
00653 int allowedLimit = teamMemberSize/teamLimits.Size() + 1;
00654 return teamMemberCounts[teamId]>=allowedLimit;
00655 }
00656
00657 return false;
00658 }
00659 bool TeamBalancer::TeamWouldBeUnderpopulatedOnLeave(TeamId teamId, unsigned int teamMemberSize)
00660 {
00661 if (forceTeamsToBeEven)
00662 {
00663 unsigned int minMembersOnASingleTeam = (teamMemberSize-1)/teamLimits.Size();
00664 return teamMemberCounts[teamId]<=minMembersOnASingleTeam;
00665 }
00666 return false;
00667 }
00668 TeamId TeamBalancer::GetSmallestNonFullTeam(void) const
00669 {
00670 TeamId idx;
00671 unsigned long smallestTeamCount=MAX_UNSIGNED_LONG;
00672 TeamId smallestTeamIndex = UNASSIGNED_TEAM_ID;
00673 for (idx=0; idx < teamMemberCounts.Size(); idx++)
00674 {
00675 if (teamMemberCounts[idx]<smallestTeamCount && teamMemberCounts[idx]<teamLimits[idx])
00676 {
00677 smallestTeamCount=teamMemberCounts[idx];
00678 smallestTeamIndex=idx;
00679 }
00680 }
00681 return smallestTeamIndex;
00682 }
00683 TeamId TeamBalancer::GetFirstNonFullTeam(void) const
00684 {
00685 TeamId idx;
00686 for (idx=0; idx < teamMemberCounts.Size(); idx++)
00687 {
00688 if (teamMemberCounts[idx]<teamLimits[idx])
00689 {
00690 return idx;
00691 }
00692 }
00693 return UNASSIGNED_TEAM_ID;
00694 }
00695 void TeamBalancer::MoveMemberThatWantsToJoinTeam(TeamId teamId)
00696 {
00697 RakAssert(forceTeamsToBeEven==false && lockTeams==false);
00698
00699 do
00700 {
00701 teamId = MoveMemberThatWantsToJoinTeamInternal(teamId);
00702 } while (teamId!=UNASSIGNED_TEAM_ID);
00703 }
00704 TeamId TeamBalancer::MoveMemberThatWantsToJoinTeamInternal(TeamId teamId)
00705 {
00706 DataStructures::List<TeamId> membersThatWantToJoinTheTeam;
00707 for (TeamId i=0; i < teamMembers.Size(); i++)
00708 {
00709 if (teamMembers[i].requestedTeam==teamId)
00710 membersThatWantToJoinTheTeam.Push(i,__FILE__,__LINE__);
00711 }
00712
00713 if (membersThatWantToJoinTheTeam.Size()>0)
00714 {
00715 TeamId oldTeam;
00716 unsigned int swappedMemberIndex = membersThatWantToJoinTheTeam[ randomMT() % membersThatWantToJoinTheTeam.Size() ];
00717 oldTeam=teamMembers[swappedMemberIndex].currentTeam;
00718 SwitchMemberTeam(swappedMemberIndex,teamId);
00719 NotifyTeamAssigment(swappedMemberIndex);
00720 return oldTeam;
00721 }
00722 return UNASSIGNED_TEAM_ID;
00723 }
00724 void TeamBalancer::NotifyTeamsLocked(RakNetGUID target, TeamId requestedTeam)
00725 {
00726 BitStream bsOut;
00727 bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAMS_LOCKED);
00728 bsOut.Write(requestedTeam);
00729 rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false);
00730 }
00731 void TeamBalancer::NotifyTeamSwitchPending(RakNetGUID target, TeamId requestedTeam)
00732 {
00733 BitStream bsOut;
00734 bsOut.Write((MessageID)ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING);
00735 bsOut.Write(requestedTeam);
00736 rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false);
00737 }
00738 void TeamBalancer::SwapTeamMembersByRequest(unsigned int memberIndex1, unsigned int memberIndex2)
00739 {
00740 TeamId index1Team = teamMembers[memberIndex1].currentTeam;
00741 teamMembers[memberIndex1].currentTeam=teamMembers[memberIndex2].currentTeam;
00742 teamMembers[memberIndex2].currentTeam=index1Team;
00743 teamMembers[memberIndex1].requestedTeam=UNASSIGNED_TEAM_ID;
00744 teamMembers[memberIndex2].requestedTeam=UNASSIGNED_TEAM_ID;
00745 }
00746 void TeamBalancer::NotifyNoTeam(RakNetGUID target)
00747 {
00748 BitStream bsOut;
00749 bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAM_ASSIGNED);
00750 bsOut.Write((unsigned char)UNASSIGNED_TEAM_ID);
00751 rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false);
00752 }
00753 bool TeamBalancer::TeamsWouldBeEvenOnSwitch(TeamId t1, TeamId t2)
00754 {
00755 RakAssert(teamMembers.Size()!=0);
00756 return TeamWouldBeOverpopulatedOnAddition(t1, teamMembers.Size()-1)==false &&
00757 TeamWouldBeUnderpopulatedOnLeave(t2, teamMembers.Size()-1)==false;
00758 }
00759 void TeamBalancer::SetAllowHostMigration(bool allow)
00760 {
00761 allowHostMigration=allow;
00762 }
00763
00764 #endif // _RAKNET_SUPPORT_*