Begin work on 0.7 support

This commit is contained in:
Tim Schumacher 2020-03-29 04:36:38 +02:00 committed by Learath
parent e9ba23b53a
commit 442148a194
15 changed files with 457 additions and 84 deletions

View file

@ -114,6 +114,8 @@ public:
bool Translate(int& Target, int Client)
{
if(IsSixup(Client))
return true;
CClientInfo Info;
GetClientInfo(Client, &Info);
if (Info.m_DDNetVersion >= VERSION_DDNET_OLD)
@ -134,6 +136,8 @@ public:
bool ReverseTranslate(int& Target, int Client)
{
if(IsSixup(Client))
return true;
CClientInfo Info;
GetClientInfo(Client, &Info);
if (Info.m_DDNetVersion >= VERSION_DDNET_OLD)
@ -199,6 +203,8 @@ public:
virtual void SendMsgRaw(int ClientID, const void *pData, int Size, int Flags) = 0;
virtual char *GetMapName() = 0;
virtual bool IsSixup(int ClientID) = 0;
};
class IGameServer : public IInterface

View file

@ -259,6 +259,7 @@ void CServer::CClient::Reset()
m_DDNetVersion = VERSION_NONE;
m_GotDDNetVersionPacket = false;
m_DDNetVersionSettled = false;
m_Sixup = false;
}
CServer::CServer()
@ -758,7 +759,7 @@ void CServer::DoSnapshot()
int DeltaTick = -1;
int DeltaSize;
m_SnapshotBuilder.Init();
m_SnapshotBuilder.Init(m_aClients[i].m_Sixup);
GameServer()->OnSnap(i);
@ -895,7 +896,7 @@ int CServer::NewClientNoAuthCallback(int ClientID, void *pUser)
return 0;
}
int CServer::NewClientCallback(int ClientID, void *pUser)
int CServer::NewClientCallback(int ClientID, void *pUser, bool Sixup)
{
CServer *pThis = (CServer *)pUser;
pThis->m_aClients[ClientID].m_State = CClient::STATE_PREAUTH;
@ -917,6 +918,8 @@ int CServer::NewClientCallback(int ClientID, void *pUser)
pThis->GameServer()->OnClientEngineJoin(ClientID);
pThis->Antibot()->OnEngineClientJoin(ClientID);
pThis->m_aClients[ClientID].m_Sixup = Sixup;
#if defined(CONF_FAMILY_UNIX)
pThis->SendConnLoggingCommand(OPEN_SESSION, pThis->m_NetServer.ClientAddr(ClientID));
#endif
@ -1042,7 +1045,13 @@ void CServer::SendMap(int ClientID)
Msg.AddString(GetMapName(), 0);
Msg.AddInt(m_CurrentMapCrc);
Msg.AddInt(m_CurrentMapSize);
SendMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID);
if(m_aClients[ClientID].m_Sixup)
{
Msg.AddInt(1);
Msg.AddInt(1024-128);
Msg.AddRaw(m_CurrentMapSha256.data, sizeof(m_CurrentMapSha256.data));
}
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
}
m_aClients[ClientID].m_NextMapChunk = 0;
@ -1064,11 +1073,14 @@ void CServer::SendMapData(int ClientID, int Chunk)
Last = 1;
}
CMsgPacker Msg(NETMSG_MAP_DATA, true);
Msg.AddInt(Last);
Msg.AddInt(m_CurrentMapCrc);
Msg.AddInt(Chunk);
Msg.AddInt(ChunkSize);
CMsgPacker Msg(NETMSG_MAP_DATA);
if(!m_aClients[ClientID].m_Sixup)
{
Msg.AddInt(Last);
Msg.AddInt(m_CurrentMapCrc);
Msg.AddInt(Chunk);
Msg.AddInt(ChunkSize);
}
Msg.AddRaw(&m_pCurrentMapData[Offset], ChunkSize);
SendMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID);
@ -1248,7 +1260,7 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
{
return;
}
if(str_comp(pVersion, GameServer()->NetVersion()) != 0)
if(str_comp(pVersion, GameServer()->NetVersion()) != 0 && str_comp(pVersion, "0.7 802f1be60a05665f") != 0)
{
// wrong version
char aReason[256];
@ -1287,6 +1299,13 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) == 0 || m_aClients[ClientID].m_State < CClient::STATE_CONNECTING)
return;
if(m_aClients[ClientID].m_Sixup)
{
SendMapData(ClientID, m_aClients[ClientID].m_NextMapChunk);
m_aClients[ClientID].m_NextMapChunk++;
return;
}
int Chunk = Unpacker.GetInt();
if(Chunk != m_aClients[ClientID].m_NextMapChunk || !g_Config.m_SvFastDownload)
{

View file

@ -200,6 +200,8 @@ public:
// DNSBL
int m_DnsblState;
std::shared_ptr<CHostLookup> m_pDnsblLookup;
bool m_Sixup;
};
CClient m_aClients[MAX_CLIENTS];
@ -290,7 +292,7 @@ public:
void DoSnapshot();
static int NewClientCallback(int ClientID, void *pUser);
static int NewClientCallback(int ClientID, void *pUser, bool Sixup);
static int NewClientNoAuthCallback(int ClientID, void *pUser);
static int DelClientCallback(int ClientID, const char *pReason, void *pUser);
@ -433,6 +435,8 @@ public:
bool ErrorShutdown() const { return m_aErrorShutdownReason[0] != 0; }
void SetErrorShutdown(const char *pReason);
bool IsSixup(int ClientID) { return m_aClients[ClientID].m_Sixup; }
#ifdef CONF_FAMILY_UNIX
enum CONN_LOGGING_CMD
{

View file

@ -41,12 +41,12 @@ int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk)
// TODO: add checking here so we don't read too far
for(int i = 0; i < m_CurrentChunk; i++)
{
pData = Header.Unpack(pData);
pData = Header.Unpack(pData, (m_pConnection && m_pConnection->m_Sixup) ? 6 : 4);
pData += Header.m_Size;
}
// unpack the header
pData = Header.Unpack(pData);
pData = Header.Unpack(pData, (m_pConnection && m_pConnection->m_Sixup) ? 6 : 4);
m_CurrentChunk++;
if(pData+Header.m_Size > pEnd)
@ -110,7 +110,7 @@ void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *
net_udp_send(Socket, pAddr, aBuffer, DataSize + DATA_OFFSET);
}
void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken)
void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken, bool Sixup)
{
unsigned char aBuffer[NET_MAX_PACKETSIZE];
int CompressedSize = -1;
@ -126,7 +126,13 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
io_flush(ms_DataLogSent);
}
if (SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
int HeaderSize = NET_PACKETHEADERSIZE;
if (Sixup)
{
HeaderSize += 4;
mem_copy(&aBuffer[3], &SecurityToken, 4);
}
else if (SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
{
// append security token
// if SecurityToken is NET_SECURITY_TOKEN_UNKNOWN we will still append it hoping to negotiate it
@ -135,7 +141,7 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
}
// compress
CompressedSize = ms_Huffman.Compress(pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4);
CompressedSize = ms_Huffman.Compress(pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[HeaderSize], NET_MAX_PACKETSIZE-HeaderSize);
// check if the compression was enabled, successful and good enough
#ifndef FUZZING
@ -149,14 +155,23 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
{
// use uncompressed data
FinalSize = pPacket->m_DataSize;
mem_copy(&aBuffer[3], pPacket->m_aChunkData, pPacket->m_DataSize);
mem_copy(&aBuffer[HeaderSize], pPacket->m_aChunkData, pPacket->m_DataSize);
pPacket->m_Flags &= ~NET_PACKETFLAG_COMPRESSION;
}
if(Sixup)
{
unsigned Flags = 0;
if (pPacket->m_Flags&NET_PACKETFLAG_CONTROL) Flags |= 1;
if (pPacket->m_Flags&NET_PACKETFLAG_RESEND) Flags |= 2;
if (pPacket->m_Flags&NET_PACKETFLAG_COMPRESSION) Flags |= 4;
pPacket->m_Flags = Flags;
}
// set header and send the packet if all things are good
if(FinalSize >= 0)
{
FinalSize += NET_PACKETHEADERSIZE;
FinalSize += HeaderSize;
aBuffer[0] = ((pPacket->m_Flags<<2)&0xfc)|((pPacket->m_Ack>>8)&0x3);
aBuffer[1] = pPacket->m_Ack&0xff;
aBuffer[2] = pPacket->m_NumChunks;
@ -175,7 +190,7 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
}
// TODO: rename this function
int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket)
int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket, SECURITY_TOKEN *SecurityToken, bool Sixup)
{
// check the size
if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE)
@ -200,6 +215,15 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct
pPacket->m_NumChunks = pBuffer[2];
pPacket->m_DataSize = Size - NET_PACKETHEADERSIZE;
if(Sixup)
{
unsigned Flags = 0;
if (pPacket->m_Flags&1) Flags |= NET_PACKETFLAG_CONTROL;
if (pPacket->m_Flags&2) Flags |= NET_PACKETFLAG_RESEND;
if (pPacket->m_Flags&4) Flags |= NET_PACKETFLAG_COMPRESSION;
pPacket->m_Flags = Flags;
}
if(pPacket->m_Flags&NET_PACKETFLAG_CONNLESS)
{
const int DATA_OFFSET = 6;
@ -223,6 +247,15 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct
}
else
{
int DataStart = NET_PACKETHEADERSIZE;
if(Sixup)
{
if(Size < NET_PACKETHEADERSIZE+4)
return -1;
pPacket->m_DataSize -= 4;
DataStart += 4;
mem_copy(SecurityToken, &pBuffer[3], 4);
}
if(pPacket->m_Flags&NET_PACKETFLAG_COMPRESSION)
{
// Don't allow compressed control packets.
@ -230,10 +263,10 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct
{
return -1;
}
pPacket->m_DataSize = ms_Huffman.Decompress(&pBuffer[3], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData));
pPacket->m_DataSize = ms_Huffman.Decompress(&pBuffer[DataStart], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData));
}
else
mem_copy(pPacket->m_aChunkData, &pBuffer[3], pPacket->m_DataSize);
mem_copy(pPacket->m_aChunkData, &pBuffer[DataStart], pPacket->m_DataSize);
}
// check for errors
@ -259,7 +292,7 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct
}
void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken)
void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken, bool Sixup)
{
CNetPacketConstruct Construct;
Construct.m_Flags = NET_PACKETFLAG_CONTROL;
@ -270,32 +303,31 @@ void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int Con
mem_copy(&Construct.m_aChunkData[1], pExtra, ExtraSize);
// send the control message
CNetBase::SendPacket(Socket, pAddr, &Construct, SecurityToken);
CNetBase::SendPacket(Socket, pAddr, &Construct, SecurityToken, Sixup);
}
unsigned char *CNetChunkHeader::Pack(unsigned char *pData)
unsigned char *CNetChunkHeader::Pack(unsigned char *pData, int split)
{
pData[0] = ((m_Flags&3)<<6)|((m_Size>>4)&0x3f);
pData[1] = (m_Size&0xf);
pData[0] = ((m_Flags&3)<<6)|((m_Size>>split)&0x3f);
pData[1] = (m_Size&((1<<split)-1));
if(m_Flags&NET_CHUNKFLAG_VITAL)
{
pData[1] |= (m_Sequence>>2)&0xf0;
pData[1] |= (m_Sequence>>2)&(~((1<<split)-1));
pData[2] = m_Sequence&0xff;
return pData + 3;
}
return pData + 2;
}
unsigned char *CNetChunkHeader::Unpack(unsigned char *pData)
unsigned char *CNetChunkHeader::Unpack(unsigned char *pData, int split)
{
m_Flags = (pData[0]>>6)&3;
m_Size = ((pData[0]&0x3f)<<4) | (pData[1]&0xf);
m_Size = ((pData[0]&0x3f)<<split) | (pData[1]&((1<<split)-1));
m_Sequence = -1;
if(m_Flags&NET_CHUNKFLAG_VITAL)
{
m_Sequence = ((pData[1]&0xf0)<<2) | pData[2];
m_Sequence = ((pData[1]&(~((1<<split)-1)))<<2) | pData[2];
return pData + 3;
}
return pData + 2;

View file

@ -106,7 +106,8 @@ enum
};
typedef int (*NETFUNC_DELCLIENT)(int ClientID, const char *pReason, void *pUser);
typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser);
typedef int (*NETFUNC_NEWCLIENT_CON)(int ClientID, void *pUser);
typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser, bool Sixup);
typedef int (*NETFUNC_NEWCLIENT_NOAUTH)(int ClientID, void *pUser);
typedef int (*NETFUNC_CLIENTREJOIN)(int ClientID, void *pUser);
@ -130,8 +131,8 @@ public:
int m_Size;
int m_Sequence;
unsigned char *Pack(unsigned char *pData);
unsigned char *Unpack(unsigned char *pData);
unsigned char *Pack(unsigned char *pData, int split = 4);
unsigned char *Unpack(unsigned char *pData, int split = 4);
};
class CNetChunkResend
@ -170,7 +171,6 @@ private:
unsigned short m_PeerAck;
unsigned m_State;
int m_Token;
SECURITY_TOKEN m_SecurityToken;
int m_RemoteClosed;
bool m_BlockCloseMsg;
@ -237,9 +237,12 @@ public:
void SetTimedOut(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken, TStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *pResendBuffer);
// anti spoof
void DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken);
void DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken, SECURITY_TOKEN Token, bool Sixup);
void SetUnknownSeq() { m_UnknownSeq = true; }
void SetSequence(int Sequence) { m_Sequence = Sequence; }
bool m_Sixup;
SECURITY_TOKEN m_Token;
};
class CConsoleNetConnection
@ -333,13 +336,14 @@ class CNetServer
CNetRecvUnpacker m_RecvUnpacker;
void OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet);
void OnSixupCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet, SECURITY_TOKEN Token);
void OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet);
void OnConnCtrlMsg(NETADDR &Addr, int ClientID, int ControlMsg, const CNetPacketConstruct &Packet);
bool ClientExists(const NETADDR &Addr) { return GetClientSlot(Addr) != -1; };
int GetClientSlot(const NETADDR &Addr);
void SendControl(NETADDR &Addr, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken);
int TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth=false);
int TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth=false, bool Sixup=false, SECURITY_TOKEN Token=0);
int NumClientsWithAddr(NETADDR Addr);
bool Connlimit(NETADDR Addr);
void SendMsgs(NETADDR &Addr, const CMsgPacker *Msgs[], int num);
@ -393,14 +397,14 @@ class CNetConsole
class CNetBan *m_pNetBan;
CSlot m_aSlots[NET_MAX_CONSOLE_CLIENTS];
NETFUNC_NEWCLIENT m_pfnNewClient;
NETFUNC_NEWCLIENT_CON m_pfnNewClient;
NETFUNC_DELCLIENT m_pfnDelClient;
void *m_UserPtr;
CNetRecvUnpacker m_RecvUnpacker;
public:
void SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
void SetCallbacks(NETFUNC_NEWCLIENT_CON pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
//
bool Open(NETADDR BindAddr, class CNetBan *pNetBan, int Flags);
@ -472,11 +476,11 @@ public:
static int Compress(const void *pData, int DataSize, void *pOutput, int OutputSize);
static int Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize);
static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken);
static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken, bool Sixup = false);
static void SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize, bool Extended, unsigned char aExtra[4]);
static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken);
static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken, bool Sixup = false);
static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket);
static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket, SECURITY_TOKEN *SecurityToken = 0, bool Sixup = false);
// The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not
static int IsSeqInBackroom(int Seq, int Ack);

View file

@ -31,6 +31,7 @@ void CNetConnection::Reset(bool Rejoin)
m_State = NET_CONNSTATE_OFFLINE;
m_Token = -1;
m_SecurityToken = NET_SECURITY_TOKEN_UNKNOWN;
m_Sixup = false;
}
m_LastSendTime = 0;
@ -93,7 +94,7 @@ int CNetConnection::Flush()
// send of the packets
m_Construct.m_Ack = m_Ack;
CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct, m_SecurityToken);
CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct, m_SecurityToken, m_Sixup);
// update send times
m_LastSendTime = time_get();
@ -120,7 +121,7 @@ int CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int
Header.m_Size = DataSize;
Header.m_Sequence = Sequence;
pChunkData = &m_Construct.m_aChunkData[m_Construct.m_DataSize];
pChunkData = Header.Pack(pChunkData);
pChunkData = Header.Pack(pChunkData, m_Sixup ? 6 : 4);
mem_copy(pChunkData, pData, DataSize);
pChunkData += DataSize;
@ -165,7 +166,7 @@ void CNetConnection::SendControl(int ControlMsg, const void *pExtra, int ExtraSi
{
// send the control message
m_LastSendTime = time_get();
CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize, m_SecurityToken);
CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize, m_SecurityToken, m_Sixup);
}
void CNetConnection::ResendChunk(CNetChunkResend *pResend)
@ -220,7 +221,7 @@ void CNetConnection::Disconnect(const char *pReason)
Reset();
}
void CNetConnection::DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken)
void CNetConnection::DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken, SECURITY_TOKEN Token, bool Sixup)
{
Reset();
@ -235,11 +236,13 @@ void CNetConnection::DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken)
m_LastUpdateTime = Now;
m_SecurityToken = SecurityToken;
m_Token = Token;
m_Sixup = Sixup;
}
int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken)
{
if (State() != NET_CONNSTATE_OFFLINE && m_SecurityToken != NET_SECURITY_TOKEN_UNKNOWN && m_SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
if (!m_Sixup && State() != NET_CONNSTATE_OFFLINE && m_SecurityToken != NET_SECURITY_TOKEN_UNKNOWN && m_SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
{
// supposed to have a valid token in this packet, check it
if (pPacket->m_DataSize < (int)sizeof(m_SecurityToken))
@ -253,6 +256,9 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_
}
}
if(m_Sixup && SecurityToken != m_Token)
return 0;
// check if actual ack value is valid(own sequence..latest peer ack)
if(m_Sequence >= m_PeerAck)
{
@ -329,7 +335,7 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_
&& pPacket->m_DataSize >= (int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(m_SecurityToken))
&& !mem_comp(&pPacket->m_aChunkData[1], SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC)))
{
m_SecurityToken = SecurityToken;
m_SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED;
if(g_Config.m_Debug)
dbg_msg("security", "generated token %d", m_SecurityToken);
}

View file

@ -31,7 +31,7 @@ bool CNetConsole::Open(NETADDR BindAddr, CNetBan *pNetBan, int Flags)
return true;
}
void CNetConsole::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser)
void CNetConsole::SetCallbacks(NETFUNC_NEWCLIENT_CON pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser)
{
m_pfnNewClient = pfnNewClient;
m_pfnDelClient = pfnDelClient;

View file

@ -10,6 +10,7 @@
#include "network.h"
#include <engine/message.h>
#include <engine/shared/protocol.h>
#include <game/generated/protocol.h>
const int DummyMapCrc = 0x6c760ac4;
unsigned char g_aDummyMapData[] = {
@ -41,6 +42,83 @@ unsigned char g_aDummyMapData[] = {
0xc2, 0x00, 0x00, 0x38, 0x00, 0x05
};
static unsigned char MsgTypeFromSixup(unsigned char Byte)
{
unsigned char Six = Byte>>1;
unsigned char Msg;
if (Byte&1)
{
if(Six == 1)
Msg = NETMSG_INFO;
else if(Six >= 18 && Six <= 28)
Msg = NETMSG_READY + Six - 18;
else
{
dbg_msg("net", "DROP recv sys %d", Six);
return 0;
}
//dbg_msg("net", "recv sys %d <- %d", Msg, Six);
}
else
{
if(Six >= 24 && Six <= 27)
Msg = NETMSGTYPE_CL_SAY + Six - 24;
else if(Six == 28)
Msg = NETMSGTYPE_CL_KILL;
else if(Six >= 30 && Six <= 32)
Msg = NETMSGTYPE_CL_EMOTICON + Six - 30;
else
{
dbg_msg("net", "DROP recv msg %d", Six);
return 0;
}
//dbg_msg("net", "recv msg %d <- %d", Msg, Six);
}
return (Msg<<1) | (Byte&1);
}
static unsigned char MsgTypeToSixup(unsigned char Byte)
{
unsigned char Msg = Byte>>1;
unsigned char Six;
if (Byte&1)
{
if(Msg >= NETMSG_MAP_CHANGE && Msg <= NETMSG_MAP_DATA)
Six = Msg;
else if(Msg >= NETMSG_CON_READY && Msg <= NETMSG_INPUTTIMING)
Six = Msg + 1;
else if(Msg >= NETMSG_AUTH_CHALLANGE && Msg <= NETMSG_AUTH_RESULT)
Six = Msg + 4;
else if(Msg >= NETMSG_PING && Msg <= NETMSG_ERROR)
Six = Msg + 4;
else if(Msg > 24)
Six = Msg - 24;
else
{
dbg_msg("net", "DROP send sys %d", Msg);
return 0;
}
//dbg_msg("net", "send sys %d -> %d", Msg, Six);
}
else
{
if(Msg >= NETMSGTYPE_SV_MOTD && Msg <= NETMSGTYPE_SV_CHAT)
Six = Msg;
else if(Msg == NETMSGTYPE_SV_KILLMSG)
Six = Msg + 1;
else if(Msg >= NETMSGTYPE_SV_TUNEPARAMS && Msg <= NETMSGTYPE_SV_VOTESTATUS)
Six = Msg;
else if(Msg > 24)
Six = Msg - 24;
else
{
dbg_msg("net", "DROP send msg %d", Msg);
return 0;
}
//dbg_msg("net", "send msg %d -> %d", Msg, Six);
}
return (Six<<1) | (Byte&1);
}
static SECURITY_TOKEN ToSecurityToken(const unsigned char *pData)
{
@ -214,12 +292,12 @@ bool CNetServer::Connlimit(NETADDR Addr)
return false;
}
int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth)
int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth, bool Sixup, SECURITY_TOKEN Token)
{
if (Connlimit(Addr))
{
const char Msg[] = "Too many connections in a short time";
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, Msg, sizeof(Msg), SecurityToken);
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, Msg, sizeof(Msg), SecurityToken, Sixup);
return -1; // failed to add client
}
@ -228,7 +306,7 @@ int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, boo
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "Only %d players with the same IP are allowed", m_MaxClientsPerIP);
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf) + 1, SecurityToken);
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf) + 1, SecurityToken, Sixup);
return -1; // failed to add client
}
@ -245,13 +323,13 @@ int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, boo
if (Slot == -1)
{
const char FullMsg[] = "This server is full";
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg), SecurityToken);
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg), SecurityToken, Sixup);
return -1; // failed to add client
}
// init connection slot
m_aSlots[Slot].m_Connection.DirectInit(Addr, SecurityToken);
m_aSlots[Slot].m_Connection.DirectInit(Addr, SecurityToken, Token, Sixup);
if (VanillaAuth)
{
@ -273,7 +351,7 @@ int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, boo
if (VanillaAuth)
m_pfnNewClientNoAuth(Slot, m_UserPtr);
else
m_pfnNewClient(Slot, m_UserPtr);
m_pfnNewClient(Slot, m_UserPtr, Sixup);
return Slot; // done
}
@ -546,6 +624,30 @@ void CNetServer::OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketC
}
}
void CNetServer::OnSixupCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet, SECURITY_TOKEN Token)
{
if(ClientExists(Addr))
return; // silently ignore
SECURITY_TOKEN ResponseToken;
mem_copy(&ResponseToken, Packet.m_aChunkData+1, 4);
SECURITY_TOKEN MyToken = GetToken(Addr);
unsigned char aToken[4];
mem_copy(aToken, &MyToken, 4);
if(ControlMsg == 5)
{
CNetBase::SendControlMsg(m_Socket, &Addr, 0, 5, aToken, sizeof(aToken), ResponseToken, true);
}
else if(ControlMsg == NET_CTRLMSG_CONNECT)
{
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CONNECTACCEPT, aToken, sizeof(aToken), ResponseToken, true);
if(Token == MyToken)
TryAcceptClient(Addr, ResponseToken, false, true, Token);
}
}
int CNetServer::GetClientSlot(const NETADDR &Addr)
{
int Slot = -1;
@ -598,7 +700,11 @@ int CNetServer::Recv(CNetChunk *pChunk)
// check for a chunk
if(m_RecvUnpacker.FetchChunk(pChunk))
{
if(m_aSlots[pChunk->m_ClientID].m_Connection.m_Sixup)
*(unsigned char*)pChunk->m_pData = MsgTypeFromSixup(*(unsigned char*)pChunk->m_pData);
return 1;
}
// TODO: empty the recvinfo
unsigned char *pData;
@ -643,6 +749,16 @@ int CNetServer::Recv(CNetChunk *pChunk)
// normal packet, find matching slot
int Slot = GetClientSlot(Addr);
bool Sixup = false;
SECURITY_TOKEN Token;
if((Slot == -1 && m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_UNUSED)
|| (Slot != -1 && m_aSlots[Slot].m_Connection.m_Sixup))
{
Sixup = true;
if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data, &Token, Sixup))
continue;
}
if (Slot != -1)
{
// found
@ -651,7 +767,7 @@ int CNetServer::Recv(CNetChunk *pChunk)
if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL)
OnConnCtrlMsg(Addr, Slot, m_RecvUnpacker.m_Data.m_aChunkData[0], m_RecvUnpacker.m_Data);
if(m_aSlots[Slot].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr))
if(m_aSlots[Slot].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr, Token))
{
if(m_RecvUnpacker.m_Data.m_DataSize)
m_RecvUnpacker.Start(&Addr, &m_aSlots[Slot].m_Connection, Slot);
@ -661,7 +777,10 @@ int CNetServer::Recv(CNetChunk *pChunk)
{
// not found, client that wants to connect
if(IsDDNetControlMsg(&m_RecvUnpacker.m_Data))
if(Sixup)
// got 0.7 control msg
OnSixupCtrlMsg(Addr, m_RecvUnpacker.m_Data.m_aChunkData[0], m_RecvUnpacker.m_Data, Token);
else if(IsDDNetControlMsg(&m_RecvUnpacker.m_Data))
// got ddnet control msg
OnTokenCtrlMsg(Addr, m_RecvUnpacker.m_Data.m_aChunkData[0], m_RecvUnpacker.m_Data);
else
@ -694,6 +813,13 @@ int CNetServer::Send(CNetChunk *pChunk)
dbg_assert(pChunk->m_ClientID >= 0, "errornous client id");
dbg_assert(pChunk->m_ClientID < MaxClients(), "errornous client id");
if(m_aSlots[pChunk->m_ClientID].m_Connection.m_Sixup)
{
unsigned int MsgType = MsgTypeToSixup(*(unsigned char*)pChunk->m_pData);
if (MsgType == 0) return 0;
*(unsigned char*)pChunk->m_pData = MsgType;
}
if(pChunk->m_Flags&NETSENDFLAG_VITAL)
Flags = NET_CHUNKFLAG_VITAL;

View file

@ -4,6 +4,30 @@
#include "compression.h"
#include "uuid_manager.h"
#include <game/generated/protocol.h>
static int ObjTypeToSixup(int Type)
{
int Six;
if(Type >= NETOBJTYPE_PLAYERINPUT && Type <= NETOBJTYPE_FLAG)
Six = Type;
else if(Type >= NETOBJTYPE_CHARACTERCORE && Type <= NETOBJTYPE_PLAYERINFO)
Six = Type + 1;
else if(Type >= NETEVENTTYPE_COMMON && Type <= NETEVENTTYPE_DEATH)
Six = Type + 3;
else if(Type == NETEVENTTYPE_SOUNDWORLD)
Six = Type + 2;
else if(Type > 24)
Six = Type - 24;
else
{
//dbg_msg("net", "DROP obj %d", Type);
return -1;
}
//dbg_msg("net", "pack obj %d -> %d", Type, Six);
return Six;
}
// CSnapshot
CSnapshotItem *CSnapshot::GetItem(int Index)
@ -529,10 +553,11 @@ CSnapshotBuilder::CSnapshotBuilder()
m_NumExtendedItemTypes = 0;
}
void CSnapshotBuilder::Init()
void CSnapshotBuilder::Init(bool Sixup)
{
m_DataSize = 0;
m_NumItems = 0;
m_Sixup = Sixup;
for(int i = 0; i < m_NumExtendedItemTypes; i++)
{
@ -622,6 +647,12 @@ void *CSnapshotBuilder::NewItem(int Type, int ID, int Size)
CSnapshotItem *pObj = (CSnapshotItem *)(m_aData + m_DataSize);
if(m_Sixup)
{
Type = ObjTypeToSixup(Type);
if(Type < 0) return pObj;
}
mem_zero(pObj, sizeof(CSnapshotItem) + Size);
pObj->m_TypeAndID = (Type<<16)|ID;
m_aOffsets[m_NumItems] = m_DataSize;

View file

@ -139,10 +139,12 @@ class CSnapshotBuilder
void AddExtendedItemType(int Index);
int GetExtendedItemTypeIndex(int TypeID);
bool m_Sixup;
public:
CSnapshotBuilder();
void Init();
void Init(bool Sixup = false);
void *NewItem(int Type, int ID, int Size);

View file

@ -1241,6 +1241,18 @@ void CCharacter::Snap(int SnappingClient)
pDDNetCharacter->m_Jumps = m_Core.m_Jumps;
pDDNetCharacter->m_TeleCheckpoint = m_TeleCheckpoint;
pDDNetCharacter->m_StrongWeakID = m_StrongWeakID;
if(Server()->IsSixup(SnappingClient))
{
int Offset = sizeof(CNetObj_CharacterCore) / 4;
((int*)pCharacter)[Offset+0] = pCharacter->m_Health;
((int*)pCharacter)[Offset+1] = pCharacter->m_Armor;
((int*)pCharacter)[Offset+2] = pCharacter->m_AmmoCount;
((int*)pCharacter)[Offset+3] = pCharacter->m_Weapon;
((int*)pCharacter)[Offset+4] = pCharacter->m_Emote;
((int*)pCharacter)[Offset+5] = pCharacter->m_AttackTick;
((int*)pCharacter)[Offset+6] = 0; // m_TriggeredEvents
}
}
int CCharacter::NetworkClipped(int SnappingClient)

View file

@ -162,14 +162,23 @@ void CPickup::Snap(int SnappingClient)
&& (!Tick))
return;
CNetObj_Pickup *pP = static_cast<CNetObj_Pickup *>(Server()->SnapNewItem(NETOBJTYPE_PICKUP, m_ID, sizeof(CNetObj_Pickup)));
int Size = Server()->IsSixup(SnappingClient) ? 3*4 : sizeof(CNetObj_Pickup);
CNetObj_Pickup *pP = static_cast<CNetObj_Pickup *>(Server()->SnapNewItem(NETOBJTYPE_PICKUP, m_ID, Size));
if(!pP)
return;
pP->m_X = (int)m_Pos.x;
pP->m_Y = (int)m_Pos.y;
pP->m_Type = m_Type;
pP->m_Subtype = m_Subtype;
if(Server()->IsSixup(SnappingClient))
{
if(m_Type == POWERUP_WEAPON)
pP->m_Type = m_Subtype == WEAPON_SHOTGUN ? 3 : m_Subtype == WEAPON_GRENADE ? 2 : 4;
else if(m_Type == POWERUP_NINJA)
pP->m_Type = 5;
}
else
pP->m_Subtype = m_Subtype;
}
void CPickup::Move()

View file

@ -1109,6 +1109,72 @@ void CGameContext::OnClientEnter(int ClientID)
SendVoteSet(ClientID);
Server()->ExpireServerInfo();
// update client infos (others before local)
CMsgPacker NewClientInfoMsg(18 + 24);
NewClientInfoMsg.AddInt(ClientID);
NewClientInfoMsg.AddInt(0); // m_Local
NewClientInfoMsg.AddInt(m_apPlayers[ClientID]->GetTeam());
NewClientInfoMsg.AddString(Server()->ClientName(ClientID), -1);
NewClientInfoMsg.AddString(Server()->ClientClan(ClientID), -1);
NewClientInfoMsg.AddInt(Server()->ClientCountry(ClientID));
for(int p = 0; p < 6; p++)
{
NewClientInfoMsg.AddString("", -1); // m_apSkinPartNames
NewClientInfoMsg.AddInt(0); // m_aUseCustomColors
NewClientInfoMsg.AddInt(0); // m_aSkinPartColors
}
NewClientInfoMsg.AddInt(0); // m_Silent
for(int i = 0; i < MAX_CLIENTS; ++i)
{
if(i == ClientID || !m_apPlayers[i] || !Server()->ClientIngame(i))
continue;
// new info for others
if(Server()->ClientIngame(i) && Server()->IsSixup(i))
Server()->SendMsg(&NewClientInfoMsg, MSGFLAG_VITAL|MSGFLAG_NORECORD, i);
if(Server()->IsSixup(ClientID))
{
// existing infos for new player
CMsgPacker ClientInfoMsg(18 + 24);
ClientInfoMsg.AddInt(i);
ClientInfoMsg.AddInt(0); // m_Local
ClientInfoMsg.AddInt(m_apPlayers[i]->GetTeam());
ClientInfoMsg.AddString(Server()->ClientName(i), -1);
ClientInfoMsg.AddString(Server()->ClientClan(i), -1);
ClientInfoMsg.AddInt(Server()->ClientCountry(i));
for(int p = 0; p < 6; p++)
{
ClientInfoMsg.AddString("", -1); // m_apSkinPartNames
ClientInfoMsg.AddInt(0); // m_aUseCustomColors
ClientInfoMsg.AddInt(0); // m_aSkinPartColors
}
ClientInfoMsg.AddInt(0); // m_Silent
Server()->SendMsg(&ClientInfoMsg, MSGFLAG_VITAL|MSGFLAG_NORECORD, ClientID);
}
}
// local info
if(Server()->IsSixup(ClientID))
{
CMsgPacker SelfClientInfoMsg(18 + 24);
SelfClientInfoMsg.AddInt(ClientID);
SelfClientInfoMsg.AddInt(1); // m_Local
SelfClientInfoMsg.AddInt(m_apPlayers[ClientID]->GetTeam());
SelfClientInfoMsg.AddString(Server()->ClientName(ClientID), -1);
SelfClientInfoMsg.AddString(Server()->ClientClan(ClientID), -1);
SelfClientInfoMsg.AddInt(Server()->ClientCountry(ClientID));
for(int p = 0; p < 6; p++)
{
SelfClientInfoMsg.AddString("", -1); // m_apSkinPartNames
SelfClientInfoMsg.AddInt(0); // m_aUseCustomColors
SelfClientInfoMsg.AddInt(0); // m_aSkinPartColors
}
SelfClientInfoMsg.AddInt(0); // m_Silent
Server()->SendMsg(&SelfClientInfoMsg, MSGFLAG_VITAL|MSGFLAG_NORECORD, ClientID);
}
}
void CGameContext::OnClientConnected(int ClientID)
@ -1252,23 +1318,22 @@ void CGameContext::OnClientDDNetVersionKnown(int ClientID)
void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
{
void *pRawMsg = m_NetObjHandler.SecureUnpackMsg(MsgID, pUnpacker);
void *pRawMsg = 0;
CPlayer *pPlayer = m_apPlayers[ClientID];
if(m_TeeHistorianActive)
if(MsgID != NETMSGTYPE_CL_STARTINFO)
{
if(m_NetObjHandler.TeeHistorianRecordMsg(MsgID))
{
m_TeeHistorian.RecordPlayerMessage(ClientID, pUnpacker->CompleteData(), pUnpacker->CompleteSize());
}
}
pRawMsg = m_NetObjHandler.SecureUnpackMsg(MsgID, pUnpacker);
if(!pRawMsg)
return;
if(!pRawMsg)
{
//char aBuf[256];
//str_format(aBuf, sizeof(aBuf), "dropped weird message '%s' (%d), failed on '%s'", m_NetObjHandler.GetMsgName(MsgID), MsgID, m_NetObjHandler.FailedMsgOn());
//Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf);
return;
if(m_TeeHistorianActive)
{
if(m_NetObjHandler.TeeHistorianRecordMsg(MsgID))
{
m_TeeHistorian.RecordPlayerMessage(ClientID, pUnpacker->CompleteData(), pUnpacker->CompleteSize());
}
}
}
if(Server()->ClientIngame(ClientID))
@ -1887,7 +1952,37 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
if(pPlayer->m_IsReady)
return;
CNetMsg_Cl_StartInfo *pMsg = (CNetMsg_Cl_StartInfo *)pRawMsg;
CNetMsg_Cl_StartInfo Msg;
const char *apSkinPartNames[6];
int aUseCustomColors[6];
int aSkinPartColors[6];
Msg.m_pName = pUnpacker->GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES);
Msg.m_pClan = pUnpacker->GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES);
Msg.m_Country = pUnpacker->GetInt();
if(Server()->IsSixup(ClientID))
{
for(int p = 0; p < 6; p++) apSkinPartNames[p] = pUnpacker->GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES);
for(int p = 0; p < 6; p++) aUseCustomColors[p] = pUnpacker->GetInt();
for(int p = 0; p < 6; p++) aSkinPartColors[p] = pUnpacker->GetInt();
Msg.m_pSkin = "default";
Msg.m_UseCustomColor = 0;
Msg.m_ColorBody = 0;
Msg.m_ColorFeet = 0;
}
else
{
Msg.m_pSkin = pUnpacker->GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES);
Msg.m_UseCustomColor = pUnpacker->GetInt();
Msg.m_ColorBody = pUnpacker->GetInt();
Msg.m_ColorFeet = pUnpacker->GetInt();
}
if(pUnpacker->Error())
return;
CNetMsg_Cl_StartInfo *pMsg = &Msg;
if(!str_utf8_check(pMsg->m_pName)
|| !str_utf8_check(pMsg->m_pClan)
|| !str_utf8_check(pMsg->m_pSkin))

View file

@ -558,6 +558,19 @@ void IGameController::Snap(int SnappingClient)
| GAMEINFOFLAG_ENTITIES_RACE
| GAMEINFOFLAG_RACE;
pGameInfoEx->m_Version = GAMEINFO_CURVERSION;
if(Server()->IsSixup(SnappingClient))
{
int *pGameData = (int*)Server()->SnapNewItem(6 + 24, 0, 3*4); // NETOBJTYPE_GAMEDATA
if(!pGameData)
return;
pGameData[0] = m_RoundStartTick;
pGameData[1] = 0; // m_GameStateFlags
pGameData[2] = 0; // m_GameStateEndTick
}
SnapFlags(SnappingClient);
}
int IGameController::GetAutoTeam(int NotThisID)

View file

@ -317,24 +317,38 @@ void CPlayer::Snap(int SnappingClient)
pClientInfo->m_ColorBody = m_TeeInfos.m_ColorBody;
pClientInfo->m_ColorFeet = m_TeeInfos.m_ColorFeet;
CNetObj_PlayerInfo *pPlayerInfo = static_cast<CNetObj_PlayerInfo *>(Server()->SnapNewItem(NETOBJTYPE_PLAYERINFO, id, sizeof(CNetObj_PlayerInfo)));
int Size = Server()->IsSixup(SnappingClient) ? 3*4 : sizeof(CNetObj_PlayerInfo);
CNetObj_PlayerInfo *pPlayerInfo = static_cast<CNetObj_PlayerInfo *>(Server()->SnapNewItem(NETOBJTYPE_PLAYERINFO, id, Size));
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 || ClientVersion >= VERSION_DDNET_OLD));
pPlayerInfo->m_ClientID = id;
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 && ClientVersion < VERSION_DDNET_OLD)
pPlayerInfo->m_Team = TEAM_SPECTATORS;
int Latency = SnappingClient == -1 ? m_Latency.m_Min : GameServer()->m_apPlayers[SnappingClient]->m_aActLatency[m_ClientID];
int Score = abs(m_Score) * -1;
// send 0 if times of others are not shown
if(SnappingClient != m_ClientID && g_Config.m_SvHideScore)
pPlayerInfo->m_Score = -9999;
Score = -9999;
else
pPlayerInfo->m_Score = abs(m_Score) * -1;
Score = abs(m_Score) * -1;
if(!Server()->IsSixup(SnappingClient))
{
pPlayerInfo->m_Latency = Latency;
pPlayerInfo->m_Score = Score;
pPlayerInfo->m_Local = (int)(m_ClientID == SnappingClient && (m_Paused != PAUSE_PAUSED || ClientVersion >= VERSION_DDNET_OLD));
pPlayerInfo->m_ClientID = id;
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 && ClientVersion < VERSION_DDNET_OLD)
pPlayerInfo->m_Team = TEAM_SPECTATORS;
}
else
{
((int*)pPlayerInfo)[0] = 0; // m_PlayerFlags
((int*)pPlayerInfo)[1] = Score;
((int*)pPlayerInfo)[2] = Latency;
}
if(m_ClientID == SnappingClient && (m_Team == TEAM_SPECTATORS || m_Paused))
{