2017-09-12 12:58:44 +00:00
|
|
|
#include "teehistorian.h"
|
|
|
|
|
2017-09-13 20:35:09 +00:00
|
|
|
#include <engine/shared/config.h>
|
2018-12-12 08:59:42 +00:00
|
|
|
#include <engine/shared/json.h>
|
2020-09-25 16:11:59 +00:00
|
|
|
#include <engine/shared/snapshot.h>
|
2017-09-13 20:35:09 +00:00
|
|
|
#include <game/gamecore.h>
|
2017-09-12 12:58:44 +00:00
|
|
|
|
2017-09-24 19:11:04 +00:00
|
|
|
static const char TEEHISTORIAN_NAME[] = "teehistorian@ddnet.tw";
|
|
|
|
static const CUuid TEEHISTORIAN_UUID = CalculateUuid(TEEHISTORIAN_NAME);
|
2017-11-18 02:08:16 +00:00
|
|
|
static const char TEEHISTORIAN_VERSION[] = "2";
|
2022-04-28 13:59:20 +00:00
|
|
|
static const char TEEHISTORIAN_VERSION_MINOR[] = "4";
|
2017-11-18 02:08:16 +00:00
|
|
|
|
2020-09-25 16:11:59 +00:00
|
|
|
#define UUID(id, name) static const CUuid UUID_##id = CalculateUuid(name);
|
2018-01-11 15:01:13 +00:00
|
|
|
#include <engine/shared/teehistorian_ex_chunks.h>
|
2017-11-18 02:08:16 +00:00
|
|
|
#undef UUID
|
2017-09-12 12:58:44 +00:00
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
TEEHISTORIAN_NONE,
|
|
|
|
TEEHISTORIAN_FINISH,
|
2017-09-28 00:42:35 +00:00
|
|
|
TEEHISTORIAN_TICK_SKIP,
|
2017-09-12 12:58:44 +00:00
|
|
|
TEEHISTORIAN_PLAYER_NEW,
|
|
|
|
TEEHISTORIAN_PLAYER_OLD,
|
|
|
|
TEEHISTORIAN_INPUT_DIFF,
|
|
|
|
TEEHISTORIAN_INPUT_NEW,
|
2017-09-13 20:35:09 +00:00
|
|
|
TEEHISTORIAN_MESSAGE,
|
|
|
|
TEEHISTORIAN_JOIN,
|
|
|
|
TEEHISTORIAN_DROP,
|
|
|
|
TEEHISTORIAN_CONSOLE_COMMAND,
|
2017-11-18 02:08:16 +00:00
|
|
|
TEEHISTORIAN_EX,
|
2017-09-12 12:58:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
CTeeHistorian::CTeeHistorian()
|
|
|
|
{
|
|
|
|
m_State = STATE_START;
|
|
|
|
m_pfnWriteCallback = 0;
|
|
|
|
m_pWriteCallbackUserdata = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::Reset(const CGameInfo *pGameInfo, WRITE_CALLBACK pfnWriteCallback, void *pUser)
|
|
|
|
{
|
|
|
|
dbg_assert(m_State == STATE_START || m_State == STATE_BEFORE_TICK, "invalid teehistorian state");
|
|
|
|
|
2017-09-18 16:26:37 +00:00
|
|
|
m_Debug = 0;
|
2017-09-12 12:58:44 +00:00
|
|
|
|
|
|
|
m_Tick = 0;
|
|
|
|
m_LastWrittenTick = 0;
|
2018-01-28 02:13:05 +00:00
|
|
|
// Tick 0 is implicit at the start, game starts as tick 1.
|
|
|
|
m_TickWritten = true;
|
2017-09-28 00:42:35 +00:00
|
|
|
m_MaxClientID = MAX_CLIENTS;
|
2021-08-14 11:40:13 +00:00
|
|
|
|
2021-01-28 21:19:16 +00:00
|
|
|
// `m_PrevMaxClientID` is initialized in `BeginPlayers`
|
2020-10-26 14:14:07 +00:00
|
|
|
for(auto &PrevPlayer : m_aPrevPlayers)
|
2017-09-12 12:58:44 +00:00
|
|
|
{
|
2020-10-26 14:14:07 +00:00
|
|
|
PrevPlayer.m_Alive = false;
|
2022-03-27 11:31:07 +00:00
|
|
|
// zero means no id
|
|
|
|
PrevPlayer.m_UniqueClientID = 0;
|
2021-09-15 15:25:37 +00:00
|
|
|
PrevPlayer.m_Team = 0;
|
|
|
|
}
|
|
|
|
for(auto &PrevTeam : m_aPrevTeams)
|
|
|
|
{
|
|
|
|
PrevTeam.m_Practice = false;
|
2017-09-12 12:58:44 +00:00
|
|
|
}
|
|
|
|
m_pfnWriteCallback = pfnWriteCallback;
|
|
|
|
m_pWriteCallbackUserdata = pUser;
|
|
|
|
|
|
|
|
WriteHeader(pGameInfo);
|
2017-09-28 00:42:35 +00:00
|
|
|
|
|
|
|
m_State = STATE_START;
|
2017-09-12 12:58:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::WriteHeader(const CGameInfo *pGameInfo)
|
|
|
|
{
|
|
|
|
Write(&TEEHISTORIAN_UUID, sizeof(TEEHISTORIAN_UUID));
|
|
|
|
|
|
|
|
char aGameUuid[UUID_MAXSTRSIZE];
|
|
|
|
char aStartTime[128];
|
2018-06-05 19:22:40 +00:00
|
|
|
char aMapSha256[SHA256_MAXSTRSIZE];
|
2017-09-12 12:58:44 +00:00
|
|
|
|
|
|
|
FormatUuid(pGameInfo->m_GameUuid, aGameUuid, sizeof(aGameUuid));
|
2018-01-11 14:55:32 +00:00
|
|
|
str_timestamp_ex(pGameInfo->m_StartTime, aStartTime, sizeof(aStartTime), "%Y-%m-%dT%H:%M:%S%z");
|
2018-06-05 19:22:40 +00:00
|
|
|
sha256_str(pGameInfo->m_MapSha256, aMapSha256, sizeof(aMapSha256));
|
2017-09-12 12:58:44 +00:00
|
|
|
|
2017-09-24 19:11:04 +00:00
|
|
|
char aCommentBuffer[128];
|
2017-09-12 12:58:44 +00:00
|
|
|
char aServerVersionBuffer[128];
|
|
|
|
char aStartTimeBuffer[128];
|
|
|
|
char aServerNameBuffer[128];
|
|
|
|
char aGameTypeBuffer[128];
|
|
|
|
char aMapNameBuffer[128];
|
2018-06-05 19:22:40 +00:00
|
|
|
char aMapSha256Buffer[256];
|
2020-05-25 13:08:24 +00:00
|
|
|
char aPrngDescription[128];
|
2017-09-12 12:58:44 +00:00
|
|
|
|
2017-09-13 20:35:09 +00:00
|
|
|
char aJson[2048];
|
2017-09-12 12:58:44 +00:00
|
|
|
|
2020-09-25 16:11:59 +00:00
|
|
|
#define E(buf, str) EscapeJson(buf, sizeof(buf), str)
|
2017-09-12 12:58:44 +00:00
|
|
|
|
2021-08-14 10:40:11 +00:00
|
|
|
str_format(aJson, sizeof(aJson),
|
|
|
|
"{"
|
|
|
|
"\"comment\":\"%s\","
|
|
|
|
"\"version\":\"%s\","
|
2021-09-15 15:25:37 +00:00
|
|
|
"\"version_minor\":\"%s\","
|
2021-08-14 10:40:11 +00:00
|
|
|
"\"game_uuid\":\"%s\","
|
|
|
|
"\"server_version\":\"%s\","
|
|
|
|
"\"start_time\":\"%s\","
|
|
|
|
"\"server_name\":\"%s\","
|
|
|
|
"\"server_port\":\"%d\","
|
|
|
|
"\"game_type\":\"%s\","
|
|
|
|
"\"map_name\":\"%s\","
|
|
|
|
"\"map_size\":\"%d\","
|
|
|
|
"\"map_sha256\":\"%s\","
|
|
|
|
"\"map_crc\":\"%08x\","
|
|
|
|
"\"prng_description\":\"%s\","
|
|
|
|
"\"config\":{",
|
2017-09-24 19:11:04 +00:00
|
|
|
E(aCommentBuffer, TEEHISTORIAN_NAME),
|
2017-09-12 12:58:44 +00:00
|
|
|
TEEHISTORIAN_VERSION,
|
2021-08-14 10:40:11 +00:00
|
|
|
TEEHISTORIAN_VERSION_MINOR,
|
2017-09-12 12:58:44 +00:00
|
|
|
aGameUuid,
|
|
|
|
E(aServerVersionBuffer, pGameInfo->m_pServerVersion),
|
|
|
|
E(aStartTimeBuffer, aStartTime),
|
|
|
|
E(aServerNameBuffer, pGameInfo->m_pServerName),
|
|
|
|
pGameInfo->m_ServerPort,
|
|
|
|
E(aGameTypeBuffer, pGameInfo->m_pGameType),
|
|
|
|
E(aMapNameBuffer, pGameInfo->m_pMapName),
|
|
|
|
pGameInfo->m_MapSize,
|
2018-06-05 19:22:40 +00:00
|
|
|
E(aMapSha256Buffer, aMapSha256),
|
2020-05-25 13:08:24 +00:00
|
|
|
pGameInfo->m_MapCrc,
|
|
|
|
E(aPrngDescription, pGameInfo->m_pPrngDescription));
|
2017-09-13 20:35:09 +00:00
|
|
|
Write(aJson, str_length(aJson));
|
|
|
|
|
|
|
|
char aBuffer1[1024];
|
|
|
|
char aBuffer2[1024];
|
|
|
|
bool First = true;
|
|
|
|
|
2020-09-25 16:11:59 +00:00
|
|
|
#define MACRO_CONFIG_INT(Name, ScriptName, Def, Min, Max, Flags, Desc) \
|
2017-09-24 19:11:04 +00:00
|
|
|
if((Flags)&CFGFLAG_SERVER && !((Flags)&CFGFLAG_NONTEEHISTORIC) && pGameInfo->m_pConfig->m_##Name != (Def)) \
|
2017-09-13 20:35:09 +00:00
|
|
|
{ \
|
|
|
|
str_format(aJson, sizeof(aJson), "%s\"%s\":\"%d\"", \
|
|
|
|
First ? "" : ",", \
|
|
|
|
E(aBuffer1, #ScriptName), \
|
|
|
|
pGameInfo->m_pConfig->m_##Name); \
|
|
|
|
Write(aJson, str_length(aJson)); \
|
|
|
|
First = false; \
|
|
|
|
}
|
|
|
|
|
2020-09-25 16:11:59 +00:00
|
|
|
#define MACRO_CONFIG_COL(Name, ScriptName, Def, Save, Desc) MACRO_CONFIG_INT(Name, ScriptName, Def, 0, 0, Save, Desc)
|
2019-04-25 11:48:53 +00:00
|
|
|
|
2020-09-25 16:11:59 +00:00
|
|
|
#define MACRO_CONFIG_STR(Name, ScriptName, Len, Def, Flags, Desc) \
|
2017-09-24 19:11:04 +00:00
|
|
|
if((Flags)&CFGFLAG_SERVER && !((Flags)&CFGFLAG_NONTEEHISTORIC) && str_comp(pGameInfo->m_pConfig->m_##Name, (Def)) != 0) \
|
2017-09-13 20:35:09 +00:00
|
|
|
{ \
|
|
|
|
str_format(aJson, sizeof(aJson), "%s\"%s\":\"%s\"", \
|
|
|
|
First ? "" : ",", \
|
|
|
|
E(aBuffer1, #ScriptName), \
|
|
|
|
E(aBuffer2, pGameInfo->m_pConfig->m_##Name)); \
|
|
|
|
Write(aJson, str_length(aJson)); \
|
|
|
|
First = false; \
|
|
|
|
}
|
|
|
|
|
2020-09-25 16:11:59 +00:00
|
|
|
#include <engine/shared/config_variables.h>
|
2017-09-13 20:35:09 +00:00
|
|
|
|
2020-09-25 16:11:59 +00:00
|
|
|
#undef MACRO_CONFIG_INT
|
|
|
|
#undef MACRO_CONFIG_COL
|
|
|
|
#undef MACRO_CONFIG_STR
|
2017-09-13 20:35:09 +00:00
|
|
|
|
|
|
|
str_format(aJson, sizeof(aJson), "},\"tuning\":{");
|
|
|
|
Write(aJson, str_length(aJson));
|
2017-09-12 12:58:44 +00:00
|
|
|
|
2017-09-13 20:35:09 +00:00
|
|
|
First = true;
|
|
|
|
|
2017-09-24 19:11:04 +00:00
|
|
|
static const float TicksPerSecond = 50.0f;
|
2020-09-25 16:11:59 +00:00
|
|
|
#define MACRO_TUNING_PARAM(Name, ScriptName, Value, Description) \
|
2017-09-24 19:11:04 +00:00
|
|
|
if(pGameInfo->m_pTuning->m_##Name.Get() != (int)((Value)*100)) \
|
2017-09-13 20:35:09 +00:00
|
|
|
{ \
|
|
|
|
str_format(aJson, sizeof(aJson), "%s\"%s\":\"%d\"", \
|
|
|
|
First ? "" : ",", \
|
|
|
|
E(aBuffer1, #ScriptName), \
|
|
|
|
pGameInfo->m_pTuning->m_##Name.Get()); \
|
|
|
|
Write(aJson, str_length(aJson)); \
|
|
|
|
First = false; \
|
|
|
|
}
|
2020-09-25 16:11:59 +00:00
|
|
|
#include <game/tuning.h>
|
|
|
|
#undef MACRO_TUNING_PARAM
|
2017-09-13 20:35:09 +00:00
|
|
|
|
2017-11-18 02:08:16 +00:00
|
|
|
str_format(aJson, sizeof(aJson), "},\"uuids\":[");
|
|
|
|
Write(aJson, str_length(aJson));
|
|
|
|
|
|
|
|
for(int i = 0; i < pGameInfo->m_pUuids->NumUuids(); i++)
|
|
|
|
{
|
|
|
|
str_format(aJson, sizeof(aJson), "%s\"%s\"",
|
|
|
|
i == 0 ? "" : ",",
|
|
|
|
E(aBuffer1, pGameInfo->m_pUuids->GetName(OFFSET_UUID + i)));
|
|
|
|
Write(aJson, str_length(aJson));
|
|
|
|
}
|
|
|
|
|
|
|
|
str_format(aJson, sizeof(aJson), "]}");
|
2017-09-13 20:35:09 +00:00
|
|
|
Write(aJson, str_length(aJson));
|
|
|
|
Write("", 1); // Null termination.
|
2017-09-12 12:58:44 +00:00
|
|
|
}
|
|
|
|
|
2017-11-18 02:08:16 +00:00
|
|
|
void CTeeHistorian::WriteExtra(CUuid Uuid, const void *pData, int DataSize)
|
|
|
|
{
|
|
|
|
EnsureTickWritten();
|
|
|
|
|
|
|
|
CPacker Ex;
|
|
|
|
Ex.Reset();
|
|
|
|
Ex.AddInt(-TEEHISTORIAN_EX);
|
|
|
|
Ex.AddRaw(&Uuid, sizeof(Uuid));
|
|
|
|
Ex.AddInt(DataSize);
|
|
|
|
Write(Ex.Data(), Ex.Size());
|
|
|
|
Write(pData, DataSize);
|
|
|
|
}
|
|
|
|
|
2017-09-12 12:58:44 +00:00
|
|
|
void CTeeHistorian::BeginTick(int Tick)
|
|
|
|
{
|
|
|
|
dbg_assert(m_State == STATE_START || m_State == STATE_BEFORE_TICK, "invalid teehistorian state");
|
|
|
|
|
|
|
|
m_Tick = Tick;
|
|
|
|
m_TickWritten = false;
|
|
|
|
|
2017-09-18 16:26:37 +00:00
|
|
|
if(m_Debug > 1)
|
2017-09-12 12:58:44 +00:00
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "tick %d", Tick);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_State = STATE_BEFORE_PLAYERS;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::BeginPlayers()
|
|
|
|
{
|
|
|
|
dbg_assert(m_State == STATE_BEFORE_PLAYERS, "invalid teehistorian state");
|
|
|
|
|
|
|
|
m_PrevMaxClientID = m_MaxClientID;
|
2021-01-28 21:19:16 +00:00
|
|
|
// ensure that PLAYER_{DIFF, NEW, OLD} don't cause an implicit tick after a TICK_SKIP
|
|
|
|
// by not overwriting m_MaxClientID during RecordPlayer
|
2017-09-12 12:58:44 +00:00
|
|
|
m_MaxClientID = -1;
|
|
|
|
|
|
|
|
m_State = STATE_PLAYERS;
|
|
|
|
}
|
|
|
|
|
2017-09-18 16:26:37 +00:00
|
|
|
void CTeeHistorian::EnsureTickWrittenPlayerData(int ClientID)
|
2017-09-12 12:58:44 +00:00
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
dbg_assert(ClientID > m_MaxClientID, "invalid player data order");
|
|
|
|
m_MaxClientID = ClientID;
|
|
|
|
|
2017-09-27 18:15:41 +00:00
|
|
|
if(!m_TickWritten && (ClientID > m_PrevMaxClientID || m_LastWrittenTick + 1 != m_Tick))
|
2017-09-12 12:58:44 +00:00
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
WriteTick();
|
2017-09-12 12:58:44 +00:00
|
|
|
}
|
2017-09-18 16:26:37 +00:00
|
|
|
else
|
2017-09-12 12:58:44 +00:00
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
// Tick is implicit.
|
|
|
|
m_LastWrittenTick = m_Tick;
|
|
|
|
m_TickWritten = true;
|
2017-09-12 12:58:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::RecordPlayer(int ClientID, const CNetObj_CharacterCore *pChar)
|
|
|
|
{
|
|
|
|
dbg_assert(m_State == STATE_PLAYERS, "invalid teehistorian state");
|
|
|
|
|
2022-03-27 11:31:07 +00:00
|
|
|
CTeehistorianPlayer *pPrev = &m_aPrevPlayers[ClientID];
|
2017-09-12 12:58:44 +00:00
|
|
|
if(!pPrev->m_Alive || pPrev->m_X != pChar->m_X || pPrev->m_Y != pChar->m_Y)
|
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
EnsureTickWrittenPlayerData(ClientID);
|
|
|
|
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
2017-09-12 12:58:44 +00:00
|
|
|
if(pPrev->m_Alive)
|
|
|
|
{
|
|
|
|
int dx = pChar->m_X - pPrev->m_X;
|
|
|
|
int dy = pChar->m_Y - pPrev->m_Y;
|
2017-09-18 16:26:37 +00:00
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
Buffer.AddInt(dx);
|
|
|
|
Buffer.AddInt(dy);
|
2017-09-12 12:58:44 +00:00
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "diff cid=%d dx=%d dy=%d", ClientID, dx, dy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int x = pChar->m_X;
|
|
|
|
int y = pChar->m_Y;
|
2017-09-18 16:26:37 +00:00
|
|
|
Buffer.AddInt(-TEEHISTORIAN_PLAYER_NEW);
|
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
Buffer.AddInt(x);
|
|
|
|
Buffer.AddInt(y);
|
2017-09-12 12:58:44 +00:00
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "new cid=%d x=%d y=%d", ClientID, x, y);
|
|
|
|
}
|
|
|
|
}
|
2017-09-18 16:26:37 +00:00
|
|
|
Write(Buffer.Data(), Buffer.Size());
|
2017-09-12 12:58:44 +00:00
|
|
|
}
|
|
|
|
pPrev->m_X = pChar->m_X;
|
|
|
|
pPrev->m_Y = pChar->m_Y;
|
|
|
|
pPrev->m_Alive = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::RecordDeadPlayer(int ClientID)
|
|
|
|
{
|
|
|
|
dbg_assert(m_State == STATE_PLAYERS, "invalid teehistorian state");
|
|
|
|
|
2022-03-27 11:31:07 +00:00
|
|
|
CTeehistorianPlayer *pPrev = &m_aPrevPlayers[ClientID];
|
2017-09-12 12:58:44 +00:00
|
|
|
if(pPrev->m_Alive)
|
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
EnsureTickWrittenPlayerData(ClientID);
|
|
|
|
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(-TEEHISTORIAN_PLAYER_OLD);
|
|
|
|
Buffer.AddInt(ClientID);
|
2017-09-12 12:58:44 +00:00
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "old cid=%d", ClientID);
|
|
|
|
}
|
2017-09-18 16:26:37 +00:00
|
|
|
Write(Buffer.Data(), Buffer.Size());
|
2017-09-12 12:58:44 +00:00
|
|
|
}
|
|
|
|
pPrev->m_Alive = false;
|
|
|
|
}
|
|
|
|
|
2021-08-14 11:40:13 +00:00
|
|
|
void CTeeHistorian::RecordPlayerTeam(int ClientID, int Team)
|
|
|
|
{
|
2021-09-15 15:25:37 +00:00
|
|
|
if(m_aPrevPlayers[ClientID].m_Team != Team)
|
2021-08-14 11:40:13 +00:00
|
|
|
{
|
2021-09-15 15:25:37 +00:00
|
|
|
m_aPrevPlayers[ClientID].m_Team = Team;
|
2021-08-14 11:40:13 +00:00
|
|
|
|
|
|
|
EnsureTickWritten();
|
|
|
|
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
Buffer.AddInt(Team);
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
2021-10-06 16:06:19 +00:00
|
|
|
dbg_msg("teehistorian", "player_team cid=%d team=%d", ClientID, Team);
|
2021-08-14 11:40:13 +00:00
|
|
|
}
|
|
|
|
|
2021-09-15 15:25:37 +00:00
|
|
|
WriteExtra(UUID_TEEHISTORIAN_PLAYER_TEAM, Buffer.Data(), Buffer.Size());
|
2021-08-14 11:40:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-14 12:51:11 +00:00
|
|
|
void CTeeHistorian::RecordTeamPractice(int Team, bool Practice)
|
|
|
|
{
|
2021-09-15 15:25:37 +00:00
|
|
|
if(m_aPrevTeams[Team].m_Practice != Practice)
|
2021-08-14 12:51:11 +00:00
|
|
|
{
|
2021-09-15 15:25:37 +00:00
|
|
|
m_aPrevTeams[Team].m_Practice = Practice;
|
2021-08-14 12:51:11 +00:00
|
|
|
|
|
|
|
EnsureTickWritten();
|
|
|
|
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(Team);
|
|
|
|
Buffer.AddInt(Practice);
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
2021-10-06 16:06:19 +00:00
|
|
|
dbg_msg("teehistorian", "team_practice team=%d practice=%d", Team, Practice);
|
2021-08-14 12:51:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WriteExtra(UUID_TEEHISTORIAN_TEAM_PRACTICE, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-12 12:58:44 +00:00
|
|
|
void CTeeHistorian::Write(const void *pData, int DataSize)
|
|
|
|
{
|
|
|
|
m_pfnWriteCallback(pData, DataSize, m_pWriteCallbackUserdata);
|
|
|
|
}
|
|
|
|
|
2017-09-18 16:26:37 +00:00
|
|
|
void CTeeHistorian::EnsureTickWritten()
|
|
|
|
{
|
|
|
|
if(!m_TickWritten)
|
|
|
|
{
|
|
|
|
WriteTick();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-12 12:58:44 +00:00
|
|
|
void CTeeHistorian::WriteTick()
|
|
|
|
{
|
|
|
|
CPacker TickPacker;
|
|
|
|
TickPacker.Reset();
|
|
|
|
|
|
|
|
int dt = m_Tick - m_LastWrittenTick - 1;
|
2017-09-28 00:42:35 +00:00
|
|
|
TickPacker.AddInt(-TEEHISTORIAN_TICK_SKIP);
|
2017-09-12 12:58:44 +00:00
|
|
|
TickPacker.AddInt(dt);
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
dbg_msg("teehistorian", "skip_ticks dt=%d", dt);
|
2017-09-12 12:58:44 +00:00
|
|
|
}
|
|
|
|
Write(TickPacker.Data(), TickPacker.Size());
|
|
|
|
|
|
|
|
m_TickWritten = true;
|
|
|
|
m_LastWrittenTick = m_Tick;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::EndPlayers()
|
|
|
|
{
|
|
|
|
dbg_assert(m_State == STATE_PLAYERS, "invalid teehistorian state");
|
|
|
|
|
|
|
|
m_State = STATE_BEFORE_INPUTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::BeginInputs()
|
|
|
|
{
|
|
|
|
dbg_assert(m_State == STATE_BEFORE_INPUTS, "invalid teehistorian state");
|
2017-09-18 16:26:37 +00:00
|
|
|
|
2017-09-12 12:58:44 +00:00
|
|
|
m_State = STATE_INPUTS;
|
|
|
|
}
|
|
|
|
|
2022-03-27 11:31:07 +00:00
|
|
|
void CTeeHistorian::RecordPlayerInput(int ClientID, uint32_t UniqueClientID, const CNetObj_PlayerInput *pInput)
|
2017-09-12 12:58:44 +00:00
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
CPacker Buffer;
|
|
|
|
|
2022-03-27 11:31:07 +00:00
|
|
|
CTeehistorianPlayer *pPrev = &m_aPrevPlayers[ClientID];
|
2017-09-12 12:58:44 +00:00
|
|
|
CNetObj_PlayerInput DiffInput;
|
2022-03-27 11:31:07 +00:00
|
|
|
if(pPrev->m_UniqueClientID == UniqueClientID)
|
2017-09-12 12:58:44 +00:00
|
|
|
{
|
|
|
|
if(mem_comp(&pPrev->m_Input, pInput, sizeof(pPrev->m_Input)) == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2017-09-18 16:26:37 +00:00
|
|
|
EnsureTickWritten();
|
|
|
|
Buffer.Reset();
|
|
|
|
|
|
|
|
Buffer.AddInt(-TEEHISTORIAN_INPUT_DIFF);
|
2017-09-12 12:58:44 +00:00
|
|
|
CSnapshotDelta::DiffItem((int *)&pPrev->m_Input, (int *)pInput, (int *)&DiffInput, sizeof(DiffInput) / sizeof(int));
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
const int *pData = (const int *)&DiffInput;
|
|
|
|
dbg_msg("teehistorian", "diff_input cid=%d %d %d %d %d %d %d %d %d %d %d", ClientID,
|
|
|
|
pData[0], pData[1], pData[2], pData[3], pData[4],
|
|
|
|
pData[5], pData[6], pData[7], pData[8], pData[9]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
EnsureTickWritten();
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(-TEEHISTORIAN_INPUT_NEW);
|
2017-09-12 12:58:44 +00:00
|
|
|
DiffInput = *pInput;
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "new_input cid=%d", ClientID);
|
|
|
|
}
|
|
|
|
}
|
2017-09-18 16:26:37 +00:00
|
|
|
Buffer.AddInt(ClientID);
|
2017-09-12 12:58:44 +00:00
|
|
|
for(int i = 0; i < (int)(sizeof(DiffInput) / sizeof(int)); i++)
|
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
Buffer.AddInt(((int *)&DiffInput)[i]);
|
2017-09-12 12:58:44 +00:00
|
|
|
}
|
2022-03-27 11:31:07 +00:00
|
|
|
pPrev->m_UniqueClientID = UniqueClientID;
|
2017-09-12 12:58:44 +00:00
|
|
|
pPrev->m_Input = *pInput;
|
2017-09-18 16:26:37 +00:00
|
|
|
|
|
|
|
Write(Buffer.Data(), Buffer.Size());
|
2017-09-12 12:58:44 +00:00
|
|
|
}
|
|
|
|
|
2017-09-13 20:35:09 +00:00
|
|
|
void CTeeHistorian::RecordPlayerMessage(int ClientID, const void *pMsg, int MsgSize)
|
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
EnsureTickWritten();
|
|
|
|
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(-TEEHISTORIAN_MESSAGE);
|
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
Buffer.AddInt(MsgSize);
|
|
|
|
Buffer.AddRaw(pMsg, MsgSize);
|
2017-09-13 20:35:09 +00:00
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
CUnpacker Unpacker;
|
|
|
|
Unpacker.Reset(pMsg, MsgSize);
|
|
|
|
int MsgID = Unpacker.GetInt();
|
|
|
|
int Sys = MsgID & 1;
|
|
|
|
MsgID >>= 1;
|
|
|
|
dbg_msg("teehistorian", "msg cid=%d sys=%d msgid=%d", ClientID, Sys, MsgID);
|
|
|
|
}
|
2017-09-18 16:26:37 +00:00
|
|
|
|
|
|
|
Write(Buffer.Data(), Buffer.Size());
|
2017-09-13 20:35:09 +00:00
|
|
|
}
|
|
|
|
|
2020-06-19 20:06:39 +00:00
|
|
|
void CTeeHistorian::RecordPlayerJoin(int ClientID, int Protocol)
|
2017-09-13 20:35:09 +00:00
|
|
|
{
|
2020-06-19 20:06:39 +00:00
|
|
|
dbg_assert(Protocol == PROTOCOL_6 || Protocol == PROTOCOL_7, "invalid version");
|
2017-09-18 16:26:37 +00:00
|
|
|
EnsureTickWritten();
|
|
|
|
|
2020-06-19 20:06:39 +00:00
|
|
|
{
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "joinver%d cid=%d", Protocol == PROTOCOL_6 ? 6 : 7, ClientID);
|
|
|
|
}
|
|
|
|
CUuid Uuid = Protocol == PROTOCOL_6 ? UUID_TEEHISTORIAN_JOINVER6 : UUID_TEEHISTORIAN_JOINVER7;
|
|
|
|
WriteExtra(Uuid, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
|
2017-09-18 16:26:37 +00:00
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(-TEEHISTORIAN_JOIN);
|
|
|
|
Buffer.AddInt(ClientID);
|
2017-09-13 20:35:09 +00:00
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "join cid=%d", ClientID);
|
|
|
|
}
|
2017-09-18 16:26:37 +00:00
|
|
|
|
|
|
|
Write(Buffer.Data(), Buffer.Size());
|
2017-09-13 20:35:09 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 11:16:37 +00:00
|
|
|
void CTeeHistorian::RecordPlayerReady(int ClientID)
|
|
|
|
{
|
|
|
|
EnsureTickWritten();
|
|
|
|
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "player_ready cid=%d", ClientID);
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteExtra(UUID_TEEHISTORIAN_PLAYER_READY, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
|
2017-09-13 20:35:09 +00:00
|
|
|
void CTeeHistorian::RecordPlayerDrop(int ClientID, const char *pReason)
|
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
EnsureTickWritten();
|
|
|
|
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(-TEEHISTORIAN_DROP);
|
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
Buffer.AddString(pReason, 0);
|
2017-09-13 20:35:09 +00:00
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "drop cid=%d reason='%s'", ClientID, pReason);
|
|
|
|
}
|
2017-09-18 16:26:37 +00:00
|
|
|
|
|
|
|
Write(Buffer.Data(), Buffer.Size());
|
2017-09-13 20:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::RecordConsoleCommand(int ClientID, int FlagMask, const char *pCmd, IConsole::IResult *pResult)
|
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
EnsureTickWritten();
|
|
|
|
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(-TEEHISTORIAN_CONSOLE_COMMAND);
|
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
Buffer.AddInt(FlagMask);
|
|
|
|
Buffer.AddString(pCmd, 0);
|
|
|
|
Buffer.AddInt(pResult->NumArguments());
|
2017-09-13 20:35:09 +00:00
|
|
|
for(int i = 0; i < pResult->NumArguments(); i++)
|
|
|
|
{
|
2017-09-18 16:26:37 +00:00
|
|
|
Buffer.AddString(pResult->GetString(i), 0);
|
2017-09-13 20:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "ccmd cid=%d cmd='%s'", ClientID, pCmd);
|
|
|
|
}
|
2017-09-18 16:26:37 +00:00
|
|
|
|
|
|
|
Write(Buffer.Data(), Buffer.Size());
|
2017-09-13 20:35:09 +00:00
|
|
|
}
|
|
|
|
|
2017-11-18 02:08:16 +00:00
|
|
|
void CTeeHistorian::RecordTestExtra()
|
|
|
|
{
|
2018-01-28 02:13:05 +00:00
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "test");
|
|
|
|
}
|
|
|
|
|
2017-11-18 02:08:16 +00:00
|
|
|
WriteExtra(UUID_TEEHISTORIAN_TEST, "", 0);
|
|
|
|
}
|
|
|
|
|
2022-05-03 13:38:09 +00:00
|
|
|
void CTeeHistorian::RecordPlayerSwap(int ClientID1, int ClientID2)
|
|
|
|
{
|
|
|
|
EnsureTickWritten();
|
|
|
|
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(ClientID1);
|
|
|
|
Buffer.AddInt(ClientID2);
|
|
|
|
|
|
|
|
WriteExtra(UUID_TEEHISTORIAN_PLAYER_SWITCH, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
|
2020-06-20 14:19:49 +00:00
|
|
|
void CTeeHistorian::RecordTeamSaveSuccess(int Team, CUuid SaveID, const char *pTeamSave)
|
|
|
|
{
|
2021-01-28 21:18:59 +00:00
|
|
|
EnsureTickWritten();
|
|
|
|
|
2020-06-20 14:19:49 +00:00
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(Team);
|
|
|
|
Buffer.AddRaw(&SaveID, sizeof(SaveID));
|
|
|
|
Buffer.AddString(pTeamSave, 0);
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
char aSaveID[UUID_MAXSTRSIZE];
|
|
|
|
FormatUuid(SaveID, aSaveID, sizeof(aSaveID));
|
2020-06-26 12:36:15 +00:00
|
|
|
dbg_msg("teehistorian", "save_success team=%d save_id=%s team_save='%s'", Team, aSaveID, pTeamSave);
|
2020-06-20 14:19:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WriteExtra(UUID_TEEHISTORIAN_SAVE_SUCCESS, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::RecordTeamSaveFailure(int Team)
|
|
|
|
{
|
2021-01-28 21:18:59 +00:00
|
|
|
EnsureTickWritten();
|
|
|
|
|
2020-06-20 14:19:49 +00:00
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(Team);
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
2020-06-26 12:36:15 +00:00
|
|
|
dbg_msg("teehistorian", "save_failure team=%d", Team);
|
2020-06-20 14:19:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WriteExtra(UUID_TEEHISTORIAN_SAVE_FAILURE, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::RecordTeamLoadSuccess(int Team, CUuid SaveID, const char *pTeamSave)
|
|
|
|
{
|
2021-01-28 21:18:59 +00:00
|
|
|
EnsureTickWritten();
|
|
|
|
|
2020-06-20 14:19:49 +00:00
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(Team);
|
|
|
|
Buffer.AddRaw(&SaveID, sizeof(SaveID));
|
|
|
|
Buffer.AddString(pTeamSave, 0);
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
char aSaveID[UUID_MAXSTRSIZE];
|
|
|
|
FormatUuid(SaveID, aSaveID, sizeof(aSaveID));
|
2020-06-26 12:36:15 +00:00
|
|
|
dbg_msg("teehistorian", "load_success team=%d save_id=%s team_save='%s'", Team, aSaveID, pTeamSave);
|
2020-06-20 14:19:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WriteExtra(UUID_TEEHISTORIAN_LOAD_SUCCESS, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::RecordTeamLoadFailure(int Team)
|
|
|
|
{
|
2021-01-28 21:18:59 +00:00
|
|
|
EnsureTickWritten();
|
|
|
|
|
2020-06-20 14:19:49 +00:00
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(Team);
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
2020-06-26 12:36:15 +00:00
|
|
|
dbg_msg("teehistorian", "load_failure team=%d", Team);
|
2020-06-20 14:19:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WriteExtra(UUID_TEEHISTORIAN_LOAD_FAILURE, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
|
2017-09-12 12:58:44 +00:00
|
|
|
void CTeeHistorian::EndInputs()
|
|
|
|
{
|
|
|
|
dbg_assert(m_State == STATE_INPUTS, "invalid teehistorian state");
|
|
|
|
|
|
|
|
m_State = STATE_BEFORE_ENDTICK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::EndTick()
|
|
|
|
{
|
|
|
|
dbg_assert(m_State == STATE_BEFORE_ENDTICK, "invalid teehistorian state");
|
|
|
|
m_State = STATE_BEFORE_TICK;
|
|
|
|
}
|
|
|
|
|
2020-05-22 15:58:41 +00:00
|
|
|
void CTeeHistorian::RecordDDNetVersionOld(int ClientID, int DDNetVersion)
|
|
|
|
{
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
Buffer.AddInt(DDNetVersion);
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "ddnetver_old cid=%d ddnet_version=%d", ClientID, DDNetVersion);
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteExtra(UUID_TEEHISTORIAN_DDNETVER_OLD, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::RecordDDNetVersion(int ClientID, CUuid ConnectionID, int DDNetVersion, const char *pDDNetVersionStr)
|
|
|
|
{
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
Buffer.AddRaw(&ConnectionID, sizeof(ConnectionID));
|
|
|
|
Buffer.AddInt(DDNetVersion);
|
|
|
|
Buffer.AddString(pDDNetVersionStr, 0);
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
char aConnnectionID[UUID_MAXSTRSIZE];
|
|
|
|
FormatUuid(ConnectionID, aConnnectionID, sizeof(aConnnectionID));
|
|
|
|
dbg_msg("teehistorian", "ddnetver cid=%d connection_id=%s ddnet_version=%d ddnet_version_str=%s", ClientID, aConnnectionID, DDNetVersion, pDDNetVersionStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteExtra(UUID_TEEHISTORIAN_DDNETVER, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
|
2018-01-28 02:13:05 +00:00
|
|
|
void CTeeHistorian::RecordAuthInitial(int ClientID, int Level, const char *pAuthName)
|
|
|
|
{
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
Buffer.AddInt(Level);
|
|
|
|
Buffer.AddString(pAuthName, 0);
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "auth_init cid=%d level=%d auth_name=%s", ClientID, Level, pAuthName);
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteExtra(UUID_TEEHISTORIAN_AUTH_INIT, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::RecordAuthLogin(int ClientID, int Level, const char *pAuthName)
|
|
|
|
{
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
Buffer.AddInt(Level);
|
|
|
|
Buffer.AddString(pAuthName, 0);
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "auth_login cid=%d level=%d auth_name=%s", ClientID, Level, pAuthName);
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteExtra(UUID_TEEHISTORIAN_AUTH_LOGIN, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeeHistorian::RecordAuthLogout(int ClientID)
|
|
|
|
{
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(ClientID);
|
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "auth_logout cid=%d", ClientID);
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteExtra(UUID_TEEHISTORIAN_AUTH_LOGOUT, Buffer.Data(), Buffer.Size());
|
|
|
|
}
|
|
|
|
|
2017-09-12 12:58:44 +00:00
|
|
|
void CTeeHistorian::Finish()
|
|
|
|
{
|
2017-09-28 00:42:35 +00:00
|
|
|
dbg_assert(m_State == STATE_START || m_State == STATE_INPUTS || m_State == STATE_BEFORE_ENDTICK || m_State == STATE_BEFORE_TICK, "invalid teehistorian state");
|
2017-09-12 12:58:44 +00:00
|
|
|
|
|
|
|
if(m_State == STATE_INPUTS)
|
|
|
|
{
|
|
|
|
EndInputs();
|
|
|
|
}
|
|
|
|
if(m_State == STATE_BEFORE_ENDTICK)
|
|
|
|
{
|
|
|
|
EndTick();
|
|
|
|
}
|
2017-09-18 16:26:37 +00:00
|
|
|
|
|
|
|
CPacker Buffer;
|
|
|
|
Buffer.Reset();
|
|
|
|
Buffer.AddInt(-TEEHISTORIAN_FINISH);
|
2017-09-13 20:35:09 +00:00
|
|
|
|
|
|
|
if(m_Debug)
|
|
|
|
{
|
|
|
|
dbg_msg("teehistorian", "finish");
|
|
|
|
}
|
2017-09-18 16:26:37 +00:00
|
|
|
|
|
|
|
Write(Buffer.Data(), Buffer.Size());
|
2017-09-12 12:58:44 +00:00
|
|
|
}
|