diff --git a/src/engine/client.h b/src/engine/client.h index b6ed31a03..e8b3af4be 100644 --- a/src/engine/client.h +++ b/src/engine/client.h @@ -240,6 +240,8 @@ public: virtual const char *GetItemName(int Type) = 0; virtual const char *Version() = 0; virtual const char *NetVersion() = 0; + virtual int DDNetVersion() = 0; + virtual const char *DDNetVersionStr() = 0; virtual void OnDummyDisconnect() = 0; virtual void Echo(const char *pString) = 0; diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index cd194b64d..a5b75d377 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -401,6 +401,12 @@ int CClient::SendMsg(CMsgPacker *pMsg, int Flags) void CClient::SendInfo() { + CMsgPacker MsgVer(NETMSG_CLIENTVER, true); + MsgVer.AddRaw(&m_ConnectionID, sizeof(m_ConnectionID)); + MsgVer.AddInt(GameClient()->DDNetVersion()); + MsgVer.AddString(GameClient()->DDNetVersionStr(), 0); + SendMsg(&MsgVer, MSGFLAG_VITAL); + CMsgPacker Msg(NETMSG_INFO, true); Msg.AddString(GameClient()->NetVersion(), 128); Msg.AddString(m_Password, 128); @@ -673,6 +679,7 @@ void CClient::Connect(const char *pAddress, const char *pPassword) Disconnect(); + m_ConnectionID = RandomUuid(); str_copy(m_aServerAddressStr, pAddress, sizeof(m_aServerAddressStr)); str_format(aBuf, sizeof(aBuf), "connecting to '%s'", m_aServerAddressStr); @@ -2965,6 +2972,12 @@ void CClient::Run() m_DummySendConnInfo = false; // send client info + CMsgPacker MsgVer(NETMSG_CLIENTVER, true); + MsgVer.AddRaw(&m_ConnectionID, sizeof(m_ConnectionID)); + MsgVer.AddInt(GameClient()->DDNetVersion()); + MsgVer.AddString(GameClient()->DDNetVersionStr(), 0); + SendMsg(&MsgVer, MSGFLAG_VITAL); + CMsgPacker MsgInfo(NETMSG_INFO, true); MsgInfo.AddString(GameClient()->NetVersion(), 128); MsgInfo.AddString(m_Password, 128); diff --git a/src/engine/client/client.h b/src/engine/client/client.h index a2f4c72f0..1741e42aa 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -103,6 +103,8 @@ class CClient : public IClient, public CDemoPlayer::IListener char m_aServerAddressStr[256]; + CUuid m_ConnectionID; + unsigned m_SnapshotParts[2]; int64 m_LocalStartTime; diff --git a/src/engine/server.h b/src/engine/server.h index 63dba0f0e..4dd110edc 100644 --- a/src/engine/server.h +++ b/src/engine/server.h @@ -28,7 +28,10 @@ public: { const char *m_pName; int m_Latency; - int m_ClientVersion; + bool m_GotDDNetVersion; + int m_DDNetVersion; + const char *m_pDDNetVersionStr; + const CUuid *m_pConnectionID; }; int Tick() const { return m_CurrentGameTick; } @@ -43,6 +46,7 @@ public: virtual bool ClientIngame(int ClientID) = 0; virtual bool ClientAuthed(int ClientID) = 0; virtual int GetClientInfo(int ClientID, CClientInfo *pInfo) = 0; + virtual void SetClientDDNetVersion(int ClientID, int DDNetVersion) = 0; virtual void GetClientAddr(int ClientID, char *pAddrStr, int Size) = 0; virtual void RestrictRconOutput(int ClientID) = 0; @@ -112,7 +116,7 @@ public: { CClientInfo Info; GetClientInfo(Client, &Info); - if (Info.m_ClientVersion >= VERSION_DDNET_OLD) + if (Info.m_DDNetVersion >= VERSION_DDNET_OLD) return true; int *pMap = GetIdMap(Client); bool Found = false; @@ -132,7 +136,7 @@ public: { CClientInfo Info; GetClientInfo(Client, &Info); - if (Info.m_ClientVersion >= VERSION_DDNET_OLD) + if (Info.m_DDNetVersion >= VERSION_DDNET_OLD) return true; Target = clamp(Target, 0, VANILLA_MAX_CLIENTS-1); int *pMap = GetIdMap(Client); @@ -234,8 +238,6 @@ public: // DDRace virtual void OnSetAuthed(int ClientID, int Level) = 0; - virtual int GetClientVersion(int ClientID) = 0; - virtual void SetClientVersion(int ClientID, int Version) = 0; virtual bool PlayerExists(int ClientID) = 0; virtual void OnClientEngineJoin(int ClientID) = 0; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 84ebc6196..0baa8ddec 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -256,6 +256,9 @@ void CServer::CClient::Reset() m_Score = 0; m_NextMapChunk = 0; m_Flags = 0; + m_DDNetVersion = VERSION_NONE; + m_GotDDNetVersionPacket = false; + m_DDNetVersionSettled = false; } CServer::CServer() @@ -516,12 +519,34 @@ int CServer::GetClientInfo(int ClientID, CClientInfo *pInfo) { pInfo->m_pName = m_aClients[ClientID].m_aName; pInfo->m_Latency = m_aClients[ClientID].m_Latency; - pInfo->m_ClientVersion = GameServer()->GetClientVersion(ClientID); + pInfo->m_GotDDNetVersion = m_aClients[ClientID].m_DDNetVersionSettled; + pInfo->m_DDNetVersion = m_aClients[ClientID].m_DDNetVersion >= 0 ? m_aClients[ClientID].m_DDNetVersion : VERSION_VANILLA; + if(m_aClients[ClientID].m_GotDDNetVersionPacket) + { + pInfo->m_pConnectionID = &m_aClients[ClientID].m_ConnectionID; + pInfo->m_pDDNetVersionStr = m_aClients[ClientID].m_aDDNetVersionStr; + } + else + { + pInfo->m_pConnectionID = 0; + pInfo->m_pDDNetVersionStr = 0; + } return 1; } return 0; } +void CServer::SetClientDDNetVersion(int ClientID, int DDNetVersion) +{ + dbg_assert(ClientID >= 0 && ClientID < MAX_CLIENTS, "client_id is not valid"); + + if(m_aClients[ClientID].m_State == CClient::STATE_INGAME) + { + m_aClients[ClientID].m_DDNetVersion = DDNetVersion; + m_aClients[ClientID].m_DDNetVersionSettled = true; + } +} + void CServer::GetClientAddr(int ClientID, char *pAddrStr, int Size) { if(ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_INGAME) @@ -873,7 +898,7 @@ int CServer::NewClientNoAuthCallback(int ClientID, void *pUser) int CServer::NewClientCallback(int ClientID, void *pUser) { CServer *pThis = (CServer *)pUser; - pThis->m_aClients[ClientID].m_State = CClient::STATE_AUTH; + pThis->m_aClients[ClientID].m_State = CClient::STATE_PREAUTH; pThis->m_aClients[ClientID].m_SupportsMapSha256 = false; pThis->m_aClients[ClientID].m_DnsblState = CClient::DNSBL_STATE_NONE; pThis->m_aClients[ClientID].m_aName[0] = 0; @@ -1195,9 +1220,28 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) if(Sys) { // system message - if(Msg == NETMSG_INFO) + if(Msg == NETMSG_CLIENTVER) { - if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && m_aClients[ClientID].m_State == CClient::STATE_AUTH) + if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && m_aClients[ClientID].m_State == CClient::STATE_PREAUTH) + { + CUuid *pConnectionID = (CUuid *)Unpacker.GetRaw(sizeof(*pConnectionID)); + int DDNetVersion = Unpacker.GetInt(); + const char *pDDNetVersionStr = Unpacker.GetString(CUnpacker::SANITIZE_CC); + if(Unpacker.Error() || !str_utf8_check(pDDNetVersionStr) || DDNetVersion < 0) + { + return; + } + m_aClients[ClientID].m_ConnectionID = *pConnectionID; + m_aClients[ClientID].m_DDNetVersion = DDNetVersion; + str_copy(m_aClients[ClientID].m_aDDNetVersionStr, pDDNetVersionStr, sizeof(m_aClients[ClientID].m_aDDNetVersionStr)); + m_aClients[ClientID].m_DDNetVersionSettled = true; + m_aClients[ClientID].m_GotDDNetVersionPacket = true; + m_aClients[ClientID].m_State = CClient::STATE_AUTH; + } + } + else if(Msg == NETMSG_INFO) + { + if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && (m_aClients[ClientID].m_State == CClient::STATE_PREAUTH || m_aClients[ClientID].m_State == CClient::STATE_AUTH)) { const char *pVersion = Unpacker.GetString(CUnpacker::SANITIZE_CC); if(!str_utf8_check(pVersion)) @@ -1351,11 +1395,13 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) } if(Unpacker.Error() == 0 && !str_comp(pCmd, "crashmeplx")) { - int version = GameServer()->GetClientVersion(ClientID); - if (GameServer()->PlayerExists(ClientID) && version < VERSION_DDNET_OLD) - GameServer()->SetClientVersion(ClientID, VERSION_DDNET_OLD); - } else - if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Unpacker.Error() == 0 && m_aClients[ClientID].m_Authed) + int Version = m_aClients[ClientID].m_DDNetVersion; + if (GameServer()->PlayerExists(ClientID) && Version < VERSION_DDNET_OLD) + { + m_aClients[ClientID].m_DDNetVersion = VERSION_DDNET_OLD; + } + } + else if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Unpacker.Error() == 0 && m_aClients[ClientID].m_Authed) { if (GameServer()->PlayerExists(ClientID)) { @@ -2340,7 +2386,7 @@ void CServer::ConStatus(IConsole::IResult *pResult, void *pUser) } str_format(aBuf, sizeof(aBuf), "id=%d addr=<{%s}> name='%s' client=%d secure=%s flags=%d%s%s", - i, aAddrStr, pThis->m_aClients[i].m_aName, pThis->GameServer()->GetClientVersion(i), + i, aAddrStr, pThis->m_aClients[i].m_aName, pThis->m_aClients[i].m_DDNetVersion, pThis->m_NetServer.HasSecurityToken(i) ? "yes" : "no", pThis->m_aClients[i].m_Flags, aDnsblStr, aAuthStr); } else diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 38a99ec75..6c8a456cb 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -133,6 +133,7 @@ public: enum { STATE_EMPTY = 0, + STATE_PREAUTH, STATE_AUTH, STATE_CONNECTING, STATE_READY, @@ -190,6 +191,11 @@ public: // DDRace NETADDR m_Addr; + bool m_GotDDNetVersionPacket; + bool m_DDNetVersionSettled; + int m_DDNetVersion; + char m_aDDNetVersionStr[64]; + CUuid m_ConnectionID; // DNSBL int m_DnsblState; @@ -269,6 +275,7 @@ public: const char *GetAuthName(int ClientID); void GetMapInfo(char *pMapName, int MapNameSize, int *pMapSize, SHA256_DIGEST *pMapSha256, int *pMapCrc); int GetClientInfo(int ClientID, CClientInfo *pInfo); + void SetClientDDNetVersion(int ClientID, int DDNetVersion); void GetClientAddr(int ClientID, char *pAddrStr, int Size); const char *ClientName(int ClientID); const char *ClientClan(int ClientID); diff --git a/src/engine/shared/protocol.h b/src/engine/shared/protocol.h index d1e522e70..045881074 100644 --- a/src/engine/shared/protocol.h +++ b/src/engine/shared/protocol.h @@ -99,6 +99,7 @@ enum enum { + VERSION_NONE = -1, VERSION_VANILLA = 0, VERSION_DDRACE = 1, VERSION_DDNET_OLD = 2, diff --git a/src/engine/shared/protocol_ex_msgs.h b/src/engine/shared/protocol_ex_msgs.h index 7d7b0e9da..cbd360ade 100644 --- a/src/engine/shared/protocol_ex_msgs.h +++ b/src/engine/shared/protocol_ex_msgs.h @@ -26,3 +26,4 @@ UUID(NETMSG_IDONTKNOW, "i-dont-know@ddnet.tw") UUID(NETMSG_RCONTYPE, "rcon-type@ddnet.tw") UUID(NETMSG_MAP_DETAILS, "map-details@ddnet.tw") UUID(NETMSG_CAPABILITIES, "capabilities@ddnet.tw") +UUID(NETMSG_CLIENTVER, "clientver@ddnet.tw") diff --git a/src/engine/shared/teehistorian_ex_chunks.h b/src/engine/shared/teehistorian_ex_chunks.h index 8d14ec77d..c2b34ffa9 100644 --- a/src/engine/shared/teehistorian_ex_chunks.h +++ b/src/engine/shared/teehistorian_ex_chunks.h @@ -1,6 +1,8 @@ // This file can be included several times. UUID(TEEHISTORIAN_TEST, "teehistorian-test@ddnet.tw") +UUID(TEEHISTORIAN_DDNETVER_OLD, "teehistorian-ddnetver-old@ddnet.tw") +UUID(TEEHISTORIAN_DDNETVER, "teehistorian-ddnetver@ddnet.tw") UUID(TEEHISTORIAN_AUTH_INIT, "teehistorian-auth-init@ddnet.tw") UUID(TEEHISTORIAN_AUTH_LOGIN, "teehistorian-auth-login@ddnet.tw") UUID(TEEHISTORIAN_AUTH_LOGOUT, "teehistorian-auth-logout@ddnet.tw") diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 1f43b865d..b5c114513 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -112,6 +112,8 @@ void CGameClient::CStack::Add(class CComponent *pComponent) { m_paComponents[m_N const char *CGameClient::Version() { return GAME_VERSION; } const char *CGameClient::NetVersion() { return GAME_NETVERSION; } +int CGameClient::DDNetVersion() { return CLIENT_VERSIONNR; } +const char *CGameClient::DDNetVersionStr() { return m_aDDNetVersionStr; } const char *CGameClient::GetItemName(int Type) { return m_NetObjHandler.GetObjName(Type); } void CGameClient::OnConsoleInit() @@ -287,6 +289,15 @@ void CGameClient::OnInit() int64 Start = time_get(); + if(GIT_SHORTREV_HASH) + { + str_format(m_aDDNetVersionStr, sizeof(m_aDDNetVersionStr), "%s %s (%s)", GAME_NAME, GAME_RELEASE_VERSION, GIT_SHORTREV_HASH); + } + else + { + str_format(m_aDDNetVersionStr, sizeof(m_aDDNetVersionStr), "%s %s", GAME_NAME, GAME_RELEASE_VERSION); + } + // set the language g_Localization.Load(g_Config.m_ClLanguagefile, Storage(), Console()); diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 93ad76d3a..4f5bfafc3 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -108,6 +108,8 @@ class CGameClient : public IGameClient int m_CheckInfo[2]; + char m_aDDNetVersionStr[64]; + static void ConTeam(IConsole::IResult *pResult, void *pUserData); static void ConKill(IConsole::IResult *pResult, void *pUserData); @@ -360,6 +362,8 @@ public: virtual const char *GetItemName(int Type); virtual const char *Version(); virtual const char *NetVersion(); + virtual int DDNetVersion(); + virtual const char *DDNetVersionStr(); // actions // TODO: move these diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 5d4f2cec6..2d263870c 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -1413,7 +1413,7 @@ void CGameContext::ConSetTimerType(IConsole::IResult *pResult, void *pUserData) if(str_comp_nocase(pResult->GetString(0), "gametimer") == 0) { - if(pPlayer->m_ClientVersion >= VERSION_DDNET_GAMETICK) + if(pPlayer->GetClientVersion() >= VERSION_DDNET_GAMETICK) pPlayer->m_TimerType = CPlayer::TIMERTYPE_GAMETIMER; else pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "timer", "gametimer is not supported by your client."); @@ -1422,7 +1422,7 @@ void CGameContext::ConSetTimerType(IConsole::IResult *pResult, void *pUserData) pPlayer->m_TimerType = CPlayer::TIMERTYPE_BROADCAST; else if(str_comp_nocase(pResult->GetString(0), "both") == 0) { - if(pPlayer->m_ClientVersion >= VERSION_DDNET_GAMETICK) + if(pPlayer->GetClientVersion() >= VERSION_DDNET_GAMETICK) pPlayer->m_TimerType = CPlayer::TIMERTYPE_GAMETIMER_AND_BROADCAST; else { @@ -1434,7 +1434,7 @@ void CGameContext::ConSetTimerType(IConsole::IResult *pResult, void *pUserData) pPlayer->m_TimerType = CPlayer::TIMERTYPE_NONE; else if(str_comp_nocase(pResult->GetString(0), "cycle") == 0) { - if(pPlayer->m_ClientVersion >= VERSION_DDNET_GAMETICK) + if(pPlayer->GetClientVersion() >= VERSION_DDNET_GAMETICK) { if(pPlayer->m_TimerType < CPlayer::TIMERTYPE_NONE) pPlayer->m_TimerType++; diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index a2724d5ac..6173374c1 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -1306,7 +1306,7 @@ void CCharacter::HandleBroadcast() CPlayerData *pData = GameServer()->Score()->PlayerData(m_pPlayer->GetCID()); if(m_DDRaceState == DDRACE_STARTED && m_CpLastBroadcast != m_CpActive && - m_CpActive > -1 && m_CpTick > Server()->Tick() && m_pPlayer->m_ClientVersion == VERSION_VANILLA && + m_CpActive > -1 && m_CpTick > Server()->Tick() && m_pPlayer->GetClientVersion() == VERSION_VANILLA && pData->m_BestTime && pData->m_aBestCpTime[m_CpActive] != 0) { char aBroadcast[128]; @@ -1452,7 +1452,7 @@ void CCharacter::HandleTiles(int Index) m_CpActive = cp; m_CpCurrent[cp] = m_Time; m_CpTick = Server()->Tick() + Server()->TickSpeed() * 2; - if(m_pPlayer->m_ClientVersion >= VERSION_DDRACE) { + if(m_pPlayer->GetClientVersion() >= VERSION_DDRACE) { CPlayerData *pData = GameServer()->Score()->PlayerData(m_pPlayer->GetCID()); CNetMsg_Sv_DDRaceTime Msg; Msg.m_Time = (int)m_Time; @@ -1477,7 +1477,7 @@ void CCharacter::HandleTiles(int Index) m_CpActive = cpf; m_CpCurrent[cpf] = m_Time; m_CpTick = Server()->Tick() + Server()->TickSpeed()*2; - if(m_pPlayer->m_ClientVersion >= VERSION_DDRACE) { + if(m_pPlayer->GetClientVersion() >= VERSION_DDRACE) { CPlayerData *pData = GameServer()->Score()->PlayerData(m_pPlayer->GetCID()); CNetMsg_Sv_DDRaceTime Msg; Msg.m_Time = (int)m_Time; diff --git a/src/game/server/entities/projectile.cpp b/src/game/server/entities/projectile.cpp index 65e92d1e6..efa85635a 100644 --- a/src/game/server/entities/projectile.cpp +++ b/src/game/server/entities/projectile.cpp @@ -326,7 +326,7 @@ void CProjectile::Snap(int SnappingClient) CNetObj_Projectile *pProj = static_cast(Server()->SnapNewItem(NETOBJTYPE_PROJECTILE, m_ID, sizeof(CNetObj_Projectile))); if(pProj) { - if(SnappingClient > -1 && GameServer()->m_apPlayers[SnappingClient] && GameServer()->m_apPlayers[SnappingClient]->m_ClientVersion >= VERSION_DDNET_ANTIPING_PROJECTILE) + if(SnappingClient > -1 && GameServer()->m_apPlayers[SnappingClient] && GameServer()->m_apPlayers[SnappingClient]->GetClientVersion() >= VERSION_DDNET_ANTIPING_PROJECTILE) FillExtraInfo(pProj); else FillInfo(pProj); diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 684593bc9..e8b747b4c 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -517,7 +517,7 @@ void CGameContext::SendVoteSet(int ClientID) void CGameContext::SendVoteStatus(int ClientID, int Total, int Yes, int No) { - if (Total > VANILLA_MAX_CLIENTS && m_apPlayers[ClientID] && m_apPlayers[ClientID]->m_ClientVersion <= VERSION_DDRACE) + if (Total > VANILLA_MAX_CLIENTS && m_apPlayers[ClientID] && m_apPlayers[ClientID]->GetClientVersion() <= VERSION_DDRACE) { Yes = float(Yes) * VANILLA_MAX_CLIENTS / float(Total); No = float(No) * VANILLA_MAX_CLIENTS / float(Total); @@ -592,15 +592,19 @@ void CGameContext::SendTuningParams(int ClientID, int Zone) else pParams = (int *)&(m_aTuningList[Zone]); - unsigned int last = sizeof(m_Tuning)/sizeof(int); - if (m_apPlayers[ClientID] && m_apPlayers[ClientID]->m_ClientVersion < VERSION_DDNET_EXTRATUNES) - last = 33; - else if (m_apPlayers[ClientID] && m_apPlayers[ClientID]->m_ClientVersion < VERSION_DDNET_HOOKDURATION_TUNE) - last = 37; - else if (m_apPlayers[ClientID] && m_apPlayers[ClientID]->m_ClientVersion < VERSION_DDNET_FIREDELAY_TUNE) - last = 38; + unsigned int Last = sizeof(m_Tuning)/sizeof(int); + if(m_apPlayers[ClientID]) + { + int ClientVersion = m_apPlayers[ClientID]->GetClientVersion(); + if(ClientVersion < VERSION_DDNET_EXTRATUNES) + Last = 33; + else if(ClientVersion < VERSION_DDNET_HOOKDURATION_TUNE) + Last = 37; + else if(ClientVersion < VERSION_DDNET_FIREDELAY_TUNE) + Last = 38; + } - for(unsigned i = 0; i < last; i++) + for(unsigned i = 0; i < Last; i++) { if (m_apPlayers[ClientID] && m_apPlayers[ClientID]->GetCharacter()) { @@ -1095,6 +1099,13 @@ void CGameContext::OnClientEnter(int ClientID) if(!Server()->ClientPrevIngame(ClientID)) { + IServer::CClientInfo Info; + Server()->GetClientInfo(ClientID, &Info); + if(Info.m_GotDDNetVersion) + { + OnClientDDNetVersionKnown(ClientID); + } + char aBuf[512]; str_format(aBuf, sizeof(aBuf), "'%s' entered and joined the %s", Server()->ClientName(ClientID), m_pController->GetTeamName(m_apPlayers[ClientID]->GetTeam())); SendChat(-1, CGameContext::CHAT_ALL, aBuf); @@ -1218,6 +1229,79 @@ void CGameContext::OnClientEngineDrop(int ClientID, const char *pReason) } } +void CGameContext::OnClientDDNetVersionKnown(int ClientID) +{ + IServer::CClientInfo Info; + Server()->GetClientInfo(ClientID, &Info); + int ClientVersion = Info.m_DDNetVersion; + dbg_msg("ddnet", "cid=%d version=%d", ClientID, ClientVersion); + + if(m_TeeHistorianActive) + { + if(Info.m_pConnectionID && Info.m_pDDNetVersionStr) + { + m_TeeHistorian.RecordDDNetVersion(ClientID, *Info.m_pConnectionID, ClientVersion, Info.m_pDDNetVersionStr); + } + else + { + m_TeeHistorian.RecordDDNetVersionOld(ClientID, ClientVersion); + } + } + + CPlayer *pPlayer = m_apPlayers[ClientID]; + if(ClientVersion >= VERSION_DDNET_GAMETICK) + pPlayer->m_TimerType = g_Config.m_SvDefaultTimerType; + + //first update his teams state + ((CGameControllerDDRace *)m_pController)->m_Teams.SendTeamsState(ClientID); + + //second give him records + SendRecord(ClientID); + + //third give him others current time for table score + if(g_Config.m_SvHideScore) + { + return; + } + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_apPlayers[i] && Score()->PlayerData(i)->m_CurrentTime > 0) + { + CNetMsg_Sv_PlayerTime Msg; + Msg.m_Time = Score()->PlayerData(i)->m_CurrentTime * 100; + Msg.m_ClientID = i; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientID); + //also send its time to others + + } + } + //also send its time to others + if(Score()->PlayerData(ClientID)->m_CurrentTime > 0) + { + //TODO: make function for this fucking steps + CNetMsg_Sv_PlayerTime Msg; + Msg.m_Time = Score()->PlayerData(ClientID)->m_CurrentTime * 100; + Msg.m_ClientID = ClientID; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); + } + + //and give him correct tunings + if (ClientVersion >= VERSION_DDNET_EXTRATUNES) + SendTuningParams(ClientID, pPlayer->m_TuneZone); + + //tell old clients to update + if (ClientVersion < VERSION_DDNET_UPDATER_FIXED && g_Config.m_SvClientSuggestionOld[0] != '\0') + SendBroadcast(g_Config.m_SvClientSuggestionOld, ClientID); + //tell known bot clients that they're botting and we know it + if (((ClientVersion >= 15 && ClientVersion < 100) || ClientVersion == 502) && g_Config.m_SvClientSuggestionBot[0] != '\0') + SendBroadcast(g_Config.m_SvClientSuggestionBot, ClientID); + //autoban known bot versions + if(g_Config.m_SvBannedVersions[0] != '\0' && IsVersionBanned(ClientVersion)) + { + Server()->Kick(ClientID, "unsupported client"); + } +} + void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) { void *pRawMsg = m_NetObjHandler.SecureUnpackMsg(MsgID, pUnpacker); @@ -1752,66 +1836,19 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } else if (MsgID == NETMSGTYPE_CL_ISDDNET) { - int Version = pUnpacker->GetInt(); - - if (pUnpacker->Error()) + IServer::CClientInfo Info; + Server()->GetClientInfo(ClientID, &Info); + if(Info.m_GotDDNetVersion) { - if (pPlayer->m_ClientVersion < VERSION_DDRACE) - pPlayer->m_ClientVersion = VERSION_DDRACE; + return; } - else if(pPlayer->m_ClientVersion < Version) - pPlayer->m_ClientVersion = Version; - - if(pPlayer->m_ClientVersion >= VERSION_DDNET_GAMETICK) - pPlayer->m_TimerType = g_Config.m_SvDefaultTimerType; - - dbg_msg("ddnet", "%d using Custom Client %d", ClientID, pPlayer->m_ClientVersion); - - //first update his teams state - ((CGameControllerDDRace*)m_pController)->m_Teams.SendTeamsState(ClientID); - - //second give him records - SendRecord(ClientID); - - //third give him others current time for table score - if(g_Config.m_SvHideScore) return; - for(int i = 0; i < MAX_CLIENTS; i++) + int DDNetVersion = pUnpacker->GetInt(); + if(pUnpacker->Error() || DDNetVersion < 0) { - if(m_apPlayers[i] && Score()->PlayerData(i)->m_CurrentTime > 0) - { - CNetMsg_Sv_PlayerTime Msg; - Msg.m_Time = Score()->PlayerData(i)->m_CurrentTime * 100; - Msg.m_ClientID = i; - Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientID); - //also send its time to others - - } - } - //also send its time to others - if(Score()->PlayerData(ClientID)->m_CurrentTime > 0) - { - //TODO: make function for this fucking steps - CNetMsg_Sv_PlayerTime Msg; - Msg.m_Time = Score()->PlayerData(ClientID)->m_CurrentTime * 100; - Msg.m_ClientID = ClientID; - Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); - } - - //and give him correct tunings - if (Version >= VERSION_DDNET_EXTRATUNES) - SendTuningParams(ClientID, pPlayer->m_TuneZone); - - //tell old clients to update - if (Version < VERSION_DDNET_UPDATER_FIXED && g_Config.m_SvClientSuggestionOld[0] != '\0') - SendBroadcast(g_Config.m_SvClientSuggestionOld, ClientID); - //tell known bot clients that they're botting and we know it - if (((Version >= 15 && Version < 100) || Version == 502) && g_Config.m_SvClientSuggestionBot[0] != '\0') - SendBroadcast(g_Config.m_SvClientSuggestionBot, ClientID); - //autoban known bot versions - if(g_Config.m_SvBannedVersions[0] != '\0' && IsVersionBanned(Version)) - { - Server()->Kick(ClientID, "unsupported client"); + DDNetVersion = VERSION_DDRACE; } + Server()->SetClientDDNetVersion(ClientID, DDNetVersion); + OnClientDDNetVersionKnown(ClientID); } else if (MsgID == NETMSGTYPE_CL_SHOWOTHERS) { @@ -3436,18 +3473,18 @@ void CGameContext::Whisper(int ClientID, char *pStr) void CGameContext::WhisperID(int ClientID, int VictimID, char *pMessage) { - if (!CheckClientID2(ClientID)) + if(!CheckClientID2(ClientID)) return; - if (!CheckClientID2(VictimID)) + if(!CheckClientID2(VictimID)) return; - if (m_apPlayers[ClientID]) + if(m_apPlayers[ClientID]) m_apPlayers[ClientID]->m_LastWhisperTo = VictimID; char aBuf[256]; - if (m_apPlayers[ClientID] && m_apPlayers[ClientID]->m_ClientVersion >= VERSION_DDNET_WHISPER) + if(GetClientVersion(ClientID) >= VERSION_DDNET_WHISPER) { CNetMsg_Sv_Chat Msg; Msg.m_Team = CHAT_WHISPER_SEND; @@ -3464,7 +3501,7 @@ void CGameContext::WhisperID(int ClientID, int VictimID, char *pMessage) SendChatTarget(ClientID, aBuf); } - if (m_apPlayers[VictimID] && m_apPlayers[VictimID]->m_ClientVersion >= VERSION_DDNET_WHISPER) + if(GetClientVersion(VictimID) >= VERSION_DDNET_WHISPER) { CNetMsg_Sv_Chat Msg2; Msg2.m_Team = CHAT_WHISPER_RECV; @@ -3550,18 +3587,9 @@ void CGameContext::List(int ClientID, const char *pFilter) int CGameContext::GetClientVersion(int ClientID) { - return m_apPlayers[ClientID] - ? m_apPlayers[ClientID]->m_ClientVersion - : 0; -} - -void CGameContext::SetClientVersion(int ClientID, int Version) -{ - if(!m_apPlayers[ClientID]) - { - return; - } - m_apPlayers[ClientID]->m_ClientVersion = Version; + IServer::CClientInfo Info = {0}; + Server()->GetClientInfo(ClientID, &Info); + return Info.m_DDNetVersion; } bool CGameContext::PlayerModerating() diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 1a28ac48f..061344005 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -251,6 +251,7 @@ public: virtual const char *NetVersion(); // DDRace + void OnClientDDNetVersionKnown(int ClientID); virtual void FillAntibot(CAntibotRoundData *pData); int ProcessSpamProtection(int ClientID); int GetDDRaceTeam(int ClientID); @@ -258,7 +259,6 @@ public: int64 m_NonEmptySince; int64 m_LastMapVote; int GetClientVersion(int ClientID); - void SetClientVersion(int ClientID, int Version); bool PlayerExists(int ClientID) { return m_apPlayers[ClientID]; }; // Returns true if someone is actively moderating. bool PlayerModerating(); diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp index 554d26696..b63a05849 100644 --- a/src/game/server/gamecontroller.cpp +++ b/src/game/server/gamecontroller.cpp @@ -516,7 +516,7 @@ void IGameController::Snap(int SnappingClient) CPlayer *pPlayer = SnappingClient > -1 ? GameServer()->m_apPlayers[SnappingClient] : 0; CPlayer *pPlayer2; - if(pPlayer && (pPlayer->m_TimerType == CPlayer::TIMERTYPE_GAMETIMER || pPlayer->m_TimerType == CPlayer::TIMERTYPE_GAMETIMER_AND_BROADCAST) && pPlayer->m_ClientVersion >= VERSION_DDNET_GAMETICK) + if(pPlayer && (pPlayer->m_TimerType == CPlayer::TIMERTYPE_GAMETIMER || pPlayer->m_TimerType == CPlayer::TIMERTYPE_GAMETIMER_AND_BROADCAST) && pPlayer->GetClientVersion() >= VERSION_DDNET_GAMETICK) { if((pPlayer->GetTeam() == -1 || pPlayer->IsPaused()) && pPlayer->m_SpectatorID != SPEC_FREEVIEW diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index a7e5ca5ff..7edfb99a1 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -186,8 +186,8 @@ void CGameWorld::UpdatePlayerMaps() !GameServer()->m_apPlayers[i]->IsPaused() && GameServer()->m_apPlayers[i]->GetTeam() != -1 && !ch->CanCollide(i) && (!GameServer()->m_apPlayers[i] || - GameServer()->m_apPlayers[i]->m_ClientVersion == VERSION_VANILLA || - (GameServer()->m_apPlayers[i]->m_ClientVersion >= VERSION_DDRACE && + GameServer()->m_apPlayers[i]->GetClientVersion() == VERSION_VANILLA || + (GameServer()->m_apPlayers[i]->GetClientVersion() >= VERSION_DDRACE && !GameServer()->m_apPlayers[i]->m_ShowOthers ) ) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index e59412703..82f21389e 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -103,7 +103,6 @@ void CPlayer::Reset() GameServer()->Score()->PlayerData(m_ClientID)->Reset(); - m_ClientVersion = VERSION_VANILLA; m_ShowOthers = g_Config.m_SvShowOthersDefault; m_ShowAll = g_Config.m_SvShowAllDefault; m_SpecTeam = 0; @@ -297,12 +296,13 @@ void CPlayer::Snap(int SnappingClient) if(!pPlayerInfo) return; + int ClientVersion = GetClientVersion(); pPlayerInfo->m_Latency = SnappingClient == -1 ? m_Latency.m_Min : GameServer()->m_apPlayers[SnappingClient]->m_aActLatency[m_ClientID]; - pPlayerInfo->m_Local = (int)(m_ClientID == SnappingClient && (m_Paused != PAUSE_PAUSED || m_ClientVersion >= VERSION_DDNET_OLD)); + pPlayerInfo->m_Local = (int)(m_ClientID == SnappingClient && (m_Paused != PAUSE_PAUSED || ClientVersion >= VERSION_DDNET_OLD)); pPlayerInfo->m_ClientID = id; - pPlayerInfo->m_Team = (m_ClientVersion < VERSION_DDNET_OLD || m_Paused != PAUSE_PAUSED || m_ClientID != SnappingClient) && m_Paused < PAUSE_SPEC ? m_Team : TEAM_SPECTATORS; + pPlayerInfo->m_Team = (ClientVersion < VERSION_DDNET_OLD || m_Paused != PAUSE_PAUSED || m_ClientID != SnappingClient) && m_Paused < PAUSE_SPEC ? m_Team : TEAM_SPECTATORS; - if(m_ClientID == SnappingClient && m_Paused == PAUSE_PAUSED && m_ClientVersion < VERSION_DDNET_OLD) + if(m_ClientID == SnappingClient && m_Paused == PAUSE_PAUSED && ClientVersion < VERSION_DDNET_OLD) pPlayerInfo->m_Team = TEAM_SPECTATORS; // send 0 if times of others are not shown @@ -338,10 +338,7 @@ void CPlayer::Snap(int SnappingClient) void CPlayer::FakeSnap() { - // This is problematic when it's sent before we know whether it's a non-64-player-client - // Then we can't spectate players at the start - - if(m_ClientVersion >= VERSION_DDNET_OLD) + if(GetClientVersion() >= VERSION_DDNET_OLD) return; int FakeID = VANILLA_MAX_CLIENTS - 1; @@ -422,7 +419,7 @@ void CPlayer::OnPredictedInput(CNetObj_PlayerInput *NewInput) // Magic number when we can hope that client has successfully identified itself if(m_NumInputs == 20) { - if(g_Config.m_SvClientSuggestion[0] != '\0' && m_ClientVersion <= VERSION_DDNET_OLD) + if(g_Config.m_SvClientSuggestion[0] != '\0' && GetClientVersion() <= VERSION_DDNET_OLD) GameServer()->SendBroadcast(g_Config.m_SvClientSuggestion, m_ClientID); } } @@ -482,6 +479,11 @@ void CPlayer::OnPredictedEarlyInput(CNetObj_PlayerInput *NewInput) m_pCharacter->OnDirectInput(NewInput); } +int CPlayer::GetClientVersion() const +{ + return m_pGameServer->GetClientVersion(m_ClientID); +} + CCharacter *CPlayer::GetCharacter() { if(m_pCharacter && m_pCharacter->IsAlive()) diff --git a/src/game/server/player.h b/src/game/server/player.h index c9a892260..ada68a22a 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -26,6 +26,7 @@ public: void SetTeam(int Team, bool DoChatMsg=true); int GetTeam() const { return m_Team; }; int GetCID() const { return m_ClientID; }; + int GetClientVersion() const; void Tick(); void PostTick(); @@ -162,7 +163,6 @@ public: bool IsPlaying(); int64 m_Last_KickVote; int64 m_Last_Team; - int m_ClientVersion; bool m_ShowOthers; bool m_ShowAll; bool m_SpecTeam; diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index 24ff3c021..5a657da26 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -424,7 +424,7 @@ void CGameTeams::SendTeamsState(int ClientID) if (g_Config.m_SvTeam == 3) return; - if (!m_pGameContext->m_apPlayers[ClientID] || m_pGameContext->m_apPlayers[ClientID]->m_ClientVersion <= VERSION_DDRACE) + if (!m_pGameContext->m_apPlayers[ClientID] || m_pGameContext->m_apPlayers[ClientID]->GetClientVersion() <= VERSION_DDRACE) return; CMsgPacker Msg(NETMSGTYPE_SV_TEAMSSTATE); @@ -626,7 +626,7 @@ void CGameTeams::OnFinish(CPlayer* Player, float Time, const char *pTimestamp) NeedToSendNewRecord = true; for (int i = 0; i < MAX_CLIENTS; i++) { - if (GetPlayer(i) && GetPlayer(i)->m_ClientVersion >= VERSION_DDRACE) + if (GetPlayer(i) && GetPlayer(i)->GetClientVersion() >= VERSION_DDRACE) { if (!g_Config.m_SvHideScore || i == Player->GetCID()) { @@ -639,19 +639,19 @@ void CGameTeams::OnFinish(CPlayer* Player, float Time, const char *pTimestamp) } } - if (NeedToSendNewRecord && Player->m_ClientVersion >= VERSION_DDRACE) + if (NeedToSendNewRecord && Player->GetClientVersion() >= VERSION_DDRACE) { for (int i = 0; i < MAX_CLIENTS; i++) { if (GameServer()->m_apPlayers[i] - && GameServer()->m_apPlayers[i]->m_ClientVersion >= VERSION_DDRACE) + && GameServer()->m_apPlayers[i]->GetClientVersion() >= VERSION_DDRACE) { GameServer()->SendRecord(i); } } } - if (Player->m_ClientVersion >= VERSION_DDRACE) + if (Player->GetClientVersion() >= VERSION_DDRACE) { CNetMsg_Sv_DDRaceTime Msg; Msg.m_Time = (int)(Time * 100.0f); diff --git a/src/game/server/teehistorian.cpp b/src/game/server/teehistorian.cpp index 2bc61295a..4b295f173 100644 --- a/src/game/server/teehistorian.cpp +++ b/src/game/server/teehistorian.cpp @@ -490,6 +490,40 @@ void CTeeHistorian::EndTick() m_State = STATE_BEFORE_TICK; } +void CTeeHistorian::RecordDDNetVersionOld(int ClientID, int DDNetVersion) +{ + CPacker Buffer; + Buffer.Reset(); + Buffer.AddInt(ClientID); + Buffer.AddInt(DDNetVersion); + + if(m_Debug) + { + dbg_msg("teehistorian", "ddnetver_old cid=%d ddnet_version=%d", ClientID, DDNetVersion); + } + + WriteExtra(UUID_TEEHISTORIAN_DDNETVER_OLD, Buffer.Data(), Buffer.Size()); +} + +void CTeeHistorian::RecordDDNetVersion(int ClientID, CUuid ConnectionID, int DDNetVersion, const char *pDDNetVersionStr) +{ + CPacker Buffer; + Buffer.Reset(); + Buffer.AddInt(ClientID); + Buffer.AddRaw(&ConnectionID, sizeof(ConnectionID)); + Buffer.AddInt(DDNetVersion); + Buffer.AddString(pDDNetVersionStr, 0); + + if(m_Debug) + { + char aConnnectionID[UUID_MAXSTRSIZE]; + FormatUuid(ConnectionID, aConnnectionID, sizeof(aConnnectionID)); + dbg_msg("teehistorian", "ddnetver cid=%d connection_id=%s ddnet_version=%d ddnet_version_str=%s", ClientID, aConnnectionID, DDNetVersion, pDDNetVersionStr); + } + + WriteExtra(UUID_TEEHISTORIAN_DDNETVER, Buffer.Data(), Buffer.Size()); +} + void CTeeHistorian::RecordAuthInitial(int ClientID, int Level, const char *pAuthName) { CPacker Buffer; diff --git a/src/game/server/teehistorian.h b/src/game/server/teehistorian.h index 56bdf25e8..c2e075583 100644 --- a/src/game/server/teehistorian.h +++ b/src/game/server/teehistorian.h @@ -64,6 +64,9 @@ public: void EndTick(); + void RecordDDNetVersionOld(int ClientID, int DDNetVersion); + void RecordDDNetVersion(int ClientID, CUuid ConnectionID, int DDNetVersion, const char *pDDNetVersionStr); + void RecordAuthInitial(int ClientID, int Level, const char *pAuthName); void RecordAuthLogin(int ClientID, int Level, const char *pAuthName); void RecordAuthLogout(int ClientID); diff --git a/src/game/version.h b/src/game/version.h index d9401ce1a..4a68f8714 100644 --- a/src/game/version.h +++ b/src/game/version.h @@ -4,6 +4,7 @@ #define GAME_VERSION_H #define GAME_VERSION "0.6.4, 13.1" #define GAME_NETVERSION "0.6 626fce9a778df4d4" +#define GAME_NAME "DDNet" #define GAME_RELEASE_VERSION "13.1" #define CLIENT_VERSIONNR 13010 extern const char *GIT_SHORTREV_HASH; diff --git a/src/test/teehistorian.cpp b/src/test/teehistorian.cpp index 0cacfb518..660110d5f 100644 --- a/src/test/teehistorian.cpp +++ b/src/test/teehistorian.cpp @@ -320,6 +320,43 @@ TEST_F(TeeHistorian, ExtraMessage) Expect(EXPECTED, sizeof(EXPECTED)); } +TEST_F(TeeHistorian, DDNetVersion) +{ + const unsigned char EXPECTED[] = { + // EX uuid=60daba5c-52c4-3aeb-b8ba-b2953fb55a17 data_len=50 + 0x4a, + 0x13, 0x97, 0xb6, 0x3e, 0xee, 0x4e, 0x39, 0x19, + 0xb8, 0x6a, 0xb0, 0x58, 0x88, 0x7f, 0xca, 0xf5, + 0x32, + // (DDNETVER) cid=0 connection_id=fb13a576-d35f-4893-b815-eedc6d98015b + // ddnet_version=13010 ddnet_version_str=DDNet 13.1 (3623f5e4cd184556) + 0x00, + 0xfb, 0x13, 0xa5, 0x76, 0xd3, 0x5f, 0x48, 0x93, + 0xb8, 0x15, 0xee, 0xdc, 0x6d, 0x98, 0x01, 0x5b, + 0x92, 0xcb, 0x01, 'D', 'D', 'N', 'e', 't', + ' ', '1', '3', '.', '1', ' ', '(', '3', + '6', '2', '3', 'f', '5', 'e', '4', 'c', + 'd', '1', '8', '4', '5', '5', '6', ')', + 0x00, + // EX uuid=1397b63e-ee4e-3919-b86a-b058887fcaf5 data_len=4 + 0x4a, + 0x41, 0xb4, 0x95, 0x41, 0xf2, 0x6f, 0x32, 0x5d, + 0x87, 0x15, 0x9b, 0xaf, 0x4b, 0x54, 0x4e, 0xf9, + 0x04, + // (DDNETVER_OLD) cid=1 ddnet_version=13010 + 0x01, 0x92, 0xcb, 0x01, + 0x40, // FINISH + }; + CUuid ConnectionID = { + 0xfb, 0x13, 0xa5, 0x76, 0xd3, 0x5f, 0x48, 0x93, + 0xb8, 0x15, 0xee, 0xdc, 0x6d, 0x98, 0x01, 0x5b, + }; + m_TH.RecordDDNetVersion(0, ConnectionID, 13010, "DDNet 13.1 (3623f5e4cd184556)"); + m_TH.RecordDDNetVersionOld(1, 13010); + Finish(); + Expect(EXPECTED, sizeof(EXPECTED)); +} + TEST_F(TeeHistorian, Auth) { const unsigned char EXPECTED[] = {