made clients sending startinfo a requirement to prevent empty player infos

This commit is contained in:
oy 2011-03-15 09:58:57 +01:00
parent 7b91ebd01c
commit 27e5a6af0d
8 changed files with 293 additions and 273 deletions

View file

@ -518,8 +518,6 @@ void CClient::SendInfo()
{
CMsgPacker Msg(NETMSG_INFO);
Msg.AddString(GameClient()->NetVersion(), 128);
Msg.AddString(g_Config.m_PlayerName, 128);
Msg.AddString(g_Config.m_ClanName, 128);
Msg.AddString(g_Config.m_Password, 128);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH);
}
@ -1175,7 +1173,6 @@ void CClient::ProcessPacket(CNetChunk *pPacket)
{
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", "loading done");
SendReady();
GameClient()->OnConnected();
}
else
{
@ -1238,8 +1235,7 @@ void CClient::ProcessPacket(CNetChunk *pPacket)
if(!pError)
{
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", "loading done");
SendReady();
GameClient()->OnConnected();
SendReady();
}
else
DisconnectWithReason(pError);
@ -1261,6 +1257,10 @@ void CClient::ProcessPacket(CNetChunk *pPacket)
}
}
}
else if(Msg == NETMSG_CON_READY)
{
GameClient()->OnConnected();
}
else if(Msg == NETMSG_PING)
{
CMsgPacker Msg(NETMSG_PING_REPLY);

View file

@ -76,6 +76,8 @@ public:
virtual void OnClientDrop(int ClientID) = 0;
virtual void OnClientDirectInput(int ClientID, void *pInput) = 0;
virtual void OnClientPredictedInput(int ClientID, void *pInput) = 0;
virtual bool IsClientReady(int ClientID) = 0;
virtual const char *GameType() = 0;
virtual const char *Version() = 0;

View file

@ -572,6 +572,12 @@ void CServer::SendMap(int ClientID)
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
}
void CServer::SendConnectionReady(int ClientID)
{
CMsgPacker Msg(NETMSG_CON_READY);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
}
void CServer::SendRconLine(int ClientID, const char *pLine)
{
CMsgPacker Msg(NETMSG_RCON_LINE);
@ -612,250 +618,243 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
if(Unpacker.Error())
return;
if(m_aClients[ClientID].m_State == CClient::STATE_AUTH)
if(Sys)
{
if(Sys && Msg == NETMSG_INFO)
// system message
if(Msg == NETMSG_INFO)
{
char aVersion[64];
const char *pPassword;
str_copy(aVersion, Unpacker.GetString(CUnpacker::SANITIZE_CC), 64);
if(str_comp(aVersion, GameServer()->NetVersion()) != 0)
if(m_aClients[ClientID].m_State == CClient::STATE_AUTH)
{
// OH FUCK! wrong version, drop him
char aReason[256];
str_format(aReason, sizeof(aReason), "Wrong version. Server is running '%s' and client '%s'", GameServer()->NetVersion(), aVersion);
m_NetServer.Drop(ClientID, aReason);
return;
const char *pVersion = Unpacker.GetString(CUnpacker::SANITIZE_CC);
if(str_comp(pVersion, GameServer()->NetVersion()) != 0)
{
// wrong version
char aReason[256];
str_format(aReason, sizeof(aReason), "Wrong version. Server is running '%s' and client '%s'", GameServer()->NetVersion(), pVersion);
m_NetServer.Drop(ClientID, aReason);
return;
}
const char *pPassword = Unpacker.GetString(CUnpacker::SANITIZE_CC);
if(g_Config.m_Password[0] != 0 && str_comp(g_Config.m_Password, pPassword) != 0)
{
// wrong password
m_NetServer.Drop(ClientID, "Wrong password");
return;
}
m_aClients[ClientID].m_State = CClient::STATE_CONNECTING;
SendMap(ClientID);
}
str_copy(m_aClients[ClientID].m_aName, Unpacker.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), MAX_NAME_LENGTH);
str_copy(m_aClients[ClientID].m_aClan, Unpacker.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), MAX_CLANNAME_LENGTH);
pPassword = Unpacker.GetString(CUnpacker::SANITIZE_CC);
if(g_Config.m_Password[0] != 0 && str_comp(g_Config.m_Password, pPassword) != 0)
}
else if(Msg == NETMSG_REQUEST_MAP_DATA)
{
int Chunk = Unpacker.GetInt();
int ChunkSize = 1024-128;
int Offset = Chunk * ChunkSize;
int Last = 0;
// drop faulty map data requests
if(Chunk < 0 || Offset > m_CurrentMapSize)
return;
if(Offset+ChunkSize >= m_CurrentMapSize)
{
// wrong password
m_NetServer.Drop(ClientID, "Wrong password");
return;
ChunkSize = m_CurrentMapSize-Offset;
if(ChunkSize < 0)
ChunkSize = 0;
Last = 1;
}
CMsgPacker Msg(NETMSG_MAP_DATA);
Msg.AddInt(Last);
Msg.AddInt(m_CurrentMapCrc);
Msg.AddInt(Chunk);
Msg.AddInt(ChunkSize);
Msg.AddRaw(&m_pCurrentMapData[Offset], ChunkSize);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
if(g_Config.m_Debug)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "sending chunk %d with size %d", Chunk, ChunkSize);
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf);
}
}
else if(Msg == NETMSG_READY)
{
if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTING)
{
Addr = m_NetServer.ClientAddr(ClientID);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "player is ready. ClientID=%x ip=%d.%d.%d.%d",
ClientID, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
m_aClients[ClientID].m_State = CClient::STATE_READY;
GameServer()->OnClientConnected(ClientID);
SendConnectionReady(ClientID);
}
}
else if(Msg == NETMSG_ENTERGAME)
{
if(m_aClients[ClientID].m_State == CClient::STATE_READY && GameServer()->IsClientReady(ClientID))
{
Addr = m_NetServer.ClientAddr(ClientID);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "player has entered the game. ClientID=%x ip=%d.%d.%d.%d",
ClientID, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
m_aClients[ClientID].m_State = CClient::STATE_INGAME;
GameServer()->OnClientEnter(ClientID);
}
}
else if(Msg == NETMSG_INPUT)
{
CClient::CInput *pInput;
int64 TagTime;
m_aClients[ClientID].m_LastAckedSnapshot = Unpacker.GetInt();
int IntendedTick = Unpacker.GetInt();
int Size = Unpacker.GetInt();
// check for errors
if(Unpacker.Error() || Size/4 > MAX_INPUT_SIZE)
return;
if(m_aClients[ClientID].m_LastAckedSnapshot > 0)
m_aClients[ClientID].m_SnapRate = CClient::SNAPRATE_FULL;
if(m_aClients[ClientID].m_Snapshots.Get(m_aClients[ClientID].m_LastAckedSnapshot, &TagTime, 0, 0) >= 0)
m_aClients[ClientID].m_Latency = (int)(((time_get()-TagTime)*1000)/time_freq());
// add message to report the input timing
// skip packets that are old
if(IntendedTick > m_aClients[ClientID].m_LastInputTick)
{
int TimeLeft = ((TickStartTime(IntendedTick)-time_get())*1000) / time_freq();
CMsgPacker Msg(NETMSG_INPUTTIMING);
Msg.AddInt(IntendedTick);
Msg.AddInt(TimeLeft);
SendMsgEx(&Msg, 0, ClientID, true);
}
m_aClients[ClientID].m_LastInputTick = IntendedTick;
pInput = &m_aClients[ClientID].m_aInputs[m_aClients[ClientID].m_CurrentInput];
if(IntendedTick <= Tick())
IntendedTick = Tick()+1;
pInput->m_GameTick = IntendedTick;
for(int i = 0; i < Size/4; i++)
pInput->m_aData[i] = Unpacker.GetInt();
mem_copy(m_aClients[ClientID].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int));
m_aClients[ClientID].m_CurrentInput++;
m_aClients[ClientID].m_CurrentInput %= 200;
m_aClients[ClientID].m_State = CClient::STATE_CONNECTING;
SendMap(ClientID);
// call the mod with the fresh input data
if(m_aClients[ClientID].m_State == CClient::STATE_INGAME)
GameServer()->OnClientDirectInput(ClientID, m_aClients[ClientID].m_LatestInput.m_aData);
}
else if(Msg == NETMSG_RCON_CMD)
{
const char *pCmd = Unpacker.GetString();
if(Unpacker.Error() == 0 && m_aClients[ClientID].m_Authed)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ClientID=%d rcon='%s'", ClientID, pCmd);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
m_RconClientID = ClientID;
Console()->ExecuteLine(pCmd);
m_RconClientID = -1;
}
}
else if(Msg == NETMSG_RCON_AUTH)
{
const char *pPw;
Unpacker.GetString(); // login name, not used
pPw = Unpacker.GetString(CUnpacker::SANITIZE_CC);
if(Unpacker.Error() == 0)
{
if(g_Config.m_SvRconPassword[0] == 0)
{
SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
}
else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0)
{
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
Msg.AddInt(1);
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
m_aClients[ClientID].m_Authed = 1;
SendRconLine(ClientID, "Authentication successful. Remote console access granted.");
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed", ClientID);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
}
else if(g_Config.m_SvRconMaxTries)
{
m_aClients[ClientID].m_AuthTries++;
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "Wrong password %d/%d.", m_aClients[ClientID].m_AuthTries, g_Config.m_SvRconMaxTries);
SendRconLine(ClientID, aBuf);
if(m_aClients[ClientID].m_AuthTries >= g_Config.m_SvRconMaxTries)
{
if(!g_Config.m_SvRconBantime)
m_NetServer.Drop(ClientID, "Too many remote console authentication tries");
else
{
NETADDR Addr = m_NetServer.ClientAddr(ClientID);
BanAdd(Addr, g_Config.m_SvRconBantime*60, "Too many remote console authentication tries");
}
}
}
else
{
SendRconLine(ClientID, "Wrong password.");
}
}
}
else if(Msg == NETMSG_PING)
{
CMsgPacker Msg(NETMSG_PING_REPLY);
SendMsgEx(&Msg, 0, ClientID, true);
}
else
{
if(g_Config.m_Debug)
{
char aHex[] = "0123456789ABCDEF";
char aBuf[512];
for(int b = 0; b < pPacket->m_DataSize && b < 32; b++)
{
aBuf[b*3] = aHex[((const unsigned char *)pPacket->m_pData)[b]>>4];
aBuf[b*3+1] = aHex[((const unsigned char *)pPacket->m_pData)[b]&0xf];
aBuf[b*3+2] = ' ';
aBuf[b*3+3] = 0;
}
char aBufMsg[256];
str_format(aBufMsg, sizeof(aBufMsg), "strange message ClientID=%d msg=%d data_size=%d", ClientID, Msg, pPacket->m_DataSize);
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBufMsg);
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf);
}
}
}
else
{
if(Sys)
{
// system message
if(Msg == NETMSG_REQUEST_MAP_DATA)
{
int Chunk = Unpacker.GetInt();
int ChunkSize = 1024-128;
int Offset = Chunk * ChunkSize;
int Last = 0;
// drop faulty map data requests
if(Chunk < 0 || Offset > m_CurrentMapSize)
return;
if(Offset+ChunkSize >= m_CurrentMapSize)
{
ChunkSize = m_CurrentMapSize-Offset;
if(ChunkSize < 0)
ChunkSize = 0;
Last = 1;
}
CMsgPacker Msg(NETMSG_MAP_DATA);
Msg.AddInt(Last);
Msg.AddInt(m_CurrentMapCrc);
Msg.AddInt(Chunk);
Msg.AddInt(ChunkSize);
Msg.AddRaw(&m_pCurrentMapData[Offset], ChunkSize);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
if(g_Config.m_Debug)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "sending chunk %d with size %d", Chunk, ChunkSize);
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf);
}
}
else if(Msg == NETMSG_READY)
{
if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTING)
{
Addr = m_NetServer.ClientAddr(ClientID);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "player is ready. ClientID=%x ip=%d.%d.%d.%d",
ClientID, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
m_aClients[ClientID].m_State = CClient::STATE_READY;
GameServer()->OnClientConnected(ClientID);
}
}
else if(Msg == NETMSG_ENTERGAME)
{
if(m_aClients[ClientID].m_State == CClient::STATE_READY)
{
Addr = m_NetServer.ClientAddr(ClientID);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "player has entered the game. ClientID=%x ip=%d.%d.%d.%d",
ClientID, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
m_aClients[ClientID].m_State = CClient::STATE_INGAME;
GameServer()->OnClientEnter(ClientID);
}
}
else if(Msg == NETMSG_INPUT)
{
CClient::CInput *pInput;
int64 TagTime;
m_aClients[ClientID].m_LastAckedSnapshot = Unpacker.GetInt();
int IntendedTick = Unpacker.GetInt();
int Size = Unpacker.GetInt();
// check for errors
if(Unpacker.Error() || Size/4 > MAX_INPUT_SIZE)
return;
if(m_aClients[ClientID].m_LastAckedSnapshot > 0)
m_aClients[ClientID].m_SnapRate = CClient::SNAPRATE_FULL;
if(m_aClients[ClientID].m_Snapshots.Get(m_aClients[ClientID].m_LastAckedSnapshot, &TagTime, 0, 0) >= 0)
m_aClients[ClientID].m_Latency = (int)(((time_get()-TagTime)*1000)/time_freq());
// add message to report the input timing
// skip packets that are old
if(IntendedTick > m_aClients[ClientID].m_LastInputTick)
{
int TimeLeft = ((TickStartTime(IntendedTick)-time_get())*1000) / time_freq();
CMsgPacker Msg(NETMSG_INPUTTIMING);
Msg.AddInt(IntendedTick);
Msg.AddInt(TimeLeft);
SendMsgEx(&Msg, 0, ClientID, true);
}
m_aClients[ClientID].m_LastInputTick = IntendedTick;
pInput = &m_aClients[ClientID].m_aInputs[m_aClients[ClientID].m_CurrentInput];
if(IntendedTick <= Tick())
IntendedTick = Tick()+1;
pInput->m_GameTick = IntendedTick;
for(int i = 0; i < Size/4; i++)
pInput->m_aData[i] = Unpacker.GetInt();
mem_copy(m_aClients[ClientID].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int));
m_aClients[ClientID].m_CurrentInput++;
m_aClients[ClientID].m_CurrentInput %= 200;
// call the mod with the fresh input data
if(m_aClients[ClientID].m_State == CClient::STATE_INGAME)
GameServer()->OnClientDirectInput(ClientID, m_aClients[ClientID].m_LatestInput.m_aData);
}
else if(Msg == NETMSG_RCON_CMD)
{
const char *pCmd = Unpacker.GetString();
if(Unpacker.Error() == 0 && m_aClients[ClientID].m_Authed)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ClientID=%d rcon='%s'", ClientID, pCmd);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
m_RconClientID = ClientID;
Console()->ExecuteLine(pCmd);
m_RconClientID = -1;
}
}
else if(Msg == NETMSG_RCON_AUTH)
{
const char *pPw;
Unpacker.GetString(); // login name, not used
pPw = Unpacker.GetString(CUnpacker::SANITIZE_CC);
if(Unpacker.Error() == 0)
{
if(g_Config.m_SvRconPassword[0] == 0)
{
SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
}
else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0)
{
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
Msg.AddInt(1);
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
m_aClients[ClientID].m_Authed = 1;
SendRconLine(ClientID, "Authentication successful. Remote console access granted.");
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed", ClientID);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
}
else if(g_Config.m_SvRconMaxTries)
{
m_aClients[ClientID].m_AuthTries++;
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "Wrong password %d/%d.", m_aClients[ClientID].m_AuthTries, g_Config.m_SvRconMaxTries);
SendRconLine(ClientID, aBuf);
if(m_aClients[ClientID].m_AuthTries >= g_Config.m_SvRconMaxTries)
{
if(!g_Config.m_SvRconBantime)
m_NetServer.Drop(ClientID, "Too many remote console authentication tries");
else
{
NETADDR Addr = m_NetServer.ClientAddr(ClientID);
BanAdd(Addr, g_Config.m_SvRconBantime*60, "Too many remote console authentication tries");
}
}
}
else
{
SendRconLine(ClientID, "Wrong password.");
}
}
}
else if(Msg == NETMSG_PING)
{
CMsgPacker Msg(NETMSG_PING_REPLY);
SendMsgEx(&Msg, 0, ClientID, true);
}
else
{
if(g_Config.m_Debug)
{
char aHex[] = "0123456789ABCDEF";
char aBuf[512];
for(int b = 0; b < pPacket->m_DataSize && b < 32; b++)
{
aBuf[b*3] = aHex[((const unsigned char *)pPacket->m_pData)[b]>>4];
aBuf[b*3+1] = aHex[((const unsigned char *)pPacket->m_pData)[b]&0xf];
aBuf[b*3+2] = ' ';
aBuf[b*3+3] = 0;
}
char aBufMsg[256];
str_format(aBufMsg, sizeof(aBufMsg), "strange message ClientID=%d msg=%d data_size=%d", ClientID, Msg, pPacket->m_DataSize);
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBufMsg);
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf);
}
}
}
else
{
// game message
if(m_aClients[ClientID].m_State >= CClient::STATE_READY)
GameServer()->OnMessage(Msg, &Unpacker, ClientID);
}
// game message
if(m_aClients[ClientID].m_State >= CClient::STATE_READY)
GameServer()->OnMessage(Msg, &Unpacker, ClientID);
}
}

View file

@ -153,6 +153,7 @@ public:
static int DelClientCallback(int ClientID, const char *pReason, void *pUser);
void SendMap(int ClientID);
void SendConnectionReady(int ClientID);
void SendRconLine(int ClientID, const char *pLine);
static void SendRconLineAuthed(const char *pLine, void *pUser);

View file

@ -39,6 +39,7 @@ enum
// sent by server
NETMSG_MAP_CHANGE, // sent when client should switch map
NETMSG_MAP_DATA, // map transfer, contains a chunk of the map file
NETMSG_CON_READY, // connection is ready, client should send start info
NETMSG_SNAP, // normal snapshot, multiple parts
NETMSG_SNAPEMPTY, // empty snapshot
NETMSG_SNAPSINGLE, // ?

View file

@ -786,57 +786,65 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
else
pPlayer->m_SpectatorID = pMsg->m_SpectatorID;
}
else if (MsgID == NETMSGTYPE_CL_CHANGEINFO || MsgID == NETMSGTYPE_CL_STARTINFO)
{
CNetMsg_Cl_ChangeInfo *pMsg = (CNetMsg_Cl_ChangeInfo *)pRawMsg;
if(g_Config.m_SvSpamprotection && pPlayer->m_LastChangeInfo && pPlayer->m_LastChangeInfo+Server()->TickSpeed()*5 > Server()->Tick())
else if (MsgID == NETMSGTYPE_CL_STARTINFO)
{
if(pPlayer->m_IsReady)
return;
CNetMsg_Cl_StartInfo *pMsg = (CNetMsg_Cl_StartInfo *)pRawMsg;
pPlayer->m_LastChangeInfo = Server()->Tick();
// set start infos
Server()->SetClientName(ClientID, pMsg->m_pName);
str_copy(pPlayer->m_TeeInfos.m_SkinName, pMsg->m_pSkin, sizeof(pPlayer->m_TeeInfos.m_SkinName));
pPlayer->m_TeeInfos.m_UseCustomColor = pMsg->m_UseCustomColor;
pPlayer->m_TeeInfos.m_ColorBody = pMsg->m_ColorBody;
pPlayer->m_TeeInfos.m_ColorFeet = pMsg->m_ColorFeet;
m_pController->OnPlayerInfoChange(pPlayer);
// copy old name
char aOldName[MAX_NAME_LENGTH];
str_copy(aOldName, Server()->ClientName(ClientID), MAX_NAME_LENGTH);
// send vote options
CNetMsg_Sv_VoteClearOptions ClearMsg;
Server()->SendPackMsg(&ClearMsg, MSGFLAG_VITAL, ClientID);
CVoteOption *pCurrent = m_pVoteOptionFirst;
while(pCurrent)
{
CNetMsg_Sv_VoteOption OptionMsg;
OptionMsg.m_pCommand = pCurrent->m_aCommand;
Server()->SendPackMsg(&OptionMsg, MSGFLAG_VITAL, ClientID);
pCurrent = pCurrent->m_pNext;
}
// send tuning parameters to client
SendTuningParams(ClientID);
// client is ready to enter
pPlayer->m_IsReady = true;
CNetMsg_Sv_ReadyToEnter m;
Server()->SendPackMsg(&m, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID);
}
else if (MsgID == NETMSGTYPE_CL_CHANGEINFO)
{
if(g_Config.m_SvSpamprotection && pPlayer->m_LastChangeInfo && pPlayer->m_LastChangeInfo+Server()->TickSpeed()*5 > Server()->Tick())
return;
CNetMsg_Cl_ChangeInfo *pMsg = (CNetMsg_Cl_ChangeInfo *)pRawMsg;
pPlayer->m_LastChangeInfo = Server()->Tick();
// set infos
char aOldName[MAX_NAME_LENGTH];
str_copy(aOldName, Server()->ClientName(ClientID), sizeof(aOldName));
Server()->SetClientName(ClientID, pMsg->m_pName);
if(MsgID == NETMSGTYPE_CL_CHANGEINFO && str_comp(aOldName, Server()->ClientName(ClientID)) != 0)
if(str_comp(aOldName, Server()->ClientName(ClientID)) != 0)
{
char aChatText[256];
str_format(aChatText, sizeof(aChatText), "'%s' changed name to '%s'", aOldName, Server()->ClientName(ClientID));
SendChat(-1, CGameContext::CHAT_ALL, aChatText);
}
// set skin
str_copy(pPlayer->m_TeeInfos.m_SkinName, pMsg->m_pSkin, sizeof(pPlayer->m_TeeInfos.m_SkinName));
pPlayer->m_TeeInfos.m_UseCustomColor = pMsg->m_UseCustomColor;
pPlayer->m_TeeInfos.m_ColorBody = pMsg->m_ColorBody;
pPlayer->m_TeeInfos.m_ColorFeet = pMsg->m_ColorFeet;
m_pController->OnPlayerInfoChange(pPlayer);
if(MsgID == NETMSGTYPE_CL_STARTINFO)
{
// send vote options
CNetMsg_Sv_VoteClearOptions ClearMsg;
Server()->SendPackMsg(&ClearMsg, MSGFLAG_VITAL, ClientID);
CVoteOption *pCurrent = m_pVoteOptionFirst;
while(pCurrent)
{
CNetMsg_Sv_VoteOption OptionMsg;
OptionMsg.m_pCommand = pCurrent->m_aCommand;
Server()->SendPackMsg(&OptionMsg, MSGFLAG_VITAL, ClientID);
pCurrent = pCurrent->m_pNext;
}
// send tuning parameters to client
SendTuningParams(ClientID);
//
CNetMsg_Sv_ReadyToEnter m;
Server()->SendPackMsg(&m, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID);
}
}
else if (MsgID == NETMSGTYPE_CL_EMOTICON && !m_World.m_Paused)
{
@ -1167,6 +1175,11 @@ void CGameContext::OnPostSnap()
m_Events.Clear();
}
bool CGameContext::IsClientReady(int ClientID)
{
return m_apPlayers[ClientID] && m_apPlayers[ClientID]->m_IsReady ? true : false;
}
const char *CGameContext::GameType() { return m_pController && m_pController->m_pGameType ? m_pController->m_pGameType : ""; }
const char *CGameContext::Version() { return GAME_VERSION; }
const char *CGameContext::NetVersion() { return GAME_NETVERSION; }

View file

@ -161,6 +161,8 @@ public:
virtual void OnClientDirectInput(int ClientID, void *pInput);
virtual void OnClientPredictedInput(int ClientID, void *pInput);
virtual bool IsClientReady(int ClientID);
virtual const char *GameType();
virtual const char *Version();
virtual const char *NetVersion();

View file

@ -47,6 +47,8 @@ public:
// used for spectator mode
int m_SpectatorID;
bool m_IsReady;
//
int m_Vote;