1 | #include "teehistorian.h" |
2 | |
3 | #include <base/system.h> |
4 | #include <engine/external/json-parser/json.h> |
5 | #include <engine/shared/config.h> |
6 | #include <engine/shared/json.h> |
7 | #include <engine/shared/snapshot.h> |
8 | #include <game/gamecore.h> |
9 | |
10 | static const char TEEHISTORIAN_NAME[] = "teehistorian@ddnet.tw" ; |
11 | static const CUuid TEEHISTORIAN_UUID = CalculateUuid(pName: TEEHISTORIAN_NAME); |
12 | static const char TEEHISTORIAN_VERSION[] = "2" ; |
13 | static const char TEEHISTORIAN_VERSION_MINOR[] = "8" ; |
14 | |
15 | #define UUID(id, name) static const CUuid UUID_##id = CalculateUuid(name); |
16 | #include <engine/shared/teehistorian_ex_chunks.h> |
17 | #undef UUID |
18 | |
19 | enum |
20 | { |
21 | TEEHISTORIAN_NONE, |
22 | TEEHISTORIAN_FINISH, |
23 | TEEHISTORIAN_TICK_SKIP, |
24 | TEEHISTORIAN_PLAYER_NEW, |
25 | TEEHISTORIAN_PLAYER_OLD, |
26 | TEEHISTORIAN_INPUT_DIFF, |
27 | TEEHISTORIAN_INPUT_NEW, |
28 | TEEHISTORIAN_MESSAGE, |
29 | TEEHISTORIAN_JOIN, |
30 | TEEHISTORIAN_DROP, |
31 | TEEHISTORIAN_CONSOLE_COMMAND, |
32 | TEEHISTORIAN_EX, |
33 | }; |
34 | |
35 | CTeeHistorian::CTeeHistorian() |
36 | { |
37 | m_State = STATE_START; |
38 | m_pfnWriteCallback = 0; |
39 | m_pWriteCallbackUserdata = 0; |
40 | } |
41 | |
42 | void CTeeHistorian::Reset(const CGameInfo *pGameInfo, WRITE_CALLBACK pfnWriteCallback, void *pUser) |
43 | { |
44 | dbg_assert(m_State == STATE_START || m_State == STATE_BEFORE_TICK, "invalid teehistorian state" ); |
45 | |
46 | m_Debug = 0; |
47 | |
48 | m_Tick = 0; |
49 | m_LastWrittenTick = 0; |
50 | // Tick 0 is implicit at the start, game starts as tick 1. |
51 | m_TickWritten = true; |
52 | m_MaxClientId = MAX_CLIENTS; |
53 | |
54 | // `m_PrevMaxClientId` is initialized in `BeginPlayers` |
55 | for(auto &PrevPlayer : m_aPrevPlayers) |
56 | { |
57 | PrevPlayer.m_Alive = false; |
58 | // zero means no id |
59 | PrevPlayer.m_UniqueClientId = 0; |
60 | PrevPlayer.m_Team = 0; |
61 | } |
62 | for(auto &PrevTeam : m_aPrevTeams) |
63 | { |
64 | PrevTeam.m_Practice = false; |
65 | } |
66 | m_pfnWriteCallback = pfnWriteCallback; |
67 | m_pWriteCallbackUserdata = pUser; |
68 | |
69 | WriteHeader(pGameInfo); |
70 | |
71 | m_State = STATE_START; |
72 | } |
73 | |
74 | void CTeeHistorian::(const CGameInfo *pGameInfo) |
75 | { |
76 | Write(pData: &TEEHISTORIAN_UUID, DataSize: sizeof(TEEHISTORIAN_UUID)); |
77 | |
78 | char aGameUuid[UUID_MAXSTRSIZE]; |
79 | char aStartTime[128]; |
80 | char aMapSha256[SHA256_MAXSTRSIZE]; |
81 | |
82 | FormatUuid(Uuid: pGameInfo->m_GameUuid, pBuffer: aGameUuid, BufferLength: sizeof(aGameUuid)); |
83 | str_timestamp_ex(time: pGameInfo->m_StartTime, buffer: aStartTime, buffer_size: sizeof(aStartTime), format: "%Y-%m-%dT%H:%M:%S%z" ); |
84 | sha256_str(digest: pGameInfo->m_MapSha256, str: aMapSha256, max_len: sizeof(aMapSha256)); |
85 | |
86 | char aPrevGameUuid[UUID_MAXSTRSIZE]; |
87 | char aPrevGameUuidJson[64]; |
88 | if(pGameInfo->m_HavePrevGameUuid) |
89 | { |
90 | FormatUuid(Uuid: pGameInfo->m_PrevGameUuid, pBuffer: aPrevGameUuid, BufferLength: sizeof(aPrevGameUuid)); |
91 | str_format(buffer: aPrevGameUuidJson, buffer_size: sizeof(aPrevGameUuidJson), format: "\"prev_game_uuid\":\"%s\"," , aPrevGameUuid); |
92 | } |
93 | else |
94 | { |
95 | aPrevGameUuidJson[0] = 0; |
96 | } |
97 | |
98 | char [128]; |
99 | char aServerVersionBuffer[128]; |
100 | char aStartTimeBuffer[128]; |
101 | char aServerNameBuffer[128]; |
102 | char aGameTypeBuffer[128]; |
103 | char aMapNameBuffer[128]; |
104 | char aMapSha256Buffer[256]; |
105 | char aPrngDescription[128]; |
106 | |
107 | char aJson[2048]; |
108 | |
109 | #define E(buf, str) EscapeJson(buf, sizeof(buf), str) |
110 | |
111 | str_format(buffer: aJson, buffer_size: sizeof(aJson), |
112 | format: "{" |
113 | "\"comment\":\"%s\"," |
114 | "\"version\":\"%s\"," |
115 | "\"version_minor\":\"%s\"," |
116 | "\"game_uuid\":\"%s\"," |
117 | "%s" |
118 | "\"server_version\":\"%s\"," |
119 | "\"start_time\":\"%s\"," |
120 | "\"server_name\":\"%s\"," |
121 | "\"server_port\":\"%d\"," |
122 | "\"game_type\":\"%s\"," |
123 | "\"map_name\":\"%s\"," |
124 | "\"map_size\":\"%d\"," |
125 | "\"map_sha256\":\"%s\"," |
126 | "\"map_crc\":\"%08x\"," |
127 | "\"prng_description\":\"%s\"," |
128 | "\"config\":{" , |
129 | E(aCommentBuffer, TEEHISTORIAN_NAME), |
130 | TEEHISTORIAN_VERSION, |
131 | TEEHISTORIAN_VERSION_MINOR, |
132 | aGameUuid, |
133 | aPrevGameUuidJson, |
134 | E(aServerVersionBuffer, pGameInfo->m_pServerVersion), |
135 | E(aStartTimeBuffer, aStartTime), |
136 | E(aServerNameBuffer, pGameInfo->m_pServerName), |
137 | pGameInfo->m_ServerPort, |
138 | E(aGameTypeBuffer, pGameInfo->m_pGameType), |
139 | E(aMapNameBuffer, pGameInfo->m_pMapName), |
140 | pGameInfo->m_MapSize, |
141 | E(aMapSha256Buffer, aMapSha256), |
142 | pGameInfo->m_MapCrc, |
143 | E(aPrngDescription, pGameInfo->m_pPrngDescription)); |
144 | Write(pData: aJson, DataSize: str_length(str: aJson)); |
145 | |
146 | char aBuffer1[1024]; |
147 | char aBuffer2[1024]; |
148 | bool First = true; |
149 | |
150 | #define MACRO_CONFIG_INT(Name, ScriptName, Def, Min, Max, Flags, Desc) \ |
151 | if((Flags)&CFGFLAG_SERVER && !((Flags)&CFGFLAG_NONTEEHISTORIC) && pGameInfo->m_pConfig->m_##Name != (Def)) \ |
152 | { \ |
153 | str_format(aJson, sizeof(aJson), "%s\"%s\":\"%d\"", \ |
154 | First ? "" : ",", \ |
155 | E(aBuffer1, #ScriptName), \ |
156 | pGameInfo->m_pConfig->m_##Name); \ |
157 | Write(aJson, str_length(aJson)); \ |
158 | First = false; \ |
159 | } |
160 | |
161 | #define MACRO_CONFIG_COL(Name, ScriptName, Def, Flags, Desc) MACRO_CONFIG_INT(Name, ScriptName, Def, 0, 0, Flags, Desc) |
162 | |
163 | #define MACRO_CONFIG_STR(Name, ScriptName, Len, Def, Flags, Desc) \ |
164 | if((Flags)&CFGFLAG_SERVER && !((Flags)&CFGFLAG_NONTEEHISTORIC) && str_comp(pGameInfo->m_pConfig->m_##Name, (Def)) != 0) \ |
165 | { \ |
166 | str_format(aJson, sizeof(aJson), "%s\"%s\":\"%s\"", \ |
167 | First ? "" : ",", \ |
168 | E(aBuffer1, #ScriptName), \ |
169 | E(aBuffer2, pGameInfo->m_pConfig->m_##Name)); \ |
170 | Write(aJson, str_length(aJson)); \ |
171 | First = false; \ |
172 | } |
173 | |
174 | #include <engine/shared/config_variables.h> |
175 | |
176 | #undef MACRO_CONFIG_INT |
177 | #undef MACRO_CONFIG_COL |
178 | #undef MACRO_CONFIG_STR |
179 | |
180 | str_copy(dst&: aJson, src: "},\"tuning\":{" ); |
181 | Write(pData: aJson, DataSize: str_length(str: aJson)); |
182 | |
183 | First = true; |
184 | |
185 | #define MACRO_TUNING_PARAM(Name, ScriptName, Value, Description) \ |
186 | if(pGameInfo->m_pTuning->m_##Name.Get() != (int)((Value)*100)) \ |
187 | { \ |
188 | str_format(aJson, sizeof(aJson), "%s\"%s\":\"%d\"", \ |
189 | First ? "" : ",", \ |
190 | E(aBuffer1, #ScriptName), \ |
191 | pGameInfo->m_pTuning->m_##Name.Get()); \ |
192 | Write(aJson, str_length(aJson)); \ |
193 | First = false; \ |
194 | } |
195 | #include <game/tuning.h> |
196 | #undef MACRO_TUNING_PARAM |
197 | |
198 | str_copy(dst&: aJson, src: "},\"uuids\":[" ); |
199 | Write(pData: aJson, DataSize: str_length(str: aJson)); |
200 | |
201 | for(int i = 0; i < pGameInfo->m_pUuids->NumUuids(); i++) |
202 | { |
203 | str_format(buffer: aJson, buffer_size: sizeof(aJson), format: "%s\"%s\"" , |
204 | i == 0 ? "" : "," , |
205 | E(aBuffer1, pGameInfo->m_pUuids->GetName(OFFSET_UUID + i))); |
206 | Write(pData: aJson, DataSize: str_length(str: aJson)); |
207 | } |
208 | |
209 | str_copy(dst&: aJson, src: "]}" ); |
210 | Write(pData: aJson, DataSize: str_length(str: aJson)); |
211 | Write(pData: "" , DataSize: 1); // Null termination. |
212 | } |
213 | |
214 | void CTeeHistorian::(CUuid Uuid, const void *pData, int DataSize) |
215 | { |
216 | EnsureTickWritten(); |
217 | |
218 | CPacker Ex; |
219 | Ex.Reset(); |
220 | Ex.AddInt(i: -TEEHISTORIAN_EX); |
221 | Ex.AddRaw(pData: &Uuid, Size: sizeof(Uuid)); |
222 | Ex.AddInt(i: DataSize); |
223 | Write(pData: Ex.Data(), DataSize: Ex.Size()); |
224 | Write(pData, DataSize); |
225 | } |
226 | |
227 | void CTeeHistorian::BeginTick(int Tick) |
228 | { |
229 | dbg_assert(m_State == STATE_START || m_State == STATE_BEFORE_TICK, "invalid teehistorian state" ); |
230 | |
231 | m_Tick = Tick; |
232 | m_TickWritten = false; |
233 | |
234 | if(m_Debug > 1) |
235 | { |
236 | dbg_msg(sys: "teehistorian" , fmt: "tick %d" , Tick); |
237 | } |
238 | |
239 | m_State = STATE_BEFORE_PLAYERS; |
240 | } |
241 | |
242 | void CTeeHistorian::BeginPlayers() |
243 | { |
244 | dbg_assert(m_State == STATE_BEFORE_PLAYERS, "invalid teehistorian state" ); |
245 | |
246 | m_PrevMaxClientId = m_MaxClientId; |
247 | // ensure that PLAYER_{DIFF, NEW, OLD} don't cause an implicit tick after a TICK_SKIP |
248 | // by not overwriting m_MaxClientId during RecordPlayer |
249 | m_MaxClientId = -1; |
250 | |
251 | m_State = STATE_PLAYERS; |
252 | } |
253 | |
254 | void CTeeHistorian::EnsureTickWrittenPlayerData(int ClientId) |
255 | { |
256 | dbg_assert(ClientId > m_MaxClientId, "invalid player data order" ); |
257 | m_MaxClientId = ClientId; |
258 | |
259 | if(!m_TickWritten && (ClientId > m_PrevMaxClientId || m_LastWrittenTick + 1 != m_Tick)) |
260 | { |
261 | WriteTick(); |
262 | } |
263 | else |
264 | { |
265 | // Tick is implicit. |
266 | m_LastWrittenTick = m_Tick; |
267 | m_TickWritten = true; |
268 | } |
269 | } |
270 | |
271 | void CTeeHistorian::RecordPlayer(int ClientId, const CNetObj_CharacterCore *pChar) |
272 | { |
273 | dbg_assert(m_State == STATE_PLAYERS, "invalid teehistorian state" ); |
274 | |
275 | CTeehistorianPlayer *pPrev = &m_aPrevPlayers[ClientId]; |
276 | if(!pPrev->m_Alive || pPrev->m_X != pChar->m_X || pPrev->m_Y != pChar->m_Y) |
277 | { |
278 | EnsureTickWrittenPlayerData(ClientId); |
279 | |
280 | CPacker Buffer; |
281 | Buffer.Reset(); |
282 | if(pPrev->m_Alive) |
283 | { |
284 | int dx = pChar->m_X - pPrev->m_X; |
285 | int dy = pChar->m_Y - pPrev->m_Y; |
286 | Buffer.AddInt(i: ClientId); |
287 | Buffer.AddInt(i: dx); |
288 | Buffer.AddInt(i: dy); |
289 | if(m_Debug) |
290 | { |
291 | dbg_msg(sys: "teehistorian" , fmt: "diff cid=%d dx=%d dy=%d" , ClientId, dx, dy); |
292 | } |
293 | } |
294 | else |
295 | { |
296 | int x = pChar->m_X; |
297 | int y = pChar->m_Y; |
298 | Buffer.AddInt(i: -TEEHISTORIAN_PLAYER_NEW); |
299 | Buffer.AddInt(i: ClientId); |
300 | Buffer.AddInt(i: x); |
301 | Buffer.AddInt(i: y); |
302 | if(m_Debug) |
303 | { |
304 | dbg_msg(sys: "teehistorian" , fmt: "new cid=%d x=%d y=%d" , ClientId, x, y); |
305 | } |
306 | } |
307 | Write(pData: Buffer.Data(), DataSize: Buffer.Size()); |
308 | } |
309 | pPrev->m_X = pChar->m_X; |
310 | pPrev->m_Y = pChar->m_Y; |
311 | pPrev->m_Alive = true; |
312 | } |
313 | |
314 | void CTeeHistorian::RecordDeadPlayer(int ClientId) |
315 | { |
316 | dbg_assert(m_State == STATE_PLAYERS, "invalid teehistorian state" ); |
317 | |
318 | CTeehistorianPlayer *pPrev = &m_aPrevPlayers[ClientId]; |
319 | if(pPrev->m_Alive) |
320 | { |
321 | EnsureTickWrittenPlayerData(ClientId); |
322 | |
323 | CPacker Buffer; |
324 | Buffer.Reset(); |
325 | Buffer.AddInt(i: -TEEHISTORIAN_PLAYER_OLD); |
326 | Buffer.AddInt(i: ClientId); |
327 | if(m_Debug) |
328 | { |
329 | dbg_msg(sys: "teehistorian" , fmt: "old cid=%d" , ClientId); |
330 | } |
331 | Write(pData: Buffer.Data(), DataSize: Buffer.Size()); |
332 | } |
333 | pPrev->m_Alive = false; |
334 | } |
335 | |
336 | void CTeeHistorian::RecordPlayerTeam(int ClientId, int Team) |
337 | { |
338 | if(m_aPrevPlayers[ClientId].m_Team != Team) |
339 | { |
340 | m_aPrevPlayers[ClientId].m_Team = Team; |
341 | |
342 | EnsureTickWritten(); |
343 | |
344 | CPacker Buffer; |
345 | Buffer.Reset(); |
346 | Buffer.AddInt(i: ClientId); |
347 | Buffer.AddInt(i: Team); |
348 | |
349 | if(m_Debug) |
350 | { |
351 | dbg_msg(sys: "teehistorian" , fmt: "player_team cid=%d team=%d" , ClientId, Team); |
352 | } |
353 | |
354 | WriteExtra(Uuid: UUID_TEEHISTORIAN_PLAYER_TEAM, pData: Buffer.Data(), DataSize: Buffer.Size()); |
355 | } |
356 | } |
357 | |
358 | void CTeeHistorian::RecordTeamPractice(int Team, bool Practice) |
359 | { |
360 | if(m_aPrevTeams[Team].m_Practice != Practice) |
361 | { |
362 | m_aPrevTeams[Team].m_Practice = Practice; |
363 | |
364 | EnsureTickWritten(); |
365 | |
366 | CPacker Buffer; |
367 | Buffer.Reset(); |
368 | Buffer.AddInt(i: Team); |
369 | Buffer.AddInt(i: Practice); |
370 | |
371 | if(m_Debug) |
372 | { |
373 | dbg_msg(sys: "teehistorian" , fmt: "team_practice team=%d practice=%d" , Team, Practice); |
374 | } |
375 | |
376 | WriteExtra(Uuid: UUID_TEEHISTORIAN_TEAM_PRACTICE, pData: Buffer.Data(), DataSize: Buffer.Size()); |
377 | } |
378 | } |
379 | |
380 | void CTeeHistorian::Write(const void *pData, int DataSize) |
381 | { |
382 | m_pfnWriteCallback(pData, DataSize, m_pWriteCallbackUserdata); |
383 | } |
384 | |
385 | void CTeeHistorian::EnsureTickWritten() |
386 | { |
387 | if(!m_TickWritten) |
388 | { |
389 | WriteTick(); |
390 | } |
391 | } |
392 | |
393 | void CTeeHistorian::WriteTick() |
394 | { |
395 | CPacker TickPacker; |
396 | TickPacker.Reset(); |
397 | |
398 | int dt = m_Tick - m_LastWrittenTick - 1; |
399 | TickPacker.AddInt(i: -TEEHISTORIAN_TICK_SKIP); |
400 | TickPacker.AddInt(i: dt); |
401 | if(m_Debug) |
402 | { |
403 | dbg_msg(sys: "teehistorian" , fmt: "skip_ticks dt=%d" , dt); |
404 | } |
405 | Write(pData: TickPacker.Data(), DataSize: TickPacker.Size()); |
406 | |
407 | m_TickWritten = true; |
408 | m_LastWrittenTick = m_Tick; |
409 | } |
410 | |
411 | void CTeeHistorian::EndPlayers() |
412 | { |
413 | dbg_assert(m_State == STATE_PLAYERS, "invalid teehistorian state" ); |
414 | |
415 | m_State = STATE_BEFORE_INPUTS; |
416 | } |
417 | |
418 | void CTeeHistorian::BeginInputs() |
419 | { |
420 | dbg_assert(m_State == STATE_BEFORE_INPUTS, "invalid teehistorian state" ); |
421 | |
422 | m_State = STATE_INPUTS; |
423 | } |
424 | |
425 | void CTeeHistorian::RecordPlayerInput(int ClientId, uint32_t UniqueClientId, const CNetObj_PlayerInput *pInput) |
426 | { |
427 | CPacker Buffer; |
428 | |
429 | CTeehistorianPlayer *pPrev = &m_aPrevPlayers[ClientId]; |
430 | CNetObj_PlayerInput DiffInput; |
431 | if(pPrev->m_UniqueClientId == UniqueClientId) |
432 | { |
433 | if(mem_comp(a: &pPrev->m_Input, b: pInput, size: sizeof(pPrev->m_Input)) == 0) |
434 | { |
435 | return; |
436 | } |
437 | EnsureTickWritten(); |
438 | Buffer.Reset(); |
439 | |
440 | Buffer.AddInt(i: -TEEHISTORIAN_INPUT_DIFF); |
441 | CSnapshotDelta::DiffItem(pPast: (int *)&pPrev->m_Input, pCurrent: (int *)pInput, pOut: (int *)&DiffInput, Size: sizeof(DiffInput) / sizeof(int32_t)); |
442 | if(m_Debug) |
443 | { |
444 | const int *pData = (const int *)&DiffInput; |
445 | dbg_msg(sys: "teehistorian" , fmt: "diff_input cid=%d %d %d %d %d %d %d %d %d %d %d" , ClientId, |
446 | pData[0], pData[1], pData[2], pData[3], pData[4], |
447 | pData[5], pData[6], pData[7], pData[8], pData[9]); |
448 | } |
449 | } |
450 | else |
451 | { |
452 | EnsureTickWritten(); |
453 | Buffer.Reset(); |
454 | Buffer.AddInt(i: -TEEHISTORIAN_INPUT_NEW); |
455 | DiffInput = *pInput; |
456 | if(m_Debug) |
457 | { |
458 | dbg_msg(sys: "teehistorian" , fmt: "new_input cid=%d" , ClientId); |
459 | } |
460 | } |
461 | Buffer.AddInt(i: ClientId); |
462 | for(size_t i = 0; i < sizeof(DiffInput) / sizeof(int32_t); i++) |
463 | { |
464 | Buffer.AddInt(i: ((int *)&DiffInput)[i]); |
465 | } |
466 | pPrev->m_UniqueClientId = UniqueClientId; |
467 | pPrev->m_Input = *pInput; |
468 | |
469 | Write(pData: Buffer.Data(), DataSize: Buffer.Size()); |
470 | } |
471 | |
472 | void CTeeHistorian::RecordPlayerMessage(int ClientId, const void *pMsg, int MsgSize) |
473 | { |
474 | EnsureTickWritten(); |
475 | |
476 | CPacker Buffer; |
477 | Buffer.Reset(); |
478 | Buffer.AddInt(i: -TEEHISTORIAN_MESSAGE); |
479 | Buffer.AddInt(i: ClientId); |
480 | Buffer.AddInt(i: MsgSize); |
481 | Buffer.AddRaw(pData: pMsg, Size: MsgSize); |
482 | |
483 | if(m_Debug) |
484 | { |
485 | CUnpacker Unpacker; |
486 | Unpacker.Reset(pData: pMsg, Size: MsgSize); |
487 | int MsgId = Unpacker.GetInt(); |
488 | int Sys = MsgId & 1; |
489 | MsgId >>= 1; |
490 | dbg_msg(sys: "teehistorian" , fmt: "msg cid=%d sys=%d msgid=%d" , ClientId, Sys, MsgId); |
491 | } |
492 | |
493 | Write(pData: Buffer.Data(), DataSize: Buffer.Size()); |
494 | } |
495 | |
496 | void CTeeHistorian::RecordPlayerJoin(int ClientId, int Protocol) |
497 | { |
498 | dbg_assert(Protocol == PROTOCOL_6 || Protocol == PROTOCOL_7, "invalid version" ); |
499 | EnsureTickWritten(); |
500 | |
501 | { |
502 | CPacker Buffer; |
503 | Buffer.Reset(); |
504 | Buffer.AddInt(i: ClientId); |
505 | if(m_Debug) |
506 | { |
507 | dbg_msg(sys: "teehistorian" , fmt: "joinver%d cid=%d" , Protocol == PROTOCOL_6 ? 6 : 7, ClientId); |
508 | } |
509 | CUuid Uuid = Protocol == PROTOCOL_6 ? UUID_TEEHISTORIAN_JOINVER6 : UUID_TEEHISTORIAN_JOINVER7; |
510 | WriteExtra(Uuid, pData: Buffer.Data(), DataSize: Buffer.Size()); |
511 | } |
512 | |
513 | CPacker Buffer; |
514 | Buffer.Reset(); |
515 | Buffer.AddInt(i: -TEEHISTORIAN_JOIN); |
516 | Buffer.AddInt(i: ClientId); |
517 | |
518 | if(m_Debug) |
519 | { |
520 | dbg_msg(sys: "teehistorian" , fmt: "join cid=%d" , ClientId); |
521 | } |
522 | |
523 | Write(pData: Buffer.Data(), DataSize: Buffer.Size()); |
524 | } |
525 | |
526 | void CTeeHistorian::RecordPlayerRejoin(int ClientId) |
527 | { |
528 | EnsureTickWritten(); |
529 | |
530 | CPacker Buffer; |
531 | Buffer.Reset(); |
532 | Buffer.AddInt(i: ClientId); |
533 | |
534 | if(m_Debug) |
535 | { |
536 | dbg_msg(sys: "teehistorian" , fmt: "player_rejoin cid=%d" , ClientId); |
537 | } |
538 | |
539 | WriteExtra(Uuid: UUID_TEEHISTORIAN_PLAYER_REJOIN, pData: Buffer.Data(), DataSize: Buffer.Size()); |
540 | } |
541 | |
542 | void CTeeHistorian::RecordPlayerReady(int ClientId) |
543 | { |
544 | EnsureTickWritten(); |
545 | |
546 | CPacker Buffer; |
547 | Buffer.Reset(); |
548 | Buffer.AddInt(i: ClientId); |
549 | |
550 | if(m_Debug) |
551 | { |
552 | dbg_msg(sys: "teehistorian" , fmt: "player_ready cid=%d" , ClientId); |
553 | } |
554 | |
555 | WriteExtra(Uuid: UUID_TEEHISTORIAN_PLAYER_READY, pData: Buffer.Data(), DataSize: Buffer.Size()); |
556 | } |
557 | |
558 | void CTeeHistorian::RecordPlayerDrop(int ClientId, const char *pReason) |
559 | { |
560 | EnsureTickWritten(); |
561 | |
562 | CPacker Buffer; |
563 | Buffer.Reset(); |
564 | Buffer.AddInt(i: -TEEHISTORIAN_DROP); |
565 | Buffer.AddInt(i: ClientId); |
566 | Buffer.AddString(pStr: pReason, Limit: 0); |
567 | |
568 | if(m_Debug) |
569 | { |
570 | dbg_msg(sys: "teehistorian" , fmt: "drop cid=%d reason='%s'" , ClientId, pReason); |
571 | } |
572 | |
573 | Write(pData: Buffer.Data(), DataSize: Buffer.Size()); |
574 | } |
575 | |
576 | void CTeeHistorian::RecordPlayerName(int ClientId, const char *pName) |
577 | { |
578 | EnsureTickWritten(); |
579 | |
580 | CPacker Buffer; |
581 | Buffer.Reset(); |
582 | Buffer.AddInt(i: ClientId); |
583 | Buffer.AddString(pStr: pName, Limit: 0); |
584 | |
585 | if(m_Debug) |
586 | { |
587 | dbg_msg(sys: "teehistorian" , fmt: "player_name cid=%d name='%s'" , ClientId, pName); |
588 | } |
589 | |
590 | WriteExtra(Uuid: UUID_TEEHISTORIAN_PLAYER_NAME, pData: Buffer.Data(), DataSize: Buffer.Size()); |
591 | } |
592 | |
593 | void CTeeHistorian::RecordConsoleCommand(int ClientId, int FlagMask, const char *pCmd, IConsole::IResult *pResult) |
594 | { |
595 | EnsureTickWritten(); |
596 | |
597 | CPacker Buffer; |
598 | Buffer.Reset(); |
599 | Buffer.AddInt(i: -TEEHISTORIAN_CONSOLE_COMMAND); |
600 | Buffer.AddInt(i: ClientId); |
601 | Buffer.AddInt(i: FlagMask); |
602 | Buffer.AddString(pStr: pCmd, Limit: 0); |
603 | Buffer.AddInt(i: pResult->NumArguments()); |
604 | for(int i = 0; i < pResult->NumArguments(); i++) |
605 | { |
606 | Buffer.AddString(pStr: pResult->GetString(Index: i), Limit: 0); |
607 | } |
608 | |
609 | if(m_Debug) |
610 | { |
611 | dbg_msg(sys: "teehistorian" , fmt: "ccmd cid=%d cmd='%s'" , ClientId, pCmd); |
612 | } |
613 | |
614 | Write(pData: Buffer.Data(), DataSize: Buffer.Size()); |
615 | } |
616 | |
617 | void CTeeHistorian::() |
618 | { |
619 | if(m_Debug) |
620 | { |
621 | dbg_msg(sys: "teehistorian" , fmt: "test" ); |
622 | } |
623 | |
624 | WriteExtra(Uuid: UUID_TEEHISTORIAN_TEST, pData: "" , DataSize: 0); |
625 | } |
626 | |
627 | void CTeeHistorian::RecordPlayerSwap(int ClientId1, int ClientId2) |
628 | { |
629 | EnsureTickWritten(); |
630 | |
631 | CPacker Buffer; |
632 | Buffer.Reset(); |
633 | Buffer.AddInt(i: ClientId1); |
634 | Buffer.AddInt(i: ClientId2); |
635 | |
636 | WriteExtra(Uuid: UUID_TEEHISTORIAN_PLAYER_SWITCH, pData: Buffer.Data(), DataSize: Buffer.Size()); |
637 | } |
638 | |
639 | void CTeeHistorian::RecordTeamSaveSuccess(int Team, CUuid SaveId, const char *pTeamSave) |
640 | { |
641 | EnsureTickWritten(); |
642 | |
643 | CPacker Buffer; |
644 | Buffer.Reset(); |
645 | Buffer.AddInt(i: Team); |
646 | Buffer.AddRaw(pData: &SaveId, Size: sizeof(SaveId)); |
647 | Buffer.AddString(pStr: pTeamSave, Limit: 0); |
648 | |
649 | if(m_Debug) |
650 | { |
651 | char aSaveId[UUID_MAXSTRSIZE]; |
652 | FormatUuid(Uuid: SaveId, pBuffer: aSaveId, BufferLength: sizeof(aSaveId)); |
653 | dbg_msg(sys: "teehistorian" , fmt: "save_success team=%d save_id=%s team_save='%s'" , Team, aSaveId, pTeamSave); |
654 | } |
655 | |
656 | WriteExtra(Uuid: UUID_TEEHISTORIAN_SAVE_SUCCESS, pData: Buffer.Data(), DataSize: Buffer.Size()); |
657 | } |
658 | |
659 | void CTeeHistorian::RecordTeamSaveFailure(int Team) |
660 | { |
661 | EnsureTickWritten(); |
662 | |
663 | CPacker Buffer; |
664 | Buffer.Reset(); |
665 | Buffer.AddInt(i: Team); |
666 | |
667 | if(m_Debug) |
668 | { |
669 | dbg_msg(sys: "teehistorian" , fmt: "save_failure team=%d" , Team); |
670 | } |
671 | |
672 | WriteExtra(Uuid: UUID_TEEHISTORIAN_SAVE_FAILURE, pData: Buffer.Data(), DataSize: Buffer.Size()); |
673 | } |
674 | |
675 | void CTeeHistorian::RecordTeamLoadSuccess(int Team, CUuid SaveId, const char *pTeamSave) |
676 | { |
677 | EnsureTickWritten(); |
678 | |
679 | CPacker Buffer; |
680 | Buffer.Reset(); |
681 | Buffer.AddInt(i: Team); |
682 | Buffer.AddRaw(pData: &SaveId, Size: sizeof(SaveId)); |
683 | Buffer.AddString(pStr: pTeamSave, Limit: 0); |
684 | |
685 | if(m_Debug) |
686 | { |
687 | char aSaveId[UUID_MAXSTRSIZE]; |
688 | FormatUuid(Uuid: SaveId, pBuffer: aSaveId, BufferLength: sizeof(aSaveId)); |
689 | dbg_msg(sys: "teehistorian" , fmt: "load_success team=%d save_id=%s team_save='%s'" , Team, aSaveId, pTeamSave); |
690 | } |
691 | |
692 | WriteExtra(Uuid: UUID_TEEHISTORIAN_LOAD_SUCCESS, pData: Buffer.Data(), DataSize: Buffer.Size()); |
693 | } |
694 | |
695 | void CTeeHistorian::RecordTeamLoadFailure(int Team) |
696 | { |
697 | EnsureTickWritten(); |
698 | |
699 | CPacker Buffer; |
700 | Buffer.Reset(); |
701 | Buffer.AddInt(i: Team); |
702 | |
703 | if(m_Debug) |
704 | { |
705 | dbg_msg(sys: "teehistorian" , fmt: "load_failure team=%d" , Team); |
706 | } |
707 | |
708 | WriteExtra(Uuid: UUID_TEEHISTORIAN_LOAD_FAILURE, pData: Buffer.Data(), DataSize: Buffer.Size()); |
709 | } |
710 | |
711 | void CTeeHistorian::EndInputs() |
712 | { |
713 | dbg_assert(m_State == STATE_INPUTS, "invalid teehistorian state" ); |
714 | |
715 | m_State = STATE_BEFORE_ENDTICK; |
716 | } |
717 | |
718 | void CTeeHistorian::EndTick() |
719 | { |
720 | dbg_assert(m_State == STATE_BEFORE_ENDTICK, "invalid teehistorian state" ); |
721 | m_State = STATE_BEFORE_TICK; |
722 | } |
723 | |
724 | void CTeeHistorian::RecordDDNetVersionOld(int ClientId, int DDNetVersion) |
725 | { |
726 | CPacker Buffer; |
727 | Buffer.Reset(); |
728 | Buffer.AddInt(i: ClientId); |
729 | Buffer.AddInt(i: DDNetVersion); |
730 | |
731 | if(m_Debug) |
732 | { |
733 | dbg_msg(sys: "teehistorian" , fmt: "ddnetver_old cid=%d ddnet_version=%d" , ClientId, DDNetVersion); |
734 | } |
735 | |
736 | WriteExtra(Uuid: UUID_TEEHISTORIAN_DDNETVER_OLD, pData: Buffer.Data(), DataSize: Buffer.Size()); |
737 | } |
738 | |
739 | void CTeeHistorian::RecordDDNetVersion(int ClientId, CUuid ConnectionId, int DDNetVersion, const char *pDDNetVersionStr) |
740 | { |
741 | CPacker Buffer; |
742 | Buffer.Reset(); |
743 | Buffer.AddInt(i: ClientId); |
744 | Buffer.AddRaw(pData: &ConnectionId, Size: sizeof(ConnectionId)); |
745 | Buffer.AddInt(i: DDNetVersion); |
746 | Buffer.AddString(pStr: pDDNetVersionStr, Limit: 0); |
747 | |
748 | if(m_Debug) |
749 | { |
750 | char aConnnectionId[UUID_MAXSTRSIZE]; |
751 | FormatUuid(Uuid: ConnectionId, pBuffer: aConnnectionId, BufferLength: sizeof(aConnnectionId)); |
752 | dbg_msg(sys: "teehistorian" , fmt: "ddnetver cid=%d connection_id=%s ddnet_version=%d ddnet_version_str=%s" , ClientId, aConnnectionId, DDNetVersion, pDDNetVersionStr); |
753 | } |
754 | |
755 | WriteExtra(Uuid: UUID_TEEHISTORIAN_DDNETVER, pData: Buffer.Data(), DataSize: Buffer.Size()); |
756 | } |
757 | |
758 | void CTeeHistorian::RecordAuthInitial(int ClientId, int Level, const char *pAuthName) |
759 | { |
760 | CPacker Buffer; |
761 | Buffer.Reset(); |
762 | Buffer.AddInt(i: ClientId); |
763 | Buffer.AddInt(i: Level); |
764 | Buffer.AddString(pStr: pAuthName, Limit: 0); |
765 | |
766 | if(m_Debug) |
767 | { |
768 | dbg_msg(sys: "teehistorian" , fmt: "auth_init cid=%d level=%d auth_name=%s" , ClientId, Level, pAuthName); |
769 | } |
770 | |
771 | WriteExtra(Uuid: UUID_TEEHISTORIAN_AUTH_INIT, pData: Buffer.Data(), DataSize: Buffer.Size()); |
772 | } |
773 | |
774 | void CTeeHistorian::RecordAuthLogin(int ClientId, int Level, const char *pAuthName) |
775 | { |
776 | CPacker Buffer; |
777 | Buffer.Reset(); |
778 | Buffer.AddInt(i: ClientId); |
779 | Buffer.AddInt(i: Level); |
780 | Buffer.AddString(pStr: pAuthName, Limit: 0); |
781 | |
782 | if(m_Debug) |
783 | { |
784 | dbg_msg(sys: "teehistorian" , fmt: "auth_login cid=%d level=%d auth_name=%s" , ClientId, Level, pAuthName); |
785 | } |
786 | |
787 | WriteExtra(Uuid: UUID_TEEHISTORIAN_AUTH_LOGIN, pData: Buffer.Data(), DataSize: Buffer.Size()); |
788 | } |
789 | |
790 | void CTeeHistorian::RecordAuthLogout(int ClientId) |
791 | { |
792 | CPacker Buffer; |
793 | Buffer.Reset(); |
794 | Buffer.AddInt(i: ClientId); |
795 | |
796 | if(m_Debug) |
797 | { |
798 | dbg_msg(sys: "teehistorian" , fmt: "auth_logout cid=%d" , ClientId); |
799 | } |
800 | |
801 | WriteExtra(Uuid: UUID_TEEHISTORIAN_AUTH_LOGOUT, pData: Buffer.Data(), DataSize: Buffer.Size()); |
802 | } |
803 | |
804 | void CTeeHistorian::RecordAntibot(const void *pData, int DataSize) |
805 | { |
806 | if(m_Debug) |
807 | { |
808 | dbg_msg(sys: "teehistorian" , fmt: "antibot data_size=%d" , DataSize); |
809 | } |
810 | |
811 | WriteExtra(Uuid: UUID_TEEHISTORIAN_ANTIBOT, pData, DataSize); |
812 | } |
813 | |
814 | void CTeeHistorian::RecordPlayerFinish(int ClientId, int TimeTicks) |
815 | { |
816 | CPacker Buffer; |
817 | Buffer.Reset(); |
818 | Buffer.AddInt(i: ClientId); |
819 | Buffer.AddInt(i: TimeTicks); |
820 | if(m_Debug) |
821 | { |
822 | dbg_msg(sys: "teehistorian" , fmt: "player_finish cid=%d time=%d" , ClientId, TimeTicks); |
823 | } |
824 | |
825 | WriteExtra(Uuid: UUID_TEEHISTORIAN_PLAYER_FINISH, pData: Buffer.Data(), DataSize: Buffer.Size()); |
826 | } |
827 | |
828 | void CTeeHistorian::RecordTeamFinish(int TeamId, int TimeTicks) |
829 | { |
830 | CPacker Buffer; |
831 | Buffer.Reset(); |
832 | Buffer.AddInt(i: TeamId); |
833 | Buffer.AddInt(i: TimeTicks); |
834 | if(m_Debug) |
835 | { |
836 | dbg_msg(sys: "teehistorian" , fmt: "team_finish cid=%d time=%d" , TeamId, TimeTicks); |
837 | } |
838 | |
839 | WriteExtra(Uuid: UUID_TEEHISTORIAN_TEAM_FINISH, pData: Buffer.Data(), DataSize: Buffer.Size()); |
840 | } |
841 | |
842 | void CTeeHistorian::Finish() |
843 | { |
844 | dbg_assert(m_State == STATE_START || m_State == STATE_INPUTS || m_State == STATE_BEFORE_ENDTICK || m_State == STATE_BEFORE_TICK, "invalid teehistorian state" ); |
845 | |
846 | if(m_State == STATE_INPUTS) |
847 | { |
848 | EndInputs(); |
849 | } |
850 | if(m_State == STATE_BEFORE_ENDTICK) |
851 | { |
852 | EndTick(); |
853 | } |
854 | |
855 | CPacker Buffer; |
856 | Buffer.Reset(); |
857 | Buffer.AddInt(i: -TEEHISTORIAN_FINISH); |
858 | |
859 | if(m_Debug) |
860 | { |
861 | dbg_msg(sys: "teehistorian" , fmt: "finish" ); |
862 | } |
863 | |
864 | Write(pData: Buffer.Data(), DataSize: Buffer.Size()); |
865 | } |
866 | |