| 1 | #include <base/detect.h> |
| 2 | #include <base/io.h> |
| 3 | #include <base/time.h> |
| 4 | |
| 5 | #include <engine/external/json-parser/json.h> |
| 6 | #include <engine/server.h> |
| 7 | #include <engine/shared/config.h> |
| 8 | |
| 9 | #include <game/gamecore.h> |
| 10 | #include <game/server/teehistorian.h> |
| 11 | |
| 12 | #include <gtest/gtest.h> |
| 13 | |
| 14 | #include <string> |
| 15 | #include <vector> |
| 16 | |
| 17 | void RegisterGameUuids(CUuidManager *pManager); |
| 18 | |
| 19 | class TeeHistorian : public ::testing::Test |
| 20 | { |
| 21 | protected: |
| 22 | CTeeHistorian m_TH; |
| 23 | CConfig m_Config; |
| 24 | CTuningParams m_Tuning; |
| 25 | CUuidManager m_UuidManager; |
| 26 | CTeeHistorian::CGameInfo m_GameInfo; |
| 27 | |
| 28 | std::vector<unsigned char> m_vBuffer; |
| 29 | |
| 30 | enum |
| 31 | { |
| 32 | STATE_NONE, |
| 33 | STATE_PLAYERS, |
| 34 | STATE_INPUTS, |
| 35 | }; |
| 36 | |
| 37 | int m_State; |
| 38 | |
| 39 | TeeHistorian() |
| 40 | { |
| 41 | mem_zero(block: &m_Config, size: sizeof(m_Config)); |
| 42 | #define MACRO_CONFIG_INT(Name, ScriptName, Def, Min, Max, Save, Desc) \ |
| 43 | m_Config.m_##Name = (Def); |
| 44 | #define MACRO_CONFIG_COL(Name, ScriptName, Def, Save, Desc) MACRO_CONFIG_INT(Name, ScriptName, Def, 0, 0, Save, Desc) |
| 45 | #define MACRO_CONFIG_STR(Name, ScriptName, Len, Def, Save, Desc) \ |
| 46 | str_copy(m_Config.m_##Name, (Def), sizeof(m_Config.m_##Name)); |
| 47 | #include <engine/shared/config_variables.h> |
| 48 | #undef MACRO_CONFIG_STR |
| 49 | #undef MACRO_CONFIG_COL |
| 50 | #undef MACRO_CONFIG_INT |
| 51 | |
| 52 | RegisterUuids(pManager: &m_UuidManager); |
| 53 | RegisterTeehistorianUuids(pManager: &m_UuidManager); |
| 54 | RegisterGameUuids(pManager: &m_UuidManager); |
| 55 | |
| 56 | SHA256_DIGEST Sha256 = {.data: {0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, 0x67, 0x89, |
| 57 | 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, 0x67, 0x89, |
| 58 | 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, 0x67, 0x89, |
| 59 | 0x01, 0x23}}; |
| 60 | |
| 61 | mem_zero(block: &m_GameInfo, size: sizeof(m_GameInfo)); |
| 62 | |
| 63 | m_GameInfo.m_GameUuid = CalculateUuid(pName: "test@ddnet.tw" ); |
| 64 | m_GameInfo.m_pServerVersion = "DDNet test" ; |
| 65 | m_GameInfo.m_StartTime = time(timer: nullptr); |
| 66 | m_GameInfo.m_pPrngDescription = "test-prng:02468ace" ; |
| 67 | |
| 68 | m_GameInfo.m_pServerName = "server name" ; |
| 69 | m_GameInfo.m_ServerPort = 8303; |
| 70 | m_GameInfo.m_pGameType = "game type" ; |
| 71 | |
| 72 | m_GameInfo.m_pMapName = "Kobra 3 Solo" ; |
| 73 | m_GameInfo.m_MapSize = 903514; |
| 74 | m_GameInfo.m_MapSha256 = Sha256; |
| 75 | m_GameInfo.m_MapCrc = 0xeceaf25c; |
| 76 | |
| 77 | m_GameInfo.m_HavePrevGameUuid = false; |
| 78 | mem_zero(block: &m_GameInfo.m_PrevGameUuid, size: sizeof(m_GameInfo.m_PrevGameUuid)); |
| 79 | |
| 80 | m_GameInfo.m_pConfig = &m_Config; |
| 81 | m_GameInfo.m_pTuning = &m_Tuning; |
| 82 | m_GameInfo.m_pUuids = &m_UuidManager; |
| 83 | |
| 84 | Reset(pGameInfo: &m_GameInfo); |
| 85 | } |
| 86 | |
| 87 | static void WriteBuffer(std::vector<unsigned char> &vBuffer, const void *pData, size_t DataSize) |
| 88 | { |
| 89 | if(DataSize <= 0) |
| 90 | return; |
| 91 | |
| 92 | const size_t OldSize = vBuffer.size(); |
| 93 | vBuffer.resize(sz: OldSize + DataSize); |
| 94 | mem_copy(dest: &vBuffer[OldSize], source: pData, size: DataSize); |
| 95 | } |
| 96 | |
| 97 | static void Write(const void *pData, int DataSize, void *pUser) |
| 98 | { |
| 99 | TeeHistorian *pThis = (TeeHistorian *)pUser; |
| 100 | WriteBuffer(vBuffer&: pThis->m_vBuffer, pData, DataSize); |
| 101 | } |
| 102 | |
| 103 | void Reset(const CTeeHistorian::CGameInfo *pGameInfo) |
| 104 | { |
| 105 | m_vBuffer.clear(); |
| 106 | m_TH.Reset(pGameInfo, pfnWriteCallback: Write, pUser: this); |
| 107 | m_State = STATE_NONE; |
| 108 | } |
| 109 | |
| 110 | void Expect(const unsigned char *pOutput, size_t OutputSize) |
| 111 | { |
| 112 | static CUuid TEEHISTORIAN_UUID = CalculateUuid(pName: "teehistorian@ddnet.tw" ); |
| 113 | static const char PREFIX1[] = "{\"comment\":\"teehistorian@ddnet.tw\",\"version\":\"2\",\"version_minor\":\"20\",\"game_uuid\":\"a1eb7182-796e-3b3e-941d-38ca71b2a4a8\",\"server_version\":\"DDNet test\",\"start_time\":\"" ; |
| 114 | static const char PREFIX2[] = "\",\"server_name\":\"server name\",\"server_port\":\"8303\",\"game_type\":\"game type\",\"map_name\":\"Kobra 3 Solo\",\"map_size\":\"903514\",\"map_sha256\":\"0123456789012345678901234567890123456789012345678901234567890123\",\"map_crc\":\"eceaf25c\",\"prng_description\":\"test-prng:02468ace\",\"config\":{},\"tuning\":{},\"uuids\":[" ; |
| 115 | static const char PREFIX3[] = "]}" ; |
| 116 | |
| 117 | char aTimeBuf[64]; |
| 118 | str_timestamp_ex(time: m_GameInfo.m_StartTime, buffer: aTimeBuf, buffer_size: sizeof(aTimeBuf), format: "%Y-%m-%dT%H:%M:%S%z" ); |
| 119 | |
| 120 | std::vector<unsigned char> vBuffer; |
| 121 | WriteBuffer(vBuffer, pData: &TEEHISTORIAN_UUID, DataSize: sizeof(TEEHISTORIAN_UUID)); |
| 122 | WriteBuffer(vBuffer, pData: PREFIX1, DataSize: str_length(str: PREFIX1)); |
| 123 | WriteBuffer(vBuffer, pData: aTimeBuf, DataSize: str_length(str: aTimeBuf)); |
| 124 | WriteBuffer(vBuffer, pData: PREFIX2, DataSize: str_length(str: PREFIX2)); |
| 125 | for(int i = 0; i < m_UuidManager.NumUuids(); i++) |
| 126 | { |
| 127 | char aBuf[64]; |
| 128 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s\"%s\"" , |
| 129 | i == 0 ? "" : "," , |
| 130 | m_UuidManager.GetName(Id: OFFSET_UUID + i)); |
| 131 | WriteBuffer(vBuffer, pData: aBuf, DataSize: str_length(str: aBuf)); |
| 132 | } |
| 133 | WriteBuffer(vBuffer, pData: PREFIX3, DataSize: str_length(str: PREFIX3)); |
| 134 | WriteBuffer(vBuffer, pData: "" , DataSize: 1); |
| 135 | WriteBuffer(vBuffer, pData: pOutput, DataSize: OutputSize); |
| 136 | |
| 137 | ExpectFull(pOutput: vBuffer.data(), OutputSize: vBuffer.size()); |
| 138 | } |
| 139 | |
| 140 | void ExpectFull(const unsigned char *pOutput, size_t OutputSize) |
| 141 | { |
| 142 | const ::testing::TestInfo *pTestInfo = |
| 143 | ::testing::UnitTest::GetInstance()->current_test_info(); |
| 144 | const char *pTestName = pTestInfo->name(); |
| 145 | |
| 146 | if(m_vBuffer.size() != OutputSize || mem_comp(a: m_vBuffer.data(), b: pOutput, size: OutputSize) != 0) |
| 147 | { |
| 148 | char aFilename[IO_MAX_PATH_LENGTH]; |
| 149 | IOHANDLE File; |
| 150 | |
| 151 | str_format(buffer: aFilename, buffer_size: sizeof(aFilename), format: "%sGot.teehistorian" , pTestName); |
| 152 | File = io_open(filename: aFilename, flags: IOFLAG_WRITE); |
| 153 | ASSERT_TRUE(File); |
| 154 | io_write(io: File, buffer: m_vBuffer.data(), size: m_vBuffer.size()); |
| 155 | io_close(io: File); |
| 156 | |
| 157 | str_format(buffer: aFilename, buffer_size: sizeof(aFilename), format: "%sExpected.teehistorian" , pTestName); |
| 158 | File = io_open(filename: aFilename, flags: IOFLAG_WRITE); |
| 159 | ASSERT_TRUE(File); |
| 160 | io_write(io: File, buffer: pOutput, size: OutputSize); |
| 161 | io_close(io: File); |
| 162 | } |
| 163 | |
| 164 | // skip over header |
| 165 | size_t StartActual = 0; |
| 166 | for(size_t i = 0; i < m_vBuffer.size(); i++) |
| 167 | { |
| 168 | if(m_vBuffer[i] == 0) |
| 169 | { |
| 170 | StartActual = i + 1; |
| 171 | break; |
| 172 | } |
| 173 | } |
| 174 | size_t StartExpected = 0; |
| 175 | for(size_t i = 0; i < OutputSize; i++) |
| 176 | { |
| 177 | if(pOutput[i] == 0) |
| 178 | { |
| 179 | StartExpected = i + 1; |
| 180 | break; |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | std::string OutputActualHex; |
| 185 | if(StartActual < m_vBuffer.size()) |
| 186 | { |
| 187 | const size_t DataSize = m_vBuffer.size() - StartActual; |
| 188 | OutputActualHex.resize(n: 6 * DataSize + 1); |
| 189 | str_hex_cstyle(dst: OutputActualHex.data(), dst_size: OutputActualHex.length(), data: &m_vBuffer[StartActual], data_size: DataSize, bytes_per_line: 10); |
| 190 | } |
| 191 | std::string OutputExpectedHex; |
| 192 | if(StartExpected < OutputSize) |
| 193 | { |
| 194 | const size_t DataSize = OutputSize - StartExpected; |
| 195 | OutputExpectedHex.resize(n: 6 * DataSize + 1); |
| 196 | str_hex_cstyle(dst: OutputExpectedHex.data(), dst_size: OutputExpectedHex.length(), data: &pOutput[StartExpected], data_size: DataSize, bytes_per_line: 10); |
| 197 | } |
| 198 | |
| 199 | ASSERT_EQ(StartActual, StartExpected) << "Header size mismatch. Actual " << StartActual << ", expected " << StartExpected << "." ; |
| 200 | ASSERT_TRUE(mem_comp(m_vBuffer.data(), pOutput, StartExpected) == 0) << "Header mismatch. Check full output in .teehistorian files." ; |
| 201 | ASSERT_EQ(m_vBuffer.size(), OutputSize) << "Output size mismatch. Actual " << m_vBuffer.size() << ", expected " << OutputSize << "." ; |
| 202 | ASSERT_TRUE(mem_comp(m_vBuffer.data(), pOutput, OutputSize) == 0) << "Output mismatch. Actual m_vBuffer = {\n" |
| 203 | << OutputActualHex.c_str() << "\n}\nExpected pOutput = {\n" |
| 204 | << OutputExpectedHex.c_str() << "\n}" ; |
| 205 | } |
| 206 | |
| 207 | void Tick(int Tick) |
| 208 | { |
| 209 | if(m_State == STATE_PLAYERS) |
| 210 | { |
| 211 | Inputs(); |
| 212 | } |
| 213 | if(m_State == STATE_INPUTS) |
| 214 | { |
| 215 | m_TH.EndInputs(); |
| 216 | m_TH.EndTick(); |
| 217 | } |
| 218 | m_TH.BeginTick(Tick); |
| 219 | m_TH.BeginPlayers(); |
| 220 | m_State = STATE_PLAYERS; |
| 221 | } |
| 222 | void Inputs() |
| 223 | { |
| 224 | m_TH.EndPlayers(); |
| 225 | m_TH.BeginInputs(); |
| 226 | m_State = STATE_INPUTS; |
| 227 | } |
| 228 | void Finish() |
| 229 | { |
| 230 | if(m_State == STATE_PLAYERS) |
| 231 | { |
| 232 | Inputs(); |
| 233 | } |
| 234 | if(m_State == STATE_INPUTS) |
| 235 | { |
| 236 | m_TH.EndInputs(); |
| 237 | m_TH.EndTick(); |
| 238 | } |
| 239 | m_TH.Finish(); |
| 240 | } |
| 241 | void DeadPlayer(int ClientId) |
| 242 | { |
| 243 | m_TH.RecordDeadPlayer(ClientId); |
| 244 | } |
| 245 | void Player(int ClientId, int x, int y) |
| 246 | { |
| 247 | CNetObj_CharacterCore Char; |
| 248 | mem_zero(block: &Char, size: sizeof(Char)); |
| 249 | Char.m_X = x; |
| 250 | Char.m_Y = y; |
| 251 | m_TH.RecordPlayer(ClientId, pChar: &Char); |
| 252 | } |
| 253 | }; |
| 254 | |
| 255 | TEST_F(TeeHistorian, Empty) |
| 256 | { |
| 257 | Expect(pOutput: (const unsigned char *)"" , OutputSize: 0); |
| 258 | } |
| 259 | |
| 260 | TEST_F(TeeHistorian, Finished) |
| 261 | { |
| 262 | const unsigned char EXPECTED[] = {0x40}; // FINISH |
| 263 | m_TH.Finish(); |
| 264 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 265 | } |
| 266 | |
| 267 | TEST_F(TeeHistorian, TickImplicitOneTick) |
| 268 | { |
| 269 | const unsigned char EXPECTED[] = { |
| 270 | 0x42, 0x00, 0x01, 0x02, // PLAYERNEW cid=0 x=1 y=2 |
| 271 | 0x40, // FINISH |
| 272 | }; |
| 273 | Tick(Tick: 1); |
| 274 | Player(ClientId: 0, x: 1, y: 2); |
| 275 | Finish(); |
| 276 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 277 | } |
| 278 | |
| 279 | TEST_F(TeeHistorian, TickImplicitTwoTicks) |
| 280 | { |
| 281 | const unsigned char EXPECTED[] = { |
| 282 | 0x42, 0x00, 0x01, 0x02, // PLAYER_NEW cid=0 x=1 y=2 |
| 283 | 0x00, 0x01, 0x40, // PLAYER cid=0 dx=1 dy=-1 |
| 284 | 0x40, // FINISH |
| 285 | }; |
| 286 | Tick(Tick: 1); |
| 287 | Player(ClientId: 0, x: 1, y: 2); |
| 288 | Tick(Tick: 2); |
| 289 | Player(ClientId: 0, x: 2, y: 1); |
| 290 | Finish(); |
| 291 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 292 | } |
| 293 | |
| 294 | TEST_F(TeeHistorian, TickImplicitDescendingClientId) |
| 295 | { |
| 296 | const unsigned char EXPECTED[] = { |
| 297 | 0x42, 0x01, 0x02, 0x03, // PLAYER_NEW cid=1 x=2 y=3 |
| 298 | 0x42, 0x00, 0x04, 0x05, // PLAYER_NEW cid=0 x=4 y=5 |
| 299 | 0x40, // FINISH |
| 300 | }; |
| 301 | Tick(Tick: 1); |
| 302 | DeadPlayer(ClientId: 0); |
| 303 | Player(ClientId: 1, x: 2, y: 3); |
| 304 | Tick(Tick: 2); |
| 305 | Player(ClientId: 0, x: 4, y: 5); |
| 306 | Player(ClientId: 1, x: 2, y: 3); |
| 307 | Finish(); |
| 308 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 309 | } |
| 310 | |
| 311 | TEST_F(TeeHistorian, TickExplicitAscendingClientId) |
| 312 | { |
| 313 | const unsigned char EXPECTED[] = { |
| 314 | 0x42, 0x00, 0x04, 0x05, // PLAYER_NEW cid=0 x=4 y=5 |
| 315 | 0x41, 0x00, // TICK_SKIP dt=0 |
| 316 | 0x42, 0x01, 0x02, 0x03, // PLAYER_NEW cid=1 x=2 y=3 |
| 317 | 0x40, // FINISH |
| 318 | }; |
| 319 | Tick(Tick: 1); |
| 320 | Player(ClientId: 0, x: 4, y: 5); |
| 321 | DeadPlayer(ClientId: 1); |
| 322 | Tick(Tick: 2); |
| 323 | Player(ClientId: 0, x: 4, y: 5); |
| 324 | Player(ClientId: 1, x: 2, y: 3); |
| 325 | Finish(); |
| 326 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 327 | } |
| 328 | |
| 329 | TEST_F(TeeHistorian, TickImplicitEmpty) |
| 330 | { |
| 331 | const unsigned char EXPECTED[] = { |
| 332 | 0x40, // FINISH |
| 333 | }; |
| 334 | for(int i = 1; i < 500; i++) |
| 335 | { |
| 336 | Tick(Tick: i); |
| 337 | } |
| 338 | for(int i = 1000; i < 100000; i += 1000) |
| 339 | { |
| 340 | Tick(Tick: i); |
| 341 | } |
| 342 | Finish(); |
| 343 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 344 | } |
| 345 | |
| 346 | TEST_F(TeeHistorian, TickExplicitStart) |
| 347 | { |
| 348 | const unsigned char EXPECTED[] = { |
| 349 | 0x41, 0xb3, 0x07, // TICK_SKIP dt=499 |
| 350 | 0x42, 0x00, 0x40, 0x40, // PLAYER_NEW cid=0 x=-1 y=-1 |
| 351 | 0x40, // FINISH |
| 352 | }; |
| 353 | Tick(Tick: 500); |
| 354 | Player(ClientId: 0, x: -1, y: -1); |
| 355 | Finish(); |
| 356 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 357 | } |
| 358 | |
| 359 | TEST_F(TeeHistorian, TickExplicitPlayerMessage) |
| 360 | { |
| 361 | const unsigned char EXPECTED[] = { |
| 362 | 0x41, 0x00, // TICK_SKIP dt=0 |
| 363 | 0x46, 0x3f, 0x01, 0x00, // MESSAGE cid=63 msg="\0" |
| 364 | 0x40, // FINISH |
| 365 | }; |
| 366 | Tick(Tick: 1); |
| 367 | Inputs(); |
| 368 | m_TH.RecordPlayerMessage(ClientId: 63, pMsg: "" , MsgSize: 1); |
| 369 | Finish(); |
| 370 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 371 | } |
| 372 | |
| 373 | TEST_F(TeeHistorian, ExtraMessage) |
| 374 | { |
| 375 | const unsigned char EXPECTED[] = { |
| 376 | 0x41, 0x00, // TICK_SKIP dt=0 |
| 377 | // EX uuid=6bb8ba88-0f0b-382e-8dae-dbf4052b8b7d data_len=0 |
| 378 | 0x4a, |
| 379 | 0x6b, 0xb8, 0xba, 0x88, 0x0f, 0x0b, 0x38, 0x2e, |
| 380 | 0x8d, 0xae, 0xdb, 0xf4, 0x05, 0x2b, 0x8b, 0x7d, |
| 381 | 0x00, |
| 382 | 0x40, // FINISH |
| 383 | }; |
| 384 | Tick(Tick: 1); |
| 385 | Inputs(); |
| 386 | m_TH.RecordTestExtra(); |
| 387 | Finish(); |
| 388 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 389 | } |
| 390 | |
| 391 | TEST_F(TeeHistorian, DDNetVersion) |
| 392 | { |
| 393 | const unsigned char EXPECTED[] = { |
| 394 | // EX uuid=60daba5c-52c4-3aeb-b8ba-b2953fb55a17 data_len=50 |
| 395 | 0x4a, |
| 396 | 0x13, 0x97, 0xb6, 0x3e, 0xee, 0x4e, 0x39, 0x19, |
| 397 | 0xb8, 0x6a, 0xb0, 0x58, 0x88, 0x7f, 0xca, 0xf5, |
| 398 | 0x32, |
| 399 | // (DDNETVER) cid=0 connection_id=fb13a576-d35f-4893-b815-eedc6d98015b |
| 400 | // ddnet_version=13010 ddnet_version_str=DDNet 13.1 (3623f5e4cd184556) |
| 401 | 0x00, |
| 402 | 0xfb, 0x13, 0xa5, 0x76, 0xd3, 0x5f, 0x48, 0x93, |
| 403 | 0xb8, 0x15, 0xee, 0xdc, 0x6d, 0x98, 0x01, 0x5b, |
| 404 | 0x92, 0xcb, 0x01, 'D', 'D', 'N', 'e', 't', |
| 405 | ' ', '1', '3', '.', '1', ' ', '(', '3', |
| 406 | '6', '2', '3', 'f', '5', 'e', '4', 'c', |
| 407 | 'd', '1', '8', '4', '5', '5', '6', ')', |
| 408 | 0x00, |
| 409 | // EX uuid=41b49541-f26f-325d-8715-9baf4b544ef9 data_len=4 |
| 410 | 0x4a, |
| 411 | 0x41, 0xb4, 0x95, 0x41, 0xf2, 0x6f, 0x32, 0x5d, |
| 412 | 0x87, 0x15, 0x9b, 0xaf, 0x4b, 0x54, 0x4e, 0xf9, |
| 413 | 0x04, |
| 414 | // (DDNETVER_OLD) cid=1 ddnet_version=13010 |
| 415 | 0x01, 0x92, 0xcb, 0x01, |
| 416 | 0x40, // FINISH |
| 417 | }; |
| 418 | CUuid ConnectionId = { |
| 419 | 0xfb, 0x13, 0xa5, 0x76, 0xd3, 0x5f, 0x48, 0x93, |
| 420 | 0xb8, 0x15, 0xee, 0xdc, 0x6d, 0x98, 0x01, 0x5b}; |
| 421 | m_TH.RecordDDNetVersion(ClientId: 0, ConnectionId, DDNetVersion: 13010, pDDNetVersionStr: "DDNet 13.1 (3623f5e4cd184556)" ); |
| 422 | m_TH.RecordDDNetVersionOld(ClientId: 1, DDNetVersion: 13010); |
| 423 | Finish(); |
| 424 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 425 | } |
| 426 | |
| 427 | TEST_F(TeeHistorian, Auth) |
| 428 | { |
| 429 | const unsigned char EXPECTED[] = { |
| 430 | // EX uuid=60daba5c-52c4-3aeb-b8ba-b2953fb55a17 data_len=16 |
| 431 | 0x4a, |
| 432 | 0x60, 0xda, 0xba, 0x5c, 0x52, 0xc4, 0x3a, 0xeb, |
| 433 | 0xb8, 0xba, 0xb2, 0x95, 0x3f, 0xb5, 0x5a, 0x17, |
| 434 | 0x10, |
| 435 | // (AUTH_INIT) cid=0 level=3 auth_name="default_admin" |
| 436 | 0x00, 0x03, 'd', 'e', 'f', 'a', 'u', 'l', |
| 437 | 't', '_', 'a', 'd', 'm', 'i', 'n', 0x00, |
| 438 | // EX uuid=37ecd3b8-9218-3bb9-a71b-a935b86f6a81 data_len=9 |
| 439 | 0x4a, |
| 440 | 0x37, 0xec, 0xd3, 0xb8, 0x92, 0x18, 0x3b, 0xb9, |
| 441 | 0xa7, 0x1b, 0xa9, 0x35, 0xb8, 0x6f, 0x6a, 0x81, |
| 442 | 0x09, |
| 443 | // (AUTH_LOGIN) cid=1 level=2 auth_name="foobar" |
| 444 | 0x01, 0x02, 'f', 'o', 'o', 'b', 'a', 'r', |
| 445 | 0x00, |
| 446 | // EX uuid=37ecd3b8-9218-3bb9-a71b-a935b86f6a81 data_len=7 |
| 447 | 0x4a, |
| 448 | 0x37, 0xec, 0xd3, 0xb8, 0x92, 0x18, 0x3b, 0xb9, |
| 449 | 0xa7, 0x1b, 0xa9, 0x35, 0xb8, 0x6f, 0x6a, 0x81, |
| 450 | 0x07, |
| 451 | // (AUTH_LOGIN) cid=1 level=2 auth_name="help" |
| 452 | 0x02, 0x01, 'h', 'e', 'l', 'p', 0x00, |
| 453 | // EX uuid=d4f5abe8-edd2-3fb9-abd8-1c8bb84f4a63 data_len=7 |
| 454 | 0x4a, |
| 455 | 0xd4, 0xf5, 0xab, 0xe8, 0xed, 0xd2, 0x3f, 0xb9, |
| 456 | 0xab, 0xd8, 0x1c, 0x8b, 0xb8, 0x4f, 0x4a, 0x63, |
| 457 | 0x01, |
| 458 | // (AUTH_LOGOUT) cid=1 |
| 459 | 0x01, |
| 460 | 0x40, // FINISH |
| 461 | }; |
| 462 | m_TH.RecordAuthInitial(ClientId: 0, Level: AUTHED_ADMIN, pAuthName: "default_admin" ); |
| 463 | m_TH.RecordAuthLogin(ClientId: 1, Level: AUTHED_MOD, pAuthName: "foobar" ); |
| 464 | m_TH.RecordAuthLogin(ClientId: 2, Level: AUTHED_HELPER, pAuthName: "help" ); |
| 465 | m_TH.RecordAuthLogout(ClientId: 1); |
| 466 | Finish(); |
| 467 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 468 | } |
| 469 | |
| 470 | TEST_F(TeeHistorian, JoinLeave) |
| 471 | { |
| 472 | const unsigned char EXPECTED[] = { |
| 473 | // EX uuid=1899a382-71e3-36da-937d-c9de6bb95b1d data_len=1 |
| 474 | 0x4a, |
| 475 | 0x18, 0x99, 0xa3, 0x82, 0x71, 0xe3, 0x36, 0xda, |
| 476 | 0x93, 0x7d, 0xc9, 0xde, 0x6b, 0xb9, 0x5b, 0x1d, |
| 477 | 0x01, |
| 478 | // (JOINVER6) cid=6 |
| 479 | 0x06, |
| 480 | // JOIN cid=7 |
| 481 | 0x47, 0x06, |
| 482 | // EX uuid=59239b05-0540-318d-bea4-9aa1e80e7d2b data_len=1 |
| 483 | 0x4a, |
| 484 | 0x59, 0x23, 0x9b, 0x05, 0x05, 0x40, 0x31, 0x8d, |
| 485 | 0xbe, 0xa4, 0x9a, 0xa1, 0xe8, 0x0e, 0x7d, 0x2b, |
| 486 | 0x01, |
| 487 | // (JOINVER7) cid=7 |
| 488 | 0x07, |
| 489 | // JOIN cid=7 |
| 490 | 0x47, 0x07, |
| 491 | // LEAVE cid=6 reason="too many pancakes" |
| 492 | 0x48, 0x06, 't', 'o', 'o', ' ', 'm', 'a', |
| 493 | 'n', 'y', ' ', 'p', 'a', 'n', 'c', 'a', |
| 494 | 'k', 'e', 's', 0x00, |
| 495 | 0x40, // FINISH |
| 496 | }; |
| 497 | m_TH.RecordPlayerJoin(ClientId: 6, Protocol: CTeeHistorian::PROTOCOL_6); |
| 498 | m_TH.RecordPlayerJoin(ClientId: 7, Protocol: CTeeHistorian::PROTOCOL_7); |
| 499 | m_TH.RecordPlayerDrop(ClientId: 6, pReason: "too many pancakes" ); |
| 500 | Finish(); |
| 501 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 502 | } |
| 503 | |
| 504 | TEST_F(TeeHistorian, Input) |
| 505 | { |
| 506 | CNetObj_PlayerInput Input = {.m_Direction: 1, .m_TargetX: 2, .m_TargetY: 3, .m_Jump: 4, .m_Fire: 5, .m_Hook: 6, .m_PlayerFlags: 7, .m_WantedWeapon: 8, .m_NextWeapon: 9, .m_PrevWeapon: 10}; |
| 507 | const unsigned char EXPECTED[] = { |
| 508 | // TICK_SKIP dt=0 |
| 509 | 0x41, 0x00, |
| 510 | // new player -> InputNew |
| 511 | 0x45, |
| 512 | 0x00, // ClientId 0 |
| 513 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, |
| 514 | // same unique id, same input -> nothing |
| 515 | // same unique id, different input -> InputDiff |
| 516 | 0x44, |
| 517 | 0x00, // ClientId 0 |
| 518 | 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 519 | // different unique id, same input -> InputNew |
| 520 | 0x45, |
| 521 | 0x00, // ClientId 0 |
| 522 | 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, |
| 523 | // FINISH |
| 524 | 0x40}; |
| 525 | |
| 526 | Tick(Tick: 1); |
| 527 | |
| 528 | // new player -> InputNew |
| 529 | m_TH.RecordPlayerInput(ClientId: 0, UniqueClientId: 1, pInput: &Input); |
| 530 | // same unique id, same input -> nothing |
| 531 | m_TH.RecordPlayerInput(ClientId: 0, UniqueClientId: 1, pInput: &Input); |
| 532 | |
| 533 | Input.m_Direction = 0; |
| 534 | |
| 535 | // same unique id, different input -> InputDiff |
| 536 | m_TH.RecordPlayerInput(ClientId: 0, UniqueClientId: 1, pInput: &Input); |
| 537 | |
| 538 | // different unique id, same input -> InputNew |
| 539 | m_TH.RecordPlayerInput(ClientId: 0, UniqueClientId: 2, pInput: &Input); |
| 540 | |
| 541 | Finish(); |
| 542 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 543 | } |
| 544 | |
| 545 | TEST_F(TeeHistorian, SaveSuccess) |
| 546 | { |
| 547 | const unsigned char EXPECTED[] = { |
| 548 | // EX uuid=4560c756-da29-3036-81d4-90a50f0182cd datalen=42 |
| 549 | 0x4a, |
| 550 | 0x45, 0x60, 0xc7, 0x56, 0xda, 0x29, 0x30, 0x36, |
| 551 | 0x81, 0xd4, 0x90, 0xa5, 0x0f, 0x01, 0x82, 0xcd, |
| 552 | 0x1a, |
| 553 | // team=21 |
| 554 | 0x15, |
| 555 | // save_id |
| 556 | 0xfb, 0x13, 0xa5, 0x76, 0xd3, 0x5f, 0x48, 0x93, |
| 557 | 0xb8, 0x15, 0xee, 0xdc, 0x6d, 0x98, 0x01, 0x5b, |
| 558 | // team_save |
| 559 | '2', '\t', 'H', '.', '\n', 'l', 'l', '0', 0x00, |
| 560 | // FINISH |
| 561 | 0x40}; |
| 562 | |
| 563 | CUuid SaveId = { |
| 564 | 0xfb, 0x13, 0xa5, 0x76, 0xd3, 0x5f, 0x48, 0x93, |
| 565 | 0xb8, 0x15, 0xee, 0xdc, 0x6d, 0x98, 0x01, 0x5b}; |
| 566 | const char *pTeamSave = "2\tH.\nll0" ; |
| 567 | m_TH.RecordTeamSaveSuccess(Team: 21, SaveId, pTeamSave); |
| 568 | Finish(); |
| 569 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 570 | } |
| 571 | |
| 572 | TEST_F(TeeHistorian, SaveFailed) |
| 573 | { |
| 574 | const unsigned char EXPECTED[] = { |
| 575 | // EX uuid=b29901d5-1244-3bd0-bbde-23d04b1f7ba9 datalen=42 |
| 576 | 0x4a, |
| 577 | 0xb2, 0x99, 0x01, 0xd5, 0x12, 0x44, 0x3b, 0xd0, |
| 578 | 0xbb, 0xde, 0x23, 0xd0, 0x4b, 0x1f, 0x7b, 0xa9, |
| 579 | 0x01, |
| 580 | // team=12 |
| 581 | 0x0c, |
| 582 | 0x40}; |
| 583 | |
| 584 | m_TH.RecordTeamSaveFailure(Team: 12); |
| 585 | Finish(); |
| 586 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 587 | } |
| 588 | |
| 589 | TEST_F(TeeHistorian, LoadSuccess) |
| 590 | { |
| 591 | const unsigned char EXPECTED[] = { |
| 592 | // EX uuid=e05408d3-a313-33df-9eb3-ddb990ab954a datalen=42 |
| 593 | 0x4a, |
| 594 | 0xe0, 0x54, 0x08, 0xd3, 0xa3, 0x13, 0x33, 0xdf, |
| 595 | 0x9e, 0xb3, 0xdd, 0xb9, 0x90, 0xab, 0x95, 0x4a, |
| 596 | 0x1a, |
| 597 | // team=21 |
| 598 | 0x15, |
| 599 | // save_id |
| 600 | 0xfb, 0x13, 0xa5, 0x76, 0xd3, 0x5f, 0x48, 0x93, |
| 601 | 0xb8, 0x15, 0xee, 0xdc, 0x6d, 0x98, 0x01, 0x5b, |
| 602 | // team_save |
| 603 | '2', '\t', 'H', '.', '\n', 'l', 'l', '0', 0x00, |
| 604 | // FINISH |
| 605 | 0x40}; |
| 606 | |
| 607 | CUuid SaveId = { |
| 608 | 0xfb, 0x13, 0xa5, 0x76, 0xd3, 0x5f, 0x48, 0x93, |
| 609 | 0xb8, 0x15, 0xee, 0xdc, 0x6d, 0x98, 0x01, 0x5b}; |
| 610 | const char *pTeamSave = "2\tH.\nll0" ; |
| 611 | m_TH.RecordTeamLoadSuccess(Team: 21, SaveId, pTeamSave); |
| 612 | Finish(); |
| 613 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 614 | } |
| 615 | |
| 616 | TEST_F(TeeHistorian, LoadFailed) |
| 617 | { |
| 618 | const unsigned char EXPECTED[] = { |
| 619 | // EX uuid=ef8905a2-c695-3591-a1cd-53d2015992dd datalen=42 |
| 620 | 0x4a, |
| 621 | 0xef, 0x89, 0x05, 0xa2, 0xc6, 0x95, 0x35, 0x91, |
| 622 | 0xa1, 0xcd, 0x53, 0xd2, 0x01, 0x59, 0x92, 0xdd, |
| 623 | 0x01, |
| 624 | // team=12 |
| 625 | 0x0c, |
| 626 | 0x40}; |
| 627 | |
| 628 | m_TH.RecordTeamLoadFailure(Team: 12); |
| 629 | Finish(); |
| 630 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 631 | } |
| 632 | |
| 633 | TEST_F(TeeHistorian, PlayerSwap) |
| 634 | { |
| 635 | const unsigned char EXPECTED[] = { |
| 636 | // TICK_SKIP dt=0 |
| 637 | 0x41, 0x00, |
| 638 | // EX uuid=5de9b633-49cf-3e99-9a25-d4a78e9717d7 datalen=2 |
| 639 | 0x4a, |
| 640 | 0x5d, 0xe9, 0xb6, 0x33, 0x49, 0xcf, 0x3e, 0x99, |
| 641 | 0x9a, 0x25, 0xd4, 0xa7, 0x8e, 0x97, 0x17, 0xd7, |
| 642 | 0x02, |
| 643 | // playerId1=11 |
| 644 | 0x0b, |
| 645 | // playerId2=21 |
| 646 | 0x15, |
| 647 | // FINISH |
| 648 | 0x40}; |
| 649 | Tick(Tick: 1); |
| 650 | m_TH.RecordPlayerSwap(ClientId1: 11, ClientId2: 21); |
| 651 | Finish(); |
| 652 | |
| 653 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 654 | } |
| 655 | |
| 656 | TEST_F(TeeHistorian, PlayerTeam) |
| 657 | { |
| 658 | const unsigned char EXPECTED[] = { |
| 659 | // TICK_SKIP dt=0 |
| 660 | 0x41, 0x00, |
| 661 | // EX uuid=a111c04e-1ea8-38e0-90b1-d7f993ca0da9 datalen=2 |
| 662 | 0x4a, |
| 663 | 0xa1, 0x11, 0xc0, 0x4e, 0x1e, 0xa8, 0x38, 0xe0, |
| 664 | 0x90, 0xb1, 0xd7, 0xf9, 0x93, 0xca, 0x0d, 0xa9, |
| 665 | 0x02, |
| 666 | // (PLAYER_TEAM) cid=33 team=54 |
| 667 | 0x21, 0x36, |
| 668 | // TICK_SKIP dt=0 |
| 669 | 0x41, 0x00, |
| 670 | // EX uuid=a111c04e-1ea8-38e0-90b1-d7f993ca0da9 datalen=2 |
| 671 | 0x4a, |
| 672 | 0xa1, 0x11, 0xc0, 0x4e, 0x1e, 0xa8, 0x38, 0xe0, |
| 673 | 0x90, 0xb1, 0xd7, 0xf9, 0x93, 0xca, 0x0d, 0xa9, |
| 674 | 0x02, |
| 675 | // (PLAYER_TEAM) cid=3 team=12 |
| 676 | 0x03, 0x0c, |
| 677 | // EX uuid=a111c04e-1ea8-38e0-90b1-d7f993ca0da9 datalen=2 |
| 678 | 0x4a, |
| 679 | 0xa1, 0x11, 0xc0, 0x4e, 0x1e, 0xa8, 0x38, 0xe0, |
| 680 | 0x90, 0xb1, 0xd7, 0xf9, 0x93, 0xca, 0x0d, 0xa9, |
| 681 | 0x02, |
| 682 | // (PLAYER_TEAM) cid=33 team=0 |
| 683 | 0x21, 0x00, |
| 684 | // FINISH |
| 685 | 0x40}; |
| 686 | |
| 687 | Tick(Tick: 1); |
| 688 | m_TH.RecordPlayerTeam(ClientId: 3, Team: 0); |
| 689 | m_TH.RecordPlayerTeam(ClientId: 33, Team: 54); |
| 690 | m_TH.RecordPlayerTeam(ClientId: 45, Team: 0); |
| 691 | Tick(Tick: 2); |
| 692 | m_TH.RecordPlayerTeam(ClientId: 3, Team: 12); |
| 693 | m_TH.RecordPlayerTeam(ClientId: 33, Team: 0); |
| 694 | m_TH.RecordPlayerTeam(ClientId: 45, Team: 0); |
| 695 | Finish(); |
| 696 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 697 | } |
| 698 | |
| 699 | TEST_F(TeeHistorian, TeamPractice) |
| 700 | { |
| 701 | const unsigned char EXPECTED[] = { |
| 702 | // TICK_SKIP dt=0 |
| 703 | 0x41, 0x00, |
| 704 | // EX uuid=5792834e-81d1-34c9-a29b-b5ff25dac3bc datalen=2 |
| 705 | 0x4a, |
| 706 | 0x57, 0x92, 0x83, 0x4e, 0x81, 0xd1, 0x34, 0xc9, |
| 707 | 0xa2, 0x9b, 0xb5, 0xff, 0x25, 0xda, 0xc3, 0xbc, |
| 708 | 0x02, |
| 709 | // (TEAM_PRACTICE) team=23 practice=1 |
| 710 | 0x17, 0x01, |
| 711 | // TICK_SKIP dt=0 |
| 712 | 0x41, 0x00, |
| 713 | // EX uuid=5792834e-81d1-34c9-a29b-b5ff25dac3bc datalen=2 |
| 714 | 0x4a, |
| 715 | 0x57, 0x92, 0x83, 0x4e, 0x81, 0xd1, 0x34, 0xc9, |
| 716 | 0xa2, 0x9b, 0xb5, 0xff, 0x25, 0xda, 0xc3, 0xbc, |
| 717 | 0x02, |
| 718 | // (TEAM_PRACTICE) team=1 practice=1 |
| 719 | 0x01, 0x01, |
| 720 | // EX uuid=5792834e-81d1-34c9-a29b-b5ff25dac3bc datalen=2 |
| 721 | 0x4a, |
| 722 | 0x57, 0x92, 0x83, 0x4e, 0x81, 0xd1, 0x34, 0xc9, |
| 723 | 0xa2, 0x9b, 0xb5, 0xff, 0x25, 0xda, 0xc3, 0xbc, |
| 724 | 0x02, |
| 725 | // (TEAM_PRACTICE) team=23 practice=0 |
| 726 | 0x17, 0x00, |
| 727 | // FINISH |
| 728 | 0x40}; |
| 729 | |
| 730 | Tick(Tick: 1); |
| 731 | m_TH.RecordTeamPractice(Team: 1, Practice: false); |
| 732 | m_TH.RecordTeamPractice(Team: 16, Practice: false); |
| 733 | m_TH.RecordTeamPractice(Team: 23, Practice: true); |
| 734 | Tick(Tick: 2); |
| 735 | m_TH.RecordTeamPractice(Team: 1, Practice: true); |
| 736 | m_TH.RecordTeamPractice(Team: 16, Practice: false); |
| 737 | m_TH.RecordTeamPractice(Team: 23, Practice: false); |
| 738 | Finish(); |
| 739 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 740 | } |
| 741 | |
| 742 | TEST_F(TeeHistorian, PlayerRejoinVer6) |
| 743 | { |
| 744 | const unsigned char EXPECTED[] = { |
| 745 | // EX uuid=c1e921d5-96f5-37bb-8a45-7a06f163d27e datalen=1 |
| 746 | 0x4a, |
| 747 | 0xc1, 0xe9, 0x21, 0xd5, 0x96, 0xf5, 0x37, 0xbb, |
| 748 | 0x8a, 0x45, 0x7a, 0x06, 0xf1, 0x63, 0xd2, 0x7e, |
| 749 | 0x01, |
| 750 | // (PLAYER_REJOIN) cid=2 |
| 751 | 0x02, |
| 752 | // FINISH |
| 753 | 0x40}; |
| 754 | |
| 755 | m_TH.RecordPlayerRejoin(ClientId: 2); |
| 756 | Finish(); |
| 757 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 758 | } |
| 759 | |
| 760 | TEST_F(TeeHistorian, PlayerReady) |
| 761 | { |
| 762 | const unsigned char EXPECTED[] = { |
| 763 | // EX uuid=638587c9-3f75-3887-918e-a3c2614ffaa0 datalen=1 |
| 764 | 0x4a, |
| 765 | 0x63, 0x85, 0x87, 0xc9, 0x3f, 0x75, 0x38, 0x87, |
| 766 | 0x91, 0x8e, 0xa3, 0xc2, 0x61, 0x4f, 0xfa, 0xa0, |
| 767 | 0x01, |
| 768 | // (PLAYER_READY) cid=63 |
| 769 | 0x3f, |
| 770 | // FINISH |
| 771 | 0x40}; |
| 772 | |
| 773 | m_TH.RecordPlayerReady(ClientId: 63); |
| 774 | Finish(); |
| 775 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 776 | } |
| 777 | |
| 778 | TEST_F(TeeHistorian, PlayerReadyMultiple) |
| 779 | { |
| 780 | const unsigned char EXPECTED[] = { |
| 781 | // TICK_SKIP dt=0 |
| 782 | 0x41, 0x00, |
| 783 | // EX uuid=638587c9-3f75-3887-918e-a3c2614ffaa0 datalen=1 |
| 784 | 0x4a, |
| 785 | 0x63, 0x85, 0x87, 0xc9, 0x3f, 0x75, 0x38, 0x87, |
| 786 | 0x91, 0x8e, 0xa3, 0xc2, 0x61, 0x4f, 0xfa, 0xa0, |
| 787 | 0x01, |
| 788 | // (PLAYER_READY) cid=0 |
| 789 | 0x00, |
| 790 | // EX uuid=638587c9-3f75-3887-918e-a3c2614ffaa0 datalen=1 |
| 791 | 0x4a, |
| 792 | 0x63, 0x85, 0x87, 0xc9, 0x3f, 0x75, 0x38, 0x87, |
| 793 | 0x91, 0x8e, 0xa3, 0xc2, 0x61, 0x4f, 0xfa, 0xa0, |
| 794 | 0x01, |
| 795 | // (PLAYER_READY) cid=11 |
| 796 | 0x0b, |
| 797 | // TICK_SKIP dt=0 |
| 798 | 0x41, 0x00, |
| 799 | // EX uuid=638587c9-3f75-3887-918e-a3c2614ffaa0 datalen=1 |
| 800 | 0x4a, |
| 801 | 0x63, 0x85, 0x87, 0xc9, 0x3f, 0x75, 0x38, 0x87, |
| 802 | 0x91, 0x8e, 0xa3, 0xc2, 0x61, 0x4f, 0xfa, 0xa0, |
| 803 | 0x01, |
| 804 | // (PLAYER_READY) cid=63 |
| 805 | 0x3f, |
| 806 | 0x40}; |
| 807 | |
| 808 | Tick(Tick: 1); |
| 809 | m_TH.RecordPlayerReady(ClientId: 0); |
| 810 | m_TH.RecordPlayerReady(ClientId: 11); |
| 811 | Tick(Tick: 2); |
| 812 | m_TH.RecordPlayerReady(ClientId: 63); |
| 813 | Finish(); |
| 814 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 815 | } |
| 816 | |
| 817 | TEST_F(TeeHistorian, AntibotEmpty) |
| 818 | { |
| 819 | const unsigned char EXPECTED[] = { |
| 820 | // EX uuid=866bfdac-fb49-3c0b-a887-5fe1f3ea00b8 datalen=0 |
| 821 | 0x4a, |
| 822 | 0x86, 0x6b, 0xfd, 0xac, 0xfb, 0x49, 0x3c, 0x0b, |
| 823 | 0xa8, 0x87, 0x5f, 0xe1, 0xf3, 0xea, 0x00, 0xb8, |
| 824 | 0x00, |
| 825 | // (ANTIBOT) antibot_data |
| 826 | }; |
| 827 | |
| 828 | m_TH.RecordAntibot(pData: "" , DataSize: 0); |
| 829 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 830 | } |
| 831 | |
| 832 | TEST_F(TeeHistorian, AntibotEmptyNulBytes) |
| 833 | { |
| 834 | const unsigned char EXPECTED[] = { |
| 835 | // EX uuid=866bfdac-fb49-3c0b-a887-5fe1f3ea00b8 datalen=4 |
| 836 | 0x4a, |
| 837 | 0x86, 0x6b, 0xfd, 0xac, 0xfb, 0x49, 0x3c, 0x0b, |
| 838 | 0xa8, 0x87, 0x5f, 0xe1, 0xf3, 0xea, 0x00, 0xb8, |
| 839 | 0x04, |
| 840 | // (ANTIBOT) antibot_data |
| 841 | 0x00, |
| 842 | 0x00, |
| 843 | 0x00, |
| 844 | 0x00}; |
| 845 | |
| 846 | m_TH.RecordAntibot(pData: "\0\0\0\0" , DataSize: 4); |
| 847 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 848 | } |
| 849 | |
| 850 | TEST_F(TeeHistorian, AntibotEmptyMessage) |
| 851 | { |
| 852 | const unsigned char EXPECTED[] = { |
| 853 | // EX uuid=866bfdac-fb49-3c0b-a887-5fe1f3ea00b8 datalen=4 |
| 854 | 0x4a, |
| 855 | 0x86, 0x6b, 0xfd, 0xac, 0xfb, 0x49, 0x3c, 0x0b, |
| 856 | 0xa8, 0x87, 0x5f, 0xe1, 0xf3, 0xea, 0x00, 0xb8, |
| 857 | 0x04, |
| 858 | // (ANTIBOT) antibot_data |
| 859 | 0xf0, |
| 860 | 0x9f, |
| 861 | 0xa4, |
| 862 | 0x96}; |
| 863 | |
| 864 | m_TH.RecordAntibot(pData: "🤖" , DataSize: 4); |
| 865 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 866 | } |
| 867 | |
| 868 | TEST_F(TeeHistorian, PlayerName) |
| 869 | { |
| 870 | const unsigned char EXPECTED[] = { |
| 871 | // EX uuid=d016f9b9-4151-3b87-87e5-3a6087eb5f26 datalen=14 |
| 872 | 0x4a, |
| 873 | 0xd0, 0x16, 0xf9, 0xb9, 0x41, 0x51, 0x3b, 0x87, |
| 874 | 0x87, 0xe5, 0x3a, 0x60, 0x87, 0xeb, 0x5f, 0x26, |
| 875 | 0x0e, |
| 876 | // (PLAYER_NAME) id=21 name="nameless tee" |
| 877 | 0x15, |
| 878 | 0x6e, 0x61, 0x6d, 0x65, 0x6c, 0x65, 0x73, 0x73, |
| 879 | 0x20, 0x74, 0x65, 0x65, 0x00}; |
| 880 | |
| 881 | m_TH.RecordPlayerName(ClientId: 21, pName: "nameless tee" ); |
| 882 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 883 | } |
| 884 | |
| 885 | TEST_F(TeeHistorian, PlayerFinish) |
| 886 | { |
| 887 | const unsigned char EXPECTED[] = { |
| 888 | // EX uuid=68943c01-2348-3e01-9490-3f27f8269d94 datalen=4 |
| 889 | 0x4a, |
| 890 | 0x68, 0x94, 0x3c, 0x01, 0x23, 0x48, 0x3e, 0x01, |
| 891 | 0x94, 0x90, 0x3f, 0x27, 0xf8, 0x26, 0x9d, 0x94, |
| 892 | 0x04, |
| 893 | // (PLAYER_FINISH) id=1 time=1000000 |
| 894 | 0x01, 0x80, 0x89, 0x7a}; |
| 895 | |
| 896 | m_TH.RecordPlayerFinish(ClientId: 1, TimeTicks: 1000000); |
| 897 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 898 | } |
| 899 | |
| 900 | TEST_F(TeeHistorian, TeamFinish) |
| 901 | { |
| 902 | const unsigned char EXPECTED[] = { |
| 903 | // EX uuid=9588b9af-3fdc-3760-8043-82deeee317a5 datalen=3 |
| 904 | 0x4a, |
| 905 | 0x95, 0x88, 0xb9, 0xaf, 0x3f, 0xdc, 0x37, 0x60, |
| 906 | 0x80, 0x43, 0x82, 0xde, 0xee, 0xe3, 0x17, 0xa5, |
| 907 | 0x03, |
| 908 | // (TEAM_FINISH) team=63 Time=1000 |
| 909 | 0x3f, 0xa8, 0x0f}; |
| 910 | |
| 911 | m_TH.RecordTeamFinish(TeamId: 63, TimeTicks: 1000); |
| 912 | Expect(pOutput: EXPECTED, OutputSize: sizeof(EXPECTED)); |
| 913 | } |
| 914 | |
| 915 | TEST_F(TeeHistorian, PrevGameUuid) |
| 916 | { |
| 917 | m_GameInfo.m_HavePrevGameUuid = true; |
| 918 | CUuid PrevGameUuid = {.m_aData: { |
| 919 | // fe19c218-f555-4002-a273-126c59ccc17a |
| 920 | 0xfe, 0x19, 0xc2, 0x18, 0xf5, 0x55, 0x40, 0x02, |
| 921 | 0xa2, 0x73, 0x12, 0x6c, 0x59, 0xcc, 0xc1, 0x7a, |
| 922 | // |
| 923 | }}; |
| 924 | m_GameInfo.m_PrevGameUuid = PrevGameUuid; |
| 925 | Reset(pGameInfo: &m_GameInfo); |
| 926 | Finish(); |
| 927 | json_value *pJson = json_parse(json: (const char *)m_vBuffer.data() + 16, length: -1); |
| 928 | ASSERT_TRUE(pJson); |
| 929 | const json_value &JsonPrevGameUuid = (*pJson)["prev_game_uuid" ]; |
| 930 | ASSERT_EQ(JsonPrevGameUuid.type, json_string); |
| 931 | EXPECT_STREQ(JsonPrevGameUuid, "fe19c218-f555-4002-a273-126c59ccc17a" ); |
| 932 | json_value_free(pJson); |
| 933 | } |
| 934 | |