Send DDNet version early in the connection process

This gets rid of the problem that we don't know whether we should send
full snapshots to clients because they haven't told us about them being
DDNet yet.
This commit is contained in:
heinrich5991 2020-05-22 17:58:41 +02:00
parent 3623f5e4cd
commit 0d7872c79e
25 changed files with 319 additions and 123 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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))
{
@ -2332,7 +2378,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

View file

@ -125,6 +125,7 @@ public:
enum
{
STATE_EMPTY = 0,
STATE_PREAUTH,
STATE_AUTH,
STATE_CONNECTING,
STATE_READY,
@ -182,6 +183,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;
@ -261,6 +267,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);

View file

@ -99,6 +99,7 @@ enum
enum
{
VERSION_NONE = -1,
VERSION_VANILLA = 0,
VERSION_DDRACE = 1,
VERSION_DDNET_OLD = 2,

View file

@ -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")

View file

@ -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")

View file

@ -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());

View file

@ -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

View file

@ -1310,7 +1310,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.");
@ -1319,7 +1319,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
{
@ -1331,7 +1331,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++;

View file

@ -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;

View file

@ -325,7 +325,7 @@ void CProjectile::Snap(int SnappingClient)
CNetObj_Projectile *pProj = static_cast<CNetObj_Projectile *>(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);

View file

@ -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)
{
@ -3441,7 +3478,7 @@ void CGameContext::WhisperID(int ClientID, int VictimID, char *pMessage)
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;
@ -3458,7 +3495,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;
@ -3544,18 +3581,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()

View file

@ -250,6 +250,7 @@ public:
virtual const char *NetVersion();
// DDRace
void OnClientDDNetVersionKnown(int ClientID);
virtual void FillAntibot(CAntibotRoundData *pData);
int ProcessSpamProtection(int ClientID);
int GetDDRaceTeam(int ClientID);
@ -257,7 +258,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();

View file

@ -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

View file

@ -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
)
)

View file

@ -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;
@ -296,12 +295,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
@ -337,10 +337,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;
@ -421,7 +418,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);
}
}
@ -481,6 +478,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())

View file

@ -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;

View file

@ -385,7 +385,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);
@ -587,7 +587,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())
{
@ -600,19 +600,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);

View file

@ -488,6 +488,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;

View file

@ -63,6 +63,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);

View file

@ -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;

View file

@ -319,6 +319,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[] = {