1#include <engine/shared/protocolglue.h>
2#include <engine/shared/snapshot.h>
3#include <engine/shared/translation_context.h>
4
5#include <generated/protocol7.h>
6
7#include <game/client/gameclient.h>
8
9int CGameClient::TranslateSnap(CSnapshot *pSnapDstSix, CSnapshot *pSnapSrcSeven, int Conn, bool Dummy)
10{
11 CSnapshotBuilder Builder;
12 Builder.Init();
13
14 float LocalTime = Client()->LocalTime();
15 int GameTick = Client()->GameTick(Conn: g_Config.m_ClDummy);
16 CTranslationContext &TranslationContext = Client()->m_TranslationContext;
17
18 for(auto &PlayerInfosRace : TranslationContext.m_apPlayerInfosRace)
19 PlayerInfosRace = nullptr;
20
21 int SpectatorId = -3;
22
23 for(int i = 0; i < pSnapSrcSeven->NumItems(); i++)
24 {
25 const CSnapshotItem *pItem7 = (CSnapshotItem *)pSnapSrcSeven->GetItem(Index: i);
26 const int Size = pSnapSrcSeven->GetItemSize(Index: i);
27 const int ItemType = pSnapSrcSeven->GetItemType(Index: i);
28
29 // ddnet ex snap items
30 if((ItemType > __NETOBJTYPE_UUID_HELPER && ItemType < OFFSET_NETMSGTYPE_UUID) || pItem7->Type() == NETOBJTYPE_EX)
31 {
32 CUnpacker Unpacker;
33 Unpacker.Reset(pData: pItem7->Data(), Size);
34
35 void *pRawObj = GetNetObjHandler()->SecureUnpackObj(Type: ItemType, pUnpacker: &Unpacker);
36 if(!pRawObj)
37 {
38 if(ItemType != UUID_UNKNOWN)
39 dbg_msg(sys: "sixup", fmt: "dropped weird ddnet ex object '%s' (%d), failed on '%s'", GetNetObjHandler()->GetObjName(Type: ItemType), ItemType, GetNetObjHandler()->FailedObjOn());
40 continue;
41 }
42 const int ItemSize = GetNetObjHandler()->GetUnpackedObjSize(Type: ItemType);
43
44 void *pObj = Builder.NewItem(Type: pItem7->Type(), Id: pItem7->Id(), Size: ItemSize);
45 if(!pObj)
46 return -17;
47
48 mem_copy(dest: pObj, source: pRawObj, size: ItemSize);
49 continue;
50 }
51
52 if(GetNetObjHandler7()->ValidateObj(Type: pItem7->Type(), pData: pItem7->Data(), Size) != 0)
53 {
54 if(pItem7->Type() > 0 && pItem7->Type() < CSnapshot::OFFSET_UUID_TYPE)
55 {
56 dbg_msg(
57 sys: "sixup",
58 fmt: "invalidated index=%d type=%d (%s) size=%d id=%d",
59 i,
60 pItem7->Type(),
61 GetNetObjHandler7()->GetObjName(Type: pItem7->Type()),
62 Size,
63 pItem7->Id());
64 }
65 pSnapSrcSeven->InvalidateItem(Index: i);
66 }
67
68 if(pItem7->Type() == protocol7::NETOBJTYPE_PLAYERINFORACE)
69 {
70 const protocol7::CNetObj_PlayerInfoRace *pInfo = (const protocol7::CNetObj_PlayerInfoRace *)pItem7->Data();
71 int ClientId = pItem7->Id();
72 if(ClientId < MAX_CLIENTS && TranslationContext.m_aClients[ClientId].m_Active)
73 {
74 TranslationContext.m_apPlayerInfosRace[ClientId] = pInfo;
75 }
76 }
77 else if(pItem7->Type() == protocol7::NETOBJTYPE_SPECTATORINFO)
78 {
79 const protocol7::CNetObj_SpectatorInfo *pSpec7 = (const protocol7::CNetObj_SpectatorInfo *)pItem7->Data();
80 SpectatorId = pSpec7->m_SpectatorId;
81 if(pSpec7->m_SpecMode == protocol7::SPEC_FREEVIEW)
82 SpectatorId = SPEC_FREEVIEW;
83 }
84 }
85
86 // hack to put game info in the snap
87 // even though in 0.7 we get it as a game message
88 // this message has to be on the top
89 // the client looks at the items in order and needs the gameinfo at the top
90 // otherwise it will not render skins with team colors
91 if(TranslationContext.m_ShouldSendGameInfo)
92 {
93 void *pObj = Builder.NewItem(Type: NETOBJTYPE_GAMEINFO, Id: 0, Size: sizeof(CNetObj_GameInfo));
94 if(!pObj)
95 return -1;
96
97 int GameStateFlagsSix = 0;
98 if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_GAMEOVER)
99 GameStateFlagsSix |= GAMESTATEFLAG_GAMEOVER;
100 if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_SUDDENDEATH)
101 GameStateFlagsSix |= GAMESTATEFLAG_SUDDENDEATH;
102 if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_PAUSED)
103 GameStateFlagsSix |= GAMESTATEFLAG_PAUSED;
104
105 /*
106 This is a 0.7 only flag that we just ignore for now
107
108 GAMESTATEFLAG_ROUNDOVER
109 */
110
111 CNetObj_GameInfo Info6 = {};
112 Info6.m_GameFlags = TranslationContext.m_GameFlags;
113 Info6.m_GameStateFlags = GameStateFlagsSix;
114 Info6.m_RoundStartTick = TranslationContext.m_GameStartTick7;
115 Info6.m_WarmupTimer = 0; // removed in 0.7
116 if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_WARMUP)
117 Info6.m_WarmupTimer = TranslationContext.m_GameStateEndTick7 - GameTick;
118 if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_STARTCOUNTDOWN)
119 Info6.m_WarmupTimer = TranslationContext.m_GameStateEndTick7 - GameTick;
120
121 // hack to port 0.7 race timer to ddnet warmup gametimer hack
122 int TimerClientId = std::clamp(val: TranslationContext.m_aLocalClientId[Conn], lo: 0, hi: (int)MAX_CLIENTS);
123 if(SpectatorId >= 0)
124 TimerClientId = SpectatorId;
125 const protocol7::CNetObj_PlayerInfoRace *pRaceInfo = TranslationContext.m_apPlayerInfosRace[TimerClientId];
126 if(pRaceInfo)
127 {
128 Info6.m_WarmupTimer = -pRaceInfo->m_RaceStartTick;
129 Info6.m_GameStateFlags |= GAMESTATEFLAG_RACETIME;
130 }
131
132 Info6.m_ScoreLimit = TranslationContext.m_ScoreLimit;
133 Info6.m_TimeLimit = TranslationContext.m_TimeLimit;
134 Info6.m_RoundNum = TranslationContext.m_MatchNum;
135 Info6.m_RoundCurrent = TranslationContext.m_MatchCurrent;
136
137 mem_copy(dest: pObj, source: &Info6, size: sizeof(CNetObj_GameInfo));
138 }
139
140 for(int i = 0; i < MAX_CLIENTS; i++)
141 {
142 const CTranslationContext::CClientData &Client = TranslationContext.m_aClients[i];
143 if(!Client.m_Active)
144 continue;
145
146 void *pObj = Builder.NewItem(Type: NETOBJTYPE_CLIENTINFO, Id: i, Size: sizeof(CNetObj_ClientInfo));
147 if(!pObj)
148 return -2;
149
150 CNetObj_ClientInfo Info6 = {};
151 StrToInts(pInts: Info6.m_aName, NumInts: std::size(Info6.m_aName), pStr: Client.m_aName);
152 StrToInts(pInts: Info6.m_aClan, NumInts: std::size(Info6.m_aClan), pStr: Client.m_aClan);
153 Info6.m_Country = Client.m_Country;
154 StrToInts(pInts: Info6.m_aSkin, NumInts: std::size(Info6.m_aSkin), pStr: "default");
155 Info6.m_UseCustomColor = 0;
156 Info6.m_ColorBody = 0;
157 Info6.m_ColorFeet = 0;
158 mem_copy(dest: pObj, source: &Info6, size: sizeof(CNetObj_ClientInfo));
159 }
160
161 bool NewGameData = false;
162
163 for(int i = 0; i < pSnapSrcSeven->NumItems(); i++)
164 {
165 const CSnapshotItem *pItem7 = pSnapSrcSeven->GetItem(Index: i);
166 const int Size = pSnapSrcSeven->GetItemSize(Index: i);
167 // the first few items are a full match
168 // no translation needed
169 if(pItem7->Type() == protocol7::NETOBJTYPE_PROJECTILE ||
170 pItem7->Type() == protocol7::NETOBJTYPE_LASER ||
171 pItem7->Type() == protocol7::NETOBJTYPE_FLAG)
172 {
173 void *pObj = Builder.NewItem(Type: pItem7->Type(), Id: pItem7->Id(), Size);
174 if(!pObj)
175 return -4;
176
177 mem_copy(dest: pObj, source: pItem7->Data(), size: Size);
178 }
179 else if(pItem7->Type() == protocol7::NETOBJTYPE_PICKUP)
180 {
181 void *pObj = Builder.NewItem(Type: NETOBJTYPE_PICKUP, Id: pItem7->Id(), Size: sizeof(CNetObj_Pickup));
182 if(!pObj)
183 return -5;
184
185 const protocol7::CNetObj_Pickup *pPickup7 = (const protocol7::CNetObj_Pickup *)pItem7->Data();
186 CNetObj_Pickup Pickup6 = {};
187 Pickup6.m_X = pPickup7->m_X;
188 Pickup6.m_Y = pPickup7->m_Y;
189 PickupType_SevenToSix(Type7: pPickup7->m_Type, Type6&: Pickup6.m_Type, SubType6&: Pickup6.m_Subtype);
190
191 mem_copy(dest: pObj, source: &Pickup6, size: sizeof(CNetObj_Pickup));
192 }
193 else if(pItem7->Type() == protocol7::NETOBJTYPE_GAMEDATA)
194 {
195 const protocol7::CNetObj_GameData *pGameData = (const protocol7::CNetObj_GameData *)pItem7->Data();
196 TranslationContext.m_GameStateFlags7 = pGameData->m_GameStateFlags;
197 TranslationContext.m_GameStartTick7 = pGameData->m_GameStartTick;
198 TranslationContext.m_GameStateEndTick7 = pGameData->m_GameStateEndTick;
199 }
200 else if(pItem7->Type() == protocol7::NETOBJTYPE_GAMEDATATEAM)
201 {
202 // 0.7 added GameDataTeam and GameDataFlag
203 // both items merged together have all fields of the 0.6 GameData
204 // so if we get either one of them we store the details in the translation context
205 // and build one GameData snap item after this loop
206 const protocol7::CNetObj_GameDataTeam *pTeam7 = (const protocol7::CNetObj_GameDataTeam *)pItem7->Data();
207
208 TranslationContext.m_TeamscoreRed = pTeam7->m_TeamscoreRed;
209 TranslationContext.m_TeamscoreBlue = pTeam7->m_TeamscoreBlue;
210 NewGameData = true;
211 }
212 else if(pItem7->Type() == protocol7::NETOBJTYPE_GAMEDATAFLAG)
213 {
214 const protocol7::CNetObj_GameDataFlag *pFlag7 = (const protocol7::CNetObj_GameDataFlag *)pItem7->Data();
215
216 TranslationContext.m_FlagCarrierRed = pFlag7->m_FlagCarrierRed;
217 TranslationContext.m_FlagCarrierBlue = pFlag7->m_FlagCarrierBlue;
218 NewGameData = true;
219
220 // used for blinking the flags in the hud
221 // but that already works the 0.6 way
222 // and is covered by the NETOBJTYPE_GAMEDATA translation
223 // pFlag7->m_FlagDropTickRed;
224 // pFlag7->m_FlagDropTickBlue;
225 }
226 else if(pItem7->Type() == protocol7::NETOBJTYPE_CHARACTER)
227 {
228 void *pObj = Builder.NewItem(Type: NETOBJTYPE_CHARACTER, Id: pItem7->Id(), Size: sizeof(CNetObj_Character));
229 if(!pObj)
230 return -6;
231
232 const protocol7::CNetObj_Character *pChar7 = (const protocol7::CNetObj_Character *)pItem7->Data();
233
234 CNetObj_Character Char6 = {};
235 // character core is unchanged
236 mem_copy(dest: &Char6, source: pItem7->Data(), size: sizeof(CNetObj_CharacterCore));
237
238 Char6.m_PlayerFlags = 0;
239 if(pItem7->Id() >= 0 && pItem7->Id() < MAX_CLIENTS)
240 Char6.m_PlayerFlags = PlayerFlags_SevenToSix(Flags: TranslationContext.m_aClients[pItem7->Id()].m_PlayerFlags7);
241 Char6.m_Health = pChar7->m_Health;
242 Char6.m_Armor = pChar7->m_Armor;
243 Char6.m_AmmoCount = pChar7->m_AmmoCount;
244 Char6.m_Weapon = pChar7->m_Weapon;
245 Char6.m_Emote = pChar7->m_Emote;
246 Char6.m_AttackTick = pChar7->m_AttackTick;
247
248 if(pChar7->m_TriggeredEvents & protocol7::COREEVENTFLAG_HOOK_ATTACH_PLAYER)
249 {
250 void *pEvent = Builder.NewItem(Type: NETEVENTTYPE_SOUNDWORLD, Id: pItem7->Id(), Size: sizeof(CNetEvent_SoundWorld));
251 if(!pEvent)
252 return -7;
253
254 CNetEvent_SoundWorld Sound = {};
255 Sound.m_X = pChar7->m_X;
256 Sound.m_Y = pChar7->m_Y;
257 Sound.m_SoundId = SOUND_HOOK_ATTACH_PLAYER;
258 mem_copy(dest: pEvent, source: &Sound, size: sizeof(CNetEvent_SoundWorld));
259 }
260
261 if(TranslationContext.m_aLocalClientId[Conn] != pItem7->Id())
262 {
263 if(pChar7->m_TriggeredEvents & protocol7::COREEVENTFLAG_GROUND_JUMP)
264 {
265 void *pEvent = Builder.NewItem(Type: NETEVENTTYPE_SOUNDWORLD, Id: pItem7->Id(), Size: sizeof(CNetEvent_SoundWorld));
266 if(!pEvent)
267 return -7;
268
269 CNetEvent_SoundWorld Sound = {};
270 Sound.m_X = pChar7->m_X;
271 Sound.m_Y = pChar7->m_Y;
272 Sound.m_SoundId = SOUND_PLAYER_JUMP;
273 mem_copy(dest: pEvent, source: &Sound, size: sizeof(CNetEvent_SoundWorld));
274 }
275 if(pChar7->m_TriggeredEvents & protocol7::COREEVENTFLAG_HOOK_ATTACH_GROUND)
276 {
277 void *pEvent = Builder.NewItem(Type: NETEVENTTYPE_SOUNDWORLD, Id: pItem7->Id(), Size: sizeof(CNetEvent_SoundWorld));
278 if(!pEvent)
279 return -7;
280
281 CNetEvent_SoundWorld Sound = {};
282 Sound.m_X = pChar7->m_X;
283 Sound.m_Y = pChar7->m_Y;
284 Sound.m_SoundId = SOUND_HOOK_ATTACH_GROUND;
285 mem_copy(dest: pEvent, source: &Sound, size: sizeof(CNetEvent_SoundWorld));
286 }
287 if(pChar7->m_TriggeredEvents & protocol7::COREEVENTFLAG_HOOK_HIT_NOHOOK)
288 {
289 void *pEvent = Builder.NewItem(Type: NETEVENTTYPE_SOUNDWORLD, Id: pItem7->Id(), Size: sizeof(CNetEvent_SoundWorld));
290 if(!pEvent)
291 return -7;
292
293 CNetEvent_SoundWorld Sound = {};
294 Sound.m_X = pChar7->m_X;
295 Sound.m_Y = pChar7->m_Y;
296 Sound.m_SoundId = SOUND_HOOK_NOATTACH;
297 mem_copy(dest: pEvent, source: &Sound, size: sizeof(CNetEvent_SoundWorld));
298 }
299 }
300
301 mem_copy(dest: pObj, source: &Char6, size: sizeof(CNetObj_Character));
302 }
303 else if(pItem7->Type() == protocol7::NETOBJTYPE_PLAYERINFO)
304 {
305 void *pObj = Builder.NewItem(Type: NETOBJTYPE_PLAYERINFO, Id: pItem7->Id(), Size: sizeof(CNetObj_PlayerInfo));
306 if(!pObj)
307 return -8;
308
309 const protocol7::CNetObj_PlayerInfo *pInfo7 = (const protocol7::CNetObj_PlayerInfo *)pItem7->Data();
310 CNetObj_PlayerInfo Info6 = {};
311 Info6.m_Local = TranslationContext.m_aLocalClientId[Conn] == pItem7->Id();
312 Info6.m_ClientId = pItem7->Id();
313 Info6.m_Team = 0;
314 if(pItem7->Id() >= 0 && pItem7->Id() < MAX_CLIENTS)
315 {
316 Info6.m_Team = TranslationContext.m_aClients[pItem7->Id()].m_Team;
317 TranslationContext.m_aClients[pItem7->Id()].m_PlayerFlags7 = pInfo7->m_PlayerFlags;
318 }
319 Info6.m_Score = pInfo7->m_Score;
320 Info6.m_Latency = pInfo7->m_Latency;
321 mem_copy(dest: pObj, source: &Info6, size: sizeof(CNetObj_PlayerInfo));
322 }
323 else if(pItem7->Type() == protocol7::NETOBJTYPE_SPECTATORINFO)
324 {
325 void *pObj = Builder.NewItem(Type: NETOBJTYPE_SPECTATORINFO, Id: pItem7->Id(), Size: sizeof(CNetObj_SpectatorInfo));
326 if(!pObj)
327 return -9;
328
329 const protocol7::CNetObj_SpectatorInfo *pSpec7 = (const protocol7::CNetObj_SpectatorInfo *)pItem7->Data();
330 CNetObj_SpectatorInfo Spec6 = {};
331 Spec6.m_SpectatorId = pSpec7->m_SpectatorId;
332 if(pSpec7->m_SpecMode == protocol7::SPEC_FREEVIEW)
333 Spec6.m_SpectatorId = SPEC_FREEVIEW;
334 Spec6.m_X = pSpec7->m_X;
335 Spec6.m_Y = pSpec7->m_Y;
336 mem_copy(dest: pObj, source: &Spec6, size: sizeof(CNetObj_SpectatorInfo));
337 }
338 else if(pItem7->Type() == protocol7::NETEVENTTYPE_EXPLOSION)
339 {
340 void *pEvent = Builder.NewItem(Type: NETEVENTTYPE_EXPLOSION, Id: pItem7->Id(), Size: sizeof(CNetEvent_Explosion));
341 if(!pEvent)
342 return -10;
343
344 const protocol7::CNetEvent_Explosion *pExplosion7 = (const protocol7::CNetEvent_Explosion *)pItem7->Data();
345 CNetEvent_Explosion Explosion6 = {};
346 Explosion6.m_X = pExplosion7->m_X;
347 Explosion6.m_Y = pExplosion7->m_Y;
348 mem_copy(dest: pEvent, source: &Explosion6, size: sizeof(CNetEvent_Explosion));
349 }
350 else if(pItem7->Type() == protocol7::NETEVENTTYPE_SPAWN)
351 {
352 void *pEvent = Builder.NewItem(Type: NETEVENTTYPE_SPAWN, Id: pItem7->Id(), Size: sizeof(CNetEvent_Spawn));
353 if(!pEvent)
354 return -11;
355
356 const protocol7::CNetEvent_Spawn *pSpawn7 = (const protocol7::CNetEvent_Spawn *)pItem7->Data();
357 CNetEvent_Spawn Spawn6 = {};
358 Spawn6.m_X = pSpawn7->m_X;
359 Spawn6.m_Y = pSpawn7->m_Y;
360 mem_copy(dest: pEvent, source: &Spawn6, size: sizeof(CNetEvent_Spawn));
361 }
362 else if(pItem7->Type() == protocol7::NETEVENTTYPE_HAMMERHIT)
363 {
364 void *pEvent = Builder.NewItem(Type: NETEVENTTYPE_HAMMERHIT, Id: pItem7->Id(), Size: sizeof(CNetEvent_HammerHit));
365 if(!pEvent)
366 return -12;
367
368 const protocol7::CNetEvent_HammerHit *pHammerHit7 = (const protocol7::CNetEvent_HammerHit *)pItem7->Data();
369 CNetEvent_HammerHit HammerHit6 = {};
370 HammerHit6.m_X = pHammerHit7->m_X;
371 HammerHit6.m_Y = pHammerHit7->m_Y;
372 mem_copy(dest: pEvent, source: &HammerHit6, size: sizeof(CNetEvent_HammerHit));
373 }
374 else if(pItem7->Type() == protocol7::NETEVENTTYPE_DEATH)
375 {
376 void *pEvent = Builder.NewItem(Type: NETEVENTTYPE_DEATH, Id: pItem7->Id(), Size: sizeof(CNetEvent_Death));
377 if(!pEvent)
378 return -13;
379
380 const protocol7::CNetEvent_Death *pDeath7 = (const protocol7::CNetEvent_Death *)pItem7->Data();
381 CNetEvent_Death Death6 = {};
382 Death6.m_X = pDeath7->m_X;
383 Death6.m_Y = pDeath7->m_Y;
384 Death6.m_ClientId = pDeath7->m_ClientId;
385 mem_copy(dest: pEvent, source: &Death6, size: sizeof(CNetEvent_Death));
386 }
387 else if(pItem7->Type() == protocol7::NETEVENTTYPE_SOUNDWORLD)
388 {
389 void *pEvent = Builder.NewItem(Type: NETEVENTTYPE_SOUNDWORLD, Id: pItem7->Id(), Size: sizeof(CNetEvent_SoundWorld));
390 if(!pEvent)
391 return -14;
392
393 const protocol7::CNetEvent_SoundWorld *pSoundWorld7 = (const protocol7::CNetEvent_SoundWorld *)pItem7->Data();
394 CNetEvent_SoundWorld SoundWorld6 = {};
395 SoundWorld6.m_X = pSoundWorld7->m_X;
396 SoundWorld6.m_Y = pSoundWorld7->m_Y;
397 SoundWorld6.m_SoundId = pSoundWorld7->m_SoundId;
398 mem_copy(dest: pEvent, source: &SoundWorld6, size: sizeof(CNetEvent_SoundWorld));
399 }
400 else if(pItem7->Type() == protocol7::NETEVENTTYPE_DAMAGE)
401 {
402 // 0.7 introduced amount for damage indicators
403 // so for one 0.7 item we might create multiple 0.6 ones
404 const protocol7::CNetEvent_Damage *pDmg7 = (const protocol7::CNetEvent_Damage *)pItem7->Data();
405
406 int Amount = pDmg7->m_HealthAmount + pDmg7->m_ArmorAmount;
407 if(Amount < 1)
408 continue;
409
410 int ClientId = pDmg7->m_ClientId;
411 TranslationContext.m_aDamageTaken[ClientId]++;
412
413 float Angle;
414 // create healthmod indicator
415 if(LocalTime < TranslationContext.m_aDamageTakenTick[ClientId] + 0.5f)
416 {
417 // make sure that the damage indicators don't group together
418 Angle = TranslationContext.m_aDamageTaken[ClientId] * 0.25f;
419 }
420 else
421 {
422 TranslationContext.m_aDamageTaken[ClientId] = 0;
423 Angle = 0;
424 }
425
426 TranslationContext.m_aDamageTakenTick[ClientId] = LocalTime;
427
428 float a = 3 * pi / 2 + Angle;
429 float s = a - pi / 3;
430 float e = a + pi / 3;
431 for(int k = 0; k < Amount; k++)
432 {
433 // pItem7->Id() is reused that is technically wrong
434 // but the client implementation does not look at the ids
435 // and renders the damage indicators just fine
436 void *pEvent = Builder.NewItem(Type: NETEVENTTYPE_DAMAGEIND, Id: pItem7->Id(), Size: sizeof(CNetEvent_DamageInd));
437 if(!pEvent)
438 return -16;
439
440 CNetEvent_DamageInd Dmg6 = {};
441 Dmg6.m_X = pDmg7->m_X;
442 Dmg6.m_Y = pDmg7->m_Y;
443 float f = mix(a: s, b: e, amount: float(k + 1) / float(Amount + 2));
444 Dmg6.m_Angle = (int)(f * 256.0f);
445 mem_copy(dest: pEvent, source: &Dmg6, size: sizeof(CNetEvent_DamageInd));
446 }
447 }
448 else if(pItem7->Type() == protocol7::NETOBJTYPE_DE_CLIENTINFO)
449 {
450 const protocol7::CNetObj_De_ClientInfo *pInfo = (const protocol7::CNetObj_De_ClientInfo *)pItem7->Data();
451
452 const int ClientId = pItem7->Id();
453
454 if(ClientId < 0 || ClientId >= MAX_CLIENTS)
455 {
456 dbg_msg(sys: "sixup", fmt: "De_ClientInfo got invalid ClientId: %d", ClientId);
457 return -17;
458 }
459
460 if(pInfo->m_Local)
461 {
462 TranslationContext.m_aLocalClientId[Conn] = ClientId;
463 }
464 CTranslationContext::CClientData &Client = TranslationContext.m_aClients[ClientId];
465 Client.m_Active = true;
466 Client.m_Team = pInfo->m_Team;
467 IntsToStr(pInts: pInfo->m_aName, NumInts: std::size(pInfo->m_aName), pStr: Client.m_aName, StrSize: std::size(Client.m_aName));
468 IntsToStr(pInts: pInfo->m_aClan, NumInts: std::size(pInfo->m_aClan), pStr: Client.m_aClan, StrSize: std::size(Client.m_aClan));
469 Client.m_Country = pInfo->m_Country;
470
471 ApplySkin7InfoFromSnapObj(pObj: pInfo, ClientId);
472 }
473 else if(pItem7->Type() == protocol7::NETOBJTYPE_DE_GAMEINFO)
474 {
475 const protocol7::CNetObj_De_GameInfo *pInfo = (const protocol7::CNetObj_De_GameInfo *)pItem7->Data();
476
477 TranslationContext.m_GameFlags = pInfo->m_GameFlags;
478 TranslationContext.m_ScoreLimit = pInfo->m_ScoreLimit;
479 TranslationContext.m_TimeLimit = pInfo->m_TimeLimit;
480 TranslationContext.m_MatchNum = pInfo->m_MatchNum;
481 TranslationContext.m_MatchCurrent = pInfo->m_MatchCurrent;
482 TranslationContext.m_ShouldSendGameInfo = true;
483 }
484 }
485
486 if(NewGameData)
487 {
488 void *pObj = Builder.NewItem(Type: NETOBJTYPE_GAMEDATA, Id: 0, Size: sizeof(CNetObj_GameData));
489 if(!pObj)
490 return -17;
491
492 CNetObj_GameData GameData = {};
493 GameData.m_TeamscoreRed = TranslationContext.m_TeamscoreRed;
494 GameData.m_TeamscoreBlue = TranslationContext.m_TeamscoreBlue;
495 GameData.m_FlagCarrierRed = TranslationContext.m_FlagCarrierRed;
496 GameData.m_FlagCarrierBlue = TranslationContext.m_FlagCarrierBlue;
497 mem_copy(dest: pObj, source: &GameData, size: sizeof(CNetObj_GameData));
498 }
499
500 return Builder.Finish(pSnapdata: pSnapDstSix);
501}
502
503int CGameClient::OnDemoRecSnap7(CSnapshot *pFrom, CSnapshot *pTo, int Conn)
504{
505 CSnapshotBuilder Builder;
506 Builder.Init7(pSnapshot: pFrom);
507
508 // add client info
509 for(int i = 0; i < MAX_CLIENTS; i++)
510 {
511 if(!m_aClients[i].m_Active)
512 continue;
513
514 void *pItem = Builder.NewItem(Type: protocol7::NETOBJTYPE_DE_CLIENTINFO, Id: i, Size: sizeof(protocol7::CNetObj_De_ClientInfo));
515 if(!pItem)
516 return -1;
517
518 CTranslationContext::CClientData &ClientData = Client()->m_TranslationContext.m_aClients[i];
519
520 protocol7::CNetObj_De_ClientInfo ClientInfoObj;
521 ClientInfoObj.m_Local = i == Client()->m_TranslationContext.m_aLocalClientId[Conn];
522 ClientInfoObj.m_Team = ClientData.m_Team;
523 StrToInts(pInts: ClientInfoObj.m_aName, NumInts: std::size(ClientInfoObj.m_aName), pStr: m_aClients[i].m_aName);
524 StrToInts(pInts: ClientInfoObj.m_aClan, NumInts: std::size(ClientInfoObj.m_aClan), pStr: m_aClients[i].m_aClan);
525 ClientInfoObj.m_Country = ClientData.m_Country;
526
527 for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
528 {
529 StrToInts(
530 pInts: ClientInfoObj.m_aaSkinPartNames[Part],
531 NumInts: std::size(ClientInfoObj.m_aaSkinPartNames[Part]),
532 pStr: m_aClients[i].m_aSixup[Conn].m_aaSkinPartNames[Part]);
533 ClientInfoObj.m_aUseCustomColors[Part] = m_aClients[i].m_aSixup[Conn].m_aUseCustomColors[Part];
534 ClientInfoObj.m_aSkinPartColors[Part] = m_aClients[i].m_aSixup[Conn].m_aSkinPartColors[Part];
535 }
536
537 mem_copy(dest: pItem, source: &ClientInfoObj, size: sizeof(protocol7::CNetObj_De_ClientInfo));
538 }
539
540 // add tuning
541 if(mem_comp(a: &CTuningParams::DEFAULT, b: &m_aTuning[Conn], size: sizeof(CTuningParams)) != 0)
542 {
543 void *pItem = Builder.NewItem(Type: protocol7::NETOBJTYPE_DE_TUNEPARAMS, Id: 0, Size: sizeof(protocol7::CNetObj_De_TuneParams));
544 if(!pItem)
545 return -2;
546
547 protocol7::CNetObj_De_TuneParams TuneParams;
548 mem_copy(dest: &TuneParams.m_aTuneParams, source: &m_aTuning[Conn], size: sizeof(TuneParams));
549 mem_copy(dest: pItem, source: &TuneParams, size: sizeof(protocol7::CNetObj_De_TuneParams));
550 }
551
552 // add game info
553 void *pItem = Builder.NewItem(Type: protocol7::NETOBJTYPE_DE_GAMEINFO, Id: 0, Size: sizeof(protocol7::CNetObj_De_GameInfo));
554 if(!pItem)
555 return -3;
556
557 protocol7::CNetObj_De_GameInfo GameInfo;
558
559 GameInfo.m_GameFlags = Client()->m_TranslationContext.m_GameFlags;
560 GameInfo.m_ScoreLimit = Client()->m_TranslationContext.m_ScoreLimit;
561 GameInfo.m_TimeLimit = Client()->m_TranslationContext.m_TimeLimit;
562 GameInfo.m_MatchNum = Client()->m_TranslationContext.m_MatchNum;
563 GameInfo.m_MatchCurrent = Client()->m_TranslationContext.m_MatchCurrent;
564
565 mem_copy(dest: pItem, source: &GameInfo, size: sizeof(protocol7::CNetObj_De_GameInfo));
566
567 return Builder.Finish(pSnapdata: pTo);
568}
569