Files
mcpe/thirdparty/raknet/TeamManager.cpp
iProgramInCpp 9642818a88 * Initial commit.
:)
2023-07-30 22:22:02 +03:00

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 &currentMembers, 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 &currentMembers, 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 &currentMembers, 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