List of network system messages
There are three types of messages with overlapping message ids:
system messages,
game messages and
control messages
This list is only covering system messages.
When the client and server exchange packets.
An integer field which usually does not exceed one byte.
Is indicating which type of message was sent.
This field is called the message id.
These message ids are defined in
src/engine/shared/protocol.h
and their payload is defined where they are sent and received.
There can be multiple messages in so called chunks in one teeworlds packet.
In the list below you will find those message.
With their name, id and payload.
Sender: | Client |
Recipient: | Server |
Message ID: | 1 |
Response to: |
TODO |
Expected response: |
TODO |
Sent by the client in CClient::SendInfo()
void CClient::SendInfo()
{
CMsgPacker Msg(NETMSG_INFO);
Msg.AddString(GameClient()->NetVersion(), 128);
Msg.AddString(g_Config.m_Password, 128);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH);
}
Sender: | Server |
Recipient: | Client |
Message ID: | 2 |
Response to: |
TODO |
Expected response: |
TODO |
Sent by the server in
CServer::SendMap(int ClientID)
void CServer::SendMap(int ClientID)
{
CMsgPacker Msg(NETMSG_MAP_CHANGE);
Msg.AddString(GetMapName(), 0);
Msg.AddInt(m_CurrentMapCrc);
Msg.AddInt(m_CurrentMapSize);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
}
Sender: | Server |
Recipient: | Client |
Message ID: | 3 |
Response to: |
TODO |
Expected response: |
TODO |
Sent by the server in
CServer::ProcessClientPacket()
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);
Sender: | Server |
Recipient: | Client |
Message ID: | 4 |
Response to: |
TODO |
Expected response: |
TODO |
Argument name |
Type |
Note |
None |
Sent by the server in
CServer::SendConnectionReady()
void CServer::SendConnectionReady(int ClientID)
{
CMsgPacker Msg(NETMSG_CON_READY);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
}
Unpacked by the client in
CClient::ProcessClientPacket()
else if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_CON_READY)
{
GameClient()->OnConnected();
}
Sender: | Server |
Recipient: | Client |
Message ID: | 5 |
Response to: |
Updates in the world |
Expected response: |
The client acknowledges the Game tick
in the message NETMSG_INPUT
|
Argument name |
Type |
Note |
Game tick |
Int |
TODO
|
Delta tick |
Int |
TODO
|
Num parts |
Int |
TODO
|
Part |
Int |
TODO
|
Crc |
Int |
TODO
|
Part size |
Int |
The size of this part. Meaning the size in bytes of the next raw data field.
|
Data |
Raw |
The snapshot data. Which is a bunch of packed ints.
The first is the amount of removed items.
The second is the amount of item deltas.
The third a unused zero byte.
And the following ints are all snap items.
You can read more about snap items and the structure
of this data field in the
snap items
section.
|
Sender: | TODO |
Recipient: | TODO |
Message ID: | 6 |
Response to: |
TODO |
Expected response: |
TODO |
Sender: | TODO |
Recipient: | TODO |
Message ID: | 7 |
Response to: |
TODO |
Expected response: |
TODO |
Sender: | TODO |
Recipient: | TODO |
Message ID: | 8 |
Response to: |
TODO |
Expected response: |
TODO |
Sender: | Server |
Recipient: | Client |
Message ID: | 9 |
Response to: |
TODO |
Expected response: |
TODO |
Sent by the server in
CServer::ProcessClientPacket()
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);
}
Unpacked by the client in
CClient::ProcessServerPacket()
else if(Msg == NETMSG_INPUTTIMING)
{
int InputPredTick = Unpacker.GetInt();
int TimeLeft = Unpacker.GetInt();
// adjust our prediction time
int64 Target = 0;
for(int k = 0; k < 200; k++)
{
if(m_aInputs[k].m_Tick == InputPredTick)
{
Target = m_aInputs[k].m_PredictedTime + (time_get() - m_aInputs[k].m_Time);
Target = Target - (int64)(((TimeLeft-PREDICTION_MARGIN)/1000.0f)*time_freq());
break;
}
}
if(Target)
m_PredictedTime.Update(&m_InputtimeMarginGraph, Target, TimeLeft, 1);
}
Argument name |
Type |
Note |
Authed |
Int |
- 0 - off (logout)
- 1 - on (login)
|
Cmdlist |
Int |
- 0 - off (logout)
- 1 - on (login)
|
Sent on login and logout by the server to inform the client about its rcon state.
The client then activates or deactivates the remote console in the ui.
Sent by the server in
CServer::ProcessClientPacket()
on successful login.
else if(Msg == NETMSG_RCON_AUTH)
{
const char *pPw;
Unpacker.GetString(); // login name, not used
pPw = Unpacker.GetString(CUnpacker::SANITIZE_CC);
if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Unpacker.Error() == 0)
{
if(g_Config.m_SvRconPassword[0] == 0 && g_Config.m_SvRconModPassword[0] == 0)
{
SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password and/or sv_rcon_mod_password to enable the remote console.");
}
else if(g_Config.m_SvRconPassword[0] && str_comp(pPw, g_Config.m_SvRconPassword) == 0)
{
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
Msg.AddInt(1); //authed
Msg.AddInt(1); //cmdlist
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
m_aClients[ClientID].m_Authed = AUTHED_ADMIN;
int SendRconCmds = Unpacker.GetInt();
if(Unpacker.Error() == 0 && SendRconCmds)
m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_ADMIN, CFGFLAG_SERVER);
SendRconLine(ClientID, "Admin authentication successful. Full remote console access granted.");
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (admin)", ClientID);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
}
else if(g_Config.m_SvRconModPassword[0] && str_comp(pPw, g_Config.m_SvRconModPassword) == 0)
{
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
Msg.AddInt(1); //authed
Msg.AddInt(1); //cmdlist
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
m_aClients[ClientID].m_Authed = AUTHED_MOD;
int SendRconCmds = Unpacker.GetInt();
if(Unpacker.Error() == 0 && SendRconCmds)
m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_MOD, CFGFLAG_SERVER);
SendRconLine(ClientID, "Moderator authentication successful. Limited remote console access granted.");
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (moderator)", 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
m_ServerBan.BanAddr(m_NetServer.ClientAddr(ClientID), g_Config.m_SvRconBantime*60, "Too many remote console authentication tries");
}
}
else
{
SendRconLine(ClientID, "Wrong password.");
}
}
}
And also send by the server in
CServer::ConLogout()
when the rcon command
logout
is executed.
void CServer::ConLogout(IConsole::IResult *pResult, void *pUser)
{
CServer *pServer = (CServer *)pUser;
if(pServer->m_RconClientID >= 0 && pServer->m_RconClientID < MAX_CLIENTS &&
pServer->m_aClients[pServer->m_RconClientID].m_State != CServer::CClient::STATE_EMPTY)
{
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
Msg.AddInt(0); //authed
Msg.AddInt(0); //cmdlist
pServer->SendMsgEx(&Msg, MSGFLAG_VITAL, pServer->m_RconClientID, true);
pServer->m_aClients[pServer->m_RconClientID].m_Authed = AUTHED_NO;
pServer->m_aClients[pServer->m_RconClientID].m_AuthTries = 0;
pServer->m_aClients[pServer->m_RconClientID].m_pRconCmdToSend = 0;
pServer->SendRconLine(pServer->m_RconClientID, "Logout successful.");
char aBuf[32];
str_format(aBuf, sizeof(aBuf), "ClientID=%d logged out", pServer->m_RconClientID);
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
}
}
Unpacked by the client in
CClient::ProcessServerPacket()
else if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_RCON_AUTH_STATUS)
{
int Result = Unpacker.GetInt();
if(Unpacker.Error() == 0)
m_RconAuthed = Result;
int Old = m_UseTempRconCommands;
m_UseTempRconCommands = Unpacker.GetInt();
if(Unpacker.Error() != 0)
m_UseTempRconCommands = 0;
if(Old != 0 && m_UseTempRconCommands == 0)
m_pConsole->DeregisterTempAll();
}
Sender: | Server |
Recipient: | Client |
Message ID: | 11 |
Response to: |
TODO |
Expected response: |
TODO |
Sent by the server in
CServer::SendRconLine(int ClientID, const char *pLine)
void CServer::SendRconLine(int ClientID, const char *pLine)
{
CMsgPacker Msg(NETMSG_RCON_LINE);
Msg.AddString(pLine, 512);
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
}
Sender: | Unused |
Recipient: | Unused |
Message ID: | 12 |
Response to: |
Unused |
Expected response: |
Unused |
Argument name |
Type |
Note |
Unused |
This message ID is unused
Note challenge is misspelled (here to optimize search results)
Sender: | unused |
Recipient: | unused |
Message ID: | 13 |
Response to: |
unused |
Expected response: |
unused |
Argument name |
Type |
Note |
Unused |
This message ID is unused
Sender: | Client |
Recipient: | Server |
Message ID: | 14 |
Response to: |
TODO |
Expected response: |
TODO |
Argument name |
Type |
Note |
None |
Sent by the client in
CClient::SendReady()
void CClient::SendReady()
{
CMsgPacker Msg(NETMSG_READY);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH);
}
Sender: | Client |
Recipient: | Server |
Message ID: | 15 |
Response to: |
TODO |
Expected response: |
TODO |
Argument name |
Type |
Note |
None |
Sent by the client in
CClient::SendEnterGame()
void CClient::SendEnterGame()
{
CMsgPacker Msg(NETMSG_ENTERGAME);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH);
}
Sender: | Client |
Recipient: | Server |
Message ID: | 16 |
Response to: |
TODO |
Expected response: |
TODO |
struct CNetObj_PlayerInput
{
int m_Direction;
int m_TargetX;
int m_TargetY;
int m_Jump;
int m_Fire;
int m_Hook;
int m_PlayerFlags;
int m_WantedWeapon;
int m_NextWeapon;
int m_PrevWeapon;
};
Sender: | Client |
Recipient: | Server |
Message ID: | 17 |
Response to: |
TODO |
Expected response: |
TODO |
Argument name |
Type |
Note |
Command |
String |
The rcon command like for example status or shutdown |
Sent by the client in
CClient::Rcon(const char *pCmd)
void CClient::Rcon(const char *pCmd)
{
CMsgPacker Msg(NETMSG_RCON_CMD);
Msg.AddString(pCmd, 256);
SendMsgEx(&Msg, MSGFLAG_VITAL);
}
Unpacked by the server in
CServer::ProcessClientPacket()
else if(Msg == NETMSG_RCON_CMD)
{
const char *pCmd = Unpacker.GetString();
if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && 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;
m_RconAuthLevel = m_aClients[ClientID].m_Authed;
Console()->SetAccessLevel(m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD);
Console()->ExecuteLineFlag(pCmd, CFGFLAG_SERVER);
Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN);
m_RconClientID = IServer::RCON_CID_SERV;
m_RconAuthLevel = AUTHED_ADMIN;
}
}
Sender: | Client |
Recipient: | Server |
Message ID: | 18 |
Response to: |
TODO |
Expected response: |
TODO |
Argument name |
Type |
Note |
Name |
String |
TODO |
Password |
String |
TODO |
Send rcon commands |
Int |
The official vanilla 0.6 clients uses the fixed value 1
at all times for this field.
But technically the protocol allows optig out of the rcon command list
receival. The server would not send any if this field was set to 0
|
Sent by the client in
CClient::RconAuth()
void CClient::RconAuth(const char *pName, const char *pPassword)
{
if(RconAuthed())
return;
CMsgPacker Msg(NETMSG_RCON_AUTH);
Msg.AddString(pName, 32);
Msg.AddString(pPassword, 32);
Msg.AddInt(1);
SendMsgEx(&Msg, MSGFLAG_VITAL);
}
Sender: | Client |
Recipient: | Server |
Message ID: | 19 |
Response to: |
TODO |
Expected response: |
TODO |
Sent by the client in
CClient::ProcessServerPacket()
else if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_MAP_DATA)
{
int Last = Unpacker.GetInt();
int MapCRC = Unpacker.GetInt();
int Chunk = Unpacker.GetInt();
int Size = Unpacker.GetInt();
const unsigned char *pData = Unpacker.GetRaw(Size);
// check fior errors
if(Unpacker.Error() || Size <= 0 || MapCRC != m_MapdownloadCrc || Chunk != m_MapdownloadChunk || !m_MapdownloadFile)
return;
io_write(m_MapdownloadFile, pData, Size);
m_MapdownloadAmount += Size;
if(Last)
{
// [..]
}
else
{
// request new chunk
m_MapdownloadChunk++;
CMsgPacker Msg(NETMSG_REQUEST_MAP_DATA);
Msg.AddInt(m_MapdownloadChunk);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH);
if(g_Config.m_Debug)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "requested chunk %d", m_MapdownloadChunk);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client/network", aBuf);
}
}
}
Sender: | Unused |
Recipient: | Unused |
Message ID: | 20 |
Response to: |
Unused |
Expected response: |
Unused |
Argument name |
Type |
Note |
None |
This message ID is unused
Sender: | Unused |
Recipient: | Unused |
Message ID: | 21 |
Response to: |
Unused |
Expected response: |
Unused |
Argument name |
Type |
Note |
None |
This message ID is unused
Sender: | Client |
Recipient: | Server |
Message ID: | 22 |
Response to: |
TODO |
Expected response: |
TODO |
Argument name |
Type |
Note |
None |
Unpacked by the client in
CClient::ProcessServerPacket()
else if(Msg == NETMSG_PING)
{
CMsgPacker Msg(NETMSG_PING_REPLY);
SendMsgEx(&Msg, 0);
}
Sent by the client via the
ping
console command
void CClient::Con_Ping(IConsole::IResult *pResult, void *pUserData)
{
CClient *pSelf = (CClient *)pUserData;
CMsgPacker Msg(NETMSG_PING);
pSelf->SendMsgEx(&Msg, 0);
pSelf->m_PingStartTime = time_get();
}
Unpacked by the server in
CServer::ProcessClientPacket()
else if(Msg == NETMSG_PING)
{
CMsgPacker Msg(NETMSG_PING_REPLY);
SendMsgEx(&Msg, 0, ClientID, true);
}
Sender: | Client Server |
Recipient: | Client |
Message ID: | 23 |
Response to: |
TODO |
Expected response: |
TODO |
Argument name |
Type |
Note |
None |
Sent by the server in
CServer::ProcessClientPacket()
else if(Msg == NETMSG_PING)
{
CMsgPacker Msg(NETMSG_PING_REPLY);
SendMsgEx(&Msg, 0, ClientID, true);
}
Sent by the client in
CClient::ProcessServerPacket()
else if(Msg == NETMSG_PING)
{
CMsgPacker Msg(NETMSG_PING_REPLY);
SendMsgEx(&Msg, 0);
}
Unpacked in the client side to print the ping in
CClient::ProcessServerPacket()
else if(Msg == NETMSG_PING_REPLY)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "latency %.2f", (time_get() - m_PingStartTime)*1000 / (float)time_freq());
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client/network", aBuf);
}
Sender: | Unused |
Recipient: | Unused |
Message ID: | 24 |
Response to: |
Unused |
Expected response: |
Unused |
Argument name |
Type |
Note |
None |
This message ID is unused
Sender: | Server |
Recipient: | Client |
Message ID: | 25 |
Response to: |
TODO |
Expected response: |
TODO |
Sent by the server in
CServer::SendRconCmdAdd()
void CServer::SendRconCmdAdd(const IConsole::CCommandInfo *pCommandInfo, int ClientID)
{
CMsgPacker Msg(NETMSG_RCON_CMD_ADD);
Msg.AddString(pCommandInfo->m_pName, IConsole::TEMPCMD_NAME_LENGTH);
Msg.AddString(pCommandInfo->m_pHelp, IConsole::TEMPCMD_HELP_LENGTH);
Msg.AddString(pCommandInfo->m_pParams, IConsole::TEMPCMD_PARAMS_LENGTH);
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
}
Sender: | Server |
Recipient: | Client |
Message ID: | 26 |
Response to: |
TODO |
Expected response: |
TODO |
Sent by the server in
CServer::SendRconCmdAdd()
void CServer::SendRconCmdRem(const IConsole::CCommandInfo *pCommandInfo, int ClientID)
{
CMsgPacker Msg(NETMSG_RCON_CMD_REM);
Msg.AddString(pCommandInfo->m_pName, 256);
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
}
Unpacked by the client in
CClient::ProcessServerPacket()
else if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_RCON_CMD_REM)
{
const char *pName = Unpacker.GetString(CUnpacker::SANITIZE_CC);
if(Unpacker.Error() == 0)
m_pConsole->DeregisterTemp(pName);
}