• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

TeamBalancer.cpp

Go to the documentation of this file.
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         // If host guid did not change, return.
00038         if (hostGuid==_hostGuid)
00039                 return;
00040 
00041         hostGuid=_hostGuid;
00042 
00043         // If we never requested a team anyway, return
00044         if (expectingToReceiveTeamNumber==false && currentTeam==UNASSIGNED_TEAM_ID)
00045                 return;
00046 
00047         // Send current team, and currently requested team (if any) to new(?) host
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         // Just update the internal list. Currently active teams are not affected
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         // Just update the default. Currently active teams are not affected.
00074         defaultAssigmentAlgorithm=daa;
00075 }
00076 void TeamBalancer::SetForceEvenTeams(bool force)
00077 {
00078         // Set flag to indicate that teams should be even.
00079         forceTeamsToBeEven=force;
00080 
00081         // If teams are locked, just return.
00082         if (lockTeams==true)
00083                 return;
00084 
00085         if (forceTeamsToBeEven==true)
00086         {
00087                 // Run the even team algorithm
00088                 EvenTeams();
00089         }
00090 }
00091 void TeamBalancer::SetLockTeams(bool lock)
00092 {
00093         if (lock==lockTeams)
00094                 return;
00095 
00096         // Set flag to indicate that teams can no longer be changed.
00097         lockTeams=lock;
00098 
00099         // If lock is false, and teams were set to be forced as even, then run through the even team algorithm
00100         if (lockTeams==false)
00101         {
00102                 // Process even swaps
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                         // Process requested team changes
00128                         // Process movement while not full
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         // Send desiredTeam to the current host.
00147         // Also flag that we have requested a team, and record desiredTeam in case the host changes and it needs to be resent.
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         // Clear out that we have requested a team.
00162         requestedTeam=UNASSIGNED_TEAM_ID;
00163 
00164         // Send packet to the host to remove our request flag.
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         // If we currently have a team, just return (does nothing)
00175         if (GetMyTeam()!=UNASSIGNED_TEAM_ID)
00176                 return;
00177 
00178         // Else send to the current host that we need a team.
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         // Return team returned by last ID_TEAM_BALANCER_TEAM_ASSIGNED packet
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         // Got RequestSpecificTeam
00236         // If teams are locked
00237         // - If this user already has a team, return ID_TEAM_BALANCER_TEAMS_LOCKED
00238         // - 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.
00239         // If teams are not locked
00240         // - 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
00241         // - If the destination team is full, flag this user as wanting to join this team. Return ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING
00242         // - Else, join this team. Return ID_TEAM_BALANCER_TEAM_ASSIGNED
00243 
00244         // Got RequestAnyTeam
00245         // Put user on a team following the algorithm. No team is set as preferred.
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         // If we are the host, and the closed connection has a team, and teams are not locked:
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                                         // - teams were forced to be even, then run the even team algorithm
00271                                         EvenTeams();
00272                                 }
00273                                 else
00274                                 {
00275                                         // - 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)
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                 // Add this system (by GUID) to the list of members if he is not already there
00317                 // Also update his requested team flag.
00318                 // 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.
00319                 if (tm.currentTeam==UNASSIGNED_TEAM_ID)
00320                 {
00321                         // Assign a default team, then add team member
00322                         if (tm.requestedTeam==UNASSIGNED_TEAM_ID)
00323                         {
00324                                 // Assign a default team
00325                                 tm.currentTeam=GetNextDefaultTeam();
00326                         }
00327                         else
00328                         {
00329                                 // Assign to requested team if possible. Otherwise, assign to a default team
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                 // Assign to requested team if possible. Otherwise, assign to a default team
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                 // Assign to requested team if possible. Otherwise, assign to a default team
00429                 if (TeamsWouldBeEvenOnSwitch(tm.requestedTeam,oldTeamThisUserWasOn)==true)
00430                 {
00431                         SwitchMemberTeam(memberIndex,tm.requestedTeam);
00432                         NotifyTeamAssigment(memberIndex);
00433                 }
00434                 else
00435                 {
00436                         // If someone wants to join this user's old team, and we want to join their team, they can swap
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                                 // Full or would not be even
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         // Ensure all teams are even. If not, pick players at random from overpopulated teams, and move to underpopulated teams.
00500         int minMembersOnASingleTeam;
00501         int maxMembersOnASingleTeam;
00502         GetMinMaxTeamMembers(minMembersOnASingleTeam,maxMembersOnASingleTeam);
00503 
00504         // First select among players that have requested to switch teams, if any, before choosing players that did not want to switch teams.
00505         // Players that are moved should be notified of ID_TEAM_BALANCER_TEAM_ASSIGNED
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                         // Tell this member he switched teams
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         // Accounting for team balancing and team limits, get the team a player should be placed on
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         // Accounting for team balancing and team limits, would this team be overpopulated if a member was added to it?
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_*

Generated on Thu Sep 30 2010 01:27:28 for RakNet by  doxygen 1.7.1