diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index a6e33ccff..6c5a5a59a 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -752,6 +752,11 @@ bool CPlayer::CanOverrideDefaultEmote() const return m_LastEyeEmote == 0 || m_LastEyeEmote + (int64_t)g_Config.m_SvEyeEmoteChangeDelay * Server()->TickSpeed() < Server()->Tick(); } +bool CPlayer::CanSpec() const +{ + return m_pCharacter->IsGrounded() && m_pCharacter->m_Pos == m_pCharacter->m_PrevPos; +} + void CPlayer::ProcessPause() { if(m_ForcePauseTime && m_ForcePauseTime < Server()->Tick()) @@ -760,7 +765,7 @@ void CPlayer::ProcessPause() Pause(PAUSE_NONE, true); } - if(m_Paused == PAUSE_SPEC && !m_pCharacter->IsPaused() && m_pCharacter->IsGrounded() && m_pCharacter->m_Pos == m_pCharacter->m_PrevPos) + if(m_Paused == PAUSE_SPEC && !m_pCharacter->IsPaused() && CanSpec()) { m_pCharacter->Pause(true); GameServer()->CreateDeath(m_pCharacter->m_Pos, m_ClientId, GameServer()->m_pController->GetMaskForPlayerWorldEvent(m_ClientId)); diff --git a/src/game/server/player.h b/src/game/server/player.h index ecda4fb99..d2b029905 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -179,6 +179,7 @@ public: int Pause(int State, bool Force); int ForcePause(int Time); int IsPaused() const; + bool CanSpec() const; bool IsPlaying() const; int64_t m_Last_KickVote; diff --git a/src/game/server/save.cpp b/src/game/server/save.cpp index fa17265e0..f4f07a407 100644 --- a/src/game/server/save.cpp +++ b/src/game/server/save.cpp @@ -17,7 +17,14 @@ void CSaveTee::Save(CCharacter *pChr) str_copy(m_aName, pChr->Server()->ClientName(m_ClientId), sizeof(m_aName)); m_Alive = pChr->m_Alive; + + // This is extremely suspect code, probably interacts badly with force pause m_Paused = absolute(pChr->m_pPlayer->IsPaused()); + if(m_Paused == CPlayer::PAUSE_SPEC && !pChr->m_Paused) + { + m_Paused = CPlayer::PAUSE_NONE; + } + m_NeededFaketuning = pChr->m_NeededFaketuning; m_TeeStarted = pChr->Teams()->TeeStarted(m_ClientId); @@ -121,8 +128,10 @@ void CSaveTee::Save(CCharacter *pChr) FormatUuid(pChr->GameServer()->GameUuid(), m_aGameUuid, sizeof(m_aGameUuid)); } -void CSaveTee::Load(CCharacter *pChr, int Team, bool IsSwap) +bool CSaveTee::Load(CCharacter *pChr, int Team, bool IsSwap) { + bool Valid = true; + pChr->m_pPlayer->Pause(m_Paused, true); pChr->m_Alive = m_Alive; @@ -239,6 +248,13 @@ void CSaveTee::Load(CCharacter *pChr, int Team, bool IsSwap) pChr->ForceSetRescue(RESCUEMODE_AUTO); pChr->ForceSetRescue(RESCUEMODE_MANUAL); } + + if(pChr->m_pPlayer->IsPaused() == -1 * CPlayer::PAUSE_SPEC && !pChr->m_pPlayer->CanSpec()) + { + Valid = false; + } + + return Valid; } char *CSaveTee::GetString(const CSaveTeam *pTeam) @@ -570,7 +586,7 @@ bool CSaveTeam::HandleSaveError(int Result, int ClientId, CGameContext *pGameCon return true; } -void CSaveTeam::Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakStrong) +bool CSaveTeam::Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakStrong) { IGameController *pController = pGameServer->m_pController; CGameTeams *pTeams = &pController->Teams(); @@ -579,6 +595,7 @@ void CSaveTeam::Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakSt pTeams->SetTeamLock(Team, m_TeamLocked); pTeams->SetPractice(Team, m_Practice); + bool ContainsInvalidPlayer = false; int aPlayerCids[MAX_CLIENTS]; for(int i = m_MembersCount; i-- > 0;) { @@ -587,7 +604,7 @@ void CSaveTeam::Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakSt if(pGameServer->m_apPlayers[ClientId] && pTeams->m_Core.Team(ClientId) == Team) { CCharacter *pChr = MatchCharacter(pGameServer, m_pSavedTees[i].GetClientId(), i, KeepCurrentWeakStrong); - m_pSavedTees[i].Load(pChr, Team); + ContainsInvalidPlayer |= !m_pSavedTees[i].Load(pChr, Team); } } @@ -603,6 +620,8 @@ void CSaveTeam::Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakSt } // remove projectiles and laser pGameServer->m_World.RemoveEntitiesFromPlayers(aPlayerCids, m_MembersCount); + + return !ContainsInvalidPlayer; } CCharacter *CSaveTeam::MatchCharacter(CGameContext *pGameServer, int ClientId, int SaveId, bool KeepCurrentCharacter) const diff --git a/src/game/server/save.h b/src/game/server/save.h index ab9009488..145dbf25c 100644 --- a/src/game/server/save.h +++ b/src/game/server/save.h @@ -25,7 +25,7 @@ public: CSaveTee(); ~CSaveTee() = default; void Save(CCharacter *pchr); - void Load(CCharacter *pchr, int Team, bool IsSwap = false); + bool Load(CCharacter *pchr, int Team, bool IsSwap = false); char *GetString(const CSaveTeam *pTeam); int FromString(const char *pString); void LoadHookedPlayer(const CSaveTeam *pTeam); @@ -149,7 +149,7 @@ public: // returns true if a team can load, otherwise writes a nice error Message in pMessage bool MatchPlayers(const char (*paNames)[MAX_NAME_LENGTH], const int *pClientId, int NumPlayer, char *pMessage, int MessageLen) const; int Save(CGameContext *pGameServer, int Team, bool Dry = false); - void Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakStrong); + bool Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakStrong); CSaveTee *m_pSavedTees = nullptr; diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index daeeb7c54..30762fe8b 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -1026,11 +1026,20 @@ void CGameTeams::ProcessSaveTeam() m_apSaveTeamResult[Team]->m_SaveId, m_apSaveTeamResult[Team]->m_SavedTeam.GetString()); } + + bool TeamValid = false; if(Count(Team) > 0) { // keep current weak/strong order as on some maps there is no other way of switching - m_apSaveTeamResult[Team]->m_SavedTeam.Load(GameServer(), Team, true); + TeamValid = m_apSaveTeamResult[Team]->m_SavedTeam.Load(GameServer(), Team, true); } + + if(!TeamValid) + { + GameServer()->SendChatTeam(Team, "Your team has been killed because it contains an invalid tee state"); + KillTeam(Team, -1, -1); + } + char aSaveId[UUID_MAXSTRSIZE]; FormatUuid(m_apSaveTeamResult[Team]->m_SaveId, aSaveId, UUID_MAXSTRSIZE); dbg_msg("save", "Load successful: %s", aSaveId);