1#include <base/system.h>
2
3#include <engine/shared/protocol7.h>
4
5#include <generated/protocol7.h>
6
7#include <game/client/gameclient.h>
8#include <game/gamecore.h>
9#include <game/localization.h>
10
11enum
12{
13 STR_TEAM_GAME,
14 STR_TEAM_RED,
15 STR_TEAM_BLUE,
16 STR_TEAM_SPECTATORS,
17};
18
19static int GetStrTeam7(int Team, bool Teamplay)
20{
21 if(Teamplay)
22 {
23 if(Team == TEAM_RED)
24 return STR_TEAM_RED;
25 else if(Team == TEAM_BLUE)
26 return STR_TEAM_BLUE;
27 }
28 else if(Team == 0)
29 return STR_TEAM_GAME;
30
31 return STR_TEAM_SPECTATORS;
32}
33
34enum
35{
36 DO_CHAT = 0,
37 DO_BROADCAST,
38 DO_SPECIAL,
39};
40
41enum
42{
43 PARA_NONE = 0,
44 PARA_I,
45 PARA_II,
46 PARA_III,
47};
48
49struct CGameMsg7
50{
51 int m_Action;
52 int m_ParaType;
53 const char *m_pText;
54};
55
56static CGameMsg7 gs_GameMsgList7[protocol7::NUM_GAMEMSGS] = {
57 {/*GAMEMSG_TEAM_SWAP*/ .m_Action: DO_CHAT, .m_ParaType: PARA_NONE, .m_pText: "Teams were swapped"},
58 {/*GAMEMSG_SPEC_INVALIDID*/ .m_Action: DO_CHAT, .m_ParaType: PARA_NONE, .m_pText: "Invalid spectator id used"}, //!
59 {/*GAMEMSG_TEAM_SHUFFLE*/ .m_Action: DO_CHAT, .m_ParaType: PARA_NONE, .m_pText: "Teams were shuffled"},
60 {/*GAMEMSG_TEAM_BALANCE*/ .m_Action: DO_CHAT, .m_ParaType: PARA_NONE, .m_pText: "Teams have been balanced"},
61 {/*GAMEMSG_CTF_DROP*/ .m_Action: DO_SPECIAL, .m_ParaType: PARA_NONE, .m_pText: ""}, // special - play ctf drop sound
62 {/*GAMEMSG_CTF_RETURN*/ .m_Action: DO_SPECIAL, .m_ParaType: PARA_NONE, .m_pText: ""}, // special - play ctf return sound
63
64 {/*GAMEMSG_TEAM_ALL*/ .m_Action: DO_SPECIAL, .m_ParaType: PARA_I, .m_pText: ""}, // special - add team name
65 {/*GAMEMSG_TEAM_BALANCE_VICTIM*/ .m_Action: DO_SPECIAL, .m_ParaType: PARA_I, .m_pText: ""}, // special - add team name
66 {/*GAMEMSG_CTF_GRAB*/ .m_Action: DO_SPECIAL, .m_ParaType: PARA_I, .m_pText: ""}, // special - play ctf grab sound based on team
67
68 {/*GAMEMSG_CTF_CAPTURE*/ .m_Action: DO_SPECIAL, .m_ParaType: PARA_III, .m_pText: ""}, // special - play ctf capture sound + capture chat message
69
70 {/*GAMEMSG_GAME_PAUSED*/ .m_Action: DO_SPECIAL, .m_ParaType: PARA_I, .m_pText: ""}, // special - add player name
71};
72
73void CGameClient::DoTeamChangeMessage7(const char *pName, int ClientId, int Team, const char *pPrefix)
74{
75 char aBuf[128];
76 switch(GetStrTeam7(Team, Teamplay: m_pClient->m_TranslationContext.m_GameFlags & protocol7::GAMEFLAG_TEAMS))
77 {
78 case STR_TEAM_GAME: str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' %sjoined the game", pName, pPrefix); break;
79 case STR_TEAM_RED: str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' %sjoined the red team", pName, pPrefix); break;
80 case STR_TEAM_BLUE: str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' %sjoined the blue team", pName, pPrefix); break;
81 case STR_TEAM_SPECTATORS: str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' %sjoined the spectators", pName, pPrefix); break;
82 }
83 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: aBuf);
84}
85
86template<typename T>
87void CGameClient::ApplySkin7InfoFromGameMsg(const T *pMsg, int ClientId, int Conn)
88{
89 CClientData::CSixup &SixupData = m_aClients[ClientId].m_aSixup[Conn];
90
91 char *apSkinPartsPtr[protocol7::NUM_SKINPARTS];
92 for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
93 {
94 str_utf8_copy_num(SixupData.m_aaSkinPartNames[Part], pMsg->m_apSkinPartNames[Part], sizeof(SixupData.m_aaSkinPartNames[Part]), protocol7::MAX_SKIN_LENGTH);
95 apSkinPartsPtr[Part] = SixupData.m_aaSkinPartNames[Part];
96 SixupData.m_aUseCustomColors[Part] = pMsg->m_aUseCustomColors[Part];
97 SixupData.m_aSkinPartColors[Part] = pMsg->m_aSkinPartColors[Part];
98 }
99 m_Skins7.ValidateSkinParts(apPartNames: apSkinPartsPtr, pUseCustomColors: SixupData.m_aUseCustomColors, pPartColors: SixupData.m_aSkinPartColors, GameFlags: m_pClient->m_TranslationContext.m_GameFlags);
100}
101
102void CGameClient::ApplySkin7InfoFromSnapObj(const protocol7::CNetObj_De_ClientInfo *pObj, int ClientId)
103{
104 char aSkinPartNames[protocol7::NUM_SKINPARTS][protocol7::MAX_SKIN_ARRAY_SIZE];
105 protocol7::CNetMsg_Sv_SkinChange Msg;
106 Msg.m_ClientId = ClientId;
107 for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
108 {
109 IntsToStr(
110 pInts: pObj->m_aaSkinPartNames[Part],
111 NumInts: std::size(pObj->m_aaSkinPartNames[Part]),
112 pStr: aSkinPartNames[Part],
113 StrSize: std::size(aSkinPartNames[Part]));
114 Msg.m_apSkinPartNames[Part] = aSkinPartNames[Part];
115 Msg.m_aUseCustomColors[Part] = pObj->m_aUseCustomColors[Part];
116 Msg.m_aSkinPartColors[Part] = pObj->m_aSkinPartColors[Part];
117 }
118 ApplySkin7InfoFromGameMsg(pMsg: &Msg, ClientId, Conn: 0);
119}
120
121void CGameClient::CClientData::UpdateSkin7HatSprite(int Dummy)
122{
123 const CClientData::CSixup &SixupData = m_aSixup[Dummy];
124 CTeeRenderInfo::CSixup &SixupSkinInfo = m_pSkinInfo->TeeRenderInfo().m_aSixup[Dummy];
125
126 if(SixupSkinInfo.m_HatTexture.IsValid())
127 {
128 if(str_comp(a: SixupData.m_aaSkinPartNames[protocol7::SKINPART_BODY], b: "standard") != 0 ||
129 str_comp(a: SixupData.m_aaSkinPartNames[protocol7::SKINPART_DECORATION], b: "twinbopp") == 0)
130 {
131 SixupSkinInfo.m_HatSpriteIndex = CSkins7::HAT_OFFSET_SIDE + (ClientId() % CSkins7::HAT_NUM);
132 }
133 else
134 {
135 SixupSkinInfo.m_HatSpriteIndex = ClientId() % CSkins7::HAT_NUM;
136 }
137 }
138}
139
140void CGameClient::CClientData::UpdateSkin7BotDecoration(int Dummy)
141{
142 static const ColorRGBA BOT_COLORS[] = {
143 ColorRGBA(0xff0000),
144 ColorRGBA(0xff6600),
145 ColorRGBA(0x4d9f45),
146 ColorRGBA(0xd59e29),
147 ColorRGBA(0x9fd3a9),
148 ColorRGBA(0xbdd85e),
149 ColorRGBA(0xc07f94),
150 ColorRGBA(0xc3a267),
151 ColorRGBA(0xf8a83b),
152 ColorRGBA(0xcce2bf),
153 ColorRGBA(0xe6b498),
154 ColorRGBA(0x74c7a3),
155 };
156
157 CTeeRenderInfo::CSixup &SixupSkinInfo = m_pSkinInfo->TeeRenderInfo().m_aSixup[Dummy];
158
159 if((m_pGameClient->m_pClient->m_TranslationContext.m_aClients[ClientId()].m_PlayerFlags7 & protocol7::PLAYERFLAG_BOT) != 0)
160 {
161 if(!SixupSkinInfo.m_BotColor.a) // bot color has not been set; pick a random color once
162 {
163 SixupSkinInfo.m_BotColor = BOT_COLORS[rand() % std::size(BOT_COLORS)];
164 }
165 }
166 else
167 {
168 SixupSkinInfo.m_BotColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f);
169 }
170}
171
172void *CGameClient::TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn)
173{
174 if(!m_pClient->IsSixup())
175 {
176 return m_NetObjHandler.SecureUnpackMsg(Type: *pMsgId, pUnpacker);
177 }
178
179 void *pRawMsg = m_NetObjHandler7.SecureUnpackMsg(Type: *pMsgId, pUnpacker);
180 if(!pRawMsg)
181 {
182 if(*pMsgId > __NETMSGTYPE_UUID_HELPER && *pMsgId < OFFSET_MAPITEMTYPE_UUID)
183 {
184 void *pDDNetExMsg = m_NetObjHandler.SecureUnpackMsg(Type: *pMsgId, pUnpacker);
185 if(pDDNetExMsg)
186 return pDDNetExMsg;
187 }
188
189 dbg_msg(sys: "sixup", fmt: "dropped weird message '%s' (%d), failed on '%s'", m_NetObjHandler7.GetMsgName(Type: *pMsgId), *pMsgId, m_NetObjHandler7.FailedMsgOn());
190 return nullptr;
191 }
192 static char s_aRawMsg[1024];
193
194 if(*pMsgId == protocol7::NETMSGTYPE_SV_MOTD)
195 {
196 protocol7::CNetMsg_Sv_Motd *pMsg7 = (protocol7::CNetMsg_Sv_Motd *)pRawMsg;
197 ::CNetMsg_Sv_Motd *pMsg = (::CNetMsg_Sv_Motd *)s_aRawMsg;
198
199 pMsg->m_pMessage = pMsg7->m_pMessage;
200
201 return s_aRawMsg;
202 }
203 else if(*pMsgId == protocol7::NETMSGTYPE_SV_BROADCAST)
204 {
205 protocol7::CNetMsg_Sv_Broadcast *pMsg7 = (protocol7::CNetMsg_Sv_Broadcast *)pRawMsg;
206 ::CNetMsg_Sv_Broadcast *pMsg = (::CNetMsg_Sv_Broadcast *)s_aRawMsg;
207
208 pMsg->m_pMessage = pMsg7->m_pMessage;
209
210 return s_aRawMsg;
211 }
212 else if(*pMsgId == protocol7::NETMSGTYPE_CL_SETTEAM)
213 {
214 protocol7::CNetMsg_Cl_SetTeam *pMsg7 = (protocol7::CNetMsg_Cl_SetTeam *)pRawMsg;
215 ::CNetMsg_Cl_SetTeam *pMsg = (::CNetMsg_Cl_SetTeam *)s_aRawMsg;
216
217 pMsg->m_Team = pMsg7->m_Team;
218
219 return s_aRawMsg;
220 }
221 else if(*pMsgId == protocol7::NETMSGTYPE_SV_TEAM)
222 {
223 protocol7::CNetMsg_Sv_Team *pMsg7 = (protocol7::CNetMsg_Sv_Team *)pRawMsg;
224
225 if(Client()->State() != IClient::STATE_DEMOPLAYBACK)
226 {
227 m_aClients[pMsg7->m_ClientId].m_Team = pMsg7->m_Team;
228 m_pClient->m_TranslationContext.m_aClients[pMsg7->m_ClientId].m_Team = pMsg7->m_Team;
229 if(m_aClients[pMsg7->m_ClientId].m_Active)
230 m_aClients[pMsg7->m_ClientId].UpdateRenderInfo();
231
232 // if(pMsg7->m_ClientId == m_LocalClientId)
233 // {
234 // m_TeamCooldownTick = pMsg7->m_CooldownTick;
235 // m_TeamChangeTime = Client()->LocalTime();
236 // }
237 }
238
239 if(Conn != g_Config.m_ClDummy)
240 return nullptr;
241
242 if(pMsg7->m_Silent == 0)
243 {
244 DoTeamChangeMessage7(pName: m_aClients[pMsg7->m_ClientId].m_aName, ClientId: pMsg7->m_ClientId, Team: pMsg7->m_Team);
245 }
246
247 // we drop the message and add the new team
248 // info to the playerinfo snap item
249 // using translation context
250 return nullptr;
251 }
252 else if(*pMsgId == protocol7::NETMSGTYPE_SV_WEAPONPICKUP)
253 {
254 protocol7::CNetMsg_Sv_WeaponPickup *pMsg7 = (protocol7::CNetMsg_Sv_WeaponPickup *)pRawMsg;
255 ::CNetMsg_Sv_WeaponPickup *pMsg = (::CNetMsg_Sv_WeaponPickup *)s_aRawMsg;
256
257 pMsg->m_Weapon = pMsg7->m_Weapon;
258
259 return s_aRawMsg;
260 }
261 else if(*pMsgId == protocol7::NETMSGTYPE_SV_SERVERSETTINGS)
262 {
263 // 0.7 only message for ui enrichment like locked teams
264 protocol7::CNetMsg_Sv_ServerSettings *pMsg = (protocol7::CNetMsg_Sv_ServerSettings *)pRawMsg;
265
266 if(!m_pClient->m_TranslationContext.m_ServerSettings.m_TeamLock && pMsg->m_TeamLock)
267 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: "Teams were locked");
268 else if(m_pClient->m_TranslationContext.m_ServerSettings.m_TeamLock && !pMsg->m_TeamLock)
269 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: "Teams were unlocked");
270
271 m_pClient->m_TranslationContext.m_ServerSettings.m_KickVote = pMsg->m_KickVote;
272 m_pClient->m_TranslationContext.m_ServerSettings.m_KickMin = pMsg->m_KickMin;
273 m_pClient->m_TranslationContext.m_ServerSettings.m_SpecVote = pMsg->m_SpecVote;
274 m_pClient->m_TranslationContext.m_ServerSettings.m_TeamLock = pMsg->m_TeamLock;
275 m_pClient->m_TranslationContext.m_ServerSettings.m_TeamBalance = pMsg->m_TeamBalance;
276 m_pClient->m_TranslationContext.m_ServerSettings.m_PlayerSlots = pMsg->m_PlayerSlots;
277 return nullptr; // There is no 0.6 equivalent
278 }
279 else if(*pMsgId == protocol7::NETMSGTYPE_SV_RACEFINISH)
280 {
281 *pMsgId = NETMSGTYPE_SV_RACEFINISH;
282 protocol7::CNetMsg_Sv_RaceFinish *pMsg7 = (protocol7::CNetMsg_Sv_RaceFinish *)pRawMsg;
283 ::CNetMsg_Sv_RaceFinish *pMsg = (::CNetMsg_Sv_RaceFinish *)s_aRawMsg;
284
285 pMsg->m_ClientId = pMsg7->m_ClientId;
286 pMsg->m_Time = pMsg7->m_Time;
287 pMsg->m_Diff = pMsg7->m_Diff;
288 pMsg->m_RecordPersonal = pMsg7->m_RecordPersonal;
289 pMsg->m_RecordServer = pMsg7->m_RecordServer;
290
291 return s_aRawMsg;
292 }
293 else if(*pMsgId == protocol7::NETMSGTYPE_SV_CHECKPOINT)
294 {
295 *pMsgId = NETMSGTYPE_SV_DDRACETIME;
296 protocol7::CNetMsg_Sv_Checkpoint *pMsg7 = (protocol7::CNetMsg_Sv_Checkpoint *)pRawMsg;
297 ::CNetMsg_Sv_DDRaceTime *pMsg = (::CNetMsg_Sv_DDRaceTime *)s_aRawMsg;
298
299 pMsg->m_Time = 1;
300 pMsg->m_Finish = 0;
301 pMsg->m_Check = pMsg7->m_Diff / 10;
302
303 return s_aRawMsg;
304 }
305 else if(*pMsgId == protocol7::NETMSGTYPE_SV_COMMANDINFOREMOVE)
306 {
307 *pMsgId = NETMSGTYPE_SV_COMMANDINFOREMOVE;
308 protocol7::CNetMsg_Sv_CommandInfoRemove *pMsg7 = (protocol7::CNetMsg_Sv_CommandInfoRemove *)pRawMsg;
309 ::CNetMsg_Sv_CommandInfoRemove *pMsg = (::CNetMsg_Sv_CommandInfoRemove *)s_aRawMsg;
310
311 pMsg->m_pName = pMsg7->m_pName;
312
313 return s_aRawMsg;
314 }
315 else if(*pMsgId == protocol7::NETMSGTYPE_SV_COMMANDINFO)
316 {
317 *pMsgId = NETMSGTYPE_SV_COMMANDINFO;
318 protocol7::CNetMsg_Sv_CommandInfo *pMsg7 = (protocol7::CNetMsg_Sv_CommandInfo *)pRawMsg;
319 ::CNetMsg_Sv_CommandInfo *pMsg = (::CNetMsg_Sv_CommandInfo *)s_aRawMsg;
320
321 pMsg->m_pName = pMsg7->m_pName;
322 pMsg->m_pArgsFormat = pMsg7->m_pArgsFormat;
323 pMsg->m_pHelpText = pMsg7->m_pHelpText;
324
325 return s_aRawMsg;
326 }
327 else if(*pMsgId == protocol7::NETMSGTYPE_SV_SKINCHANGE)
328 {
329 protocol7::CNetMsg_Sv_SkinChange *pMsg7 = (protocol7::CNetMsg_Sv_SkinChange *)pRawMsg;
330
331 if(pMsg7->m_ClientId < 0 || pMsg7->m_ClientId >= MAX_CLIENTS)
332 {
333 dbg_msg(sys: "sixup", fmt: "Sv_SkinChange got invalid ClientId: %d", pMsg7->m_ClientId);
334 return nullptr;
335 }
336
337 CTranslationContext::CClientData &Client = m_pClient->m_TranslationContext.m_aClients[pMsg7->m_ClientId];
338 Client.m_Active = true;
339 ApplySkin7InfoFromGameMsg(pMsg: pMsg7, ClientId: pMsg7->m_ClientId, Conn);
340 // skin will be moved to the 0.6 snap by the translation context
341 // and we drop the game message
342 return nullptr;
343 }
344 else if(*pMsgId == protocol7::NETMSGTYPE_SV_VOTECLEAROPTIONS)
345 {
346 *pMsgId = NETMSGTYPE_SV_VOTECLEAROPTIONS;
347 return s_aRawMsg;
348 }
349 else if(*pMsgId == protocol7::NETMSGTYPE_SV_VOTEOPTIONADD)
350 {
351 *pMsgId = NETMSGTYPE_SV_VOTEOPTIONADD;
352 protocol7::CNetMsg_Sv_VoteOptionAdd *pMsg7 = (protocol7::CNetMsg_Sv_VoteOptionAdd *)pRawMsg;
353 ::CNetMsg_Sv_VoteOptionAdd *pMsg = (::CNetMsg_Sv_VoteOptionAdd *)s_aRawMsg;
354 pMsg->m_pDescription = pMsg7->m_pDescription;
355 return s_aRawMsg;
356 }
357 else if(*pMsgId == protocol7::NETMSGTYPE_SV_VOTEOPTIONREMOVE)
358 {
359 *pMsgId = NETMSGTYPE_SV_VOTEOPTIONREMOVE;
360 protocol7::CNetMsg_Sv_VoteOptionRemove *pMsg7 = (protocol7::CNetMsg_Sv_VoteOptionRemove *)pRawMsg;
361 ::CNetMsg_Sv_VoteOptionRemove *pMsg = (::CNetMsg_Sv_VoteOptionRemove *)s_aRawMsg;
362 pMsg->m_pDescription = pMsg7->m_pDescription;
363 return s_aRawMsg;
364 }
365 else if(*pMsgId == protocol7::NETMSGTYPE_SV_VOTEOPTIONLISTADD)
366 {
367 ::CNetMsg_Sv_VoteOptionListAdd *pMsg = (::CNetMsg_Sv_VoteOptionListAdd *)s_aRawMsg;
368 int NumOptions = pUnpacker->GetInt();
369 if(NumOptions > 14)
370 {
371 for(int i = 0; i < NumOptions; i++)
372 {
373 const char *pDescription = pUnpacker->GetString(SanitizeType: CUnpacker::SANITIZE_CC);
374 if(pUnpacker->Error())
375 continue;
376
377 m_Voting.AddOption(pDescription);
378 }
379 // 0.7 can send more vote options than
380 // the 0.6 protocol fit
381 // in that case we do not translate it but just
382 // reimplement what 0.6 would do
383 return nullptr;
384 }
385 pMsg->m_NumOptions = 0;
386 for(int i = 0; i < NumOptions; i++)
387 {
388 const char *pDescription = pUnpacker->GetString(SanitizeType: CUnpacker::SANITIZE_CC);
389 if(pUnpacker->Error())
390 continue;
391
392 pMsg->m_NumOptions++;
393 switch(i)
394 {
395 case 0: (pMsg->m_pDescription0 = pDescription); break;
396 case 1: (pMsg->m_pDescription1 = pDescription); break;
397 case 2: (pMsg->m_pDescription2 = pDescription); break;
398 case 3: (pMsg->m_pDescription3 = pDescription); break;
399 case 4: (pMsg->m_pDescription4 = pDescription); break;
400 case 5: (pMsg->m_pDescription5 = pDescription); break;
401 case 6: (pMsg->m_pDescription6 = pDescription); break;
402 case 7: (pMsg->m_pDescription7 = pDescription); break;
403 case 8: (pMsg->m_pDescription8 = pDescription); break;
404 case 9: (pMsg->m_pDescription9 = pDescription); break;
405 case 10: (pMsg->m_pDescription10 = pDescription); break;
406 case 11: (pMsg->m_pDescription11 = pDescription); break;
407 case 12: (pMsg->m_pDescription12 = pDescription); break;
408 case 13: (pMsg->m_pDescription13 = pDescription); break;
409 case 14: (pMsg->m_pDescription14 = pDescription);
410 }
411 }
412 return s_aRawMsg;
413 }
414 else if(*pMsgId == protocol7::NETMSGTYPE_SV_VOTESET)
415 {
416 *pMsgId = NETMSGTYPE_SV_VOTESET;
417 protocol7::CNetMsg_Sv_VoteSet *pMsg7 = (protocol7::CNetMsg_Sv_VoteSet *)pRawMsg;
418 ::CNetMsg_Sv_VoteSet *pMsg = (::CNetMsg_Sv_VoteSet *)s_aRawMsg;
419
420 pMsg->m_Timeout = pMsg7->m_Timeout;
421 pMsg->m_pDescription = pMsg7->m_pDescription;
422 pMsg->m_pReason = pMsg7->m_pReason;
423
424 char aBuf[128];
425 if(pMsg7->m_Timeout)
426 {
427 if(pMsg7->m_ClientId != -1)
428 {
429 const char *pName = m_aClients[pMsg7->m_ClientId].m_aName;
430 switch(pMsg7->m_Type)
431 {
432 case protocol7::VOTE_START_OP:
433 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' called vote to change server option '%s' (%s)", pName, pMsg7->m_pDescription, pMsg7->m_pReason);
434 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: aBuf);
435 break;
436 case protocol7::VOTE_START_KICK:
437 {
438 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' called for vote to kick '%s' (%s)", pName, pMsg7->m_pDescription, pMsg7->m_pReason);
439 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: aBuf);
440 break;
441 }
442 case protocol7::VOTE_START_SPEC:
443 {
444 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' called for vote to move '%s' to spectators (%s)", pName, pMsg7->m_pDescription, pMsg7->m_pReason);
445 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: aBuf);
446 }
447 }
448 }
449 }
450 else
451 {
452 switch(pMsg7->m_Type)
453 {
454 case protocol7::VOTE_START_OP:
455 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Admin forced server option '%s' (%s)", pMsg7->m_pDescription, pMsg7->m_pReason);
456 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: aBuf);
457 break;
458 case protocol7::VOTE_START_SPEC:
459 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Admin moved '%s' to spectator (%s)", pMsg7->m_pDescription, pMsg7->m_pReason);
460 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: aBuf);
461 break;
462 case protocol7::VOTE_END_ABORT:
463 m_Voting.OnReset();
464 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: "Vote aborted");
465 break;
466 case protocol7::VOTE_END_PASS:
467 m_Voting.OnReset();
468 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: pMsg7->m_ClientId == -1 ? "Admin forced vote yes" : "Vote passed");
469 break;
470 case protocol7::VOTE_END_FAIL:
471 m_Voting.OnReset();
472 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: pMsg7->m_ClientId == -1 ? "Admin forced vote no" : "Vote failed");
473 }
474 }
475
476 return s_aRawMsg;
477 }
478 else if(*pMsgId == protocol7::NETMSGTYPE_SV_VOTESTATUS)
479 {
480 *pMsgId = NETMSGTYPE_SV_VOTESTATUS;
481 protocol7::CNetMsg_Sv_VoteStatus *pMsg7 = (protocol7::CNetMsg_Sv_VoteStatus *)pRawMsg;
482 ::CNetMsg_Sv_VoteStatus *pMsg = (::CNetMsg_Sv_VoteStatus *)s_aRawMsg;
483
484 pMsg->m_Yes = pMsg7->m_Yes;
485 pMsg->m_No = pMsg7->m_No;
486 pMsg->m_Pass = pMsg7->m_Pass;
487 pMsg->m_Total = pMsg7->m_Total;
488
489 return s_aRawMsg;
490 }
491 else if(*pMsgId == protocol7::NETMSGTYPE_SV_READYTOENTER)
492 {
493 *pMsgId = NETMSGTYPE_SV_READYTOENTER;
494 return s_aRawMsg;
495 }
496 else if(*pMsgId == protocol7::NETMSGTYPE_SV_CLIENTDROP)
497 {
498 protocol7::CNetMsg_Sv_ClientDrop *pMsg7 = (protocol7::CNetMsg_Sv_ClientDrop *)pRawMsg;
499 if(pMsg7->m_ClientId < 0 || pMsg7->m_ClientId >= MAX_CLIENTS)
500 {
501 dbg_msg(sys: "sixup", fmt: "Sv_ClientDrop got invalid ClientId: %d", pMsg7->m_ClientId);
502 return nullptr;
503 }
504 CTranslationContext::CClientData &Client = m_pClient->m_TranslationContext.m_aClients[pMsg7->m_ClientId];
505 Client.Reset();
506
507 if(pMsg7->m_Silent)
508 return nullptr;
509
510 if(Conn != g_Config.m_ClDummy)
511 return nullptr;
512
513 static char s_aBuf[128];
514 if(pMsg7->m_pReason[0])
515 str_format(buffer: s_aBuf, buffer_size: sizeof(s_aBuf), format: "'%s' has left the game (%s)", m_aClients[pMsg7->m_ClientId].m_aName, pMsg7->m_pReason);
516 else
517 str_format(buffer: s_aBuf, buffer_size: sizeof(s_aBuf), format: "'%s' has left the game", m_aClients[pMsg7->m_ClientId].m_aName);
518 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: s_aBuf);
519
520 return nullptr;
521 }
522 else if(*pMsgId == protocol7::NETMSGTYPE_SV_CLIENTINFO)
523 {
524 protocol7::CNetMsg_Sv_ClientInfo *pMsg7 = (protocol7::CNetMsg_Sv_ClientInfo *)pRawMsg;
525 if(pMsg7->m_ClientId < 0 || pMsg7->m_ClientId >= MAX_CLIENTS)
526 {
527 dbg_msg(sys: "sixup", fmt: "Sv_ClientInfo got invalid ClientId: %d", pMsg7->m_ClientId);
528 return nullptr;
529 }
530
531 if(pMsg7->m_Local)
532 {
533 m_pClient->m_TranslationContext.m_aLocalClientId[Conn] = pMsg7->m_ClientId;
534 }
535 CTranslationContext::CClientData &Client = m_pClient->m_TranslationContext.m_aClients[pMsg7->m_ClientId];
536 Client.m_Active = true;
537 Client.m_Team = pMsg7->m_Team;
538 str_copy(dst&: Client.m_aName, src: pMsg7->m_pName);
539 str_copy(dst&: Client.m_aClan, src: pMsg7->m_pClan);
540 Client.m_Country = pMsg7->m_Country;
541 ApplySkin7InfoFromGameMsg(pMsg: pMsg7, ClientId: pMsg7->m_ClientId, Conn);
542 if(m_pClient->m_TranslationContext.m_aLocalClientId[Conn] == -1)
543 return nullptr;
544 if(pMsg7->m_Silent || pMsg7->m_Local)
545 return nullptr;
546
547 if(Conn != g_Config.m_ClDummy)
548 return nullptr;
549
550 DoTeamChangeMessage7(
551 pName: pMsg7->m_pName,
552 ClientId: pMsg7->m_ClientId,
553 Team: pMsg7->m_Team,
554 pPrefix: "entered and ");
555
556 return nullptr; // we only do side effects and add stuff to the snap here
557 }
558 else if(*pMsgId == protocol7::NETMSGTYPE_SV_GAMEINFO)
559 {
560 protocol7::CNetMsg_Sv_GameInfo *pMsg7 = (protocol7::CNetMsg_Sv_GameInfo *)pRawMsg;
561 m_pClient->m_TranslationContext.m_GameFlags = pMsg7->m_GameFlags;
562 m_pClient->m_TranslationContext.m_ScoreLimit = pMsg7->m_ScoreLimit;
563 m_pClient->m_TranslationContext.m_TimeLimit = pMsg7->m_TimeLimit;
564 m_pClient->m_TranslationContext.m_MatchNum = pMsg7->m_MatchNum;
565 m_pClient->m_TranslationContext.m_MatchCurrent = pMsg7->m_MatchCurrent;
566 m_pClient->m_TranslationContext.m_ShouldSendGameInfo = true;
567 return nullptr; // Added to snap by translation context
568 }
569 else if(*pMsgId == protocol7::NETMSGTYPE_SV_EMOTICON)
570 {
571 *pMsgId = NETMSGTYPE_SV_EMOTICON;
572 protocol7::CNetMsg_Sv_Emoticon *pMsg7 = (protocol7::CNetMsg_Sv_Emoticon *)pRawMsg;
573 ::CNetMsg_Sv_Emoticon *pMsg = (::CNetMsg_Sv_Emoticon *)s_aRawMsg;
574
575 pMsg->m_ClientId = pMsg7->m_ClientId;
576 pMsg->m_Emoticon = pMsg7->m_Emoticon;
577
578 return s_aRawMsg;
579 }
580 else if(*pMsgId == protocol7::NETMSGTYPE_SV_KILLMSG)
581 {
582 *pMsgId = NETMSGTYPE_SV_KILLMSG;
583
584 protocol7::CNetMsg_Sv_KillMsg *pMsg7 = (protocol7::CNetMsg_Sv_KillMsg *)pRawMsg;
585 ::CNetMsg_Sv_KillMsg *pMsg = (::CNetMsg_Sv_KillMsg *)s_aRawMsg;
586
587 pMsg->m_Killer = pMsg7->m_Killer;
588 pMsg->m_Victim = pMsg7->m_Victim;
589 pMsg->m_Weapon = pMsg7->m_Weapon;
590 pMsg->m_ModeSpecial = pMsg7->m_ModeSpecial;
591
592 return s_aRawMsg;
593 }
594 else if(*pMsgId == protocol7::NETMSGTYPE_SV_CHAT)
595 {
596 *pMsgId = NETMSGTYPE_SV_CHAT;
597
598 protocol7::CNetMsg_Sv_Chat *pMsg7 = (protocol7::CNetMsg_Sv_Chat *)pRawMsg;
599 ::CNetMsg_Sv_Chat *pMsg = (::CNetMsg_Sv_Chat *)s_aRawMsg;
600
601 if(pMsg7->m_Mode == protocol7::CHAT_WHISPER)
602 {
603 bool Receive = pMsg7->m_TargetId == m_pClient->m_TranslationContext.m_aLocalClientId[Conn];
604
605 pMsg->m_Team = Receive ? 3 : 2;
606 pMsg->m_ClientId = Receive ? pMsg7->m_ClientId : pMsg7->m_TargetId;
607 }
608 else
609 {
610 pMsg->m_Team = pMsg7->m_Mode == protocol7::CHAT_TEAM ? 1 : 0;
611 pMsg->m_ClientId = pMsg7->m_ClientId;
612 }
613
614 pMsg->m_pMessage = pMsg7->m_pMessage;
615
616 return s_aRawMsg;
617 }
618 else if(*pMsgId == protocol7::NETMSGTYPE_SV_GAMEMSG)
619 {
620 int GameMsgId = pUnpacker->GetInt();
621
622 /**
623 * Prints chat message only once
624 * even if it is being sent to main tee and dummy
625 */
626 auto SendChat = [Conn, GameMsgId, this](const char *pText) -> void {
627 if(GameMsgId != protocol7::GAMEMSG_TEAM_BALANCE_VICTIM && GameMsgId != protocol7::GAMEMSG_SPEC_INVALIDID)
628 {
629 if(Conn != g_Config.m_ClDummy)
630 return;
631 }
632 m_Chat.AddLine(ClientId: -1, Team: 0, pLine: pText);
633 };
634
635 // check for valid gamemsgid
636 if(GameMsgId < 0 || GameMsgId >= protocol7::NUM_GAMEMSGS)
637 return nullptr;
638
639 int aParaI[3] = {0};
640 int NumParaI = 0;
641
642 // get paras
643 switch(gs_GameMsgList7[GameMsgId].m_ParaType)
644 {
645 case PARA_I: NumParaI = 1; break;
646 case PARA_II: NumParaI = 2; break;
647 case PARA_III: NumParaI = 3; break;
648 }
649 for(int i = 0; i < NumParaI; i++)
650 {
651 aParaI[i] = pUnpacker->GetInt();
652 }
653
654 // check for unpacking errors
655 if(pUnpacker->Error())
656 return nullptr;
657
658 // handle special messages
659 char aBuf[256];
660 bool TeamPlay = m_pClient->m_TranslationContext.m_GameFlags & protocol7::GAMEFLAG_TEAMS;
661 if(gs_GameMsgList7[GameMsgId].m_Action == DO_SPECIAL)
662 {
663 switch(GameMsgId)
664 {
665 case protocol7::GAMEMSG_CTF_DROP:
666 if(Conn == g_Config.m_ClDummy)
667 m_Sounds.Enqueue(Channel: CSounds::CHN_GLOBAL, SetId: SOUND_CTF_DROP);
668 break;
669 case protocol7::GAMEMSG_CTF_RETURN:
670 if(Conn == g_Config.m_ClDummy)
671 m_Sounds.Enqueue(Channel: CSounds::CHN_GLOBAL, SetId: SOUND_CTF_RETURN);
672 break;
673 case protocol7::GAMEMSG_TEAM_ALL:
674 {
675 const char *pMsg = "";
676 switch(GetStrTeam7(Team: aParaI[0], Teamplay: TeamPlay))
677 {
678 case STR_TEAM_GAME: pMsg = "All players were moved to the game"; break;
679 case STR_TEAM_RED: pMsg = "All players were moved to the red team"; break;
680 case STR_TEAM_BLUE: pMsg = "All players were moved to the blue team"; break;
681 case STR_TEAM_SPECTATORS: pMsg = "All players were moved to the spectators"; break;
682 }
683 m_Broadcast.DoBroadcast(pText: pMsg); // client side broadcast
684 }
685 break;
686 case protocol7::GAMEMSG_TEAM_BALANCE_VICTIM:
687 {
688 const char *pMsg = "";
689 switch(GetStrTeam7(Team: aParaI[0], Teamplay: TeamPlay))
690 {
691 case STR_TEAM_RED: pMsg = "You were moved to the red team due to team balancing"; break;
692 case STR_TEAM_BLUE: pMsg = "You were moved to the blue team due to team balancing"; break;
693 }
694 m_Broadcast.DoBroadcast(pText: pMsg); // client side broadcast
695 }
696 break;
697 case protocol7::GAMEMSG_CTF_GRAB:
698 if(Conn == g_Config.m_ClDummy)
699 m_Sounds.Enqueue(Channel: CSounds::CHN_GLOBAL, SetId: SOUND_CTF_GRAB_EN);
700 break;
701 case protocol7::GAMEMSG_GAME_PAUSED:
702 {
703 int ClientId = std::clamp(val: aParaI[0], lo: 0, hi: MAX_CLIENTS - 1);
704 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' initiated a pause", m_aClients[ClientId].m_aName);
705 SendChat(aBuf);
706 }
707 break;
708 case protocol7::GAMEMSG_CTF_CAPTURE:
709 if(Conn == g_Config.m_ClDummy)
710 m_Sounds.Enqueue(Channel: CSounds::CHN_GLOBAL, SetId: SOUND_CTF_CAPTURE);
711 int ClientId = std::clamp(val: aParaI[1], lo: 0, hi: MAX_CLIENTS - 1);
712 m_aStats[ClientId].m_FlagCaptures++;
713
714 float Time = aParaI[2] / (float)Client()->GameTickSpeed();
715 if(Time <= 60)
716 {
717 if(aParaI[0])
718 {
719 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "The blue flag was captured by '%s' (%.2f seconds)", m_aClients[ClientId].m_aName, Time);
720 }
721 else
722 {
723 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "The red flag was captured by '%s' (%.2f seconds)", m_aClients[ClientId].m_aName, Time);
724 }
725 }
726 else
727 {
728 if(aParaI[0])
729 {
730 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "The blue flag was captured by '%s'", m_aClients[ClientId].m_aName);
731 }
732 else
733 {
734 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "The red flag was captured by '%s'", m_aClients[ClientId].m_aName);
735 }
736 }
737 SendChat(aBuf);
738 }
739 return nullptr;
740 }
741
742 // build message
743 const char *pText = "";
744 if(NumParaI == 0)
745 {
746 pText = gs_GameMsgList7[GameMsgId].m_pText;
747 }
748
749 // handle message
750 switch(gs_GameMsgList7[GameMsgId].m_Action)
751 {
752 case DO_CHAT:
753 SendChat(pText);
754 break;
755 case DO_BROADCAST:
756 m_Broadcast.DoBroadcast(pText); // client side broadcast
757 break;
758 }
759
760 // no need to handle it in 0.6 since we printed
761 // the message already
762 return nullptr;
763 }
764
765 char aBuf[256];
766 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "dropped weird message '%s' (%d), failed on '%s'", m_NetObjHandler7.GetMsgName(Type: *pMsgId), *pMsgId, m_NetObjHandler7.FailedMsgOn());
767 Console()->Print(Level: IConsole::OUTPUT_LEVEL_ADDINFO, pFrom: "client", pStr: aBuf);
768
769 return nullptr;
770}
771