diff --git a/datasrc/network.py b/datasrc/network.py index 97855bb9c..5283d7d9b 100644 --- a/datasrc/network.py +++ b/datasrc/network.py @@ -23,6 +23,7 @@ enum TEAM_SPECTATORS=-1, TEAM_RED, TEAM_BLUE, + NUM_TEAMS, FLAG_MISSING=-3, FLAG_ATSTAND, diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 1c76f6556..787010892 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -544,15 +544,6 @@ void CCharacter::ResetInput() void CCharacter::Tick() { - if(m_pPlayer->m_ForceBalanced) - { - char Buf[128]; - str_format(Buf, sizeof(Buf), "You were moved to %s due to team balancing", GameServer()->m_pController->GetTeamName(m_pPlayer->GetTeam())); - GameServer()->SendBroadcast(Buf, m_pPlayer->GetCID()); - - m_pPlayer->m_ForceBalanced = false; - } - m_Core.m_Input = m_Input; m_Core.Tick(true); @@ -723,7 +714,7 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon) { m_Core.m_Vel += Force; - if(GameServer()->m_pController->IsFriendlyFire(m_pPlayer->GetCID(), From) && !g_Config.m_SvTeamdamage) + if(GameServer()->m_pController->IsFriendlyFire(m_pPlayer->GetCID(), From)) return false; // m_pPlayer only inflicts half damage on self diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 5d2f22b8d..2b4b3eafe 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -364,9 +364,9 @@ void CGameContext::CheckPureTuning() if(!m_pController) return; - if( str_comp(m_pController->m_pGameType, "DM")==0 || - str_comp(m_pController->m_pGameType, "TDM")==0 || - str_comp(m_pController->m_pGameType, "CTF")==0) + if( str_comp(m_pController->GetGameType(), "DM")==0 || + str_comp(m_pController->GetGameType(), "TDM")==0 || + str_comp(m_pController->GetGameType(), "CTF")==0) { CTuningParams p; if(mem_comp(&p, &m_Tuning, sizeof(p)) != 0) @@ -398,10 +398,8 @@ void CGameContext::SwapTeams() for(int i = 0; i < MAX_CLIENTS; ++i) { if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) - m_apPlayers[i]->SetTeam(m_apPlayers[i]->GetTeam()^1, false); + m_pController->DoTeamChange(m_apPlayers[i], m_apPlayers[i]->GetTeam()^1, false); } - - (void)m_pController->CheckTeamBalance(); } void CGameContext::OnTick() @@ -506,13 +504,13 @@ void CGameContext::OnTick() #ifdef CONF_DEBUG - if(g_Config.m_DbgDummies) + for(int i = 0; i < MAX_CLIENTS; i++) { - for(int i = 0; i < g_Config.m_DbgDummies ; i++) + if(m_apPlayers[i] && m_apPlayers[i]->IsDummy()) { CNetObj_PlayerInput Input = {0}; Input.m_Direction = (i&1)?-1:1; - m_apPlayers[MAX_CLIENTS-i-1]->OnPredictedInput(&Input); + m_apPlayers[i]->OnPredictedInput(&Input); } } #endif @@ -545,24 +543,12 @@ void CGameContext::OnClientEnter(int ClientID) m_VoteUpdate = true; } -void CGameContext::OnClientConnected(int ClientID) +void CGameContext::OnClientConnected(int ClientID, bool Dummy) { - // Check which team the player should be on - const int StartTeam = g_Config.m_SvTournamentMode ? TEAM_SPECTATORS : m_pController->GetAutoTeam(ClientID); - - m_apPlayers[ClientID] = new(ClientID) CPlayer(this, ClientID, StartTeam); - //players[client_id].init(client_id); - //players[client_id].client_id = client_id; - - (void)m_pController->CheckTeamBalance(); - -#ifdef CONF_DEBUG - if(g_Config.m_DbgDummies) - { - if(ClientID >= MAX_CLIENTS-g_Config.m_DbgDummies) - return; - } -#endif + m_apPlayers[ClientID] = new(ClientID) CPlayer(this, ClientID, Dummy); + + if(Dummy) + return; // send active vote if(m_VoteCloseTime) @@ -577,11 +563,10 @@ void CGameContext::OnClientConnected(int ClientID) void CGameContext::OnClientDrop(int ClientID, const char *pReason) { AbortVoteKickOnDisconnect(ClientID); - m_apPlayers[ClientID]->OnDisconnect(pReason); + m_pController->OnPlayerDisconnect(m_apPlayers[ClientID], pReason); delete m_apPlayers[ClientID]; m_apPlayers[ClientID] = 0; - (void)m_pController->CheckTeamBalance(); m_VoteUpdate = true; // update spectator modes @@ -825,8 +810,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) pPlayer->m_LastSetTeam = Server()->Tick(); if(pPlayer->GetTeam() == TEAM_SPECTATORS || pMsg->m_Team == TEAM_SPECTATORS) m_VoteUpdate = true; - pPlayer->SetTeam(pMsg->m_Team); - (void)m_pController->CheckTeamBalance(); + m_pController->DoTeamChange(pPlayer, pMsg->m_Team); pPlayer->m_TeamChangeTick = Server()->Tick(); } else @@ -1088,8 +1072,7 @@ void CGameContext::ConSetTeam(IConsole::IResult *pResult, void *pUserData) pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); pSelf->m_apPlayers[ClientID]->m_TeamChangeTick = pSelf->Server()->Tick()+pSelf->Server()->TickSpeed()*Delay*60; - pSelf->m_apPlayers[ClientID]->SetTeam(Team); - (void)pSelf->m_pController->CheckTeamBalance(); + pSelf->m_pController->DoTeamChange(pSelf->m_apPlayers[ClientID], Team); } void CGameContext::ConSetTeamAll(IConsole::IResult *pResult, void *pUserData) @@ -1103,9 +1086,7 @@ void CGameContext::ConSetTeamAll(IConsole::IResult *pResult, void *pUserData) for(int i = 0; i < MAX_CLIENTS; ++i) if(pSelf->m_apPlayers[i]) - pSelf->m_apPlayers[i]->SetTeam(Team, false); - - (void)pSelf->m_pController->CheckTeamBalance(); + pSelf->m_pController->DoTeamChange(pSelf->m_apPlayers[i], Team, false); } void CGameContext::ConSwapTeams(IConsole::IResult *pResult, void *pUserData) @@ -1135,26 +1116,24 @@ void CGameContext::ConShuffleTeams(IConsole::IResult *pResult, void *pUserData) if(pSelf->m_apPlayers[i] && pSelf->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) { if(CounterRed == PlayerTeam) - pSelf->m_apPlayers[i]->SetTeam(TEAM_BLUE, false); + pSelf->m_pController->DoTeamChange(pSelf->m_apPlayers[i], TEAM_BLUE, false); else if(CounterBlue == PlayerTeam) - pSelf->m_apPlayers[i]->SetTeam(TEAM_RED, false); + pSelf->m_pController->DoTeamChange(pSelf->m_apPlayers[i], TEAM_RED, false); else { if(rand() % 2) { - pSelf->m_apPlayers[i]->SetTeam(TEAM_BLUE, false); + pSelf->m_pController->DoTeamChange(pSelf->m_apPlayers[i], TEAM_BLUE, false); ++CounterBlue; } else { - pSelf->m_apPlayers[i]->SetTeam(TEAM_RED, false); + pSelf->m_pController->DoTeamChange(pSelf->m_apPlayers[i], TEAM_RED, false); ++CounterRed; } } } } - - (void)pSelf->m_pController->CheckTeamBalance(); } void CGameContext::ConLockTeams(IConsole::IResult *pResult, void *pUserData) @@ -1445,26 +1424,20 @@ void CGameContext::OnConsoleInit() Console()->Chain("sv_motd", ConchainSpecialMotdupdate, this); } -void CGameContext::OnInit(/*class IKernel *pKernel*/) +void CGameContext::OnInit() { + // init everything m_pServer = Kernel()->RequestInterface(); m_pConsole = Kernel()->RequestInterface(); m_World.SetGameServer(this); m_Events.SetGameServer(this); - //if(!data) // only load once - //data = load_data_from_memory(internal_data); - for(int i = 0; i < NUM_NETOBJTYPES; i++) Server()->SnapSetStaticsize(i, m_NetObjHandler.GetObjSize(i)); m_Layers.Init(Kernel()); m_Collision.Init(&m_Layers); - // reset everything here - //world = new GAMEWORLD; - //players = new CPlayer[MAX_CLIENTS]; - // select gametype if(str_comp(g_Config.m_SvGametype, "mod") == 0) m_pController = new CGameControllerMOD(this); @@ -1475,23 +1448,9 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/) else m_pController = new CGameControllerDM(this); - // setup core world - //for(int i = 0; i < MAX_CLIENTS; i++) - // game.players[i].core.world = &game.world.core; - // create all entities from the game layer CMapItemLayerTilemap *pTileMap = m_Layers.GameLayer(); CTile *pTiles = (CTile *)Kernel()->RequestInterface()->GetData(pTileMap->m_Data); - - - - - /* - num_spawn_points[0] = 0; - num_spawn_points[1] = 0; - num_spawn_points[2] = 0; - */ - for(int y = 0; y < pTileMap->m_Height; y++) { for(int x = 0; x < pTileMap->m_Width; x++) @@ -1506,15 +1465,11 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/) } } - //game.world.insert_entity(game.Controller); - #ifdef CONF_DEBUG if(g_Config.m_DbgDummies) { for(int i = 0; i < g_Config.m_DbgDummies ; i++) - { - OnClientConnected(MAX_CLIENTS-i-1); - } + OnClientConnected(MAX_CLIENTS-i-1, true); } #endif } @@ -1565,7 +1520,7 @@ bool CGameContext::IsClientPlayer(int ClientID) return m_apPlayers[ClientID] && m_apPlayers[ClientID]->GetTeam() == TEAM_SPECTATORS ? false : true; } -const char *CGameContext::GameType() { return m_pController && m_pController->m_pGameType ? m_pController->m_pGameType : ""; } +const char *CGameContext::GameType() { return m_pController && m_pController->GetGameType() ? m_pController->GetGameType() : ""; } const char *CGameContext::Version() { return GAME_VERSION; } const char *CGameContext::NetVersion() { return GAME_NETVERSION; } diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 2ffb0ab40..1899895a5 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -162,7 +162,8 @@ public: virtual void OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID); - virtual void OnClientConnected(int ClientID); + virtual void OnClientConnected(int ClientID) { OnClientConnected(ClientID, false); } + void OnClientConnected(int ClientID, bool Dummy); virtual void OnClientEnter(int ClientID); virtual void OnClientDrop(int ClientID, const char *pReason); virtual void OnClientDirectInput(int ClientID, void *pInput); diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp index 7001ca326..d03077185 100644 --- a/src/game/server/gamecontroller.cpp +++ b/src/game/server/gamecontroller.cpp @@ -1,164 +1,257 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include -#include -#include +#include #include "entities/pickup.h" #include "gamecontroller.h" #include "gamecontext.h" -IGameController::IGameController(class CGameContext *pGameServer) +IGameController::IGameController(CGameContext *pGameServer) { m_pGameServer = pGameServer; m_pServer = m_pGameServer->Server(); - m_pGameType = "unknown"; - // - DoWarmup(g_Config.m_SvWarmup); + // balancing + m_UnbalancedTick = TBALANCE_OK; + + // game m_GameOverTick = -1; - m_SuddenDeath = 0; - m_RoundStartTick = Server()->Tick(); m_RoundCount = 0; - m_GameFlags = 0; + m_RoundStartTick = Server()->Tick(); + m_SuddenDeath = 0; m_aTeamscore[TEAM_RED] = 0; m_aTeamscore[TEAM_BLUE] = 0; + DoWarmup(g_Config.m_SvWarmup); + + // info + m_GameFlags = 0; + m_pGameType = "unknown"; + + // map m_aMapWish[0] = 0; - m_UnbalancedTick = -1; - m_ForceBalanced = false; - + // spawn m_aNumSpawnPoints[0] = 0; m_aNumSpawnPoints[1] = 0; m_aNumSpawnPoints[2] = 0; } -IGameController::~IGameController() +//activity +void IGameController::DoActivityCheck() { -} + if(g_Config.m_SvInactiveKickTime == 0) + return; -float IGameController::EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos) -{ - float Score = 0.0f; - CCharacter *pC = static_cast(GameServer()->m_World.FindFirst(CGameWorld::ENTTYPE_CHARACTER)); - for(; pC; pC = (CCharacter *)pC->TypeNext()) + for(int i = 0; i < MAX_CLIENTS; ++i) { - // team mates are not as dangerous as enemies - float Scoremod = 1.0f; - if(pEval->m_FriendlyTeam != -1 && pC->GetPlayer()->GetTeam() == pEval->m_FriendlyTeam) - Scoremod = 0.5f; - - float d = distance(Pos, pC->m_Pos); - Score += Scoremod * (d == 0 ? 1000000000.0f : 1.0f/d); - } - - return Score; -} - -void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type) -{ - // get spawn point - for(int i = 0; i < m_aNumSpawnPoints[Type]; i++) - { - // check if the position is occupado - CCharacter *aEnts[MAX_CLIENTS]; - int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER); - vec2 Positions[5] = { vec2(0.0f, 0.0f), vec2(-32.0f, 0.0f), vec2(0.0f, -32.0f), vec2(32.0f, 0.0f), vec2(0.0f, 32.0f) }; // start, left, up, right, down - int Result = -1; - for(int Index = 0; Index < 5 && Result == -1; ++Index) + if(GameServer()->m_apPlayers[i] && !GameServer()->m_apPlayers[i]->IsDummy() && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS && + !Server()->IsAuthed(i) && (Server()->Tick() > GameServer()->m_apPlayers[i]->m_LastActionTick+g_Config.m_SvInactiveKickTime*Server()->TickSpeed()*60)) { - Result = Index; - for(int c = 0; c < Num; ++c) - if(GameServer()->Collision()->CheckPoint(m_aaSpawnPoints[Type][i]+Positions[Index]) || - distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]+Positions[Index]) <= aEnts[c]->m_ProximityRadius) + switch(g_Config.m_SvInactiveKick) + { + case 0: { - Result = -1; - break; + // move player to spectator + DoTeamChange(GameServer()->m_apPlayers[i], TEAM_SPECTATORS); } - } - if(Result == -1) - continue; // try next spawn point - - vec2 P = m_aaSpawnPoints[Type][i]+Positions[Result]; - float S = EvaluateSpawnPos(pEval, P); - if(!pEval->m_Got || pEval->m_Score > S) - { - pEval->m_Got = true; - pEval->m_Score = S; - pEval->m_Pos = P; + break; + case 1: + { + // move player to spectator if the reserved slots aren't filled yet, kick him otherwise + int Spectators = 0; + for(int j = 0; j < MAX_CLIENTS; ++j) + if(GameServer()->m_apPlayers[j] && GameServer()->m_apPlayers[j]->GetTeam() == TEAM_SPECTATORS) + ++Spectators; + if(Spectators >= g_Config.m_SvSpectatorSlots) + Server()->Kick(i, "Kicked for inactivity"); + else + DoTeamChange(GameServer()->m_apPlayers[i], TEAM_SPECTATORS); + } + break; + case 2: + { + // kick the player + Server()->Kick(i, "Kicked for inactivity"); + } + } } } } -bool IGameController::CanSpawn(int Team, vec2 *pOutPos) +// balancing +bool IGameController::CanBeMovedOnBalance(int ClientID) const { - CSpawnEval Eval; + return true; +} - // spectators can't spawn - if(Team == TEAM_SPECTATORS) - return false; - - if(IsTeamplay()) +void IGameController::CheckTeamBalance() +{ + if(!IsTeamplay() || !g_Config.m_SvTeambalanceTime) { - Eval.m_FriendlyTeam = Team; + m_UnbalancedTick = TBALANCE_OK; + return; + } - // first try own team spawn, then normal spawn and then enemy - EvaluateSpawnType(&Eval, 1+(Team&1)); - if(!Eval.m_Got) - { - EvaluateSpawnType(&Eval, 0); - if(!Eval.m_Got) - EvaluateSpawnType(&Eval, 1+((Team+1)&1)); - } + // calc team sizes + int aPlayerCount[NUM_TEAMS] = {0}; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + aPlayerCount[GameServer()->m_apPlayers[i]->GetTeam()]++; + } + + // check if teams are unbalanced + char aBuf[256]; + if(absolute(aPlayerCount[TEAM_RED]-aPlayerCount[TEAM_BLUE]) >= NUM_TEAMS) + { + str_format(aBuf, sizeof(aBuf), "Teams are NOT balanced (red=%d blue=%d)", aPlayerCount[TEAM_RED], aPlayerCount[TEAM_BLUE]); + if(m_UnbalancedTick <= TBALANCE_OK) + m_UnbalancedTick = Server()->Tick(); } else { - EvaluateSpawnType(&Eval, 0); - EvaluateSpawnType(&Eval, 1); - EvaluateSpawnType(&Eval, 2); + str_format(aBuf, sizeof(aBuf), "Teams are balanced (red=%d blue=%d)", aPlayerCount[TEAM_RED], aPlayerCount[TEAM_BLUE]); + m_UnbalancedTick = TBALANCE_OK; } - - *pOutPos = Eval.m_Pos; - return Eval.m_Got; + GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); } +void IGameController::DoTeamBalance() +{ + if(!IsTeamplay()) + return; + + GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", "Balancing teams"); + + int aPlayerCount[NUM_TEAMS] = {0}; + float aTeamScore[NUM_TEAMS] = {0}; + float aPlayerScore[MAX_CLIENTS] = {0.0f}; + + // gather stats + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + { + aPlayerCount[GameServer()->m_apPlayers[i]->GetTeam()]++; + aPlayerScore[i] = GameServer()->m_apPlayers[i]->m_Score*Server()->TickSpeed()*60.0f/ + (Server()->Tick()-GameServer()->m_apPlayers[i]->m_ScoreStartTick); + aTeamScore[GameServer()->m_apPlayers[i]->GetTeam()] += aPlayerScore[i]; + } + } + + // check if teams are unbalanced + if(absolute(aPlayerCount[TEAM_RED]-aPlayerCount[TEAM_BLUE]) >= NUM_TEAMS) + { + int BiggerTeam = (aPlayerCount[TEAM_RED] > aPlayerCount[TEAM_BLUE]) ? TEAM_RED : TEAM_BLUE; + int NumBalance = absolute(aPlayerCount[TEAM_RED]-aPlayerCount[TEAM_BLUE]) / NUM_TEAMS; + + do + { + CPlayer *pPlayer = 0; + float ScoreDiff = aTeamScore[BiggerTeam]; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(!GameServer()->m_apPlayers[i] || !CanBeMovedOnBalance(i)) + continue; + + // remember the player whom would cause lowest score-difference + if(GameServer()->m_apPlayers[i]->GetTeam() == BiggerTeam && + (!pPlayer || absolute((aTeamScore[BiggerTeam^1]+aPlayerScore[i]) - (aTeamScore[BiggerTeam]-aPlayerScore[i])) < ScoreDiff)) + { + pPlayer = GameServer()->m_apPlayers[i]; + ScoreDiff = absolute((aTeamScore[BiggerTeam^1]+aPlayerScore[i]) - (aTeamScore[BiggerTeam]-aPlayerScore[i])); + } + } + + // move the player to the other team + int Temp = pPlayer->m_LastActionTick; + DoTeamChange(pPlayer, BiggerTeam^1); + pPlayer->m_LastActionTick = Temp; + pPlayer->Respawn(); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "You were moved to %s due to team balancing", GetTeamName(pPlayer->GetTeam())); + GameServer()->SendBroadcast(aBuf, pPlayer->GetCID()); + } + while(--NumBalance); + } + + m_UnbalancedTick = TBALANCE_OK; + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, "Teams have been balanced"); +} + +// event +int IGameController::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon) +{ + // do scoreing + if(!pKiller || Weapon == WEAPON_GAME) + return 0; + if(pKiller == pVictim->GetPlayer()) + pVictim->GetPlayer()->m_Score--; // suicide + else + { + if(IsTeamplay() && pVictim->GetPlayer()->GetTeam() == pKiller->GetTeam()) + pKiller->m_Score--; // teamkill + else + pKiller->m_Score++; // normal kill + } + if(Weapon == WEAPON_SELF) + pVictim->GetPlayer()->m_RespawnTick = Server()->Tick()+Server()->TickSpeed()*3.0f; + return 0; +} + +void IGameController::OnCharacterSpawn(class CCharacter *pChr) +{ + // default health + pChr->IncreaseHealth(10); + + // give default weapons + pChr->GiveWeapon(WEAPON_HAMMER, -1); + pChr->GiveWeapon(WEAPON_GUN, 10); +} bool IGameController::OnEntity(int Index, vec2 Pos) { int Type = -1; int SubType = 0; - if(Index == ENTITY_SPAWN) - m_aaSpawnPoints[0][m_aNumSpawnPoints[0]++] = Pos; - else if(Index == ENTITY_SPAWN_RED) - m_aaSpawnPoints[1][m_aNumSpawnPoints[1]++] = Pos; - else if(Index == ENTITY_SPAWN_BLUE) - m_aaSpawnPoints[2][m_aNumSpawnPoints[2]++] = Pos; - else if(Index == ENTITY_ARMOR_1) - Type = POWERUP_ARMOR; - else if(Index == ENTITY_HEALTH_1) - Type = POWERUP_HEALTH; - else if(Index == ENTITY_WEAPON_SHOTGUN) + switch(Index) { + case ENTITY_SPAWN: + m_aaSpawnPoints[0][m_aNumSpawnPoints[0]++] = Pos; + break; + case ENTITY_SPAWN_RED: + m_aaSpawnPoints[1][m_aNumSpawnPoints[1]++] = Pos; + break; + case ENTITY_SPAWN_BLUE: + m_aaSpawnPoints[2][m_aNumSpawnPoints[2]++] = Pos; + break; + case ENTITY_ARMOR_1: + Type = POWERUP_ARMOR; + break; + case ENTITY_HEALTH_1: + Type = POWERUP_HEALTH; + break; + case ENTITY_WEAPON_SHOTGUN: Type = POWERUP_WEAPON; SubType = WEAPON_SHOTGUN; - } - else if(Index == ENTITY_WEAPON_GRENADE) - { + break; + case ENTITY_WEAPON_GRENADE: Type = POWERUP_WEAPON; SubType = WEAPON_GRENADE; - } - else if(Index == ENTITY_WEAPON_RIFLE) - { + break; + case ENTITY_WEAPON_RIFLE: Type = POWERUP_WEAPON; SubType = WEAPON_RIFLE; - } - else if(Index == ENTITY_POWERUP_NINJA && g_Config.m_SvPowerups) - { - Type = POWERUP_NINJA; - SubType = WEAPON_NINJA; + break; + case ENTITY_POWERUP_NINJA: + if(g_Config.m_SvPowerups) + { + Type = POWERUP_NINJA; + SubType = WEAPON_NINJA; + } } if(Type != -1) @@ -171,6 +264,114 @@ bool IGameController::OnEntity(int Index, vec2 Pos) return false; } +void IGameController::OnPlayerDisconnect(CPlayer *pPlayer, const char *pReason) +{ + pPlayer->OnDisconnect(); + + int ClientID = pPlayer->GetCID(); + if(Server()->ClientIngame(ClientID)) + { + char aBuf[512]; + if(pReason && *pReason) + str_format(aBuf, sizeof(aBuf), "'%s' has left the game (%s)", Server()->ClientName(ClientID), pReason); + else + str_format(aBuf, sizeof(aBuf), "'%s' has left the game", Server()->ClientName(ClientID)); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + + str_format(aBuf, sizeof(aBuf), "leave player='%d:%s'", ClientID, Server()->ClientName(ClientID)); + GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "game", aBuf); + } + + m_UnbalancedTick = TBALANCE_CHECK; +} + +void IGameController::OnPlayerInfoChange(class CPlayer *pPlayer) +{ + const int aTeamColors[2] = {65387, 10223467}; + if(IsTeamplay()) + { + pPlayer->m_TeeInfos.m_UseCustomColor = 1; + if(pPlayer->GetTeam() >= TEAM_RED && pPlayer->GetTeam() <= TEAM_BLUE) + { + pPlayer->m_TeeInfos.m_ColorBody = aTeamColors[pPlayer->GetTeam()]; + pPlayer->m_TeeInfos.m_ColorFeet = aTeamColors[pPlayer->GetTeam()]; + } + else + { + pPlayer->m_TeeInfos.m_ColorBody = 12895054; + pPlayer->m_TeeInfos.m_ColorFeet = 12895054; + } + } +} + +void IGameController::OnReset() +{ + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i]) + { + GameServer()->m_apPlayers[i]->Respawn(); + GameServer()->m_apPlayers[i]->m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2; + GameServer()->m_apPlayers[i]->m_Score = 0; + GameServer()->m_apPlayers[i]->m_ScoreStartTick = Server()->Tick(); + } + } +} + +// game +void IGameController::DoWarmup(int Seconds) +{ + if(Seconds < 0) + m_Warmup = 0; + else + m_Warmup = Seconds*Server()->TickSpeed(); +} + +void IGameController::DoWincheck() +{ + if(IsTeamplay()) + { + // check score win condition + if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) + { + if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) + EndRound(); + else + m_SuddenDeath = 1; + } + } + else + { + // gather some stats + int Topscore = 0; + int TopscoreCount = 0; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i]) + { + if(GameServer()->m_apPlayers[i]->m_Score > Topscore) + { + Topscore = GameServer()->m_apPlayers[i]->m_Score; + TopscoreCount = 1; + } + else if(GameServer()->m_apPlayers[i]->m_Score == Topscore) + TopscoreCount++; + } + } + + // check score win condition + if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) + { + if(TopscoreCount == 1) + EndRound(); + else + m_SuddenDeath = 1; + } + } +} + void IGameController::EndRound() { if(m_Warmup) // game can't end when we are running warmup @@ -186,7 +387,115 @@ void IGameController::ResetGame() GameServer()->m_World.m_ResetRequested = true; } -const char *IGameController::GetTeamName(int Team) +void IGameController::StartRound() +{ + ResetGame(); + + m_GameOverTick = -1; + m_RoundStartTick = Server()->Tick(); + m_SuddenDeath = 0; + m_aTeamscore[TEAM_RED] = 0; + m_aTeamscore[TEAM_BLUE] = 0; + + if(m_UnbalancedTick == TBALANCE_CHECK) + CheckTeamBalance(); + if(m_UnbalancedTick > TBALANCE_OK) + DoTeamBalance(); + + GameServer()->m_World.m_Paused = false; + + Server()->DemoRecorder_HandleAutoStart(); + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "start round type='%s' teamplay='%d'", m_pGameType, m_GameFlags&GAMEFLAG_TEAMS); + GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); +} + +// general +void IGameController::Snap(int SnappingClient) +{ + CNetObj_GameInfo *pGameInfoObj = (CNetObj_GameInfo *)Server()->SnapNewItem(NETOBJTYPE_GAMEINFO, 0, sizeof(CNetObj_GameInfo)); + if(!pGameInfoObj) + return; + + pGameInfoObj->m_GameFlags = m_GameFlags; + pGameInfoObj->m_GameStateFlags = 0; + if(m_GameOverTick != -1) + pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_GAMEOVER; + if(m_SuddenDeath) + pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_SUDDENDEATH; + if(GameServer()->m_World.m_Paused) + pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_PAUSED; + pGameInfoObj->m_RoundStartTick = m_RoundStartTick; + pGameInfoObj->m_WarmupTimer = m_Warmup; + + pGameInfoObj->m_ScoreLimit = g_Config.m_SvScorelimit; + pGameInfoObj->m_TimeLimit = g_Config.m_SvTimelimit; + + pGameInfoObj->m_RoundNum = (str_length(g_Config.m_SvMaprotation) && g_Config.m_SvRoundsPerMap) ? g_Config.m_SvRoundsPerMap : 0; + pGameInfoObj->m_RoundCurrent = m_RoundCount+1; +} + +void IGameController::Tick() +{ + // do warmup + if(m_Warmup) + { + m_Warmup--; + if(!m_Warmup) + StartRound(); + } + + if(m_GameOverTick != -1) + { + // game over.. wait for restart + if(Server()->Tick() > m_GameOverTick+Server()->TickSpeed()*10) + { + CycleMap(); + StartRound(); + m_RoundCount++; + } + } + + // game is Paused + if(GameServer()->m_World.m_Paused) + ++m_RoundStartTick; + + // do team-balancing + if(IsTeamplay()) + { + if(m_UnbalancedTick == TBALANCE_CHECK) + CheckTeamBalance(); + if(m_UnbalancedTick > TBALANCE_OK && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60) + DoTeamBalance(); + } + + // check for inactive players + DoActivityCheck(); + + // win check + if(m_GameOverTick == -1 && !m_Warmup && !GameServer()->m_World.m_ResetRequested) + DoWincheck(); +} + +// info +bool IGameController::IsFriendlyFire(int ClientID1, int ClientID2) const +{ + if(ClientID1 == ClientID2) + return false; + + if(IsTeamplay()) + { + if(!GameServer()->m_apPlayers[ClientID1] || !GameServer()->m_apPlayers[ClientID2]) + return false; + + if(!g_Config.m_SvTeamdamage && GameServer()->m_apPlayers[ClientID1]->GetTeam() == GameServer()->m_apPlayers[ClientID2]->GetTeam()) + return true; + } + + return false; +} + +const char *IGameController::GetTeamName(int Team) const { if(IsTeamplay()) { @@ -204,25 +513,9 @@ const char *IGameController::GetTeamName(int Team) return "spectators"; } +// map static bool IsSeparator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; } -void IGameController::StartRound() -{ - ResetGame(); - - m_RoundStartTick = Server()->Tick(); - m_SuddenDeath = 0; - m_GameOverTick = -1; - GameServer()->m_World.m_Paused = false; - m_aTeamscore[TEAM_RED] = 0; - m_aTeamscore[TEAM_BLUE] = 0; - m_ForceBalanced = false; - Server()->DemoRecorder_HandleAutoStart(); - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "start round type='%s' teamplay='%d'", m_pGameType, m_GameFlags&GAMEFLAG_TEAMS); - GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); -} - void IGameController::ChangeMap(const char *pToMap) { str_copy(m_aMapWish, pToMap, sizeof(m_aMapWish)); @@ -305,430 +598,186 @@ void IGameController::CycleMap() str_copy(g_Config.m_SvMap, &aBuf[i], sizeof(g_Config.m_SvMap)); } -void IGameController::PostReset() +// spawn +bool IGameController::CanSpawn(int Team, vec2 *pOutPos) const { - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(GameServer()->m_apPlayers[i]) - { - GameServer()->m_apPlayers[i]->Respawn(); - GameServer()->m_apPlayers[i]->m_Score = 0; - GameServer()->m_apPlayers[i]->m_ScoreStartTick = Server()->Tick(); - GameServer()->m_apPlayers[i]->m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2; - } - } -} + CSpawnEval Eval; -void IGameController::OnPlayerInfoChange(class CPlayer *pP) -{ - const int aTeamColors[2] = {65387, 10223467}; - if(IsTeamplay()) - { - pP->m_TeeInfos.m_UseCustomColor = 1; - if(pP->GetTeam() >= TEAM_RED && pP->GetTeam() <= TEAM_BLUE) - { - pP->m_TeeInfos.m_ColorBody = aTeamColors[pP->GetTeam()]; - pP->m_TeeInfos.m_ColorFeet = aTeamColors[pP->GetTeam()]; - } - else - { - pP->m_TeeInfos.m_ColorBody = 12895054; - pP->m_TeeInfos.m_ColorFeet = 12895054; - } - } -} - - -int IGameController::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon) -{ - // do scoreing - if(!pKiller || Weapon == WEAPON_GAME) - return 0; - if(pKiller == pVictim->GetPlayer()) - pVictim->GetPlayer()->m_Score--; // suicide - else - { - if(IsTeamplay() && pVictim->GetPlayer()->GetTeam() == pKiller->GetTeam()) - pKiller->m_Score--; // teamkill - else - pKiller->m_Score++; // normal kill - } - if(Weapon == WEAPON_SELF) - pVictim->GetPlayer()->m_RespawnTick = Server()->Tick()+Server()->TickSpeed()*3.0f; - return 0; -} - -void IGameController::OnCharacterSpawn(class CCharacter *pChr) -{ - // default health - pChr->IncreaseHealth(10); - - // give default weapons - pChr->GiveWeapon(WEAPON_HAMMER, -1); - pChr->GiveWeapon(WEAPON_GUN, 10); -} - -void IGameController::DoWarmup(int Seconds) -{ - if(Seconds < 0) - m_Warmup = 0; - else - m_Warmup = Seconds*Server()->TickSpeed(); -} - -bool IGameController::IsFriendlyFire(int ClientID1, int ClientID2) -{ - if(ClientID1 == ClientID2) + // spectators can't spawn + if(Team == TEAM_SPECTATORS) return false; if(IsTeamplay()) { - if(!GameServer()->m_apPlayers[ClientID1] || !GameServer()->m_apPlayers[ClientID2]) - return false; + Eval.m_FriendlyTeam = Team; - if(GameServer()->m_apPlayers[ClientID1]->GetTeam() == GameServer()->m_apPlayers[ClientID2]->GetTeam()) - return true; - } - - return false; -} - -bool IGameController::IsForceBalanced() -{ - if(m_ForceBalanced) - { - m_ForceBalanced = false; - return true; + // first try own team spawn, then normal spawn and then enemy + EvaluateSpawnType(&Eval, 1+(Team&1)); + if(!Eval.m_Got) + { + EvaluateSpawnType(&Eval, 0); + if(!Eval.m_Got) + EvaluateSpawnType(&Eval, 1+((Team+1)&1)); + } } else - return false; -} - -bool IGameController::CanBeMovedOnBalance(int ClientID) -{ - return true; -} - -void IGameController::Tick() -{ - // do warmup - if(m_Warmup) { - m_Warmup--; - if(!m_Warmup) - StartRound(); + EvaluateSpawnType(&Eval, 0); + EvaluateSpawnType(&Eval, 1); + EvaluateSpawnType(&Eval, 2); } - if(m_GameOverTick != -1) + *pOutPos = Eval.m_Pos; + return Eval.m_Got; +} + +float IGameController::EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos) const +{ + float Score = 0.0f; + CCharacter *pC = static_cast(GameServer()->m_World.FindFirst(CGameWorld::ENTTYPE_CHARACTER)); + for(; pC; pC = (CCharacter *)pC->TypeNext()) { - // game over.. wait for restart - if(Server()->Tick() > m_GameOverTick+Server()->TickSpeed()*10) - { - CycleMap(); - StartRound(); - m_RoundCount++; - } + // team mates are not as dangerous as enemies + float Scoremod = 1.0f; + if(pEval->m_FriendlyTeam != -1 && pC->GetPlayer()->GetTeam() == pEval->m_FriendlyTeam) + Scoremod = 0.5f; + + float d = distance(Pos, pC->m_Pos); + Score += Scoremod * (d == 0 ? 1000000000.0f : 1.0f/d); } - // game is Paused - if(GameServer()->m_World.m_Paused) - ++m_RoundStartTick; + return Score; +} - // do team-balancing - if(IsTeamplay() && m_UnbalancedTick != -1 && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60) +void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type) const +{ + // get spawn point + for(int i = 0; i < m_aNumSpawnPoints[Type]; i++) { - GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", "Balancing teams"); - - int aT[2] = {0,0}; - float aTScore[2] = {0,0}; - float aPScore[MAX_CLIENTS] = {0.0f}; - for(int i = 0; i < MAX_CLIENTS; i++) + // check if the position is occupado + CCharacter *aEnts[MAX_CLIENTS]; + int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER); + vec2 Positions[5] = { vec2(0.0f, 0.0f), vec2(-32.0f, 0.0f), vec2(0.0f, -32.0f), vec2(32.0f, 0.0f), vec2(0.0f, 32.0f) }; // start, left, up, right, down + int Result = -1; + for(int Index = 0; Index < 5 && Result == -1; ++Index) { - if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) - { - aT[GameServer()->m_apPlayers[i]->GetTeam()]++; - aPScore[i] = GameServer()->m_apPlayers[i]->m_Score*Server()->TickSpeed()*60.0f/ - (Server()->Tick()-GameServer()->m_apPlayers[i]->m_ScoreStartTick); - aTScore[GameServer()->m_apPlayers[i]->GetTeam()] += aPScore[i]; - } - } - - // are teams unbalanced? - if(absolute(aT[0]-aT[1]) >= 2) - { - int M = (aT[0] > aT[1]) ? 0 : 1; - int NumBalance = absolute(aT[0]-aT[1]) / 2; - - do - { - CPlayer *pP = 0; - float PD = aTScore[M]; - for(int i = 0; i < MAX_CLIENTS; i++) + Result = Index; + for(int c = 0; c < Num; ++c) + if(GameServer()->Collision()->CheckPoint(m_aaSpawnPoints[Type][i]+Positions[Index]) || + distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]+Positions[Index]) <= aEnts[c]->m_ProximityRadius) { - if(!GameServer()->m_apPlayers[i] || !CanBeMovedOnBalance(i)) - continue; - // remember the player who would cause lowest score-difference - if(GameServer()->m_apPlayers[i]->GetTeam() == M && (!pP || absolute((aTScore[M^1]+aPScore[i]) - (aTScore[M]-aPScore[i])) < PD)) - { - pP = GameServer()->m_apPlayers[i]; - PD = absolute((aTScore[M^1]+aPScore[i]) - (aTScore[M]-aPScore[i])); - } - } - - // move the player to the other team - int Temp = pP->m_LastActionTick; - pP->SetTeam(M^1); - pP->m_LastActionTick = Temp; - - pP->Respawn(); - pP->m_ForceBalanced = true; - } while (--NumBalance); - - m_ForceBalanced = true; - } - m_UnbalancedTick = -1; - } - - // check for inactive players - if(g_Config.m_SvInactiveKickTime > 0) - { - for(int i = 0; i < MAX_CLIENTS; ++i) - { - #ifdef CONF_DEBUG - if(g_Config.m_DbgDummies) - { - if(i >= MAX_CLIENTS-g_Config.m_DbgDummies) + Result = -1; break; - } - #endif - if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS && !Server()->IsAuthed(i)) - { - if(Server()->Tick() > GameServer()->m_apPlayers[i]->m_LastActionTick+g_Config.m_SvInactiveKickTime*Server()->TickSpeed()*60) - { - switch(g_Config.m_SvInactiveKick) - { - case 0: - { - // move player to spectator - GameServer()->m_apPlayers[i]->SetTeam(TEAM_SPECTATORS); - } - break; - case 1: - { - // move player to spectator if the reserved slots aren't filled yet, kick him otherwise - int Spectators = 0; - for(int j = 0; j < MAX_CLIENTS; ++j) - if(GameServer()->m_apPlayers[j] && GameServer()->m_apPlayers[j]->GetTeam() == TEAM_SPECTATORS) - ++Spectators; - if(Spectators >= g_Config.m_SvSpectatorSlots) - Server()->Kick(i, "Kicked for inactivity"); - else - GameServer()->m_apPlayers[i]->SetTeam(TEAM_SPECTATORS); - } - break; - case 2: - { - // kick the player - Server()->Kick(i, "Kicked for inactivity"); - } - } } - } + } + if(Result == -1) + continue; // try next spawn point + + vec2 P = m_aaSpawnPoints[Type][i]+Positions[Result]; + float S = EvaluateSpawnPos(pEval, P); + if(!pEval->m_Got || pEval->m_Score > S) + { + pEval->m_Got = true; + pEval->m_Score = S; + pEval->m_Pos = P; } } - - DoWincheck(); } - -bool IGameController::IsTeamplay() const +// team +bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam) const { - return m_GameFlags&GAMEFLAG_TEAMS; -} + if(!IsTeamplay() || JoinTeam == TEAM_SPECTATORS || !g_Config.m_SvTeambalanceTime) + return true; -void IGameController::Snap(int SnappingClient) -{ - CNetObj_GameInfo *pGameInfoObj = (CNetObj_GameInfo *)Server()->SnapNewItem(NETOBJTYPE_GAMEINFO, 0, sizeof(CNetObj_GameInfo)); - if(!pGameInfoObj) - return; - - pGameInfoObj->m_GameFlags = m_GameFlags; - pGameInfoObj->m_GameStateFlags = 0; - if(m_GameOverTick != -1) - pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_GAMEOVER; - if(m_SuddenDeath) - pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_SUDDENDEATH; - if(GameServer()->m_World.m_Paused) - pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_PAUSED; - pGameInfoObj->m_RoundStartTick = m_RoundStartTick; - pGameInfoObj->m_WarmupTimer = m_Warmup; - - pGameInfoObj->m_ScoreLimit = g_Config.m_SvScorelimit; - pGameInfoObj->m_TimeLimit = g_Config.m_SvTimelimit; - - pGameInfoObj->m_RoundNum = (str_length(g_Config.m_SvMaprotation) && g_Config.m_SvRoundsPerMap) ? g_Config.m_SvRoundsPerMap : 0; - pGameInfoObj->m_RoundCurrent = m_RoundCount+1; -} - -int IGameController::GetAutoTeam(int NotThisID) -{ - // this will force the auto balancer to work overtime aswell - if(g_Config.m_DbgStress) - return 0; - - int aNumplayers[2] = {0,0}; + // calc team sizes + int aPlayerCount[NUM_TEAMS] = {0}; for(int i = 0; i < MAX_CLIENTS; i++) { - if(GameServer()->m_apPlayers[i] && i != NotThisID) - { - if(GameServer()->m_apPlayers[i]->GetTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetTeam() <= TEAM_BLUE) - aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++; - } + if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + aPlayerCount[GameServer()->m_apPlayers[i]->GetTeam()]++; } - int Team = 0; - if(IsTeamplay()) - Team = aNumplayers[TEAM_RED] > aNumplayers[TEAM_BLUE] ? TEAM_BLUE : TEAM_RED; + // simulate what would happen if the player changes team + aPlayerCount[JoinTeam]++; + if(pPlayer->GetTeam() != TEAM_SPECTATORS) + aPlayerCount[JoinTeam^1]--; - if(CanJoinTeam(Team, NotThisID)) - return Team; - return -1; + // check if the player-difference decreases or is smaller than 2 + return aPlayerCount[JoinTeam]-aPlayerCount[JoinTeam^1] < NUM_TEAMS; } -bool IGameController::CanJoinTeam(int Team, int NotThisID) +bool IGameController::CanJoinTeam(int Team, int NotThisID) const { if(Team == TEAM_SPECTATORS || (GameServer()->m_apPlayers[NotThisID] && GameServer()->m_apPlayers[NotThisID]->GetTeam() != TEAM_SPECTATORS)) return true; - int aNumplayers[2] = {0,0}; + int PlayerCount = 0; for(int i = 0; i < MAX_CLIENTS; i++) { - if(GameServer()->m_apPlayers[i] && i != NotThisID) - { - if(GameServer()->m_apPlayers[i]->GetTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetTeam() <= TEAM_BLUE) - aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++; - } + if(GameServer()->m_apPlayers[i] && i != NotThisID && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + ++PlayerCount; } - return (aNumplayers[0] + aNumplayers[1]) < Server()->MaxClients()-g_Config.m_SvSpectatorSlots; + return PlayerCount < Server()->MaxClients()-g_Config.m_SvSpectatorSlots; } -bool IGameController::CheckTeamBalance() +int IGameController::ClampTeam(int Team) const { - if(!IsTeamplay() || !g_Config.m_SvTeambalanceTime) - return true; - - int aT[2] = {0, 0}; - for(int i = 0; i < MAX_CLIENTS; i++) - { - CPlayer *pP = GameServer()->m_apPlayers[i]; - if(pP && pP->GetTeam() != TEAM_SPECTATORS) - aT[pP->GetTeam()]++; - } - - char aBuf[256]; - if(absolute(aT[0]-aT[1]) >= 2) - { - str_format(aBuf, sizeof(aBuf), "Teams are NOT balanced (red=%d blue=%d)", aT[0], aT[1]); - GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); - if(GameServer()->m_pController->m_UnbalancedTick == -1) - GameServer()->m_pController->m_UnbalancedTick = Server()->Tick(); - return false; - } - else - { - str_format(aBuf, sizeof(aBuf), "Teams are balanced (red=%d blue=%d)", aT[0], aT[1]); - GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); - GameServer()->m_pController->m_UnbalancedTick = -1; - return true; - } -} - -bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam) -{ - int aT[2] = {0, 0}; - - if (!IsTeamplay() || JoinTeam == TEAM_SPECTATORS || !g_Config.m_SvTeambalanceTime) - return true; - - for(int i = 0; i < MAX_CLIENTS; i++) - { - CPlayer *pP = GameServer()->m_apPlayers[i]; - if(pP && pP->GetTeam() != TEAM_SPECTATORS) - aT[pP->GetTeam()]++; - } - - // simulate what would happen if changed team - aT[JoinTeam]++; - if (pPlayer->GetTeam() != TEAM_SPECTATORS) - aT[JoinTeam^1]--; - - // there is a player-difference of at least 2 - if(absolute(aT[0]-aT[1]) >= 2) - { - // player wants to join team with less players - if ((aT[0] < aT[1] && JoinTeam == TEAM_RED) || (aT[0] > aT[1] && JoinTeam == TEAM_BLUE)) - return true; - else - return false; - } - else - return true; -} - -void IGameController::DoWincheck() -{ - if(m_GameOverTick == -1 && !m_Warmup && !GameServer()->m_World.m_ResetRequested) - { - if(IsTeamplay()) - { - // check score win condition - if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || - (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) - { - if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) - EndRound(); - else - m_SuddenDeath = 1; - } - } - else - { - // gather some stats - int Topscore = 0; - int TopscoreCount = 0; - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(GameServer()->m_apPlayers[i]) - { - if(GameServer()->m_apPlayers[i]->m_Score > Topscore) - { - Topscore = GameServer()->m_apPlayers[i]->m_Score; - TopscoreCount = 1; - } - else if(GameServer()->m_apPlayers[i]->m_Score == Topscore) - TopscoreCount++; - } - } - - // check score win condition - if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) || - (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) - { - if(TopscoreCount == 1) - EndRound(); - else - m_SuddenDeath = 1; - } - } - } -} - -int IGameController::ClampTeam(int Team) -{ - if(Team < 0) + if(Team < TEAM_RED) return TEAM_SPECTATORS; if(IsTeamplay()) return Team&1; - return 0; + return TEAM_RED; +} + +void IGameController::DoTeamChange(CPlayer *pPlayer, int Team, bool DoChatMsg) +{ + Team = ClampTeam(Team); + if(Team == pPlayer->GetTeam()) + return; + + pPlayer->SetTeam(Team); + + int ClientID = pPlayer->GetCID(); + char aBuf[128]; + if(DoChatMsg) + { + str_format(aBuf, sizeof(aBuf), "'%s' joined the %s", Server()->ClientName(ClientID), GetTeamName(Team)); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + } + + str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' m_Team=%d", ClientID, Server()->ClientName(ClientID), Team); + GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); + + m_UnbalancedTick = TBALANCE_CHECK; + OnPlayerInfoChange(pPlayer); +} + +int IGameController::GetStartTeam(int NotThisID) +{ + // this will force the auto balancer to work overtime aswell + if(g_Config.m_DbgStress) + return TEAM_RED; + + if(g_Config.m_SvTournamentMode) + return TEAM_SPECTATORS; + + int aPlayerCount[NUM_TEAMS] = {0}; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(i != NotThisID && GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetTeam() <= TEAM_BLUE) + aPlayerCount[GameServer()->m_apPlayers[i]->GetTeam()]++; + } + + int Team = TEAM_RED; + if(IsTeamplay()) + Team = aPlayerCount[TEAM_RED] > aPlayerCount[TEAM_BLUE] ? TEAM_BLUE : TEAM_RED; + + if(aPlayerCount[TEAM_RED]+aPlayerCount[TEAM_BLUE] < Server()->MaxClients()-g_Config.m_SvSpectatorSlots) + { + m_UnbalancedTick = TBALANCE_CHECK; + return Team; + } + return TEAM_SPECTATORS; } diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h index 1675fe35a..2bdc3f6f1 100644 --- a/src/game/server/gamecontroller.h +++ b/src/game/server/gamecontroller.h @@ -4,6 +4,7 @@ #define GAME_SERVER_GAMECONTROLLER_H #include +#include /* Class: Game Controller @@ -12,16 +13,33 @@ */ class IGameController { - vec2 m_aaSpawnPoints[3][64]; - int m_aNumSpawnPoints[3]; - class CGameContext *m_pGameServer; class IServer *m_pServer; -protected: - CGameContext *GameServer() const { return m_pGameServer; } - IServer *Server() const { return m_pServer; } + // activity + void DoActivityCheck(); + // balancing + enum + { + TBALANCE_CHECK=-2, + TBALANCE_OK, + }; + int m_UnbalancedTick; + + virtual bool CanBeMovedOnBalance(int ClientID) const; + void CheckTeamBalance(); + void DoTeamBalance(); + + // game + virtual void DoWincheck(); + + // map + char m_aMapWish[128]; + + void CycleMap(); + + // spawn struct CSpawnEval { CSpawnEval() @@ -36,59 +54,58 @@ protected: int m_FriendlyTeam; float m_Score; }; + vec2 m_aaSpawnPoints[3][64]; + int m_aNumSpawnPoints[3]; + + float EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos) const; + void EvaluateSpawnType(CSpawnEval *pEval, int Type) const; - float EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos); - void EvaluateSpawnType(CSpawnEval *pEval, int Type); - bool EvaluateSpawn(class CPlayer *pP, vec2 *pPos); + // team + int ClampTeam(int Team) const; - void CycleMap(); +protected: + CGameContext *GameServer() const { return m_pGameServer; } + IServer *Server() const { return m_pServer; } + + // game + int m_GameOverTick; + int m_RoundCount; + int m_RoundStartTick; + int m_SuddenDeath; + int m_aTeamscore[NUM_TEAMS]; + int m_Warmup; + + void EndRound(); void ResetGame(); - char m_aMapWish[128]; - - - int m_RoundStartTick; - int m_GameOverTick; - int m_SuddenDeath; - - int m_aTeamscore[2]; - - int m_Warmup; - int m_RoundCount; - + // info int m_GameFlags; - int m_UnbalancedTick; - bool m_ForceBalanced; - -public: const char *m_pGameType; - bool IsTeamplay() const; - bool IsGameOver() const { return m_GameOverTick != -1; } - +public: IGameController(class CGameContext *pGameServer); - virtual ~IGameController(); - - virtual void DoWincheck(); - - void DoWarmup(int Seconds); - - void StartRound(); - void EndRound(); - void ChangeMap(const char *pToMap); - - bool IsFriendlyFire(int ClientID1, int ClientID2); - - bool IsForceBalanced(); + virtual ~IGameController() {}; + // event /* + Function: on_CCharacter_death + Called when a CCharacter in the world dies. + Arguments: + victim - The CCharacter that died. + killer - The player that killed it. + weapon - What weapon that killed it. Can be -1 for undefined + weapon when switching team or player suicides. */ - virtual bool CanBeMovedOnBalance(int ClientID); + virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon); + /* + Function: on_CCharacter_spawn + Called when a CCharacter spawns into the game world. - virtual void Tick(); - - virtual void Snap(int SnappingClient); + Arguments: + chr - The CCharacter that was spawned. + */ + virtual void OnCharacterSpawn(class CCharacter *pChr); /* Function: on_entity @@ -104,44 +121,41 @@ public: */ virtual bool OnEntity(int Index, vec2 Pos); - /* - Function: on_CCharacter_spawn - Called when a CCharacter spawns into the game world. + void OnPlayerDisconnect(class CPlayer *pPlayer, const char *pReason); + void OnPlayerInfoChange(class CPlayer *pPlayer); - Arguments: - chr - The CCharacter that was spawned. - */ - virtual void OnCharacterSpawn(class CCharacter *pChr); + void OnReset(); - /* - Function: on_CCharacter_death - Called when a CCharacter in the world dies. + // game + void DoWarmup(int Seconds); - Arguments: - victim - The CCharacter that died. - killer - The player that killed it. - weapon - What weapon that killed it. Can be -1 for undefined - weapon when switching team or player suicides. - */ - virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon); + void StartRound(); + // general + virtual void Snap(int SnappingClient); + virtual void Tick(); - virtual void OnPlayerInfoChange(class CPlayer *pP); + // info + bool IsFriendlyFire(int ClientID1, int ClientID2) const; + bool IsGameOver() const { return m_GameOverTick != -1; } + bool IsTeamplay() const { return m_GameFlags&GAMEFLAG_TEAMS; } + + const char *GetGameType() const { return m_pGameType; } + const char *GetTeamName(int Team) const; + + // map + void ChangeMap(const char *pToMap); - // - virtual bool CanSpawn(int Team, vec2 *pPos); + //spawn + bool CanSpawn(int Team, vec2 *pPos) const; - /* + // team + bool CanJoinTeam(int Team, int NotThisID) const; + bool CanChangeTeam(CPlayer *pPplayer, int JoinTeam) const; - */ - virtual const char *GetTeamName(int Team); - virtual int GetAutoTeam(int NotThisID); - virtual bool CanJoinTeam(int Team, int NotThisID); - bool CheckTeamBalance(); - bool CanChangeTeam(CPlayer *pPplayer, int JoinTeam); - int ClampTeam(int Team); - - virtual void PostReset(); + void DoTeamChange(class CPlayer *pPlayer, int Team, bool DoChatMsg=true); + + int GetStartTeam(int NotThisID); }; #endif diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp index 9e45c1fe9..4747ac7ce 100644 --- a/src/game/server/gamemodes/ctf.cpp +++ b/src/game/server/gamemodes/ctf.cpp @@ -10,35 +10,34 @@ #include #include "ctf.h" -CGameControllerCTF::CGameControllerCTF(class CGameContext *pGameServer) +CGameControllerCTF::CGameControllerCTF(CGameContext *pGameServer) : IGameController(pGameServer) { + // game m_apFlags[0] = 0; m_apFlags[1] = 0; m_pGameType = "CTF"; m_GameFlags = GAMEFLAG_TEAMS|GAMEFLAG_FLAGS; } -bool CGameControllerCTF::OnEntity(int Index, vec2 Pos) +// balancing +bool CGameControllerCTF::CanBeMovedOnBalance(int ClientID) { - if(IGameController::OnEntity(Index, Pos)) - return true; - - int Team = -1; - if(Index == ENTITY_FLAGSTAND_RED) Team = TEAM_RED; - if(Index == ENTITY_FLAGSTAND_BLUE) Team = TEAM_BLUE; - if(Team == -1 || m_apFlags[Team]) - return false; - - CFlag *F = new CFlag(&GameServer()->m_World, Team); - F->m_StandPos = Pos; - F->m_Pos = Pos; - m_apFlags[Team] = F; - GameServer()->m_World.InsertEntity(F); + CCharacter* Character = GameServer()->m_apPlayers[ClientID]->GetCharacter(); + if(Character) + { + for(int fi = 0; fi < 2; fi++) + { + CFlag *F = m_apFlags[fi]; + if(F->m_pCarryingCharacter == Character) + return false; + } + } return true; } -int CGameControllerCTF::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int WeaponID) +// event +int CGameControllerCTF::OnCharacterDeath(CCharacter *pVictim, CPlayer *pKiller, int WeaponID) { IGameController::OnCharacterDeath(pVictim, pKiller, WeaponID); int HadFlag = 0; @@ -66,45 +65,48 @@ int CGameControllerCTF::OnCharacterDeath(class CCharacter *pVictim, class CPlaye return HadFlag; } -void CGameControllerCTF::DoWincheck() +bool CGameControllerCTF::OnEntity(int Index, vec2 Pos) { - if(m_GameOverTick == -1 && !m_Warmup) - { - // check score win condition - if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || - (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) - { - if(m_SuddenDeath) - { - if(m_aTeamscore[TEAM_RED]/100 != m_aTeamscore[TEAM_BLUE]/100) - EndRound(); - } - else - { - if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) - EndRound(); - else - m_SuddenDeath = 1; - } - } - } -} + if(IGameController::OnEntity(Index, Pos)) + return true; -bool CGameControllerCTF::CanBeMovedOnBalance(int ClientID) -{ - CCharacter* Character = GameServer()->m_apPlayers[ClientID]->GetCharacter(); - if(Character) - { - for(int fi = 0; fi < 2; fi++) - { - CFlag *F = m_apFlags[fi]; - if(F->m_pCarryingCharacter == Character) - return false; - } - } + int Team = -1; + if(Index == ENTITY_FLAGSTAND_RED) Team = TEAM_RED; + if(Index == ENTITY_FLAGSTAND_BLUE) Team = TEAM_BLUE; + if(Team == -1 || m_apFlags[Team]) + return false; + + CFlag *F = new CFlag(&GameServer()->m_World, Team); + F->m_StandPos = Pos; + F->m_Pos = Pos; + m_apFlags[Team] = F; + GameServer()->m_World.InsertEntity(F); return true; } +// game +void CGameControllerCTF::DoWincheck() +{ + // check score win condition + if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) + { + if(m_SuddenDeath) + { + if(m_aTeamscore[TEAM_RED]/100 != m_aTeamscore[TEAM_BLUE]/100) + EndRound(); + } + else + { + if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) + EndRound(); + else + m_SuddenDeath = 1; + } + } +} + +// general void CGameControllerCTF::Snap(int SnappingClient) { IGameController::Snap(SnappingClient); diff --git a/src/game/server/gamemodes/ctf.h b/src/game/server/gamemodes/ctf.h index 72747ed75..f61f6df7e 100644 --- a/src/game/server/gamemodes/ctf.h +++ b/src/game/server/gamemodes/ctf.h @@ -7,17 +7,24 @@ class CGameControllerCTF : public IGameController { -public: + // balancing + virtual bool CanBeMovedOnBalance(int ClientID); + + // game class CFlag *m_apFlags[2]; - CGameControllerCTF(class CGameContext *pGameServer); virtual void DoWincheck(); - virtual bool CanBeMovedOnBalance(int ClientID); + +public: + CGameControllerCTF(class CGameContext *pGameServer); + + // event + virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon); + virtual bool OnEntity(int Index, vec2 Pos); + + // general virtual void Snap(int SnappingClient); virtual void Tick(); - - virtual bool OnEntity(int Index, vec2 Pos); - virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon); }; #endif diff --git a/src/game/server/gamemodes/dm.cpp b/src/game/server/gamemodes/dm.cpp index bdca4c9a5..f4a309c93 100644 --- a/src/game/server/gamemodes/dm.cpp +++ b/src/game/server/gamemodes/dm.cpp @@ -3,13 +3,8 @@ #include "dm.h" -CGameControllerDM::CGameControllerDM(class CGameContext *pGameServer) +CGameControllerDM::CGameControllerDM(CGameContext *pGameServer) : IGameController(pGameServer) { m_pGameType = "DM"; } - -void CGameControllerDM::Tick() -{ - IGameController::Tick(); -} diff --git a/src/game/server/gamemodes/dm.h b/src/game/server/gamemodes/dm.h index e88fad0de..6ec9adbe0 100644 --- a/src/game/server/gamemodes/dm.h +++ b/src/game/server/gamemodes/dm.h @@ -8,6 +8,6 @@ class CGameControllerDM : public IGameController { public: CGameControllerDM(class CGameContext *pGameServer); - virtual void Tick(); }; + #endif diff --git a/src/game/server/gamemodes/tdm.cpp b/src/game/server/gamemodes/tdm.cpp index 50ecd93ec..5c7d369aa 100644 --- a/src/game/server/gamemodes/tdm.cpp +++ b/src/game/server/gamemodes/tdm.cpp @@ -12,6 +12,7 @@ CGameControllerTDM::CGameControllerTDM(class CGameContext *pGameServer) : IGameC m_GameFlags = GAMEFLAG_TEAMS; } +// event int CGameControllerTDM::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon) { IGameController::OnCharacterDeath(pVictim, pKiller, Weapon); @@ -31,6 +32,7 @@ int CGameControllerTDM::OnCharacterDeath(class CCharacter *pVictim, class CPlaye return 0; } +// general void CGameControllerTDM::Snap(int SnappingClient) { IGameController::Snap(SnappingClient); @@ -45,8 +47,3 @@ void CGameControllerTDM::Snap(int SnappingClient) pGameDataObj->m_FlagCarrierRed = 0; pGameDataObj->m_FlagCarrierBlue = 0; } - -void CGameControllerTDM::Tick() -{ - IGameController::Tick(); -} diff --git a/src/game/server/gamemodes/tdm.h b/src/game/server/gamemodes/tdm.h index 297b48c0e..3f90a5e20 100644 --- a/src/game/server/gamemodes/tdm.h +++ b/src/game/server/gamemodes/tdm.h @@ -9,8 +9,11 @@ class CGameControllerTDM : public IGameController public: CGameControllerTDM(class CGameContext *pGameServer); - int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon); + // event + virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon); + + // general virtual void Snap(int SnappingClient); - virtual void Tick(); }; + #endif diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index d803ae67b..33ee81e88 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -125,7 +125,7 @@ void CGameWorld::Reset() } RemoveEntities(); - GameServer()->m_pController->PostReset(); + GameServer()->m_pController->OnReset(); RemoveEntities(); m_ResetRequested = false; @@ -154,8 +154,6 @@ void CGameWorld::Tick() if(!m_Paused) { - if(GameServer()->m_pController->IsForceBalanced()) - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, "Teams have been balanced"); // update all objects for(int i = 0; i < NUM_ENTTYPES; i++) for(CEntity *pEnt = m_apFirstEntityTypes[i]; pEnt; ) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 75c2c1c6a..f36866bf5 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -9,7 +9,7 @@ MACRO_ALLOC_POOL_ID_IMPL(CPlayer, MAX_CLIENTS) IServer *CPlayer::Server() const { return m_pGameServer->Server(); } -CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team) +CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, bool Dummy) { m_pGameServer = pGameServer; m_RespawnTick = Server()->Tick(); @@ -17,10 +17,11 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team) m_ScoreStartTick = Server()->Tick(); m_pCharacter = 0; m_ClientID = ClientID; - m_Team = GameServer()->m_pController->ClampTeam(Team); + m_Team = GameServer()->m_pController->GetStartTeam(ClientID); m_SpectatorID = SPEC_FREEVIEW; m_LastActionTick = Server()->Tick(); m_TeamChangeTick = Server()->Tick(); + m_Dummy = Dummy; } CPlayer::~CPlayer() @@ -31,10 +32,7 @@ CPlayer::~CPlayer() void CPlayer::Tick() { -#ifdef CONF_DEBUG - if(!g_Config.m_DbgDummies || m_ClientID < MAX_CLIENTS-g_Config.m_DbgDummies) -#endif - if(!Server()->ClientIngame(m_ClientID)) + if(!IsDummy() && !Server()->ClientIngame(m_ClientID)) return; Server()->SetClientScore(m_ClientID, m_Score); @@ -112,10 +110,7 @@ void CPlayer::PostTick() void CPlayer::Snap(int SnappingClient) { -#ifdef CONF_DEBUG - if(!g_Config.m_DbgDummies || m_ClientID < MAX_CLIENTS-g_Config.m_DbgDummies) -#endif - if(!Server()->ClientIngame(m_ClientID)) + if(!IsDummy() && !Server()->ClientIngame(m_ClientID)) return; CNetObj_ClientInfo *pClientInfo = static_cast(Server()->SnapNewItem(NETOBJTYPE_CLIENTINFO, m_ClientID, sizeof(CNetObj_ClientInfo))); @@ -155,22 +150,9 @@ void CPlayer::Snap(int SnappingClient) } } -void CPlayer::OnDisconnect(const char *pReason) +void CPlayer::OnDisconnect() { KillCharacter(); - - if(Server()->ClientIngame(m_ClientID)) - { - char aBuf[512]; - if(pReason && *pReason) - str_format(aBuf, sizeof(aBuf), "'%s' has left the game (%s)", Server()->ClientName(m_ClientID), pReason); - else - str_format(aBuf, sizeof(aBuf), "'%s' has left the game", Server()->ClientName(m_ClientID)); - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); - - str_format(aBuf, sizeof(aBuf), "leave player='%d:%s'", m_ClientID, Server()->ClientName(m_ClientID)); - GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "game", aBuf); - } } void CPlayer::OnPredictedInput(CNetObj_PlayerInput *NewInput) @@ -243,29 +225,14 @@ void CPlayer::Respawn() void CPlayer::SetTeam(int Team, bool DoChatMsg) { - // clamp the team - Team = GameServer()->m_pController->ClampTeam(Team); - if(m_Team == Team) - return; - - char aBuf[512]; - if(DoChatMsg) - { - str_format(aBuf, sizeof(aBuf), "'%s' joined the %s", Server()->ClientName(m_ClientID), GameServer()->m_pController->GetTeamName(Team)); - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); - } - KillCharacter(); m_Team = Team; m_LastActionTick = Server()->Tick(); + // we got to wait 0.5 secs before respawning m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2; - str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' m_Team=%d", m_ClientID, Server()->ClientName(m_ClientID), m_Team); - GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); - - GameServer()->m_pController->OnPlayerInfoChange(GameServer()->m_apPlayers[m_ClientID]); - + if(Team == TEAM_SPECTATORS) { // update spectator modes diff --git a/src/game/server/player.h b/src/game/server/player.h index dd804a957..cebf18069 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -13,7 +13,7 @@ class CPlayer MACRO_ALLOC_POOL_ID() public: - CPlayer(CGameContext *pGameServer, int ClientID, int Team); + CPlayer(CGameContext *pGameServer, int ClientID, bool Dummy); ~CPlayer(); void Init(int CID); @@ -23,6 +23,7 @@ public: void SetTeam(int Team, bool DoChatMsg=true); int GetTeam() const { return m_Team; }; int GetCID() const { return m_ClientID; }; + bool IsDummy() const { return m_Dummy; } void Tick(); void PostTick(); @@ -30,7 +31,7 @@ public: void OnDirectInput(CNetObj_PlayerInput *NewInput); void OnPredictedInput(CNetObj_PlayerInput *NewInput); - void OnDisconnect(const char *pReason); + void OnDisconnect(); void KillCharacter(int Weapon = WEAPON_GAME); CCharacter *GetCharacter(); @@ -76,7 +77,6 @@ public: int m_DieTick; int m_Score; int m_ScoreStartTick; - bool m_ForceBalanced; int m_LastActionTick; int m_TeamChangeTick; struct @@ -107,6 +107,7 @@ private: bool m_Spawning; int m_ClientID; int m_Team; + bool m_Dummy; }; #endif