List of network game messages
There are three types of messages with overlapping message ids:
system messages,
game messages and
control messages
This list is only covering game 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 generated/protocol.h
which is generated by
datasrc/network.py
which also includes the message payload.
There can be multiple messages in so called chunks in one teeworlds packet.
The message names give a hint about who is sending and receiving them.
The format is always
NETMSGTYPE_<sender>_<name>
Where
sender
can be one of those:
- SV - Server sending to client
- CL - Client sending to server
- DE - used for Demos only and is not actually sent
In the list below you will find those message.
With their name, id and payload.
Sender: | Server |
Recipient: | Client |
Message ID: | 1 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Argument name |
Type |
Note |
Message |
String |
The message of the day (motd) that will be displayed to clients
that connect. (The one with the dark background)
|
Sender: | Server |
Recipient: | Client |
Message ID: | 2 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Argument name |
Type |
Note |
Team |
Int |
- 0 - public chat
- 1 - team chat
Technically the range from TEAM_SPECTATORS (-1) to TEAM_BLUE (1) are
the allowed values. But it is not used like that.
Be careful! the ddracenetwork protocol extended this
with their own team values for whisper.
|
Client ID |
Int |
Client ID of the message author.
|
Message |
String |
Message that will be displayed in the chat.
|
Sender: | Server |
Recipient: | Client |
Message ID: | 4 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Sender: | Server |
Recipient: | Client |
Message ID: | 5 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Sender: | Server |
Recipient: | Client |
Message ID: | 6 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Unpacked by the client in
CGameClient::OnMessage()
void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker)
{
// special messages
if(MsgId == NETMSGTYPE_SV_TUNEPARAMS)
{
// unpack the new tuning
CTuningParams NewTuning;
int *pParams = (int *)&NewTuning;
for(unsigned i = 0; i < sizeof(CTuningParams)/sizeof(int); i++)
pParams[i] = pUnpacker->GetInt();
// check for unpacking errors
if(pUnpacker->Error())
return;
m_ServerMode = SERVERMODE_PURE;
// apply new tuning
m_Tuning = NewTuning;
return;
}
Sent by the server in
CGameContext::SendTuningParams()
void CGameContext::SendTuningParams(int ClientID)
{
CheckPureTuning();
CMsgPacker Msg(NETMSGTYPE_SV_TUNEPARAMS);
int *pParams = (int *)&m_Tuning;
for(unsigned i = 0; i < sizeof(m_Tuning)/sizeof(int); i++)
Msg.AddInt(pParams[i]);
Server()->SendMsg(&Msg, MSGFLAG_VITAL, ClientID);
}
Sender: | Server |
Recipient: | Client |
Message ID: | 7 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Argument name |
Type |
Note |
Int |
The amount of CNetObj_Projectile structs in this message |
Int |
m_X in the CNetObj_Projectile struct |
Int |
m_Y in the CNetObj_Projectile struct |
Int |
m_VelX in the CNetObj_Projectile struct |
Int |
m_VelY in the CNetObj_Projectile struct |
Int |
m_Type in the CNetObj_Projectile struct |
Int |
m_StartTick in the CNetObj_Projectile struct |
Latest 0.6 clients do not know this message.
It was
removed in 2015.
It has two different fields. The first being the amount of projectiles.
The rest being integer fields representing this struct.
struct CNetObj_Projectile
{
int m_X;
int m_Y;
int m_VelX;
int m_VelY;
int m_Type;
int m_StartTick;
};
Sender: | Server |
Recipient: | Client |
Message ID: | 8 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Argument name |
Type |
Note |
None |
Sent by the server in CGameContext::OnMessage
CNetMsg_Sv_ReadyToEnter m;
Server()->SendPackMsg(&m, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID);
Sender: | Server |
Recipient: | Client |
Message ID: | 9 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Sender: | Server |
Recipient: | Client |
Message ID: | 10 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Argument name |
Type |
Note |
Client ID |
Int |
Client ID of the tee who sent the emoticon.
|
Emoticon |
Int |
- 0 - oop!
- 1 - alert
- 2 - heart
- 3 - tear
- 4 - ...
- 5 - music
- 6 - sorry
- 7 - ghost
- 8 - annoyed
- 9 - angry
- 10 - devil
- 11 - swearing
- 12 - zzZ
- 13 - WTF
- 14 - happy
- 15 - ??
|
Argument name |
Type |
Note |
None |
Unpacked by the client in
CVoting::OnMessage()
else if(MsgType == NETMSGTYPE_SV_VOTECLEAROPTIONS)
{
ClearOptions();
}
The server sends the message from multiple places. For example when the a rcon command clears it.
Or when a new client joins. And the layout is always something like this:
CNetMsg_Sv_VoteClearOptions ClearMsg;
Server()->SendPackMsg(&ClearMsg, MSGFLAG_VITAL, ClientID);
The message always contains 15 description strings. If less are needed the
Num options value is indicating the amount of used ones.
The client is supposed to only look at the options from zero to Num options.
Sent by the server in
CGameContext::OnMessage()
if(MsgID == NETMSGTYPE_CL_STARTINFO)
{
// [..]
// send vote options
CNetMsg_Sv_VoteClearOptions ClearMsg;
Server()->SendPackMsg(&ClearMsg, MSGFLAG_VITAL, ClientID);
CNetMsg_Sv_VoteOptionListAdd OptionMsg;
int NumOptions = 0;
OptionMsg.m_pDescription0 = "";
OptionMsg.m_pDescription1 = "";
OptionMsg.m_pDescription2 = "";
OptionMsg.m_pDescription3 = "";
OptionMsg.m_pDescription4 = "";
OptionMsg.m_pDescription5 = "";
OptionMsg.m_pDescription6 = "";
OptionMsg.m_pDescription7 = "";
OptionMsg.m_pDescription8 = "";
OptionMsg.m_pDescription9 = "";
OptionMsg.m_pDescription10 = "";
OptionMsg.m_pDescription11 = "";
OptionMsg.m_pDescription12 = "";
OptionMsg.m_pDescription13 = "";
OptionMsg.m_pDescription14 = "";
CVoteOptionServer *pCurrent = m_pVoteOptionFirst;
while(pCurrent)
{
switch(NumOptions++)
{
case 0: OptionMsg.m_pDescription0 = pCurrent->m_aDescription; break;
case 1: OptionMsg.m_pDescription1 = pCurrent->m_aDescription; break;
case 2: OptionMsg.m_pDescription2 = pCurrent->m_aDescription; break;
case 3: OptionMsg.m_pDescription3 = pCurrent->m_aDescription; break;
case 4: OptionMsg.m_pDescription4 = pCurrent->m_aDescription; break;
case 5: OptionMsg.m_pDescription5 = pCurrent->m_aDescription; break;
case 6: OptionMsg.m_pDescription6 = pCurrent->m_aDescription; break;
case 7: OptionMsg.m_pDescription7 = pCurrent->m_aDescription; break;
case 8: OptionMsg.m_pDescription8 = pCurrent->m_aDescription; break;
case 9: OptionMsg.m_pDescription9 = pCurrent->m_aDescription; break;
case 10: OptionMsg.m_pDescription10 = pCurrent->m_aDescription; break;
case 11: OptionMsg.m_pDescription11 = pCurrent->m_aDescription; break;
case 12: OptionMsg.m_pDescription12 = pCurrent->m_aDescription; break;
case 13: OptionMsg.m_pDescription13 = pCurrent->m_aDescription; break;
case 14:
{
OptionMsg.m_pDescription14 = pCurrent->m_aDescription;
OptionMsg.m_NumOptions = NumOptions;
Server()->SendPackMsg(&OptionMsg, MSGFLAG_VITAL, ClientID);
OptionMsg = CNetMsg_Sv_VoteOptionListAdd();
NumOptions = 0;
OptionMsg.m_pDescription1 = "";
OptionMsg.m_pDescription2 = "";
OptionMsg.m_pDescription3 = "";
OptionMsg.m_pDescription4 = "";
OptionMsg.m_pDescription5 = "";
OptionMsg.m_pDescription6 = "";
OptionMsg.m_pDescription7 = "";
OptionMsg.m_pDescription8 = "";
OptionMsg.m_pDescription9 = "";
OptionMsg.m_pDescription10 = "";
OptionMsg.m_pDescription11 = "";
OptionMsg.m_pDescription12 = "";
OptionMsg.m_pDescription13 = "";
OptionMsg.m_pDescription14 = "";
}
}
pCurrent = pCurrent->m_pNext;
}
if(NumOptions > 0)
{
OptionMsg.m_NumOptions = NumOptions;
Server()->SendPackMsg(&OptionMsg, MSGFLAG_VITAL, ClientID);
}
Unpacked by the client in
CVoting::OnMessage()
else if(MsgType == NETMSGTYPE_SV_VOTEOPTIONLISTADD)
{
CNetMsg_Sv_VoteOptionListAdd *pMsg = (CNetMsg_Sv_VoteOptionListAdd *)pRawMsg;
int NumOptions = pMsg->m_NumOptions;
for(int i = 0; i < NumOptions; ++i)
{
switch(i)
{
case 0: AddOption(pMsg->m_pDescription0); break;
case 1: AddOption(pMsg->m_pDescription1); break;
case 2: AddOption(pMsg->m_pDescription2); break;
case 3: AddOption(pMsg->m_pDescription3); break;
case 4: AddOption(pMsg->m_pDescription4); break;
case 5: AddOption(pMsg->m_pDescription5); break;
case 6: AddOption(pMsg->m_pDescription6); break;
case 7: AddOption(pMsg->m_pDescription7); break;
case 8: AddOption(pMsg->m_pDescription8); break;
case 9: AddOption(pMsg->m_pDescription9); break;
case 10: AddOption(pMsg->m_pDescription10); break;
case 11: AddOption(pMsg->m_pDescription11); break;
case 12: AddOption(pMsg->m_pDescription12); break;
case 13: AddOption(pMsg->m_pDescription13); break;
case 14: AddOption(pMsg->m_pDescription14);
}
}
}
Sender: | Server |
Recipient: | Client |
Message ID: | 13 |
Response to: |
TODO
|
Expected response: |
None |
Flags: |
TODO |
Unpacked by the client in
CVoting::OnMessage()
else if(MsgType == NETMSGTYPE_SV_VOTEOPTIONADD)
{
CNetMsg_Sv_VoteOptionAdd *pMsg = (CNetMsg_Sv_VoteOptionAdd *)pRawMsg;
AddOption(pMsg->m_pDescription);
}
Sender: | Server |
Recipient: | Client |
Message ID: | 14 |
Response to: |
TODO
|
Expected response: |
None |
Flags: |
TODO |
Unpacked by the client in
CVoting::OnMessage()
else if(MsgType == NETMSGTYPE_SV_VOTEOPTIONREMOVE)
{
CNetMsg_Sv_VoteOptionRemove *pMsg = (CNetMsg_Sv_VoteOptionRemove *)pRawMsg;
for(CVoteOptionClient *pOption = m_pFirst; pOption; pOption = pOption->m_pNext)
{
if(str_comp(pOption->m_aDescription, pMsg->m_pDescription) == 0)
{
// remove it from the list
if(m_pFirst == pOption)
m_pFirst = m_pFirst->m_pNext;
if(m_pLast == pOption)
m_pLast = m_pLast->m_pPrev;
if(pOption->m_pPrev)
pOption->m_pPrev->m_pNext = pOption->m_pNext;
if(pOption->m_pNext)
pOption->m_pNext->m_pPrev = pOption->m_pPrev;
--m_NumVoteOptions;
// add it to recycle list
pOption->m_pNext = 0;
pOption->m_pPrev = m_pRecycleLast;
if(pOption->m_pPrev)
pOption->m_pPrev->m_pNext = pOption;
m_pRecycleLast = pOption;
if(!m_pRecycleFirst)
m_pRecycleLast = pOption;
break;
}
}
}
Sender: | Server |
Recipient: | Client |
Message ID: | 15 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Unpacked by the client in voting.cpp
OnMessage()
void CVoting::OnMessage(int MsgType, void *pRawMsg)
{
if(MsgType == NETMSGTYPE_SV_VOTESET)
{
CNetMsg_Sv_VoteSet *pMsg = (CNetMsg_Sv_VoteSet *)pRawMsg;
if(pMsg->m_Timeout)
{
OnReset();
str_copy(m_aDescription, pMsg->m_pDescription, sizeof(m_aDescription));
str_copy(m_aReason, pMsg->m_pReason, sizeof(m_aReason));
m_Closetime = time_get() + time_freq() * pMsg->m_Timeout;
}
else
OnReset();
}
Sender: | Server |
Recipient: | Client |
Message ID: | 16 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Unpacked by the client in voting.cpp
OnMessage()
else if(MsgType == NETMSGTYPE_SV_VOTESTATUS)
{
CNetMsg_Sv_VoteStatus *pMsg = (CNetMsg_Sv_VoteStatus *)pRawMsg;
m_Yes = pMsg->m_Yes;
m_No = pMsg->m_No;
m_Pass = pMsg->m_Pass;
m_Total = pMsg->m_Total;
}
Sent by the server in
SendVoteStatus()
void CGameContext::SendVoteStatus(int ClientID, int Total, int Yes, int No)
{
CNetMsg_Sv_VoteStatus Msg = {0};
Msg.m_Total = Total;
Msg.m_Yes = Yes;
Msg.m_No = No;
Msg.m_Pass = Total - (Yes+No);
Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientID);
}
Which is called in
CGameContext::OnTick()
Argument name |
Type |
Note |
Team
|
Int |
Indicating wether it is team chat or not.
- 0 - public chat
- 1 - team chat
The client only tells the server that it wants team chat.
And the server then figures out the team.
int Team = pMsg->m_Team ? pPlayer->GetTeam() : CGameContext::CHAT_ALL;
|
Message
|
String |
TODO
|
Unpacked by the server in
CGameContext::OnMessage()
if(MsgID == NETMSGTYPE_CL_SAY)
{
if(g_Config.m_SvSpamprotection && pPlayer->m_LastChat && pPlayer->m_LastChat+Server()->TickSpeed() > Server()->Tick())
return;
CNetMsg_Cl_Say *pMsg = (CNetMsg_Cl_Say *)pRawMsg;
int Team = pMsg->m_Team ? pPlayer->GetTeam() : CGameContext::CHAT_ALL;
// trim right and set maximum length to 128 utf8-characters
// [...]
// drop empty and autocreated spam messages (more than 16 characters per second)
if(Length == 0 || (g_Config.m_SvSpamprotection && pPlayer->m_LastChat && pPlayer->m_LastChat+Server()->TickSpeed()*((15+Length)/16) > Server()->Tick()))
return;
pPlayer->m_LastChat = Server()->Tick();
SendChat(ClientID, Team, pMsg->m_pMessage);
}
Sender: | Client |
Recipient: | Server |
Message ID: | 18 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Sender: | Client |
Recipient: | Server |
Message ID: | 19 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Sender: | Client |
Recipient: | Server |
Message ID: | 20 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Argument name |
Type |
Note |
Name |
String |
Player name displayed in scoreboard and above every tee.
If an empty string is sent the server sets the name (1).
If there are two players with no name the second gets the name (2).
The official implemenation supports some unicode characters.
|
Clan |
String |
Displayed in the scoreabord. Can be an empty string.
The official implemenation supports some unicode characters.
|
Country |
Int |
This is used for the country flag that is displayed in the scoreboard.
Here a few examples:
- -1 - default
- 4 - AF
- 904 - XWA
|
Skin name |
String |
If a matching filename without .png extension in the skins folder is found that texture is used as skin for the tee.
|
Use custom color |
Int |
If set to 0 the next two fields (color body, color feet) are supposed to be ignored. And the default colors of the skin image file are used.
|
Color body |
Int |
TODO |
Color feet |
Int |
TODO |
The new info will be put into the
snap item
obj_client_info
by the server.
The official client implementation does not apply the new info locally on send but on receive of the snap item from the server.
So custom servers could enforce or ignore any kind of info and clients are supposed to pick it up.
Sender: | Client |
Recipient: | Server |
Message ID: | 22 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Sender: | Client |
Recipient: | Server |
Message ID: | 23 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Argument name |
Type |
Note |
Emoticon |
Int |
- 0 - oop!
- 1 - alert
- 2 - heart
- 3 - tear
- 4 - ...
- 5 - music
- 6 - sorry
- 7 - ghost
- 8 - annoyed
- 9 - angry
- 10 - devil
- 11 - swearing
- 12 - zzZ
- 13 - WTF
- 14 - happy
- 15 - ??
|
Sender: | Client |
Recipient: | Server |
Message ID: | 24 |
Response to: |
TODO
|
Expected response: |
TODO |
Flags: |
TODO |
Sender: | Client |
Recipient: | Server |
Message ID: | 25 |
Response to: |
The user clicking "Call vote" button in the menu or using the callvote console command.
|
Expected response: |
TODO |
Flags: |
TODO |
It is sent by the client from the voting.cpp component in the
Callvote()
method.
void CVoting::Callvote(const char *pType, const char *pValue, const char *pReason)
{
CNetMsg_Cl_CallVote Msg = {0};
Msg.m_Type = pType;
Msg.m_Value = pValue;
Msg.m_Reason = pReason;
Client()->SendPackMsg(&Msg, MSGFLAG_VITAL);
}