diff --git a/src/engine/server.h b/src/engine/server.h index 4e8b280f46..3d2e33a5df 100644 --- a/src/engine/server.h +++ b/src/engine/server.h @@ -85,7 +85,22 @@ class IGameServer : public IInterface virtual void OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) = 0; - virtual void OnClientConnected(int ClientID, bool AsSpec) = 0; + // Called before map reload, for any data that the game wants to + // persist to the next map. + // + // Has the size of the return value of `PersistentClientDataSize()`. + // + // Returns whether the game should be supplied with the data when the + // client connects for the next map. + virtual bool OnClientDataPersist(int ClientID, void *pData) = 0; + + // Called when a client connects. + // + // If it is reconnecting to the game after a map change, the + // `pPersistentData` point is nonnull and contains the data the game + // previously stored. + virtual void OnClientConnected(int ClientID, void *pPersistentData) = 0; + virtual void OnClientEnter(int ClientID) = 0; virtual void OnClientDrop(int ClientID, const char *pReason) = 0; virtual void OnClientDirectInput(int ClientID, void *pInput) = 0; @@ -93,7 +108,8 @@ class IGameServer : public IInterface virtual bool IsClientReady(int ClientID) const = 0; virtual bool IsClientPlayer(int ClientID) const = 0; - virtual bool IsClientSpectator(int ClientID) const = 0; + + virtual int PersistentClientDataSize() const = 0; virtual const char *GameType() const = 0; virtual const char *Version() const = 0; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 0ac0ee9962..ee5bc1fdde 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -266,6 +266,7 @@ void CServer::CClient::Reset() m_SnapRate = CClient::SNAPRATE_INIT; m_Score = 0; m_MapChunk = 0; + m_HasPersistentData = false; } CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta) @@ -858,7 +859,7 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) } else if(Msg == NETMSG_REQUEST_MAP_DATA) { - if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) == 0 || m_aClients[ClientID].m_State == CClient::STATE_CONNECTING || m_aClients[ClientID].m_State == CClient::STATE_CONNECTING_AS_SPEC) + if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) == 0 || m_aClients[ClientID].m_State == CClient::STATE_CONNECTING) { int ChunkSize = MAP_CHUNK_SIZE; @@ -892,7 +893,7 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) } else if(Msg == NETMSG_READY) { - if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && (m_aClients[ClientID].m_State == CClient::STATE_CONNECTING || m_aClients[ClientID].m_State == CClient::STATE_CONNECTING_AS_SPEC)) + if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && (m_aClients[ClientID].m_State == CClient::STATE_CONNECTING)) { char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(m_NetServer.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); @@ -901,9 +902,14 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) str_format(aBuf, sizeof(aBuf), "player is ready. ClientID=%x addr=%s", ClientID, aAddrStr); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf); - bool ConnectAsSpec = m_aClients[ClientID].m_State == CClient::STATE_CONNECTING_AS_SPEC; + void *pPersistentData = 0; + if(m_aClients[ClientID].m_HasPersistentData) + { + pPersistentData = m_aClients[ClientID].m_pPersistentData; + m_aClients[ClientID].m_HasPersistentData = false; + } m_aClients[ClientID].m_State = CClient::STATE_READY; - GameServer()->OnClientConnected(ClientID, ConnectAsSpec); + GameServer()->OnClientConnected(ClientID, pPersistentData); SendConnectionReady(ClientID); } } @@ -1281,6 +1287,14 @@ int CServer::Run() // m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, SendRconLineAuthed, this); + { + int Size = GameServer()->PersistentClientDataSize(); + for(int i = 0; i < MAX_CLIENTS; i++) + { + m_aClients[i].m_pPersistentData = mem_alloc(Size, 1); + } + } + // list maps m_pMapListHeap = new CHeap(); CSubdirCallbackUserdata Userdata; @@ -1361,9 +1375,15 @@ int CServer::Run() if(LoadMap(g_Config.m_SvMap)) { // new map loaded - bool aSpecs[MAX_CLIENTS]; - for(int c = 0; c < MAX_CLIENTS; c++) - aSpecs[c] = GameServer()->IsClientSpectator(c); + + // ask the game to for the data it wants to persist past a map change + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_aClients[i].m_State == CClient::STATE_INGAME) + { + m_aClients[i].m_HasPersistentData = GameServer()->OnClientDataPersist(i, m_aClients[i].m_pPersistentData); + } + } GameServer()->OnShutdown(); @@ -1373,8 +1393,10 @@ int CServer::Run() continue; SendMap(c); + bool HasPersistentData = m_aClients[c].m_HasPersistentData; m_aClients[c].Reset(); - m_aClients[c].m_State = aSpecs[c] ? CClient::STATE_CONNECTING_AS_SPEC : CClient::STATE_CONNECTING; + m_aClients[c].m_HasPersistentData = HasPersistentData; + m_aClients[c].m_State = CClient::STATE_CONNECTING; } m_GameStartTime = time_get(); diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 0b5b8b1608..3ce170fd65 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -92,7 +92,6 @@ class CServer : public IServer STATE_EMPTY = 0, STATE_AUTH, STATE_CONNECTING, - STATE_CONNECTING_AS_SPEC, STATE_READY, STATE_INGAME, @@ -135,6 +134,9 @@ class CServer : public IServer const IConsole::CCommandInfo *m_pRconCmdToSend; const CMapListEntry *m_pMapListEntryToSend; + bool m_HasPersistentData; + void *m_pPersistentData; + void Reset(); }; diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 6fb0f6b8ee..ac21435440 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -656,9 +656,25 @@ void CGameContext::OnClientEnter(int ClientID) } } -void CGameContext::OnClientConnected(int ClientID, bool Dummy, bool AsSpec) +bool CGameContext::OnClientDataPersist(int ClientID, void *pData) { - m_apPlayers[ClientID] = new(ClientID) CPlayer(this, ClientID, Dummy, AsSpec); + CPersistentClientData *pPersistent = (CPersistentClientData *)pData; + if(!m_apPlayers[ClientID]) + { + return false; + } + pPersistent->m_IsSpectator = m_apPlayers[ClientID]->GetTeam() == TEAM_SPECTATORS; + return true; +} + +void CGameContext::OnClientConnected(int ClientID, bool Dummy, CPersistentClientData *pPersistent) +{ + bool Spec = false; + if(pPersistent) + { + Spec = pPersistent->m_IsSpectator; + } + m_apPlayers[ClientID] = new(ClientID) CPlayer(this, ClientID, Dummy, Spec); if(Dummy) return; @@ -1482,7 +1498,7 @@ void CGameContext::OnInit() if(g_Config.m_DbgDummies) { for(int i = 0; i < g_Config.m_DbgDummies ; i++) - OnClientConnected(Server()->MaxClients() -i-1, true); + OnClientConnected(Server()->MaxClients() -i-1, true, 0); } #endif } @@ -1534,11 +1550,6 @@ bool CGameContext::IsClientPlayer(int ClientID) const return m_apPlayers[ClientID] && m_apPlayers[ClientID]->GetTeam() != TEAM_SPECTATORS; } -bool CGameContext::IsClientSpectator(int ClientID) const -{ - return m_apPlayers[ClientID] && m_apPlayers[ClientID]->GetTeam() == TEAM_SPECTATORS; -} - const char *CGameContext::GameType() const { return m_pController && m_pController->GetGameType() ? m_pController->GetGameType() : ""; } const char *CGameContext::Version() const { return GAME_VERSION; } const char *CGameContext::NetVersion() const { return GAME_NETVERSION; } diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 3b299621ce..2b3586b010 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -68,6 +68,12 @@ class CGameContext : public IGameServer void Construct(int Resetting); bool m_Resetting; + + struct CPersistentClientData + { + bool m_IsSpectator; + }; + public: IServer *Server() const { return m_pServer; } class IConsole *Console() { return m_pConsole; } @@ -163,8 +169,9 @@ class CGameContext : public IGameServer virtual void OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID); - virtual void OnClientConnected(int ClientID, bool AsSpec) { OnClientConnected(ClientID, false, AsSpec); } - void OnClientConnected(int ClientID, bool Dummy, bool AsSpec); + virtual bool OnClientDataPersist(int ClientID, void *pData); + virtual void OnClientConnected(int ClientID, void *pPersistentData) { OnClientConnected(ClientID, false, (CPersistentClientData *)pPersistentData); } + void OnClientConnected(int ClientID, bool Dummy, CPersistentClientData *pPersistent); void OnClientTeamChange(int ClientID); virtual void OnClientEnter(int ClientID); virtual void OnClientDrop(int ClientID, const char *pReason); @@ -173,7 +180,7 @@ class CGameContext : public IGameServer virtual bool IsClientReady(int ClientID) const; virtual bool IsClientPlayer(int ClientID) const; - virtual bool IsClientSpectator(int ClientID) const; + virtual int PersistentClientDataSize() const { return sizeof(CPersistentClientData); } virtual const char *GameType() const; virtual const char *Version() const; diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index ebf31c159e..f8f0032183 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -12,7 +12,7 @@ MACRO_ALLOC_POOL_ID_IMPL(CPlayer, MAX_CLIENTS) IServer *CPlayer::Server() const { return m_pGameServer->Server(); } -CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, bool Dummy, bool AsSpec) +CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, bool Dummy, bool Spec) { m_pGameServer = pGameServer; m_RespawnTick = Server()->Tick(); @@ -20,7 +20,7 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, bool Dummy, bool AsSpe m_ScoreStartTick = Server()->Tick(); m_pCharacter = 0; m_ClientID = ClientID; - m_Team = AsSpec ? TEAM_SPECTATORS : GameServer()->m_pController->GetStartTeam(); + m_Team = Spec ? TEAM_SPECTATORS : GameServer()->m_pController->GetStartTeam(); m_SpecMode = SPEC_FREEVIEW; m_SpectatorID = -1; m_pSpecFlag = 0; diff --git a/src/game/server/player.h b/src/game/server/player.h index 568c7befa9..e365efa75c 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -19,7 +19,7 @@ class CPlayer MACRO_ALLOC_POOL_ID() public: - CPlayer(CGameContext *pGameServer, int ClientID, bool Dummy, bool AsSpec = false); + CPlayer(CGameContext *pGameServer, int ClientID, bool Dummy, bool Spec); ~CPlayer(); void Init(int CID);