mirror of
https://github.com/celisej567/mcpe.git
synced 2026-01-02 01:48:27 +03:00
2854 lines
97 KiB
C++
2854 lines
97 KiB
C++
/*
|
|
* Copyright (c) 2014, Oculus VR, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
|
|
#include "NativeFeatureIncludes.h"
|
|
#if _RAKNET_SUPPORT_TeamManager==1
|
|
|
|
#include "TeamManager.h"
|
|
#include "BitStream.h"
|
|
#include "MessageIdentifiers.h"
|
|
#include "GetTime.h"
|
|
|
|
using namespace RakNet;
|
|
|
|
|
|
enum TeamManagerOperations
|
|
{
|
|
ID_RUN_UpdateListsToNoTeam,
|
|
ID_RUN_UpdateTeamsRequestedToAny,
|
|
ID_RUN_JoinAnyTeam,
|
|
ID_RUN_JoinRequestedTeam,
|
|
ID_RUN_UpdateTeamsRequestedToNoneAndAddTeam,
|
|
ID_RUN_RemoveFromTeamsRequestedAndAddTeam,
|
|
ID_RUN_AddToRequestedTeams,
|
|
ID_RUN_LeaveTeam,
|
|
ID_RUN_SetMemberLimit,
|
|
ID_RUN_SetJoinPermissions,
|
|
ID_RUN_SetBalanceTeams,
|
|
ID_RUN_SetBalanceTeamsInitial,
|
|
ID_RUN_SerializeWorld,
|
|
};
|
|
|
|
STATIC_FACTORY_DEFINITIONS(TM_TeamMember,TM_TeamMember);
|
|
STATIC_FACTORY_DEFINITIONS(TM_Team,TM_Team);
|
|
STATIC_FACTORY_DEFINITIONS(TeamManager,TeamManager);
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
int TM_World::JoinRequestHelperComp(const TM_World::JoinRequestHelper &key, const TM_World::JoinRequestHelper &data)
|
|
{
|
|
if (key.whenRequestMade < data.whenRequestMade)
|
|
return -1;
|
|
if (key.whenRequestMade > data.whenRequestMade)
|
|
return 1;
|
|
if (key.requestIndex < data.requestIndex)
|
|
return -1;
|
|
if (key.requestIndex > data.requestIndex)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamSelection::TeamSelection() {}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamSelection::TeamSelection(JoinTeamType itt) : joinTeamType(itt) {}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamSelection::TeamSelection(JoinTeamType itt, TM_Team *param) : joinTeamType(itt) {teamParameter.specificTeamToJoin=param;}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamSelection::TeamSelection(JoinTeamType itt, NoTeamId param) : joinTeamType(itt) {teamParameter.noTeamSubcategory=param;}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamSelection TeamSelection::AnyAvailable(void) {return TeamSelection(JOIN_ANY_AVAILABLE_TEAM);}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamSelection TeamSelection::SpecificTeam(TM_Team *specificTeamToJoin) {return TeamSelection(JOIN_SPECIFIC_TEAM, specificTeamToJoin);}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamSelection TeamSelection::NoTeam(NoTeamId noTeamSubcategory) {return TeamSelection(JOIN_NO_TEAM, noTeamSubcategory);}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_TeamMember::TM_TeamMember()
|
|
{
|
|
networkId=0;
|
|
world=0;
|
|
joinTeamType=JOIN_NO_TEAM;
|
|
noTeamSubcategory=0;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_TeamMember::~TM_TeamMember()
|
|
{
|
|
if (world)
|
|
{
|
|
world->DereferenceTeamMember(this);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::RequestTeam(TeamSelection teamSelection)
|
|
{
|
|
if (teamSelection.joinTeamType==JOIN_NO_TEAM)
|
|
{
|
|
// If joining no team:
|
|
// - If already no team, and no team category is the same, return false.
|
|
// - Execute JoinNoTeam() locally. Return ID_TEAM_BALANCER_TEAM_ASSIGNED locally.
|
|
// - If we are host, broadcast event. Done.
|
|
// - Send to remote host event to call JoinNoTeam()
|
|
// - remote Host executes JoinNoTeam() and broadcasts event. This may cause may cause rebalance if team balancing is on.
|
|
// - - JoinNoTeam(): Remove from all current and requested teams. Set no-team category.
|
|
|
|
if (teams.Size()==0 && noTeamSubcategory==teamSelection.teamParameter.noTeamSubcategory)
|
|
{
|
|
// No change
|
|
return false;
|
|
}
|
|
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_UpdateListsToNoTeam);
|
|
bsOut.Write(world->GetWorldId());
|
|
bsOut.Write(networkId);
|
|
bsOut.Write(teamSelection.teamParameter.noTeamSubcategory);
|
|
world->BroadcastToParticipants(&bsOut, UNASSIGNED_RAKNET_GUID);
|
|
|
|
StoreLastTeams();
|
|
|
|
UpdateListsToNoTeam(teamSelection.teamParameter.noTeamSubcategory);
|
|
|
|
world->GetTeamManager()->PushTeamAssigned(this);
|
|
if (world->GetHost()==world->GetTeamManager()->GetMyGUIDUnified())
|
|
{
|
|
world->FillRequestedSlots();
|
|
world->EnforceTeamBalance(teamSelection.teamParameter.noTeamSubcategory);
|
|
}
|
|
}
|
|
else if (teamSelection.joinTeamType==JOIN_ANY_AVAILABLE_TEAM)
|
|
{
|
|
// If joining any team
|
|
// Execute JoinAnyTeamCheck()
|
|
// - JoinAnyTeamCheck():
|
|
// - - If already on a team, return false
|
|
// - - If any team is already in requested teams, return false.
|
|
// On local, call UpdateTeamsRequestedToAny(). Send event to also execute this to remote host
|
|
// If we are host, execute JoinAnyTeam(myguid).
|
|
// - JoinAnyTeam(requesterGuid): Attempt to join any team immediately. If fails, send to all except requestGuid UpdateTeamsRequestedToAny(). Else sends out new team, including to caller.
|
|
// On remote host, execute JoinAnyTeamCheck(). If fails, this was because you were added to a team simultaneously on host. This is OK, just ignore the call.
|
|
// Assuming JoinAnyTeamCheck() passed on remote host, call UpdateTeamsRequestedToAny() for this player. execute JoinAnyTeam(packet->guid).
|
|
|
|
if (JoinAnyTeamCheck()==false)
|
|
return false;
|
|
|
|
UpdateTeamsRequestedToAny();
|
|
|
|
// Send request to host to execute JoinAnyTeam()
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_JoinAnyTeam);
|
|
bsOut.Write(world->GetWorldId());
|
|
bsOut.Write(networkId);
|
|
world->GetTeamManager()->SendUnified(&bsOut,HIGH_PRIORITY, RELIABLE_ORDERED, 0, world->GetHost(), false);
|
|
}
|
|
else
|
|
{
|
|
RakAssert(teamSelection.joinTeamType==JOIN_SPECIFIC_TEAM);
|
|
|
|
// If joining specific team
|
|
// Execute JoinSpecificTeamCheck()
|
|
// JoinSpecificTeamCheck():
|
|
// - If already on specific team, return false
|
|
// - If specific team is in requested list, return false
|
|
// On local, call AddToRequestedTeams(). Send event to also execute this to remote host
|
|
// If we are host, execute JoinSpecificTeam(myguid)
|
|
// - JoinSpecificTeam(requesterGuid): Attempt to join specific team immediately. If fails, send to all except requesterGuid to execute AddSpecificToRequested(). Else sends out new team, including to caller.
|
|
// On remote host, execute JoinSpecificTeamCheck(). If fails, just ignore.
|
|
// Assuming JoinSpecificTeamCheck() passed on host, call AddSpecificToRequestedList(). Execute JoinSpecificTeam(packet->guid)
|
|
|
|
if (JoinSpecificTeamCheck(teamSelection.teamParameter.specificTeamToJoin,false)==false)
|
|
return false;
|
|
|
|
AddToRequestedTeams(teamSelection.teamParameter.specificTeamToJoin);
|
|
|
|
// Send request to host to execute JoinRequestedTeam()
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_JoinRequestedTeam);
|
|
bsOut.Write(world->GetWorldId());
|
|
bsOut.Write(networkId);
|
|
bsOut.Write(teamSelection.teamParameter.specificTeamToJoin->GetNetworkID());
|
|
bsOut.Write(false);
|
|
world->GetTeamManager()->SendUnified(&bsOut,HIGH_PRIORITY, RELIABLE_ORDERED, 0, world->GetHost(), false);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::RequestTeamSwitch(TM_Team *teamToJoin, TM_Team *teamToLeave)
|
|
{
|
|
if (SwitchSpecificTeamCheck(teamToJoin,teamToLeave,false)==false)
|
|
return false;
|
|
|
|
AddToRequestedTeams(teamToJoin, teamToLeave);
|
|
|
|
// Send request to host to execute JoinRequestedTeam()
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_JoinRequestedTeam);
|
|
bsOut.Write(world->GetWorldId());
|
|
bsOut.Write(networkId);
|
|
bsOut.Write(teamToJoin->GetNetworkID());
|
|
bsOut.Write(true);
|
|
if (teamToLeave)
|
|
{
|
|
bsOut.Write(true);
|
|
bsOut.Write(teamToLeave->GetNetworkID());
|
|
}
|
|
else
|
|
{
|
|
bsOut.Write(false);
|
|
}
|
|
world->GetTeamManager()->SendUnified(&bsOut,HIGH_PRIORITY, RELIABLE_ORDERED, 0, world->GetHost(), false);
|
|
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamSelection TM_TeamMember::GetRequestedTeam(void) const
|
|
{
|
|
if (teamsRequested.Size()>0)
|
|
return TeamSelection::SpecificTeam(teamsRequested[0].requested);
|
|
else if (joinTeamType==JOIN_NO_TEAM)
|
|
return TeamSelection::NoTeam(noTeamSubcategory);
|
|
else
|
|
return TeamSelection::AnyAvailable();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::GetRequestedSpecificTeams(DataStructures::List<TM_Team*> &requestedTeams) const
|
|
{
|
|
requestedTeams.Clear(true, _FILE_AND_LINE_);
|
|
for (unsigned int i=0; i < teamsRequested.Size(); i++)
|
|
requestedTeams.Push(teamsRequested[i].requested, _FILE_AND_LINE_);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::HasRequestedTeam(TM_Team *team) const
|
|
{
|
|
unsigned int i = GetRequestedTeamIndex(team);
|
|
if (i==(unsigned int)-1)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned int TM_TeamMember::GetRequestedTeamIndex(TM_Team *team) const
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < teamsRequested.Size(); i++)
|
|
{
|
|
if (teamsRequested[i].requested==team)
|
|
return i;
|
|
}
|
|
return (unsigned int) -1;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned int TM_TeamMember::GetRequestedTeamCount(void) const
|
|
{
|
|
return teamsRequested.Size();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::CancelTeamRequest(TM_Team *specificTeamToCancel)
|
|
{
|
|
if (RemoveFromRequestedTeams(specificTeamToCancel)==false)
|
|
return false;
|
|
|
|
// Send request to host to execute JoinRequestedTeam()
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED);
|
|
bsOut.Write(world->GetWorldId());
|
|
bsOut.Write(networkId);
|
|
if (specificTeamToCancel)
|
|
{
|
|
bsOut.Write(true);
|
|
bsOut.Write(specificTeamToCancel->GetNetworkID());
|
|
}
|
|
else
|
|
{
|
|
bsOut.Write(false);
|
|
}
|
|
world->BroadcastToParticipants(&bsOut, UNASSIGNED_RAKNET_GUID);
|
|
|
|
world->GetTeamManager()->PushBitStream(&bsOut);
|
|
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::LeaveTeam(TM_Team* team, NoTeamId _noTeamSubcategory)
|
|
{
|
|
if (LeaveTeamCheck(team)==false)
|
|
return false;
|
|
|
|
RemoveFromSpecificTeamInternal(team);
|
|
if (teams.Size()==0)
|
|
{
|
|
noTeamSubcategory=_noTeamSubcategory;
|
|
joinTeamType=JOIN_NO_TEAM;
|
|
}
|
|
|
|
// Execute LeaveTeamCheck()
|
|
// - LeaveTeamCheck():
|
|
// - - If not on this team, return false
|
|
// On local, call RemoteFromTeamsList(). Send event to also execute this to remote host
|
|
// If we are host, execute OnLeaveTeamEvent(myGuid)
|
|
// - OnLeaveTeamEvent(requesterGuid):
|
|
// - - If rebalancing is active, rebalance
|
|
// - - If someone else wants to join this team, let them.
|
|
// - - Send leave team event notification to all except requesterGuid-
|
|
// On remote host, execute LeaveTeamCheck(). If fails, ignore.
|
|
// On remote host, execute RemoteFromTeamsList() followed by OnLeaveTeamEvent(packet->guid)
|
|
|
|
// Pattern:
|
|
// Execute local check, if fails return false
|
|
// Locally execute non-host guaranteed changes
|
|
// If local system is also host, execute host changes. Relay to all but local
|
|
// On remote host, execute check. If check passes, execute non-host changes, followed by host changes. Relay to all but sender.
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_LeaveTeam);
|
|
bsOut.Write(world->GetWorldId());
|
|
bsOut.Write(networkId);
|
|
bsOut.Write(team->GetNetworkID());
|
|
bsOut.Write(noTeamSubcategory);
|
|
world->BroadcastToParticipants(&bsOut, UNASSIGNED_RAKNET_GUID);
|
|
|
|
if (world->GetHost()==world->GetTeamManager()->GetMyGUIDUnified())
|
|
{
|
|
// Rebalance teams
|
|
world->FillRequestedSlots();
|
|
world->EnforceTeamBalance(noTeamSubcategory);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::LeaveAllTeams(NoTeamId noTeamSubcategory)
|
|
{
|
|
return RequestTeam(TeamSelection::NoTeam(noTeamSubcategory));
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_Team* TM_TeamMember::GetCurrentTeam(void) const
|
|
{
|
|
if (teams.Size()>0)
|
|
return teams[0];
|
|
return 0;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned int TM_TeamMember::GetCurrentTeamCount(void) const
|
|
{
|
|
return teams.Size();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_Team* TM_TeamMember::GetCurrentTeamByIndex(unsigned int index)
|
|
{
|
|
return teams[index];
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::GetCurrentTeams(DataStructures::List<TM_Team*> &_teams) const
|
|
{
|
|
_teams=teams;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::GetLastTeams(DataStructures::List<TM_Team*> &_teams) const
|
|
{
|
|
_teams=lastTeams;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::IsOnTeam(TM_Team *team) const
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < teams.Size(); i++)
|
|
{
|
|
if (teams[i]==team)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
NetworkID TM_TeamMember::GetNetworkID(void) const
|
|
{
|
|
return networkId;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_World* TM_TeamMember::GetTM_World(void) const
|
|
{
|
|
return world;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::SerializeConstruction(BitStream *constructionBitstream)
|
|
{
|
|
// Write requested teams
|
|
constructionBitstream->Write(world->GetWorldId());
|
|
constructionBitstream->Write(networkId);
|
|
constructionBitstream->WriteCasted<uint16_t>(teamsRequested.Size());
|
|
for (unsigned int i=0; i < teamsRequested.Size(); i++)
|
|
{
|
|
constructionBitstream->Write(teamsRequested[i].isTeamSwitch);
|
|
if (teamsRequested[i].teamToLeave)
|
|
{
|
|
constructionBitstream->Write(true);
|
|
constructionBitstream->Write(teamsRequested[i].teamToLeave->GetNetworkID());
|
|
}
|
|
else
|
|
{
|
|
constructionBitstream->Write(false);
|
|
}
|
|
if (teamsRequested[i].requested)
|
|
{
|
|
constructionBitstream->Write(true);
|
|
constructionBitstream->Write(teamsRequested[i].requested->GetNetworkID());
|
|
}
|
|
else
|
|
{
|
|
constructionBitstream->Write(false);
|
|
}
|
|
}
|
|
|
|
world->teamManager->EncodeTeamAssigned(constructionBitstream, this);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::DeserializeConstruction(TeamManager *teamManager, BitStream *constructionBitstream)
|
|
{
|
|
// Read requested teams
|
|
bool success;
|
|
uint16_t teamsRequestedSize;
|
|
|
|
WorldId worldId;
|
|
constructionBitstream->Read(worldId);
|
|
TM_World *world = teamManager->GetWorldWithId(worldId);
|
|
RakAssert(world);
|
|
constructionBitstream->Read(networkId);
|
|
world->ReferenceTeamMember(this,networkId);
|
|
|
|
success=constructionBitstream->Read(teamsRequestedSize);
|
|
for (unsigned int i=0; i < teamsRequestedSize; i++)
|
|
{
|
|
RequestedTeam rt;
|
|
rt.isTeamSwitch=false;
|
|
rt.requested=0;
|
|
rt.whenRequested=0;
|
|
constructionBitstream->Read(rt.isTeamSwitch);
|
|
bool hasTeamToLeave=false;
|
|
constructionBitstream->Read(hasTeamToLeave);
|
|
NetworkID teamToLeaveId;
|
|
if (hasTeamToLeave)
|
|
{
|
|
constructionBitstream->Read(teamToLeaveId);
|
|
rt.teamToLeave = world->GetTeamByNetworkID(teamToLeaveId);
|
|
RakAssert(rt.teamToLeave);
|
|
}
|
|
else
|
|
rt.teamToLeave=0;
|
|
bool hasTeamRequested=false;
|
|
success=constructionBitstream->Read(hasTeamRequested);
|
|
NetworkID teamRequestedId;
|
|
if (hasTeamRequested)
|
|
{
|
|
success=constructionBitstream->Read(teamRequestedId);
|
|
rt.requested = world->GetTeamByNetworkID(teamRequestedId);
|
|
RakAssert(rt.requested);
|
|
}
|
|
rt.whenRequested=RakNet::GetTime();
|
|
rt.requestIndex=world->teamRequestIndex++; // In case whenRequested is the same between two teams when sorting team requests
|
|
if (
|
|
(hasTeamToLeave==false || (hasTeamToLeave==true && rt.teamToLeave!=0)) &&
|
|
(hasTeamRequested==false || (hasTeamRequested==true && rt.requested!=0))
|
|
)
|
|
{
|
|
teamsRequested.Push(rt, _FILE_AND_LINE_);
|
|
}
|
|
}
|
|
|
|
|
|
if (success)
|
|
world->teamManager->ProcessTeamAssigned(constructionBitstream);
|
|
return success;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void *TM_TeamMember::GetOwner(void) const
|
|
{
|
|
return owner;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::SetOwner(void *o)
|
|
{
|
|
owner=o;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
NoTeamId TM_TeamMember::GetNoTeamId(void) const
|
|
{
|
|
return noTeamSubcategory;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned int TM_TeamMember::GetWorldIndex(void) const
|
|
{
|
|
return world->GetTeamMemberIndex(this);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned long TM_TeamMember::ToUint32( const NetworkID &g )
|
|
{
|
|
return g & 0xFFFFFFFF;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::UpdateListsToNoTeam(NoTeamId nti)
|
|
{
|
|
teamsRequested.Clear(true, _FILE_AND_LINE_ );
|
|
for (unsigned int i=0; i < teams.Size(); i++)
|
|
{
|
|
teams[i]->RemoveFromTeamMemberList(this);
|
|
}
|
|
teams.Clear(true, _FILE_AND_LINE_ );
|
|
noTeamSubcategory=nti;
|
|
joinTeamType=JOIN_NO_TEAM;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::JoinAnyTeamCheck(void) const
|
|
{
|
|
// - - If already on a team, return false
|
|
if (teams.Size() > 0)
|
|
return false;
|
|
|
|
// - - If any team is already in requested teams, return false.
|
|
if (teamsRequested.Size()==0 && joinTeamType==JOIN_ANY_AVAILABLE_TEAM)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::JoinSpecificTeamCheck(TM_Team *specificTeamToJoin, bool ignoreRequested) const
|
|
{
|
|
// - If already on specific team, return false
|
|
if (IsOnTeam(specificTeamToJoin))
|
|
return false;
|
|
|
|
if (ignoreRequested)
|
|
return true;
|
|
|
|
unsigned int i;
|
|
for (i=0; i < teamsRequested.Size(); i++)
|
|
{
|
|
if (teamsRequested[i].requested==specificTeamToJoin)
|
|
{
|
|
if (teamsRequested[i].isTeamSwitch==true)
|
|
return true; // Turn off team switch
|
|
|
|
// Same thing
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Not in teams requested
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::SwitchSpecificTeamCheck(TM_Team *teamToJoin, TM_Team *teamToLeave, bool ignoreRequested) const
|
|
{
|
|
RakAssert(teamToJoin!=0);
|
|
|
|
// - If already on specific team, return false
|
|
if (IsOnTeam(teamToJoin))
|
|
return false;
|
|
|
|
if (teamToLeave!=0 && IsOnTeam(teamToLeave)==false)
|
|
return false;
|
|
|
|
if (teamToJoin==teamToLeave)
|
|
return false;
|
|
|
|
if (ignoreRequested)
|
|
return true;
|
|
|
|
unsigned int i;
|
|
for (i=0; i < teamsRequested.Size(); i++)
|
|
{
|
|
if (teamsRequested[i].requested==teamToJoin)
|
|
{
|
|
if (teamsRequested[i].isTeamSwitch==false)
|
|
return true; // Different - leave team was off, turn on
|
|
|
|
if (teamsRequested[i].teamToLeave==teamToLeave)
|
|
return false; // Same thing - leave all or a specific team
|
|
|
|
// Change leave team
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Not in teams requested
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::LeaveTeamCheck(TM_Team *team) const
|
|
{
|
|
if (IsOnTeam(team)==false)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::UpdateTeamsRequestedToAny(void)
|
|
{
|
|
teamsRequested.Clear(true, _FILE_AND_LINE_);
|
|
joinTeamType=JOIN_ANY_AVAILABLE_TEAM;
|
|
whenJoinAnyRequested=RakNet::GetTime();
|
|
joinAnyRequestIndex=world->teamRequestIndex++; // In case whenRequested is the same between two teams when sorting team requests
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::UpdateTeamsRequestedToNone(void)
|
|
{
|
|
teamsRequested.Clear(true, _FILE_AND_LINE_);
|
|
joinTeamType=JOIN_NO_TEAM;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::AddToRequestedTeams(TM_Team *teamToJoin)
|
|
{
|
|
RemoveFromRequestedTeams(teamToJoin);
|
|
|
|
RequestedTeam rt;
|
|
rt.isTeamSwitch=false;
|
|
rt.requested=teamToJoin;
|
|
rt.teamToLeave=0;
|
|
rt.whenRequested=RakNet::GetTime();
|
|
rt.requestIndex=world->teamRequestIndex++; // In case whenRequested is the same between two teams when sorting team requests
|
|
teamsRequested.Push(rt, _FILE_AND_LINE_ );
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::AddToRequestedTeams(TM_Team *teamToJoin, TM_Team *teamToLeave)
|
|
{
|
|
RemoveFromRequestedTeams(teamToJoin);
|
|
|
|
RequestedTeam rt;
|
|
rt.isTeamSwitch=true;
|
|
rt.requested=teamToJoin;
|
|
rt.teamToLeave=teamToLeave;
|
|
rt.whenRequested=RakNet::GetTime();
|
|
rt.requestIndex=world->teamRequestIndex++; // In case whenRequested is the same between two teams when sorting team requests
|
|
teamsRequested.Push(rt, _FILE_AND_LINE_ );
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_TeamMember::RemoveFromRequestedTeams(TM_Team *team)
|
|
{
|
|
if (team==0)
|
|
{
|
|
teamsRequested.Clear(true, _FILE_AND_LINE_);
|
|
joinTeamType=JOIN_NO_TEAM;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < teamsRequested.Size(); i++)
|
|
{
|
|
if (teamsRequested[i].requested==team)
|
|
{
|
|
teamsRequested.RemoveAtIndex(i);
|
|
if (teamsRequested.Size()==0)
|
|
{
|
|
joinTeamType=JOIN_NO_TEAM;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::AddToTeamList(TM_Team *team)
|
|
{
|
|
team->teamMembers.Push(this, _FILE_AND_LINE_ );
|
|
teams.Push(team, _FILE_AND_LINE_ );
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::RemoveFromSpecificTeamInternal(TM_Team *team)
|
|
{
|
|
unsigned int i,j;
|
|
for (i=0; i < teams.Size(); i++)
|
|
{
|
|
if (teams[i]==team)
|
|
{
|
|
for (j=0; j < team->teamMembers.Size(); j++)
|
|
{
|
|
if (team->teamMembers[j]==this)
|
|
{
|
|
team->teamMembers.RemoveAtIndex(j);
|
|
break;
|
|
}
|
|
}
|
|
teams.RemoveAtIndex(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::RemoveFromAllTeamsInternal(void)
|
|
{
|
|
TM_Team *team;
|
|
unsigned int i,j;
|
|
for (i=0; i < teams.Size(); i++)
|
|
{
|
|
team = teams[i];
|
|
|
|
for (j=0; j < team->teamMembers.Size(); j++)
|
|
{
|
|
if (team->teamMembers[j]==this)
|
|
{
|
|
team->teamMembers.RemoveAtIndex(j);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
teams.Clear(true, _FILE_AND_LINE_);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_TeamMember::StoreLastTeams(void)
|
|
{
|
|
lastTeams=teams;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_Team::TM_Team()
|
|
{
|
|
ID=0;
|
|
world=0;
|
|
joinPermissions=ALLOW_JOIN_ANY_AVAILABLE_TEAM|ALLOW_JOIN_SPECIFIC_TEAM|ALLOW_JOIN_REBALANCING;
|
|
balancingApplies=true;
|
|
teamMemberLimit=65535;
|
|
owner=0;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_Team::~TM_Team()
|
|
{
|
|
if (world)
|
|
world->DereferenceTeam(this, 0);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_Team::SetMemberLimit(TeamMemberLimit _teamMemberLimit, NoTeamId noTeamId)
|
|
{
|
|
if (teamMemberLimit==_teamMemberLimit)
|
|
return false;
|
|
|
|
teamMemberLimit=_teamMemberLimit;
|
|
// Network this as request to host
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_SetMemberLimit);
|
|
bsOut.Write(world->GetWorldId());
|
|
bsOut.Write(GetNetworkID());
|
|
bsOut.Write(teamMemberLimit);
|
|
bsOut.Write(noTeamId);
|
|
world->GetTeamManager()->Send(&bsOut, world->GetHost(), false);
|
|
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamMemberLimit TM_Team::GetMemberLimit(void) const
|
|
{
|
|
if (world->GetBalanceTeams()==false)
|
|
{
|
|
return teamMemberLimit;
|
|
}
|
|
else
|
|
{
|
|
TeamMemberLimit limitWithBalancing=world->GetBalancedTeamLimit();
|
|
if (limitWithBalancing < teamMemberLimit)
|
|
return limitWithBalancing;
|
|
return teamMemberLimit;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamMemberLimit TM_Team::GetMemberLimitSetting(void) const
|
|
{
|
|
return teamMemberLimit;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_Team::SetJoinPermissions(JoinPermissions _joinPermissions)
|
|
{
|
|
if (joinPermissions==_joinPermissions)
|
|
return false;
|
|
|
|
joinPermissions=_joinPermissions;
|
|
|
|
// Network this as request to host
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_SetJoinPermissions);
|
|
bsOut.Write(world->GetWorldId());
|
|
bsOut.Write(GetNetworkID());
|
|
bsOut.Write(_joinPermissions);
|
|
world->GetTeamManager()->Send(&bsOut,world->GetHost(), false);
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
JoinPermissions TM_Team::GetJoinPermissions(void) const
|
|
{
|
|
return joinPermissions;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_Team::LeaveTeam(TM_TeamMember* teamMember, NoTeamId noTeamSubcategory)
|
|
{
|
|
teamMember->LeaveTeam(this, noTeamSubcategory);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_Team::GetBalancingApplies(void) const
|
|
{
|
|
return balancingApplies;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_Team::GetTeamMembers(DataStructures::List<TM_TeamMember*> &_teamMembers) const
|
|
{
|
|
_teamMembers=teamMembers;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned int TM_Team::GetTeamMembersCount(void) const
|
|
{
|
|
return teamMembers.Size();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_TeamMember *TM_Team::GetTeamMemberByIndex(unsigned int index) const
|
|
{
|
|
return teamMembers[index];
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
NetworkID TM_Team::GetNetworkID(void) const
|
|
{
|
|
return ID;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_World* TM_Team::GetTM_World(void) const
|
|
{
|
|
return world;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_Team::SerializeConstruction(BitStream *constructionBitstream)
|
|
{
|
|
// Do not need to serialize member lists, the team members do this
|
|
constructionBitstream->Write(world->GetWorldId());
|
|
constructionBitstream->Write(ID);
|
|
constructionBitstream->Write(joinPermissions);
|
|
constructionBitstream->Write(balancingApplies);
|
|
constructionBitstream->Write(teamMemberLimit);
|
|
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_Team::DeserializeConstruction(TeamManager *teamManager, BitStream *constructionBitstream)
|
|
{
|
|
WorldId worldId;
|
|
constructionBitstream->Read(worldId);
|
|
TM_World *world = teamManager->GetWorldWithId(worldId);
|
|
RakAssert(world);
|
|
constructionBitstream->Read(ID);
|
|
constructionBitstream->Read(joinPermissions);
|
|
constructionBitstream->Read(balancingApplies);
|
|
bool b = constructionBitstream->Read(teamMemberLimit);
|
|
RakAssert(b);
|
|
if (b)
|
|
{
|
|
world->ReferenceTeam(this,ID,balancingApplies);
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned long TM_Team::ToUint32( const NetworkID &g )
|
|
{
|
|
return g & 0xFFFFFFFF;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void *TM_Team::GetOwner(void) const
|
|
{
|
|
return owner;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
unsigned int TM_Team::GetWorldIndex(void) const
|
|
{
|
|
return world->GetTeamIndex(this);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_Team::SetOwner(void *o)
|
|
{
|
|
owner=o;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_Team::RemoveFromTeamMemberList(TM_TeamMember *teamMember)
|
|
{
|
|
unsigned int index = teamMembers.GetIndexOf(teamMember);
|
|
RakAssert(index != (unsigned int) -1);
|
|
teamMembers.RemoveAtIndex(index);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned int TM_Team::GetMemberWithRequestedSingleTeamSwitch(TM_Team *team)
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < teamMembers.Size(); i++)
|
|
{
|
|
if (teamMembers[i]->GetCurrentTeamCount()==1)
|
|
{
|
|
unsigned int j = teamMembers[i]->GetRequestedTeamIndex(team);
|
|
if (j!=(unsigned int)-1)
|
|
{
|
|
if (teamMembers[i]->teamsRequested[j].isTeamSwitch &&
|
|
(teamMembers[i]->teamsRequested[j].teamToLeave==0 ||
|
|
teamMembers[i]->teamsRequested[j].teamToLeave==teamMembers[i]->teams[0])
|
|
)
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
return (unsigned int) -1;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_World::TM_World()
|
|
{
|
|
teamManager=0;
|
|
balanceTeamsIsActive=false;
|
|
hostGuid=UNASSIGNED_RAKNET_GUID;
|
|
worldId=0;
|
|
autoAddParticipants=true;
|
|
teamRequestIndex=0;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_World::~TM_World()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamManager *TM_World::GetTeamManager(void) const
|
|
{
|
|
return teamManager;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::AddParticipant(RakNetGUID rakNetGUID)
|
|
{
|
|
participants.Push(rakNetGUID, _FILE_AND_LINE_ );
|
|
|
|
// Send to remote system status of balanceTeamsIsActive
|
|
|
|
if (GetTeamManager()->GetMyGUIDUnified()==GetHost())
|
|
{
|
|
// Actually just transmitting initial value of balanceTeamsIsActive
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_SetBalanceTeamsInitial);
|
|
bsOut.Write(GetWorldId());
|
|
bsOut.Write(balanceTeamsIsActive);
|
|
teamManager->SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0,rakNetGUID, false);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::RemoveParticipant(RakNetGUID rakNetGUID)
|
|
{
|
|
unsigned int i;
|
|
i = participants.GetIndexOf(rakNetGUID);
|
|
if (i!=(unsigned int)-1)
|
|
participants.RemoveAtIndex(i);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::SetAutoManageConnections(bool autoAdd)
|
|
{
|
|
autoAddParticipants=autoAdd;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::GetParticipantList(DataStructures::List<RakNetGUID> &participantList)
|
|
{
|
|
participantList = participants;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::ReferenceTeam(TM_Team *team, NetworkID networkId, bool applyBalancing)
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < teams.Size(); i++)
|
|
{
|
|
if (teams[i]==team)
|
|
return;
|
|
}
|
|
|
|
team->ID=networkId;
|
|
team->balancingApplies=applyBalancing;
|
|
team->world=this;
|
|
|
|
// Add this team to the list of teams
|
|
teams.Push(team, _FILE_AND_LINE_);
|
|
|
|
teamsHash.Push(networkId,team,_FILE_AND_LINE_);
|
|
|
|
// If autobalancing is on, and the team lock state supports it, then call EnforceTeamBalancing()
|
|
if (applyBalancing && balanceTeamsIsActive)
|
|
{
|
|
EnforceTeamBalance(0);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::DereferenceTeam(TM_Team *team, NoTeamId noTeamSubcategory)
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < teams.Size(); i++)
|
|
{
|
|
if (teams[i]==team)
|
|
{
|
|
TM_Team *team = teams[i];
|
|
while (team->teamMembers.Size())
|
|
{
|
|
team->teamMembers[team->teamMembers.Size()-1]->LeaveTeam(team, noTeamSubcategory);
|
|
}
|
|
teams.RemoveAtIndex(i);
|
|
|
|
teamsHash.Remove(team->GetNetworkID(),_FILE_AND_LINE_);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned int TM_World::GetTeamCount(void) const
|
|
{
|
|
return teams.Size();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_Team *TM_World::GetTeamByIndex(unsigned int index) const
|
|
{
|
|
return teams[index];
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_Team *TM_World::GetTeamByNetworkID(NetworkID teamId)
|
|
{
|
|
DataStructures::HashIndex hi = teamsHash.GetIndexOf(teamId);
|
|
if (hi.IsInvalid())
|
|
return 0;
|
|
return teamsHash.ItemAtIndex(hi);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned int TM_World::GetTeamIndex(const TM_Team *team) const
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < teams.Size(); i++)
|
|
{
|
|
if (teams[i]==team)
|
|
return i;
|
|
}
|
|
return (unsigned int) -1;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::ReferenceTeamMember(TM_TeamMember *teamMember, NetworkID networkId)
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < teamMembers.Size(); i++)
|
|
{
|
|
if (teamMembers[i]==teamMember)
|
|
return;
|
|
}
|
|
|
|
teamMember->world=this;
|
|
teamMember->networkId=networkId;
|
|
|
|
teamMembers.Push(teamMember, _FILE_AND_LINE_);
|
|
|
|
teamMembersHash.Push(networkId,teamMember,_FILE_AND_LINE_);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::DereferenceTeamMember(TM_TeamMember *teamMember)
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < teamMembers.Size(); i++)
|
|
{
|
|
if (teamMembers[i]==teamMember)
|
|
{
|
|
teamMembers[i]->UpdateListsToNoTeam(0);
|
|
teamMembersHash.Remove(teamMembers[i]->GetNetworkID(),_FILE_AND_LINE_);
|
|
teamMembers.RemoveAtIndex(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned int TM_World::GetTeamMemberCount(void) const
|
|
{
|
|
return teamMembers.Size();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_TeamMember *TM_World::GetTeamMemberByIndex(unsigned int index) const
|
|
{
|
|
return teamMembers[index];
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
NetworkID TM_World::GetTeamMemberIDByIndex(unsigned int index) const
|
|
{
|
|
return teamMembers[index]->GetNetworkID();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_TeamMember *TM_World::GetTeamMemberByNetworkID(NetworkID teamMemberId)
|
|
{
|
|
DataStructures::HashIndex hi = teamMembersHash.GetIndexOf(teamMemberId);
|
|
if (hi.IsInvalid())
|
|
return 0;
|
|
return teamMembersHash.ItemAtIndex(hi);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned int TM_World::GetTeamMemberIndex(const TM_TeamMember *teamMember) const
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < teamMembers.Size(); i++)
|
|
{
|
|
if (teamMembers[i]==teamMember)
|
|
return i;
|
|
}
|
|
return (unsigned int) -1;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_World::SetBalanceTeams(bool balanceTeams, NoTeamId noTeamId)
|
|
{
|
|
if (balanceTeams==balanceTeamsIsActive)
|
|
return false;
|
|
|
|
balanceTeamsIsActive=balanceTeams;
|
|
|
|
// Network this as request to host
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_SetBalanceTeams);
|
|
bsOut.Write(GetWorldId());
|
|
bsOut.Write(balanceTeams);
|
|
bsOut.Write(noTeamId);
|
|
GetTeamManager()->SendUnified(&bsOut,HIGH_PRIORITY, RELIABLE_ORDERED, 0, GetHost(), false);
|
|
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TM_World::GetBalanceTeams(void) const
|
|
{
|
|
return balanceTeamsIsActive;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::SetHost(RakNetGUID _hostGuid)
|
|
{
|
|
if (hostGuid==_hostGuid)
|
|
return;
|
|
|
|
RakAssert(_hostGuid!=UNASSIGNED_RAKNET_GUID);
|
|
|
|
hostGuid=_hostGuid;
|
|
|
|
if (GetHost()==GetTeamManager()->GetMyGUIDUnified())
|
|
FillRequestedSlots();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
RakNetGUID TM_World::GetHost(void) const
|
|
{
|
|
return hostGuid;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
WorldId TM_World::GetWorldId(void) const
|
|
{
|
|
return worldId;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::Clear(void)
|
|
{
|
|
for (unsigned int i=0; i < teams.Size(); i++)
|
|
{
|
|
teams[i]->world=0;
|
|
}
|
|
for (unsigned int i=0; i < teamMembers.Size(); i++)
|
|
{
|
|
teamMembers[i]->world=0;
|
|
}
|
|
participants.Clear(true, _FILE_AND_LINE_);
|
|
teams.Clear(true, _FILE_AND_LINE_);
|
|
teamMembers.Clear(true, _FILE_AND_LINE_);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
|
|
{
|
|
(void) lostConnectionReason;
|
|
(void) systemAddress;
|
|
|
|
RemoveParticipant(rakNetGUID);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming)
|
|
{
|
|
(void) isIncoming;
|
|
(void) systemAddress;
|
|
|
|
if (autoAddParticipants)
|
|
AddParticipant(rakNetGUID);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::EnforceTeamBalance(NoTeamId noTeamId)
|
|
{
|
|
// Host only function
|
|
RakAssert(GetHost()==GetTeamManager()->GetMyGUIDUnified());
|
|
|
|
KickExcessMembers(noTeamId);
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::KickExcessMembers(NoTeamId noTeamId)
|
|
{
|
|
// Host only function
|
|
RakAssert(GetHost()==GetTeamManager()->GetMyGUIDUnified());
|
|
|
|
// For each team that applies balancing, if the team is overfull, put on a team that is not overfull if the team has ALLOW_JOIN_REBALANCING set
|
|
// If cannot move the player to another team, just take the player off the team and set to noTeamId if they have no team at that point
|
|
|
|
TeamMemberLimit balancedTeamLimit;
|
|
if (balanceTeamsIsActive)
|
|
balancedTeamLimit = GetBalancedTeamLimit();
|
|
else
|
|
balancedTeamLimit = (TeamMemberLimit) -1;
|
|
|
|
TM_Team *team, *teamToJoin;
|
|
unsigned int i, teamIndex;
|
|
for (i=0; i < teams.Size(); i++)
|
|
{
|
|
team = teams[i];
|
|
while (team->GetMemberLimitSetting() < team->GetTeamMembersCount() ||
|
|
(balancedTeamLimit < team->GetTeamMembersCount() && team->GetBalancingApplies()) )
|
|
{
|
|
TM_TeamMember *teamMember = team->teamMembers[team->teamMembers.Size()-1];
|
|
|
|
teamIndex = GetAvailableTeamIndexWithFewestMembers(balancedTeamLimit, ALLOW_JOIN_REBALANCING);
|
|
if (teamIndex == (unsigned int)-1)
|
|
{
|
|
// Move this member to no team
|
|
teamMember->LeaveTeam(team, noTeamId);
|
|
teamManager->PushTeamAssigned(teamMember);
|
|
}
|
|
else
|
|
{
|
|
teamToJoin = teams[teamIndex];
|
|
|
|
// Move this member
|
|
teamMember->StoreLastTeams();
|
|
teamManager->RemoveFromTeamsRequestedAndAddTeam(teamMember, teamToJoin, true, team);
|
|
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_RemoveFromTeamsRequestedAndAddTeam);
|
|
bsOut.Write(GetWorldId());
|
|
bsOut.Write(teamMember->GetNetworkID());
|
|
bsOut.Write(teamToJoin->GetNetworkID());
|
|
bsOut.Write(true);
|
|
bsOut.Write(true);
|
|
bsOut.Write(team->GetNetworkID());
|
|
BroadcastToParticipants(&bsOut, UNASSIGNED_RAKNET_GUID);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::FillRequestedSlots(void)
|
|
{
|
|
// Host only function
|
|
RakAssert(GetHost()==GetTeamManager()->GetMyGUIDUnified());
|
|
|
|
|
|
TeamMemberLimit balancedTeamLimit;
|
|
if (balanceTeamsIsActive)
|
|
balancedTeamLimit = GetBalancedTeamLimit();
|
|
else
|
|
balancedTeamLimit = (TeamMemberLimit) -1;
|
|
|
|
unsigned int teamIndex, indexIntoTeamsRequested = (unsigned int)-1;
|
|
TM_Team *team;
|
|
TM_TeamMember *teamMember;
|
|
DataStructures::OrderedList<TM_World::JoinRequestHelper, TM_World::JoinRequestHelper, JoinRequestHelperComp> joinRequests;
|
|
GetSortedJoinRequests(joinRequests);
|
|
unsigned int joinRequestIndex;
|
|
|
|
for (joinRequestIndex=0; joinRequestIndex < joinRequests.Size(); joinRequestIndex++)
|
|
{
|
|
teamMember = teamMembers[joinRequests[joinRequestIndex].teamMemberIndex];
|
|
if (teamMember->teamsRequested.Size()==0)
|
|
{
|
|
if (teamMember->joinTeamType==JOIN_ANY_AVAILABLE_TEAM)
|
|
teamIndex = GetAvailableTeamIndexWithFewestMembers(balancedTeamLimit, ALLOW_JOIN_ANY_AVAILABLE_TEAM);
|
|
else
|
|
teamIndex=(unsigned int)-1;
|
|
}
|
|
else
|
|
{
|
|
indexIntoTeamsRequested = joinRequests[joinRequestIndex].indexIntoTeamsRequested;
|
|
|
|
team = teamMember->teamsRequested[indexIntoTeamsRequested].requested;
|
|
if (team->GetTeamMembersCount() < balancedTeamLimit &&
|
|
team->GetTeamMembersCount() < team->GetMemberLimitSetting() &&
|
|
(ALLOW_JOIN_SPECIFIC_TEAM & team->GetJoinPermissions())!=0)
|
|
{
|
|
teamIndex=teams.GetIndexOf(team);
|
|
}
|
|
else
|
|
{
|
|
teamIndex=(unsigned int)-1;
|
|
}
|
|
}
|
|
|
|
if (teamIndex != (unsigned int)-1)
|
|
{
|
|
team = teams[teamIndex];
|
|
|
|
if (teamMember->teamsRequested.Size()==0)
|
|
{
|
|
if (teamMember->joinTeamType==JOIN_ANY_AVAILABLE_TEAM)
|
|
{
|
|
// Join any
|
|
teamMember->StoreLastTeams();
|
|
teamMember->UpdateTeamsRequestedToNone();
|
|
teamMember->AddToTeamList(teams[teamIndex]);
|
|
teamManager->PushTeamAssigned(teamMember);
|
|
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_UpdateTeamsRequestedToNoneAndAddTeam);
|
|
bsOut.Write(GetWorldId());
|
|
bsOut.Write(teamMember->GetNetworkID());
|
|
bsOut.Write(team->GetNetworkID());
|
|
BroadcastToParticipants(&bsOut, UNASSIGNED_RAKNET_GUID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Switch or join specific
|
|
DataStructures::List<TM_Team*> teamsWeAreLeaving;
|
|
bool isSwitch = teamMember->teamsRequested[indexIntoTeamsRequested].isTeamSwitch;
|
|
TM_Team *teamToLeave;
|
|
if (isSwitch)
|
|
{
|
|
teamToLeave=teamMember->teamsRequested[indexIntoTeamsRequested].teamToLeave;
|
|
if (teamToLeave)
|
|
{
|
|
if (teamMember->IsOnTeam(teamToLeave))
|
|
{
|
|
teamsWeAreLeaving.Push(teamToLeave, _FILE_AND_LINE_);
|
|
}
|
|
else
|
|
{
|
|
teamToLeave=0;
|
|
isSwitch=false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
teamsWeAreLeaving=teamMember->teams;
|
|
}
|
|
}
|
|
else
|
|
teamToLeave=0;
|
|
|
|
int teamJoined = JoinSpecificTeam(teamMember, team, isSwitch, teamToLeave, teamsWeAreLeaving);
|
|
|
|
if (teamJoined==1)
|
|
{
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_RemoveFromTeamsRequestedAndAddTeam);
|
|
bsOut.Write(GetWorldId());
|
|
bsOut.Write(teamMember->GetNetworkID());
|
|
bsOut.Write(team->GetNetworkID());
|
|
bsOut.Write(isSwitch);
|
|
if (teamToLeave!=0)
|
|
{
|
|
bsOut.Write(true);
|
|
bsOut.Write(teamToLeave->GetNetworkID());
|
|
}
|
|
else
|
|
bsOut.Write(false);
|
|
BroadcastToParticipants(&bsOut, UNASSIGNED_RAKNET_GUID);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned int TM_World::GetAvailableTeamIndexWithFewestMembers(TeamMemberLimit secondaryLimit, JoinPermissions joinPermissions)
|
|
{
|
|
unsigned int teamIndex;
|
|
|
|
unsigned int lowestTeamMembers = (unsigned int) -1;
|
|
unsigned int lowestIndex = (unsigned int) -1;
|
|
|
|
for (teamIndex=0; teamIndex < teams.Size(); teamIndex++)
|
|
{
|
|
if (teams[teamIndex]->GetTeamMembersCount() < secondaryLimit &&
|
|
teams[teamIndex]->GetTeamMembersCount() < teams[teamIndex]->GetMemberLimitSetting() &&
|
|
teams[teamIndex]->GetTeamMembersCount() < lowestTeamMembers &&
|
|
(joinPermissions & teams[teamIndex]->GetJoinPermissions())!=0)
|
|
{
|
|
lowestTeamMembers = teams[teamIndex]->GetTeamMembersCount();
|
|
lowestIndex = teamIndex;
|
|
}
|
|
}
|
|
|
|
return lowestIndex;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::GetSortedJoinRequests(DataStructures::OrderedList<TM_World::JoinRequestHelper, TM_World::JoinRequestHelper, JoinRequestHelperComp> &joinRequests)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i=0; i < teamMembers.Size(); i++)
|
|
{
|
|
TM_TeamMember *teamMember = teamMembers[i];
|
|
if (teamMember->teamsRequested.Size()==0)
|
|
{
|
|
if (teamMember->joinTeamType==JOIN_ANY_AVAILABLE_TEAM)
|
|
{
|
|
TM_World::JoinRequestHelper jrh;
|
|
jrh.whenRequestMade=teamMember->whenJoinAnyRequested;
|
|
jrh.teamMemberIndex=i;
|
|
jrh.requestIndex=teamMember->joinAnyRequestIndex;
|
|
joinRequests.Insert(jrh, jrh, true, _FILE_AND_LINE_);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned int j;
|
|
for (j=0; j < teamMember->teamsRequested.Size(); j++)
|
|
{
|
|
TM_World::JoinRequestHelper jrh;
|
|
jrh.whenRequestMade=teamMember->teamsRequested[j].whenRequested;
|
|
jrh.teamMemberIndex=i;
|
|
jrh.indexIntoTeamsRequested=j;
|
|
jrh.requestIndex=teamMember->teamsRequested[j].requestIndex;
|
|
joinRequests.Insert(jrh, jrh, true, _FILE_AND_LINE_);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::BroadcastToParticipants(RakNet::BitStream *bsOut, RakNetGUID exclusionGuid)
|
|
{
|
|
for (unsigned int i=0; i < participants.Size(); i++)
|
|
{
|
|
if (participants[i]==exclusionGuid)
|
|
continue;
|
|
teamManager->SendUnified(bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, participants[i], false);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TM_World::BroadcastToParticipants(unsigned char *data, const int length, RakNetGUID exclusionGuid)
|
|
{
|
|
for (unsigned int i=0; i < participants.Size(); i++)
|
|
{
|
|
if (participants[i]==exclusionGuid)
|
|
continue;
|
|
teamManager->SendUnified((const char*) data, length, HIGH_PRIORITY, RELIABLE_ORDERED, 0, participants[i], false);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_Team* TM_World::JoinAnyTeam(TM_TeamMember *teamMember, int *resultCode)
|
|
{
|
|
TeamMemberLimit balancedLimit = GetBalancedTeamLimit();
|
|
|
|
unsigned int idx = GetAvailableTeamIndexWithFewestMembers(balancedLimit, ALLOW_JOIN_ANY_AVAILABLE_TEAM);
|
|
if (idx == (unsigned int ) -1)
|
|
{
|
|
// If any team is joinable but full, return full. Otherwise return locked
|
|
for (idx=0; idx < teams.Size(); idx++)
|
|
{
|
|
if ((teams[idx]->GetTeamMembersCount() >= balancedLimit ||
|
|
teams[idx]->GetTeamMembersCount() >= teams[idx]->GetMemberLimitSetting()) &&
|
|
teams[idx]->GetMemberLimitSetting() != 0 &&
|
|
(ALLOW_JOIN_ANY_AVAILABLE_TEAM & teams[idx]->GetJoinPermissions())!=0)
|
|
{
|
|
// Full
|
|
*resultCode=-2;
|
|
return teams[idx];
|
|
}
|
|
}
|
|
|
|
// Locked
|
|
*resultCode=-1;
|
|
return 0;
|
|
}
|
|
|
|
TM_Team* lowestMemberTeam = teams[idx];
|
|
|
|
teamMember->StoreLastTeams();
|
|
teamMember->UpdateTeamsRequestedToNone();
|
|
teamMember->AddToTeamList(lowestMemberTeam);
|
|
teamManager->PushTeamAssigned(teamMember);
|
|
|
|
*resultCode=1;
|
|
return lowestMemberTeam;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
int TM_World::JoinSpecificTeam(TM_TeamMember *teamMember, TM_Team *team, bool isTeamSwitch, TM_Team *teamToLeave, DataStructures::List<TM_Team*> &teamsWeAreLeaving)
|
|
{
|
|
if (team->GetJoinPermissions() & ALLOW_JOIN_SPECIFIC_TEAM)
|
|
{
|
|
if (balanceTeamsIsActive==false || teamsWeAreLeaving.Size()==0)
|
|
{
|
|
if (team->GetMemberLimit() > team->GetTeamMembersCount())
|
|
{
|
|
// Can join normally
|
|
teamMember->StoreLastTeams();
|
|
teamManager->RemoveFromTeamsRequestedAndAddTeam(teamMember, team, isTeamSwitch, teamToLeave);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
// Full
|
|
return -2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Note: balanceTeamsIsActive==true && isTeamSwitch==true
|
|
|
|
// Do limited team swap
|
|
// We must be on one team, target must be on one team, and we want to exchange teams
|
|
if (teamsWeAreLeaving.Size()==1)
|
|
{
|
|
unsigned int j = team->GetMemberWithRequestedSingleTeamSwitch(teamsWeAreLeaving[0]);
|
|
if (j!=(unsigned int)-1)
|
|
{
|
|
TM_TeamMember *swappingMember = team->teamMembers[j];
|
|
teamMember->StoreLastTeams();
|
|
swappingMember->StoreLastTeams();
|
|
teamManager->RemoveFromTeamsRequestedAndAddTeam(teamMember, team, true, 0);
|
|
teamManager->RemoveFromTeamsRequestedAndAddTeam(swappingMember, teamsWeAreLeaving[0], true, 0);
|
|
|
|
// Send ID_TEAM_BALANCER_TEAM_ASSIGNED to all, for swapped member
|
|
// Calling function sends ID_RUN_RemoveFromTeamsRequestedAndAddTeam which pushes ID_TEAM_BALANCER_TEAM_ASSIGNED for teamMember
|
|
RakNet::BitStream bitStream;
|
|
bitStream.WriteCasted<MessageID>(ID_TEAM_BALANCER_TEAM_ASSIGNED);
|
|
teamManager->EncodeTeamAssigned(&bitStream, swappingMember);
|
|
BroadcastToParticipants(&bitStream, UNASSIGNED_RAKNET_GUID);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Full
|
|
return -2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Locked
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamMemberLimit TM_World::GetBalancedTeamLimit(void) const
|
|
{
|
|
if (teams.Size()==0)
|
|
return 0;
|
|
|
|
if (balanceTeamsIsActive==false)
|
|
return (TeamMemberLimit) -1;
|
|
|
|
unsigned int i;
|
|
bool additionalTeamsExcluded;
|
|
TeamMemberLimit balancedLimit;
|
|
unsigned int teamsCount=teams.Size();
|
|
unsigned int membersCount=teamMembers.Size();
|
|
DataStructures::List<TM_Team*> consideredTeams = teams;
|
|
|
|
do
|
|
{
|
|
additionalTeamsExcluded=false;
|
|
balancedLimit = (TeamMemberLimit) ((membersCount+(teamsCount-1))/(teamsCount));
|
|
i=0;
|
|
while (i < consideredTeams.Size())
|
|
{
|
|
if (consideredTeams[i]->GetMemberLimitSetting() < balancedLimit)
|
|
{
|
|
additionalTeamsExcluded=true;
|
|
membersCount-=consideredTeams[i]->GetMemberLimitSetting();
|
|
teamsCount--;
|
|
consideredTeams.RemoveAtIndexFast(i);
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
|
|
} while (additionalTeamsExcluded==true && teamsCount>0);
|
|
|
|
return balancedLimit;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamManager::TeamManager()
|
|
{
|
|
for (unsigned int i=0; i < 255; i++)
|
|
worldsArray[i]=0;
|
|
autoAddParticipants=true;
|
|
topology=TM_PEER_TO_PEER;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TeamManager::~TeamManager()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_World* TeamManager::AddWorld(WorldId worldId)
|
|
{
|
|
RakAssert(worldsArray[worldId]==0 && "World already in use");
|
|
|
|
TM_World *newWorld = RakNet::OP_NEW<TM_World>(_FILE_AND_LINE_);
|
|
newWorld->worldId=worldId;
|
|
newWorld->teamManager=this;
|
|
newWorld->hostGuid=GetMyGUIDUnified();
|
|
worldsArray[worldId]=newWorld;
|
|
worldsList.Push(newWorld,_FILE_AND_LINE_);
|
|
return newWorld;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::RemoveWorld(WorldId worldId)
|
|
{
|
|
RakAssert(worldsArray[worldId]!=0 && "World not in use");
|
|
for (unsigned int i=0; i < worldsList.Size(); i++)
|
|
{
|
|
if (worldsList[i]==worldsArray[worldId])
|
|
{
|
|
RakNet::OP_DELETE(worldsList[i],_FILE_AND_LINE_);
|
|
worldsList.RemoveAtIndexFast(i);
|
|
break;
|
|
}
|
|
}
|
|
worldsArray[worldId]=0;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
unsigned int TeamManager::GetWorldCount(void) const
|
|
{
|
|
return worldsList.Size();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_World* TeamManager::GetWorldAtIndex(unsigned int index) const
|
|
{
|
|
return worldsList[index];
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
TM_World* TeamManager::GetWorldWithId(WorldId worldId) const
|
|
{
|
|
return worldsArray[worldId];
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::SetAutoManageConnections(bool autoAdd)
|
|
{
|
|
autoAddParticipants=autoAdd;
|
|
|
|
for (unsigned int i=0; i < worldsList.Size(); i++)
|
|
{
|
|
worldsList[i]->SetAutoManageConnections(autoAdd);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::SetTopology(TMTopology _topology)
|
|
{
|
|
topology=_topology;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::EncodeTeamFull(RakNet::BitStream *bitStream, TM_TeamMember *teamMember, TM_Team *team)
|
|
{
|
|
bitStream->WriteCasted<MessageID>(ID_TEAM_BALANCER_REQUESTED_TEAM_FULL);
|
|
EncodeTeamFullOrLocked(bitStream, teamMember, team);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::DecomposeTeamFull(Packet *packet,
|
|
TM_World **world, TM_TeamMember **teamMember, TM_Team **team,
|
|
uint16_t ¤tMembers, uint16_t &memberLimitIncludingBalancing, bool &balancingIsActive, JoinPermissions &joinPermissions)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(sizeof(MessageID));
|
|
DecomposeTeamFullOrLocked(&bsIn, world, teamMember, team, currentMembers, memberLimitIncludingBalancing, balancingIsActive, joinPermissions);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::EncodeTeamLocked(RakNet::BitStream *bitStream, TM_TeamMember *teamMember, TM_Team *team)
|
|
{
|
|
bitStream->WriteCasted<MessageID>(ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED);
|
|
EncodeTeamFullOrLocked(bitStream, teamMember, team);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::EncodeTeamFullOrLocked(RakNet::BitStream *bitStream, TM_TeamMember *teamMember, TM_Team *team)
|
|
{
|
|
bitStream->Write(teamMember->world->GetWorldId());
|
|
bitStream->Write(teamMember->GetNetworkID());
|
|
bitStream->Write(team->GetNetworkID());
|
|
bitStream->WriteCasted<uint16_t>(team->GetTeamMembersCount());
|
|
bitStream->Write(team->GetMemberLimit());
|
|
bitStream->Write(team->GetBalancingApplies());
|
|
bitStream->Write(team->GetJoinPermissions());
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::DecomposeTeamFullOrLocked(RakNet::BitStream *bsIn, TM_World **world, TM_TeamMember **teamMember, TM_Team **team,
|
|
uint16_t ¤tMembers, uint16_t &memberLimitIncludingBalancing, bool &balancingIsActive, JoinPermissions &joinPermissions)
|
|
{
|
|
WorldId worldId;
|
|
NetworkID teamMemberId;
|
|
NetworkID teamId;
|
|
|
|
*teamMember=0;
|
|
*team=0;
|
|
*world=0;
|
|
|
|
bsIn->Read(worldId);
|
|
bsIn->Read(teamMemberId);
|
|
bsIn->Read(teamId);
|
|
bsIn->Read(currentMembers);
|
|
bsIn->Read(memberLimitIncludingBalancing);
|
|
bsIn->Read(balancingIsActive);
|
|
bsIn->Read(joinPermissions);
|
|
|
|
*world = GetWorldWithId(worldId);
|
|
if (*world)
|
|
{
|
|
*teamMember = (*world)->GetTeamMemberByNetworkID(teamMemberId);
|
|
*team = (*world)->GetTeamByNetworkID(teamId);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::DecomposeTeamLocked(Packet *packet,
|
|
TM_World **world, TM_TeamMember **teamMember, TM_Team **team,
|
|
uint16_t ¤tMembers, uint16_t &memberLimitIncludingBalancing, bool &balancingIsActive, JoinPermissions &joinPermissions)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(sizeof(MessageID));
|
|
DecomposeTeamFullOrLocked(&bsIn, world, teamMember, team, currentMembers, memberLimitIncludingBalancing, balancingIsActive, joinPermissions);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::EncodeTeamAssigned(RakNet::BitStream *bitStream, TM_TeamMember *teamMember)
|
|
{
|
|
bitStream->Write(teamMember->world->GetWorldId());
|
|
bitStream->Write(teamMember->GetNetworkID());
|
|
bitStream->WriteCasted<uint16_t>(teamMember->teams.Size());
|
|
for (unsigned int i=0; i < teamMember->teams.Size(); i++)
|
|
{
|
|
bitStream->Write(teamMember->teams[i]->GetNetworkID());
|
|
}
|
|
bitStream->Write(teamMember->noTeamSubcategory);
|
|
bitStream->Write(teamMember->joinTeamType);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::ProcessTeamAssigned(RakNet::BitStream *bsIn)
|
|
{
|
|
TM_World *world;
|
|
TM_TeamMember *teamMember;
|
|
NoTeamId noTeamId;
|
|
JoinTeamType joinTeamType;
|
|
DataStructures::List<TM_Team *> newTeam;
|
|
DataStructures::List<TM_Team *> teamsLeft;
|
|
DataStructures::List<TM_Team *> teamsJoined;
|
|
DecodeTeamAssigned(bsIn, &world, &teamMember, noTeamId, joinTeamType, newTeam, teamsLeft, teamsJoined);
|
|
if (teamMember)
|
|
{
|
|
teamMember->StoreLastTeams();
|
|
for (unsigned int i=0; i < teamsLeft.Size(); i++)
|
|
{
|
|
teamMember->RemoveFromSpecificTeamInternal(teamsLeft[i]);
|
|
}
|
|
for (unsigned int i=0; i < teamsJoined.Size(); i++)
|
|
{
|
|
if (teamMember->IsOnTeam(teamsJoined[i])==false)
|
|
{
|
|
teamMember->RemoveFromRequestedTeams(teamsJoined[i]);
|
|
teamMember->AddToTeamList(teamsJoined[i]);
|
|
}
|
|
}
|
|
teamMember->noTeamSubcategory=noTeamId;
|
|
teamMember->joinTeamType=joinTeamType;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::DecodeTeamAssigned(Packet *packet, TM_World **world, TM_TeamMember **teamMember)
|
|
{
|
|
WorldId worldId;
|
|
NetworkID teamMemberId;
|
|
|
|
RakNet::BitStream bsIn(packet->data, packet->length, false);
|
|
bsIn.IgnoreBytes(sizeof(MessageID));
|
|
bsIn.Read(worldId);
|
|
bsIn.Read(teamMemberId);
|
|
*world = GetWorldWithId(worldId);
|
|
if (*world)
|
|
{
|
|
*teamMember = (*world)->GetTeamMemberByNetworkID(teamMemberId);
|
|
}
|
|
else
|
|
{
|
|
*teamMember=0;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::DecodeTeamCancelled(Packet *packet, TM_World **world, TM_TeamMember **teamMember, TM_Team **teamCancelled)
|
|
{
|
|
WorldId worldId;
|
|
NetworkID teamMemberId;
|
|
|
|
RakNet::BitStream bsIn(packet->data, packet->length, false);
|
|
bsIn.IgnoreBytes(sizeof(MessageID));
|
|
bsIn.Read(worldId);
|
|
bsIn.Read(teamMemberId);
|
|
bool sp=false;
|
|
*world = GetWorldWithId(worldId);
|
|
if (*world)
|
|
{
|
|
*teamMember = (*world)->GetTeamMemberByNetworkID(teamMemberId);
|
|
}
|
|
else
|
|
{
|
|
*teamMember=0;
|
|
}
|
|
|
|
bsIn.Read(sp);
|
|
if (sp)
|
|
{
|
|
NetworkID nid;
|
|
bsIn.Read(nid);
|
|
*teamCancelled = (*world)->GetTeamByNetworkID(nid);
|
|
}
|
|
else
|
|
{
|
|
*teamCancelled = 0;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::DecodeTeamAssigned(BitStream *bsIn, TM_World **world, TM_TeamMember **teamMember, NoTeamId &noTeamId,
|
|
JoinTeamType &joinTeamType, DataStructures::List<TM_Team *> &newTeam,
|
|
DataStructures::List<TM_Team *> &teamsLeft, DataStructures::List<TM_Team *> &teamsJoined
|
|
)
|
|
{
|
|
newTeam.Clear(true, _FILE_AND_LINE_);
|
|
teamsLeft.Clear(true, _FILE_AND_LINE_);
|
|
teamsJoined.Clear(true, _FILE_AND_LINE_);
|
|
|
|
WorldId worldId;
|
|
NetworkID teamMemberId;
|
|
NetworkID teamId;
|
|
|
|
bsIn->Read(worldId);
|
|
bsIn->Read(teamMemberId);
|
|
*world = GetWorldWithId(worldId);
|
|
if (*world)
|
|
{
|
|
*teamMember = (*world)->GetTeamMemberByNetworkID(teamMemberId);
|
|
uint16_t teamsCount;
|
|
bsIn->Read(teamsCount);
|
|
|
|
for (unsigned int i=0; i < teamsCount; i++)
|
|
{
|
|
bsIn->Read(teamId);
|
|
TM_Team * team = (*world)->GetTeamByNetworkID(teamId);
|
|
RakAssert(team);
|
|
if (team)
|
|
newTeam.Push(team, _FILE_AND_LINE_);
|
|
// else probably didn't reference team first
|
|
}
|
|
|
|
if (*teamMember)
|
|
{
|
|
for (unsigned int i=0; i < (*teamMember)->teams.Size(); i++)
|
|
{
|
|
TM_Team *team = (*teamMember)->teams[i];
|
|
if (newTeam.GetIndexOf(team)==(unsigned int)-1)
|
|
teamsLeft.Push(team, _FILE_AND_LINE_);
|
|
}
|
|
}
|
|
|
|
for (unsigned int i=0; i < newTeam.Size(); i++)
|
|
{
|
|
TM_Team *team = newTeam[i];
|
|
if ((*teamMember)->teams.GetIndexOf(team)==(unsigned int)-1)
|
|
teamsJoined.Push(team, _FILE_AND_LINE_);
|
|
}
|
|
|
|
bsIn->Read(noTeamId);
|
|
bsIn->Read(joinTeamType);
|
|
}
|
|
else
|
|
{
|
|
*teamMember=0;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::Clear(void)
|
|
{
|
|
for (unsigned int i=0; i < worldsList.Size(); i++)
|
|
{
|
|
worldsArray[worldsList[i]->worldId]=0;
|
|
worldsList[i]->Clear();
|
|
delete worldsList[i];
|
|
}
|
|
worldsList.Clear(false, _FILE_AND_LINE_);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::Update(void)
|
|
{
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
PluginReceiveResult TeamManager::OnReceive(Packet *packet)
|
|
{
|
|
switch (packet->data[0])
|
|
{
|
|
case ID_FCM2_NEW_HOST:
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < worldsList.Size(); i++)
|
|
worldsList[i]->SetHost(packet->guid);
|
|
}
|
|
break;
|
|
case ID_TEAM_BALANCER_TEAM_ASSIGNED:
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(sizeof(MessageID));
|
|
|
|
ProcessTeamAssigned(&bsIn);
|
|
}
|
|
break;
|
|
case ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED:
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(1);
|
|
WorldId worldId;
|
|
bsIn.Read(worldId);
|
|
TM_World *world = GetWorldWithId(worldId);
|
|
if (world==0)
|
|
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
|
bool validPacket = OnRemoveFromRequestedTeams(packet, world);
|
|
if (validPacket==false)
|
|
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
|
break;
|
|
}
|
|
case ID_TEAM_BALANCER_INTERNAL:
|
|
{
|
|
if (packet->length>=2)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2);
|
|
WorldId worldId;
|
|
bsIn.Read(worldId);
|
|
TM_World *world = GetWorldWithId(worldId);
|
|
if (world==0)
|
|
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
|
|
|
switch (packet->data[1])
|
|
{
|
|
case ID_RUN_UpdateListsToNoTeam:
|
|
OnUpdateListsToNoTeam(packet, world);
|
|
break;
|
|
case ID_RUN_UpdateTeamsRequestedToAny:
|
|
OnUpdateTeamsRequestedToAny(packet, world);
|
|
break;
|
|
case ID_RUN_JoinAnyTeam:
|
|
OnJoinAnyTeam(packet, world);
|
|
break;
|
|
case ID_RUN_JoinRequestedTeam:
|
|
OnJoinRequestedTeam(packet, world);
|
|
break;
|
|
case ID_RUN_UpdateTeamsRequestedToNoneAndAddTeam:
|
|
OnUpdateTeamsRequestedToNoneAndAddTeam(packet, world);
|
|
break;
|
|
case ID_RUN_RemoveFromTeamsRequestedAndAddTeam:
|
|
OnRemoveFromTeamsRequestedAndAddTeam(packet, world);
|
|
break;
|
|
case ID_RUN_AddToRequestedTeams:
|
|
OnAddToRequestedTeams(packet, world);
|
|
break;
|
|
case ID_RUN_LeaveTeam:
|
|
OnLeaveTeam(packet, world);
|
|
break;
|
|
case ID_RUN_SetMemberLimit:
|
|
OnSetMemberLimit(packet, world);
|
|
break;
|
|
case ID_RUN_SetJoinPermissions:
|
|
OnSetJoinPermissions(packet, world);
|
|
break;
|
|
case ID_RUN_SetBalanceTeams:
|
|
OnSetBalanceTeams(packet, world);
|
|
break;
|
|
case ID_RUN_SetBalanceTeamsInitial:
|
|
OnSetBalanceTeamsInitial(packet, world);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
|
}
|
|
|
|
return RR_CONTINUE_PROCESSING;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
|
|
{
|
|
for (unsigned int i=0; i < worldsList.Size(); i++)
|
|
{
|
|
worldsList[i]->OnClosedConnection(systemAddress, rakNetGUID, lostConnectionReason);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming)
|
|
{
|
|
for (unsigned int i=0; i < worldsList.Size(); i++)
|
|
{
|
|
worldsList[i]->OnNewConnection(systemAddress, rakNetGUID, isIncoming);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::Send( const RakNet::BitStream * bitStream, const AddressOrGUID systemIdentifier, bool broadcast )
|
|
{
|
|
SendUnified(bitStream,HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, broadcast);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::RemoveFromTeamsRequestedAndAddTeam(TM_TeamMember *teamMember, TM_Team *team, bool isTeamSwitch, TM_Team *teamToLeave)
|
|
{
|
|
teamMember->RemoveFromRequestedTeams(team);
|
|
if (isTeamSwitch)
|
|
{
|
|
if (teamToLeave==0)
|
|
{
|
|
// Leave all teams
|
|
teamMember->RemoveFromAllTeamsInternal();
|
|
}
|
|
else
|
|
{
|
|
// Leave specific team if it exists
|
|
teamMember->RemoveFromSpecificTeamInternal(teamToLeave);
|
|
}
|
|
}
|
|
teamMember->AddToTeamList(team);
|
|
PushTeamAssigned(teamMember);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::PushTeamAssigned(TM_TeamMember *teamMember)
|
|
{
|
|
// Push ID_TEAM_BALANCER_TEAM_ASSIGNED locally
|
|
RakNet::BitStream bitStream;
|
|
bitStream.WriteCasted<MessageID>(ID_TEAM_BALANCER_TEAM_ASSIGNED);
|
|
EncodeTeamAssigned(&bitStream, teamMember);
|
|
|
|
PushBitStream(&bitStream);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::PushBitStream(RakNet::BitStream *bitStream)
|
|
{
|
|
Packet *p = AllocatePacketUnified(bitStream->GetNumberOfBytesUsed());
|
|
memcpy(p->data, bitStream->GetData(), bitStream->GetNumberOfBytesUsed());
|
|
p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS;
|
|
p->systemAddress.systemIndex=(SystemIndex)-1;
|
|
p->guid=UNASSIGNED_RAKNET_GUID;
|
|
p->wasGeneratedLocally=true;
|
|
PushBackPacketUnified(p, true);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnUpdateListsToNoTeam(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2+sizeof(WorldId));
|
|
NetworkID networkId;
|
|
bsIn.Read(networkId);
|
|
TM_TeamMember *teamMember = world->GetTeamMemberByNetworkID(networkId);
|
|
NoTeamId noTeamId;
|
|
bsIn.Read(noTeamId);
|
|
if (teamMember)
|
|
{
|
|
teamMember->StoreLastTeams();
|
|
teamMember->UpdateListsToNoTeam(noTeamId);
|
|
PushTeamAssigned(teamMember);
|
|
|
|
if (world->GetHost()==world->GetTeamManager()->GetMyGUIDUnified())
|
|
{
|
|
world->FillRequestedSlots();
|
|
world->EnforceTeamBalance(noTeamId);
|
|
|
|
if (topology==TM_CLIENT_SERVER)
|
|
{
|
|
// Relay
|
|
world->BroadcastToParticipants(packet->data, packet->length, packet->guid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnUpdateTeamsRequestedToAny(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2+sizeof(WorldId));
|
|
NetworkID networkId;
|
|
bsIn.Read(networkId);
|
|
TM_TeamMember *teamMember = world->GetTeamMemberByNetworkID(networkId);
|
|
if (teamMember)
|
|
{
|
|
teamMember->UpdateTeamsRequestedToAny();
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnJoinAnyTeam(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2+sizeof(WorldId));
|
|
NetworkID networkId;
|
|
bsIn.Read(networkId);
|
|
TM_TeamMember *teamMember = world->GetTeamMemberByNetworkID(networkId);
|
|
if (teamMember)
|
|
{
|
|
// This is a host-only operation
|
|
RakAssert(world->GetHost()==world->GetTeamManager()->GetMyGUIDUnified());
|
|
|
|
teamMember->UpdateTeamsRequestedToAny();
|
|
|
|
int resultCode;
|
|
TM_Team *newTeam = world->JoinAnyTeam(teamMember, &resultCode);
|
|
|
|
if (resultCode==1)
|
|
{
|
|
// Broadcast packet - remote systems should clear requested teams to none, and add the team we joined.
|
|
// Broadcast includes non-host sender (all participants)
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_UpdateTeamsRequestedToNoneAndAddTeam);
|
|
bsOut.Write(world->GetWorldId());
|
|
bsOut.Write(networkId);
|
|
bsOut.Write(newTeam->GetNetworkID());
|
|
world->BroadcastToParticipants(&bsOut, packet->guid);
|
|
|
|
// Send to sender ID_TEAM_BALANCER_TEAM_ASSIGNED
|
|
if (packet->guid!=GetMyGUIDUnified())
|
|
{
|
|
RakNet::BitStream bitStream;
|
|
bitStream.WriteCasted<MessageID>(ID_TEAM_BALANCER_TEAM_ASSIGNED);
|
|
EncodeTeamAssigned(&bitStream, teamMember);
|
|
SendUnified(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->guid, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Relay packet to set requested teams to any
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_UpdateTeamsRequestedToAny);
|
|
bsOut.Write(world->GetWorldId());
|
|
bsOut.Write(networkId);
|
|
world->BroadcastToParticipants(&bsOut, packet->guid);
|
|
|
|
bsOut.Reset();
|
|
if (resultCode==-2)
|
|
{
|
|
EncodeTeamFull(&bsOut, teamMember, newTeam);
|
|
}
|
|
else if (resultCode==-1)
|
|
{
|
|
EncodeTeamLocked(&bsOut, teamMember, newTeam);
|
|
}
|
|
// SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->guid, false);
|
|
world->BroadcastToParticipants(&bsOut, UNASSIGNED_RAKNET_GUID);
|
|
if (packet->guid!=GetMyGUIDUnified())
|
|
PushBitStream(&bsOut);
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnJoinRequestedTeam(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2+sizeof(WorldId));
|
|
NetworkID networkId;
|
|
bsIn.Read(networkId);
|
|
TM_TeamMember *teamMember = world->GetTeamMemberByNetworkID(networkId);
|
|
NetworkID teamToJoinNetworkId;
|
|
bsIn.Read(teamToJoinNetworkId);
|
|
TM_Team *teamToJoin = world->GetTeamByNetworkID(teamToJoinNetworkId);
|
|
bool isTeamSwitch=false;
|
|
bool switchSpecificTeam=false;
|
|
NetworkID teamToLeaveNetworkId=UNASSIGNED_NETWORK_ID;
|
|
TM_Team *teamToLeave=0;
|
|
bsIn.Read(isTeamSwitch);
|
|
if (isTeamSwitch)
|
|
{
|
|
bsIn.Read(switchSpecificTeam);
|
|
if (switchSpecificTeam)
|
|
{
|
|
bsIn.Read(teamToLeaveNetworkId);
|
|
teamToLeave = world->GetTeamByNetworkID(teamToLeaveNetworkId);
|
|
if (teamToLeave==0)
|
|
isTeamSwitch=false;
|
|
}
|
|
}
|
|
if (teamToJoin && teamMember)
|
|
{
|
|
if (isTeamSwitch)
|
|
{
|
|
if (teamMember->SwitchSpecificTeamCheck(teamToJoin, teamToLeave, packet->guid==GetMyGUIDUnified())==false)
|
|
return;
|
|
|
|
teamMember->AddToRequestedTeams(teamToJoin, teamToLeave);
|
|
}
|
|
else
|
|
{
|
|
if (teamMember->JoinSpecificTeamCheck(teamToJoin, packet->guid==GetMyGUIDUnified())==false)
|
|
return;
|
|
|
|
teamMember->AddToRequestedTeams(teamToJoin);
|
|
}
|
|
|
|
DataStructures::List<TM_Team*> teamsWeAreLeaving;
|
|
if (isTeamSwitch)
|
|
{
|
|
if (teamToLeave==0)
|
|
{
|
|
teamsWeAreLeaving=teamMember->teams;
|
|
}
|
|
else
|
|
{
|
|
if (teamMember->IsOnTeam(teamToLeave))
|
|
teamsWeAreLeaving.Push(teamToLeave, _FILE_AND_LINE_);
|
|
}
|
|
|
|
if (teamsWeAreLeaving.Size()==0)
|
|
isTeamSwitch=false;
|
|
}
|
|
|
|
int resultCode = world->JoinSpecificTeam(teamMember, teamToJoin, isTeamSwitch, teamToLeave, teamsWeAreLeaving);
|
|
|
|
if (resultCode==1)
|
|
{
|
|
// Broadcast packet - remote systems should remove from requested teams and add the team we joined.
|
|
// Broadcast includes non-host sender (all participants)
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_RemoveFromTeamsRequestedAndAddTeam);
|
|
bsOut.Write(world->GetWorldId());
|
|
bsOut.Write(networkId);
|
|
bsOut.Write(teamToJoin->GetNetworkID());
|
|
bsOut.Write(isTeamSwitch);
|
|
if (isTeamSwitch)
|
|
{
|
|
bsOut.Write(switchSpecificTeam);
|
|
if (switchSpecificTeam)
|
|
bsOut.Write(teamToLeaveNetworkId);
|
|
}
|
|
world->BroadcastToParticipants(&bsOut, packet->guid);
|
|
|
|
// Send to sender ID_TEAM_BALANCER_TEAM_ASSIGNED
|
|
if (packet->guid!=GetMyGUIDUnified())
|
|
{
|
|
RakNet::BitStream bitStream;
|
|
bitStream.WriteCasted<MessageID>(ID_TEAM_BALANCER_TEAM_ASSIGNED);
|
|
EncodeTeamAssigned(&bitStream, teamMember);
|
|
SendUnified(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->guid, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Relay packet to set requested teams to any
|
|
BitStream bsOut;
|
|
bsOut.WriteCasted<MessageID>(ID_TEAM_BALANCER_INTERNAL);
|
|
bsOut.WriteCasted<MessageID>(ID_RUN_AddToRequestedTeams);
|
|
bsOut.Write(world->GetWorldId());
|
|
bsOut.Write(networkId);
|
|
bsOut.Write(teamToJoin->GetNetworkID());
|
|
bsOut.Write(isTeamSwitch);
|
|
if (isTeamSwitch)
|
|
{
|
|
bsOut.Write(switchSpecificTeam);
|
|
if (switchSpecificTeam)
|
|
bsOut.Write(teamToLeaveNetworkId);
|
|
}
|
|
world->BroadcastToParticipants(&bsOut, packet->guid);
|
|
|
|
bsOut.Reset();
|
|
if (resultCode==-2)
|
|
{
|
|
EncodeTeamFull(&bsOut, teamMember, teamToJoin);
|
|
}
|
|
else if (resultCode==-1)
|
|
{
|
|
EncodeTeamLocked(&bsOut, teamMember, teamToJoin);
|
|
}
|
|
// SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->guid, false);
|
|
|
|
world->BroadcastToParticipants(&bsOut, UNASSIGNED_RAKNET_GUID);
|
|
if (packet->guid!=GetMyGUIDUnified())
|
|
PushBitStream(&bsOut);
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnUpdateTeamsRequestedToNoneAndAddTeam(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2+sizeof(WorldId));
|
|
NetworkID networkId;
|
|
bsIn.Read(networkId);
|
|
TM_TeamMember *teamMember = world->GetTeamMemberByNetworkID(networkId);
|
|
NetworkID teamNetworkId;
|
|
bsIn.Read(teamNetworkId);
|
|
TM_Team *team = world->GetTeamByNetworkID(teamNetworkId);
|
|
|
|
if (team && teamMember)
|
|
{
|
|
teamMember->StoreLastTeams();
|
|
teamMember->UpdateTeamsRequestedToNone();
|
|
teamMember->AddToTeamList(team);
|
|
world->GetTeamManager()->PushTeamAssigned(teamMember);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnRemoveFromTeamsRequestedAndAddTeam(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2+sizeof(WorldId));
|
|
NetworkID networkId;
|
|
bsIn.Read(networkId);
|
|
TM_TeamMember *teamMember = world->GetTeamMemberByNetworkID(networkId);
|
|
NetworkID teamNetworkId;
|
|
bsIn.Read(teamNetworkId);
|
|
bool isTeamSwitch=false, switchSpecificTeam=false;
|
|
NetworkID teamToLeaveNetworkId;
|
|
TM_Team *teamToLeave=0;
|
|
bsIn.Read(isTeamSwitch);
|
|
if (isTeamSwitch)
|
|
{
|
|
bsIn.Read(switchSpecificTeam);
|
|
if (switchSpecificTeam)
|
|
{
|
|
bsIn.Read(teamToLeaveNetworkId);
|
|
teamToLeave = world->GetTeamByNetworkID(teamToLeaveNetworkId);
|
|
}
|
|
}
|
|
|
|
TM_Team *team = world->GetTeamByNetworkID(teamNetworkId);
|
|
if (team && teamMember)
|
|
{
|
|
teamMember->StoreLastTeams();
|
|
if (teamToLeave)
|
|
teamMember->RemoveFromSpecificTeamInternal(teamToLeave);
|
|
else if (isTeamSwitch==true && switchSpecificTeam==false)
|
|
teamMember->RemoveFromAllTeamsInternal();
|
|
RemoveFromTeamsRequestedAndAddTeam(teamMember, team, false, 0);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnAddToRequestedTeams(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2+sizeof(WorldId));
|
|
NetworkID networkId;
|
|
bsIn.Read(networkId);
|
|
TM_TeamMember *teamMember = world->GetTeamMemberByNetworkID(networkId);
|
|
NetworkID teamNetworkId;
|
|
bsIn.Read(teamNetworkId);
|
|
TM_Team *team = world->GetTeamByNetworkID(teamNetworkId);
|
|
|
|
bool isTeamSwitch=false;
|
|
bool switchSpecificTeam=false;
|
|
NetworkID teamToLeaveNetworkId=UNASSIGNED_NETWORK_ID;
|
|
TM_Team *teamToLeave=0;
|
|
bsIn.Read(isTeamSwitch);
|
|
if (isTeamSwitch)
|
|
{
|
|
bsIn.Read(switchSpecificTeam);
|
|
if (switchSpecificTeam)
|
|
{
|
|
bsIn.Read(teamToLeaveNetworkId);
|
|
teamToLeave = world->GetTeamByNetworkID(teamToLeaveNetworkId);
|
|
if (teamToLeave==0)
|
|
isTeamSwitch=false;
|
|
}
|
|
}
|
|
|
|
if (team && teamMember)
|
|
{
|
|
if (isTeamSwitch)
|
|
teamMember->AddToRequestedTeams(team, teamToLeave);
|
|
else
|
|
teamMember->AddToRequestedTeams(team);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
bool TeamManager::OnRemoveFromRequestedTeams(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(1+sizeof(WorldId));
|
|
NetworkID networkId;
|
|
bsIn.Read(networkId);
|
|
TM_TeamMember *teamMember = world->GetTeamMemberByNetworkID(networkId);
|
|
bool hasSpecificTeam=false;
|
|
NetworkID teamNetworkId;
|
|
TM_Team *team;
|
|
bsIn.Read(hasSpecificTeam);
|
|
if (hasSpecificTeam)
|
|
{
|
|
bsIn.Read(teamNetworkId);
|
|
team = world->GetTeamByNetworkID(teamNetworkId);
|
|
if (team==0)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
team=0;
|
|
}
|
|
|
|
if (teamMember)
|
|
{
|
|
teamMember->RemoveFromRequestedTeams(team);
|
|
|
|
// Relay as host
|
|
if (world->GetHost()==world->GetTeamManager()->GetMyGUIDUnified() && topology==TM_CLIENT_SERVER)
|
|
{
|
|
world->BroadcastToParticipants(packet->data, packet->length, packet->guid);
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnLeaveTeam(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2+sizeof(WorldId));
|
|
NetworkID networkId;
|
|
bsIn.Read(networkId);
|
|
TM_TeamMember *teamMember = world->GetTeamMemberByNetworkID(networkId);
|
|
NetworkID teamNetworkId;
|
|
bsIn.Read(teamNetworkId);
|
|
TM_Team *team = world->GetTeamByNetworkID(teamNetworkId);
|
|
NoTeamId noTeamId;
|
|
bsIn.Read(noTeamId);
|
|
|
|
if (team && teamMember)
|
|
{
|
|
if (teamMember->LeaveTeamCheck(team)==false)
|
|
return;
|
|
|
|
teamMember->StoreLastTeams();
|
|
teamMember->RemoveFromSpecificTeamInternal(team);
|
|
if (teamMember->GetCurrentTeamCount()==0)
|
|
{
|
|
teamMember->noTeamSubcategory=noTeamId;
|
|
teamMember->joinTeamType=JOIN_NO_TEAM;
|
|
}
|
|
PushTeamAssigned(teamMember);
|
|
|
|
if (world->GetHost()==world->GetTeamManager()->GetMyGUIDUnified())
|
|
{
|
|
// Rebalance teams
|
|
world->FillRequestedSlots();
|
|
world->EnforceTeamBalance(noTeamId);
|
|
|
|
// Relay as host
|
|
if (topology==TM_CLIENT_SERVER)
|
|
world->BroadcastToParticipants(packet->data, packet->length, packet->guid);
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnSetMemberLimit(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2+sizeof(WorldId));
|
|
NetworkID teamNetworkId;
|
|
bsIn.Read(teamNetworkId);
|
|
TeamMemberLimit teamMemberLimit;
|
|
NoTeamId noTeamId;
|
|
bsIn.Read(teamMemberLimit);
|
|
bsIn.Read(noTeamId);
|
|
|
|
TM_Team *team = world->GetTeamByNetworkID(teamNetworkId);
|
|
if (team)
|
|
{
|
|
team->teamMemberLimit=teamMemberLimit;
|
|
|
|
if (world->GetHost()==world->GetTeamManager()->GetMyGUIDUnified())
|
|
{
|
|
if (packet->guid==GetMyGUIDUnified())
|
|
world->BroadcastToParticipants(packet->data, packet->length, packet->guid);
|
|
else
|
|
world->BroadcastToParticipants(packet->data, packet->length, UNASSIGNED_RAKNET_GUID);
|
|
world->FillRequestedSlots();
|
|
world->KickExcessMembers(noTeamId);
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnSetJoinPermissions(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2+sizeof(WorldId));
|
|
NetworkID teamNetworkId;
|
|
bsIn.Read(teamNetworkId);
|
|
JoinPermissions joinPermissions;
|
|
bsIn.Read(joinPermissions);
|
|
|
|
TM_Team *team = world->GetTeamByNetworkID(teamNetworkId);
|
|
if (team)
|
|
{
|
|
team->joinPermissions=joinPermissions;
|
|
|
|
if (world->GetHost()==world->GetTeamManager()->GetMyGUIDUnified())
|
|
{
|
|
if (packet->guid==GetMyGUIDUnified())
|
|
world->BroadcastToParticipants(packet->data, packet->length, packet->guid);
|
|
else
|
|
world->BroadcastToParticipants(packet->data, packet->length, UNASSIGNED_RAKNET_GUID);
|
|
world->FillRequestedSlots();
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnSetBalanceTeams(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2+sizeof(WorldId));
|
|
bool balanceTeams=false;
|
|
bsIn.Read(balanceTeams);
|
|
NoTeamId noTeamId;
|
|
bsIn.Read(noTeamId);
|
|
|
|
world->balanceTeamsIsActive=balanceTeams;
|
|
if (world->GetHost()==world->GetTeamManager()->GetMyGUIDUnified())
|
|
{
|
|
if (packet->guid==GetMyGUIDUnified())
|
|
world->BroadcastToParticipants(packet->data, packet->length, packet->guid);
|
|
else
|
|
world->BroadcastToParticipants(packet->data, packet->length, UNASSIGNED_RAKNET_GUID);
|
|
|
|
if (balanceTeams)
|
|
world->EnforceTeamBalance(noTeamId);
|
|
else
|
|
world->FillRequestedSlots();
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TeamManager::OnSetBalanceTeamsInitial(Packet *packet, TM_World *world)
|
|
{
|
|
BitStream bsIn(packet->data,packet->length,false);
|
|
bsIn.IgnoreBytes(2+sizeof(WorldId));
|
|
bool balanceTeams=false;
|
|
bsIn.Read(balanceTeams);
|
|
world->balanceTeamsIsActive=balanceTeams;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
#endif // _RAKNET_SUPPORT_TeamManager==1
|
|
|