summaryrefslogtreecommitdiff
path: root/sourcemod/scripting/gokz-replays/recording.sp
diff options
context:
space:
mode:
Diffstat (limited to 'sourcemod/scripting/gokz-replays/recording.sp')
-rw-r--r--sourcemod/scripting/gokz-replays/recording.sp990
1 files changed, 0 insertions, 990 deletions
diff --git a/sourcemod/scripting/gokz-replays/recording.sp b/sourcemod/scripting/gokz-replays/recording.sp
deleted file mode 100644
index babbd5e..0000000
--- a/sourcemod/scripting/gokz-replays/recording.sp
+++ /dev/null
@@ -1,990 +0,0 @@
-/*
- Bot replay recording logic and processes.
-
- Records data every time OnPlayerRunCmdPost is called.
- If the player doesn't have their timer running, it keeps track
- of the last 2 minutes of their actions. If a player is banned
- while their timer isn't running, those 2 minutes are saved.
- If the player has their timer running, the recording is done from
- the beginning of the run. If the player can no longer beat their PB,
- then the recording goes back to only keeping track of the last
- two minutes. Upon beating their PB, a temporary binary file will be
- written with a 'header' containing information about the run,
- followed by the recorded tick data from OnPlayerRunCmdPost.
- The binary file will be permanently locally saved on the server
- if the run beats the server record.
-*/
-
-static float tickrate;
-static int preAndPostRunTickCount;
-static int maxCheaterReplayTicks;
-static int recordingIndex[MAXPLAYERS + 1];
-static float playerSensitivity[MAXPLAYERS + 1];
-static float playerMYaw[MAXPLAYERS + 1];
-static bool isTeleportTick[MAXPLAYERS + 1];
-static ReplaySaveState replaySaveState[MAXPLAYERS + 1];
-static bool recordingPaused[MAXPLAYERS + 1];
-static bool postRunRecording[MAXPLAYERS + 1];
-static ArrayList recordedRecentData[MAXPLAYERS + 1];
-static ArrayList recordedRunData[MAXPLAYERS + 1];
-static ArrayList recordedPostRunData[MAXPLAYERS + 1];
-static Handle runningRunBreatherTimer[MAXPLAYERS + 1];
-static ArrayList runningJumpstatTimers[MAXPLAYERS + 1];
-
-// =====[ EVENTS ]=====
-
-void OnMapStart_Recording()
-{
- CreateReplaysDirectory(gC_CurrentMap);
- tickrate = 1/GetTickInterval();
- preAndPostRunTickCount = RoundToZero(RP_PLAYBACK_BREATHER_TIME * tickrate);
- maxCheaterReplayTicks = RoundToCeil(RP_MAX_CHEATER_REPLAY_LENGTH * tickrate);
-}
-
-void OnClientPutInServer_Recording(int client)
-{
- ClearClientRecordingState(client);
-}
-
-void OnClientAuthorized_Recording(int client)
-{
- // Apparently the client isn't valid yet here, so we can't check for that!
- if(!IsFakeClient(client))
- {
- // Create directory path for player if not exists
- char replayPath[PLATFORM_MAX_PATH];
- BuildPath(Path_SM, replayPath, sizeof(replayPath), "%s/%d", RP_DIRECTORY_JUMPS, GetSteamAccountID(client));
- if (!DirExists(replayPath))
- {
- CreateDirectory(replayPath, 511);
- }
- BuildPath(Path_SM, replayPath, sizeof(replayPath), "%s/%d/%s", RP_DIRECTORY_JUMPS, GetSteamAccountID(client), RP_DIRECTORY_BLOCKJUMPS);
- if (!DirExists(replayPath))
- {
- CreateDirectory(replayPath, 511);
- }
- }
-}
-
-void OnClientDisconnect_Recording(int client)
-{
- // Stop exceptions if OnClientPutInServer was never ran for this client id.
- // As long as the arrays aren't null we'll be fine.
- if (runningJumpstatTimers[client] == null)
- {
- return;
- }
-
- // Trigger all timers early
- if(!IsFakeClient(client))
- {
- if (runningRunBreatherTimer[client] != INVALID_HANDLE)
- {
- TriggerTimer(runningRunBreatherTimer[client], false);
- }
-
- // We have to clone the array because the timer callback removes the timer
- // from the array we're running over, and doing weird tricks is scary.
- ArrayList timers = runningJumpstatTimers[client].Clone();
- for (int i = 0; i < timers.Length; i++)
- {
- Handle timer = timers.Get(i);
- TriggerTimer(timer, false);
- }
- delete timers;
- }
-
- ClearClientRecordingState(client);
-}
-
-void OnPlayerRunCmdPost_Recording(int client, int buttons, int tickCount, const float vel[3], const int mouse[2])
-{
- if (!IsValidClient(client) || IsFakeClient(client) || !IsPlayerAlive(client) || recordingPaused[client])
- {
- return;
- }
-
- ReplayTickData tickData;
-
- Movement_GetOrigin(client, tickData.origin);
-
- tickData.mouse = mouse;
- tickData.vel = vel;
- Movement_GetVelocity(client, tickData.velocity);
- Movement_GetEyeAngles(client, tickData.angles);
- tickData.flags = EncodePlayerFlags(client, buttons, tickCount);
- tickData.packetsPerSecond = GetClientAvgPackets(client, NetFlow_Incoming);
- tickData.laggedMovementValue = GetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue");
- tickData.buttonsForced = GetEntProp(client, Prop_Data, "m_afButtonForced");
-
- // HACK: Reset teleport tick marker. Too bad!
- if (isTeleportTick[client])
- {
- isTeleportTick[client] = false;
- }
-
- if (replaySaveState[client] != ReplaySave_Disabled)
- {
- int runTick = GetArraySize(recordedRunData[client]);
- if (runTick < RP_MAX_DURATION)
- {
- // Resize might fail if the timer exceed the max duration,
- // as it is not guaranteed to allocate more than 1GB of contiguous memory,
- // causing mass lag spikes that kick everyone out of the server.
- // We can still attempt to save the rest of the recording though.
- recordedRunData[client].Resize(runTick + 1);
- recordedRunData[client].SetArray(runTick, tickData);
- }
- }
- if (postRunRecording[client])
- {
- int tick = GetArraySize(recordedPostRunData[client]);
- if (tick < RP_MAX_DURATION)
- {
- recordedPostRunData[client].Resize(tick + 1);
- recordedPostRunData[client].SetArray(tick, tickData);
- }
- }
-
- int tick = recordingIndex[client];
- if (recordedRecentData[client].Length < maxCheaterReplayTicks)
- {
- recordedRecentData[client].Resize(recordedRecentData[client].Length + 1);
- recordingIndex[client] = recordingIndex[client] + 1 == maxCheaterReplayTicks ? 0 : recordingIndex[client] + 1;
- }
- else
- {
- recordingIndex[client] = RecordingIndexAdd(client, 1);
- }
-
- recordedRecentData[client].SetArray(tick, tickData);
-}
-
-Action GOKZ_OnTimerStart_Recording(int client)
-{
- // Hack to fix an exception when starting the timer on the very
- // first tick after loading the plugin.
- if (recordedRecentData[client].Length == 0)
- {
- return Plugin_Handled;
- }
-
- return Plugin_Continue;
-}
-
-void GOKZ_OnTimerStart_Post_Recording(int client)
-{
- replaySaveState[client] = ReplaySave_Local;
- StartRunRecording(client);
-}
-
-void GOKZ_OnTimerEnd_Recording(int client, int course, float time, int teleportsUsed)
-{
- if (replaySaveState[client] == ReplaySave_Disabled)
- {
- return;
- }
-
- DataPack data = new DataPack();
- data.WriteCell(GetClientUserId(client));
- data.WriteCell(course);
- data.WriteFloat(time);
- data.WriteCell(teleportsUsed);
- data.WriteCell(replaySaveState[client]);
- // The previous run breather still did not finish, end it now or
- // we will start overwriting the data.
- if (runningRunBreatherTimer[client] != INVALID_HANDLE)
- {
- TriggerTimer(runningRunBreatherTimer[client], false);
- }
-
- replaySaveState[client] = ReplaySave_Disabled;
- postRunRecording[client] = true;
-
- // Swap recordedRunData and recordedPostRunData.
- // This lets new runs start immediately, before the post-run breather is
- // finished recording.
- ArrayList tmp = recordedPostRunData[client];
- recordedPostRunData[client] = recordedRunData[client];
- recordedRunData[client] = tmp;
- recordedRunData[client].Clear();
-
- runningRunBreatherTimer[client] = CreateTimer(RP_PLAYBACK_BREATHER_TIME, Timer_EndRecording, data);
- if (runningRunBreatherTimer[client] == INVALID_HANDLE)
- {
- LogError("Could not create a timer so can't end the run replay recording");
- }
-}
-
-public Action Timer_EndRecording(Handle timer, DataPack data)
-{
- data.Reset();
- int client = GetClientOfUserId(data.ReadCell());
- int course = data.ReadCell();
- float time = data.ReadFloat();
- int teleportsUsed = data.ReadCell();
- ReplaySaveState saveState = data.ReadCell();
- delete data;
-
- // The client left after the run was done but before the post-run
- // breather had the chance to finish. This should not happen, as we
- // trigger all running timers on disconnect.
- if (!IsValidClient(client))
- {
- return Plugin_Stop;
- }
-
- runningRunBreatherTimer[client] = INVALID_HANDLE;
- postRunRecording[client] = false;
-
- if (gB_GOKZLocalDB && GOKZ_DB_IsCheater(client))
- {
- // Replay might be submitted globally, but will not be saved locally.
- saveState = ReplaySave_Temp;
- }
-
- char path[PLATFORM_MAX_PATH];
- if (SaveRecordingOfRun(path, client, course, time, teleportsUsed, saveState == ReplaySave_Temp))
- {
- Call_OnTimerEnd_Post(client, path, course, time, teleportsUsed);
- }
- else
- {
- Call_OnTimerEnd_Post(client, "", course, time, teleportsUsed);
- }
-
- return Plugin_Stop;
-}
-
-void GOKZ_OnPause_Recording(int client)
-{
- PauseRecording(client);
-}
-
-void GOKZ_OnResume_Recording(int client)
-{
- ResumeRecording(client);
-}
-
-void GOKZ_OnTimerStopped_Recording(int client)
-{
- replaySaveState[client] = ReplaySave_Disabled;
-}
-
-void GOKZ_OnCountedTeleport_Recording(int client)
-{
- if (gB_NubRecordMissed[client])
- {
- replaySaveState[client] = ReplaySave_Disabled;
- }
-
- isTeleportTick[client] = true;
-}
-
-void GOKZ_LR_OnRecordMissed_Recording(int client, int recordType)
-{
- if (replaySaveState[client] == ReplaySave_Disabled)
- {
- return;
- }
- // If missed PRO record or both records, then can no longer beat a server record
- if (recordType == RecordType_NubAndPro || recordType == RecordType_Pro)
- {
- replaySaveState[client] = ReplaySave_Temp;
- }
-
- // If on a NUB run and missed NUB record, then can no longer beat a server record
- // Otherwise wait to see if they teleport before stopping the recording
- if (recordType == RecordType_Nub)
- {
- if (GOKZ_GetTeleportCount(client) > 0)
- {
- replaySaveState[client] = ReplaySave_Temp;
- }
- }
-}
-
-public void GOKZ_LR_OnPBMissed(int client, float pbTime, int course, int mode, int style, int recordType)
-{
- if (replaySaveState[client] == ReplaySave_Disabled)
- {
- return;
- }
- // If missed PRO record or both records, then can no longer beat PB
- if (recordType == RecordType_NubAndPro || recordType == RecordType_Pro)
- {
- replaySaveState[client] = ReplaySave_Disabled;
- }
-
- // If on a NUB run and missed NUB record, then can no longer beat PB
- // Otherwise wait to see if they teleport before stopping the recording
- if (recordType == RecordType_Nub)
- {
- if (GOKZ_GetTeleportCount(client) > 0)
- {
- replaySaveState[client] = ReplaySave_Disabled;
- }
- }
-}
-
-void GOKZ_AC_OnPlayerSuspected_Recording(int client, ACReason reason)
-{
- SaveRecordingOfCheater(client, reason);
-}
-
-void GOKZ_DB_OnJumpstatPB_Recording(int client, int jumptype, float distance, int block, int strafes, float sync, float pre, float max, int airtime)
-{
- DataPack data = new DataPack();
- data.WriteCell(GetClientUserId(client));
- data.WriteCell(jumptype);
- data.WriteFloat(distance);
- data.WriteCell(block);
- data.WriteCell(strafes);
- data.WriteFloat(sync);
- data.WriteFloat(pre);
- data.WriteFloat(max);
- data.WriteCell(airtime);
-
- Handle timer = CreateTimer(RP_PLAYBACK_BREATHER_TIME, SaveJump, data);
- if (timer != INVALID_HANDLE)
- {
- runningJumpstatTimers[client].Push(timer);
- }
- else
- {
- LogError("Could not create a timer so can't save jumpstat pb replay");
- }
-}
-
-public Action SaveJump(Handle timer, DataPack data)
-{
- data.Reset();
- int client = GetClientOfUserId(data.ReadCell());
- int jumptype = data.ReadCell();
- float distance = data.ReadFloat();
- int block = data.ReadCell();
- int strafes = data.ReadCell();
- float sync = data.ReadFloat();
- float pre = data.ReadFloat();
- float max = data.ReadFloat();
- int airtime = data.ReadCell();
- delete data;
-
- // The client left after the jump was done but before the post-jump
- // breather had the chance to finish. This should not happen, as we
- // trigger all running timers on disconnect.
- if (!IsValidClient(client))
- {
- return Plugin_Stop;
- }
-
- RemoveFromRunningTimers(client, timer);
-
- SaveRecordingOfJump(client, jumptype, distance, block, strafes, sync, pre, max, airtime);
- return Plugin_Stop;
-}
-
-
-
-// =====[ PRIVATE ]=====
-
-static void ClearClientRecordingState(int client)
-{
- recordingIndex[client] = 0;
- playerSensitivity[client] = -1.0;
- playerMYaw[client] = -1.0;
- isTeleportTick[client] = false;
- replaySaveState[client] = ReplaySave_Disabled;
- recordingPaused[client] = false;
- postRunRecording[client] = false;
- runningRunBreatherTimer[client] = INVALID_HANDLE;
-
- if (recordedRecentData[client] == null)
- recordedRecentData[client] = new ArrayList(sizeof(ReplayTickData));
-
- if (recordedRunData[client] == null)
- recordedRunData[client] = new ArrayList(sizeof(ReplayTickData));
-
- if (recordedPostRunData[client] == null)
- recordedPostRunData[client] = new ArrayList(sizeof(ReplayTickData));
-
- if (runningJumpstatTimers[client] == null)
- runningJumpstatTimers[client] = new ArrayList();
-
- recordedRecentData[client].Clear();
- recordedRunData[client].Clear();
- recordedPostRunData[client].Clear();
- runningJumpstatTimers[client].Clear();
-}
-
-static void StartRunRecording(int client)
-{
- if (IsFakeClient(client))
- {
- return;
- }
-
- QueryClientConVar(client, "sensitivity", SensitivityCheck, client);
- QueryClientConVar(client, "m_yaw", MYAWCheck, client);
-
- DiscardRecording(client);
- ResumeRecording(client);
-
- // Copy pre data
- int index;
- recordedRunData[client].Resize(preAndPostRunTickCount);
- if (recordedRecentData[client].Length < preAndPostRunTickCount)
- {
- index = recordingIndex[client] - preAndPostRunTickCount;
- }
- else
- {
- index = RecordingIndexAdd(client, -preAndPostRunTickCount);
- }
- for (int i = 0; i < preAndPostRunTickCount; i++)
- {
- ReplayTickData tickData;
- if (index < 0)
- {
- recordedRecentData[client].GetArray(0, tickData);
- recordedRunData[client].SetArray(i, tickData);
- index += 1;
- }
- else
- {
- recordedRecentData[client].GetArray(index, tickData);
- recordedRunData[client].SetArray(i, tickData);
- index = RecordingIndexAdd(client, -preAndPostRunTickCount + i + 1);
- }
- }
-}
-
-static void DiscardRecording(int client)
-{
- recordedRunData[client].Clear();
- Call_OnReplayDiscarded(client);
-}
-
-static void PauseRecording(int client)
-{
- recordingPaused[client] = true;
-}
-
-static void ResumeRecording(int client)
-{
- recordingPaused[client] = false;
-}
-
-static bool SaveRecordingOfRun(char replayPath[PLATFORM_MAX_PATH], int client, int course, float time, int teleportsUsed, bool temp)
-{
- // Prepare data
- int timeType = GOKZ_GetTimeTypeEx(teleportsUsed);
-
- // Create and fill General Header
- GeneralReplayHeader generalHeader;
- FillGeneralHeader(generalHeader, client, ReplayType_Run, recordedPostRunData[client].Length);
-
- // Create and fill Run Header
- RunReplayHeader runHeader;
- runHeader.time = time;
- runHeader.course = course;
- runHeader.teleportsUsed = teleportsUsed;
-
- // Build path and create/overwrite associated file
- FormatRunReplayPath(replayPath, sizeof(replayPath), course, generalHeader.mode, generalHeader.style, timeType, temp);
- if (FileExists(replayPath))
- {
- DeleteFile(replayPath);
- }
- else if (!temp)
- {
- AddToReplayInfoCache(course, generalHeader.mode, generalHeader.style, timeType);
- SortReplayInfoCache();
- }
-
- File file = OpenFile(replayPath, "wb");
- if (file == null)
- {
- LogError("Failed to create/open replay file to write to: \"%s\".", replayPath);
- return false;
- }
-
- WriteGeneralHeader(file, generalHeader);
-
- // Write run header
- file.WriteInt32(view_as<int>(runHeader.time));
- file.WriteInt8(runHeader.course);
- file.WriteInt32(runHeader.teleportsUsed);
-
- WriteTickData(file, client, ReplayType_Run);
-
- delete file;
- // If there is no plugin that wants to take over the replay file, we will delete it ourselves.
- if (Call_OnReplaySaved(client, ReplayType_Run, gC_CurrentMap, course, timeType, time, replayPath, temp) == Plugin_Continue && temp)
- {
- DeleteFile(replayPath);
- }
-
- return true;
-}
-
-static bool SaveRecordingOfCheater(int client, ACReason reason)
-{
- // Create and fill general header
- GeneralReplayHeader generalHeader;
- FillGeneralHeader(generalHeader, client, ReplayType_Cheater, recordedRecentData[client].Length);
-
- // Create and fill cheater header
- CheaterReplayHeader cheaterHeader;
- cheaterHeader.ACReason = reason;
-
- //Build path and create/overwrite associated file
- char replayPath[PLATFORM_MAX_PATH];
- FormatCheaterReplayPath(replayPath, sizeof(replayPath), client, generalHeader.mode, generalHeader.style);
-
- File file = OpenFile(replayPath, "wb");
- if (file == null)
- {
- LogError("Failed to create/open replay file to write to: \"%s\".", replayPath);
- return false;
- }
-
- WriteGeneralHeader(file, generalHeader);
- file.WriteInt8(view_as<int>(cheaterHeader.ACReason));
- WriteTickData(file, client, ReplayType_Cheater);
-
- delete file;
-
- return true;
-}
-
-static bool SaveRecordingOfJump(int client, int jumptype, float distance, int block, int strafes, float sync, float pre, float max, int airtime)
-{
- // Just cause I know how buggy jumpstats can be
- int airtimeTicks = RoundToNearest((float(airtime) / GOKZ_DB_JS_AIRTIME_PRECISION) * tickrate);
- if (airtimeTicks + 2 * preAndPostRunTickCount >= maxCheaterReplayTicks)
- {
- LogError("WARNING: Invalid airtime (this is probably a bugged jump, please report it!).");
- return false;
- }
-
- // Create and fill general header
- GeneralReplayHeader generalHeader;
- FillGeneralHeader(generalHeader, client, ReplayType_Jump, 2 * preAndPostRunTickCount + airtimeTicks);
-
- // Create and fill jump header
- JumpReplayHeader jumpHeader;
- FillJumpHeader(jumpHeader, jumptype, distance, block, strafes, sync, pre, max, airtime);
-
- // Make sure the client is authenticated
- if (GetSteamAccountID(client) == 0)
- {
- LogError("Failed to save jump, client is not authenticated.");
- return false;
- }
-
- // Build path and create/overwrite associated file
- char replayPath[PLATFORM_MAX_PATH];
- if (block > 0)
- {
- FormatBlockJumpReplayPath(replayPath, sizeof(replayPath), client, block, jumpHeader.jumpType, generalHeader.mode, generalHeader.style);
- }
- else
- {
- FormatJumpReplayPath(replayPath, sizeof(replayPath), client, jumpHeader.jumpType, generalHeader.mode, generalHeader.style);
- }
-
- File file = OpenFile(replayPath, "wb");
- if (file == null)
- {
- LogError("Failed to create/open replay file to write to: \"%s\".", replayPath);
- delete file;
- return false;
- }
-
- WriteGeneralHeader(file, generalHeader);
- WriteJumpHeader(file, jumpHeader);
- WriteTickData(file, client, ReplayType_Jump, airtimeTicks);
-
- delete file;
-
- return true;
-}
-
-static void FillGeneralHeader(GeneralReplayHeader generalHeader, int client, int replayType, int tickCount)
-{
- // Prepare data
- int mode = GOKZ_GetCoreOption(client, Option_Mode);
- int style = GOKZ_GetCoreOption(client, Option_Style);
-
- // Fill general header
- generalHeader.magicNumber = RP_MAGIC_NUMBER;
- generalHeader.formatVersion = RP_FORMAT_VERSION;
- generalHeader.replayType = replayType;
- generalHeader.gokzVersion = GOKZ_VERSION;
- generalHeader.mapName = gC_CurrentMap;
- generalHeader.mapFileSize = gI_CurrentMapFileSize;
- generalHeader.serverIP = FindConVar("hostip").IntValue;
- generalHeader.timestamp = GetTime();
- GetClientName(client, generalHeader.playerAlias, sizeof(GeneralReplayHeader::playerAlias));
- generalHeader.playerSteamID = GetSteamAccountID(client);
- generalHeader.mode = mode;
- generalHeader.style = style;
- generalHeader.playerSensitivity = playerSensitivity[client];
- generalHeader.playerMYaw = playerMYaw[client];
- generalHeader.tickrate = tickrate;
- generalHeader.tickCount = tickCount;
- generalHeader.equippedWeapon = GetPlayerWeaponSlotDefIndex(client, CS_SLOT_SECONDARY);
- generalHeader.equippedKnife = GetPlayerWeaponSlotDefIndex(client, CS_SLOT_KNIFE);
-}
-
-static void FillJumpHeader(JumpReplayHeader jumpHeader, int jumptype, float distance, int block, int strafes, float sync, float pre, float max, int airtime)
-{
- jumpHeader.jumpType = jumptype;
- jumpHeader.distance = distance;
- jumpHeader.blockDistance = block;
- jumpHeader.strafeCount = strafes;
- jumpHeader.sync = sync;
- jumpHeader.pre = pre;
- jumpHeader.max = max;
- jumpHeader.airtime = airtime;
-}
-
-static void WriteGeneralHeader(File file, GeneralReplayHeader generalHeader)
-{
- file.WriteInt32(generalHeader.magicNumber);
- file.WriteInt8(generalHeader.formatVersion);
- file.WriteInt8(generalHeader.replayType);
- file.WriteInt8(strlen(generalHeader.gokzVersion));
- file.WriteString(generalHeader.gokzVersion, false);
- file.WriteInt8(strlen(generalHeader.mapName));
- file.WriteString(generalHeader.mapName, false);
- file.WriteInt32(generalHeader.mapFileSize);
- file.WriteInt32(generalHeader.serverIP);
- file.WriteInt32(generalHeader.timestamp);
- file.WriteInt8(strlen(generalHeader.playerAlias));
- file.WriteString(generalHeader.playerAlias, false);
- file.WriteInt32(generalHeader.playerSteamID);
- file.WriteInt8(generalHeader.mode);
- file.WriteInt8(generalHeader.style);
- file.WriteInt32(view_as<int>(generalHeader.playerSensitivity));
- file.WriteInt32(view_as<int>(generalHeader.playerMYaw));
- file.WriteInt32(view_as<int>(generalHeader.tickrate));
- file.WriteInt32(generalHeader.tickCount);
- file.WriteInt32(generalHeader.equippedWeapon);
- file.WriteInt32(generalHeader.equippedKnife);
-}
-
-static void WriteJumpHeader(File file, JumpReplayHeader jumpHeader)
-{
- file.WriteInt8(jumpHeader.jumpType);
- file.WriteInt32(view_as<int>(jumpHeader.distance));
- file.WriteInt32(jumpHeader.blockDistance);
- file.WriteInt8(jumpHeader.strafeCount);
- file.WriteInt32(view_as<int>(jumpHeader.sync));
- file.WriteInt32(view_as<int>(jumpHeader.pre));
- file.WriteInt32(view_as<int>(jumpHeader.max));
- file.WriteInt32((jumpHeader.airtime));
-}
-
-static void WriteTickData(File file, int client, int replayType, int airtime = 0)
-{
- ReplayTickData tickData;
- ReplayTickData prevTickData;
- bool isFirstTick = true;
- switch(replayType)
- {
- case ReplayType_Run:
- {
- for (int i = 0; i < recordedPostRunData[client].Length; i++)
- {
- recordedPostRunData[client].GetArray(i, tickData);
- recordedPostRunData[client].GetArray(IntMax(0, i-1), prevTickData);
- WriteTickDataToFile(file, isFirstTick, tickData, prevTickData);
- isFirstTick = false;
- }
- }
- case ReplayType_Cheater:
- {
- for (int i = 0; i < recordedRecentData[client].Length; i++)
- {
- int rollingI = RecordingIndexAdd(client, i);
- recordedRecentData[client].GetArray(rollingI, tickData);
- recordedRecentData[client].GetArray(IntMax(0, i-1), prevTickData);
- WriteTickDataToFile(file, isFirstTick, tickData, prevTickData);
- isFirstTick = false;
- }
-
- }
- case ReplayType_Jump:
- {
- int replayLength = 2 * preAndPostRunTickCount + airtime;
- for (int i = 0; i < replayLength; i++)
- {
- int rollingI = RecordingIndexAdd(client, i - replayLength);
- recordedRecentData[client].GetArray(rollingI, tickData);
- recordedRecentData[client].GetArray(IntMax(0, i-1), prevTickData);
- WriteTickDataToFile(file, isFirstTick, tickData, prevTickData);
- isFirstTick = false;
- }
- }
- }
-}
-
-static void WriteTickDataToFile(File file, bool isFirstTick, ReplayTickData tickDataStruct, ReplayTickData prevTickDataStruct)
-{
- any tickData[RP_V2_TICK_DATA_BLOCKSIZE];
- any prevTickData[RP_V2_TICK_DATA_BLOCKSIZE];
- TickDataToArray(tickDataStruct, tickData);
- TickDataToArray(prevTickDataStruct, prevTickData);
-
- int deltaFlags = (1 << RPDELTA_DELTAFLAGS);
- if (isFirstTick)
- {
- // NOTE: Set every bit to 1 until RP_V2_TICK_DATA_BLOCKSIZE.
- deltaFlags = (1 << (RP_V2_TICK_DATA_BLOCKSIZE)) - 1;
- }
- else
- {
- // NOTE: Test tickData against prevTickData for differences.
- for (int i = 1; i < sizeof(tickData); i++)
- {
- // If the bits in tickData[i] are different to prevTickData[i], then
- // set the corresponding bitflag.
- if (tickData[i] ^ prevTickData[i])
- {
- deltaFlags |= (1 << i);
- }
- }
- }
-
- file.WriteInt32(deltaFlags);
- // NOTE: write only data that has changed since the previous tick.
- for (int i = 1; i < sizeof(tickData); i++)
- {
- int currentFlag = (1 << i);
- if (deltaFlags & currentFlag)
- {
- file.WriteInt32(tickData[i]);
- }
- }
-}
-
-static void FormatRunReplayPath(char[] buffer, int maxlength, int course, int mode, int style, int timeType, bool tempPath)
-{
- // Use GetEngineTime to prevent accidental replay overrides.
- // Technically it would still be possible to override this file by accident,
- // if somehow the server restarts to this exact map and course,
- // and this function is run at the exact same time, but that is extremely unlikely.
- // Also by then this file should have already been deleted.
- char tempTimeString[32];
- Format(tempTimeString, sizeof(tempTimeString), "%f_", GetEngineTime());
- BuildPath(Path_SM, buffer, maxlength,
- "%s/%s/%s%d_%s_%s_%s.%s",
- tempPath ? RP_DIRECTORY_RUNS_TEMP : RP_DIRECTORY_RUNS,
- gC_CurrentMap,
- tempPath ? tempTimeString : "",
- course,
- gC_ModeNamesShort[mode],
- gC_StyleNamesShort[style],
- gC_TimeTypeNames[timeType],
- RP_FILE_EXTENSION);
-}
-
-static void FormatCheaterReplayPath(char[] buffer, int maxlength, int client, int mode, int style)
-{
- BuildPath(Path_SM, buffer, maxlength,
- "%s/%d_%s_%d_%s_%s.%s",
- RP_DIRECTORY_CHEATERS,
- GetSteamAccountID(client),
- gC_CurrentMap,
- GetTime(),
- gC_ModeNamesShort[mode],
- gC_StyleNamesShort[style],
- RP_FILE_EXTENSION);
-}
-
-static void FormatJumpReplayPath(char[] buffer, int maxlength, int client, int jumpType, int mode, int style)
-{
- BuildPath(Path_SM, buffer, maxlength,
- "%s/%d/%d_%s_%s.%s",
- RP_DIRECTORY_JUMPS,
- GetSteamAccountID(client),
- jumpType,
- gC_ModeNamesShort[mode],
- gC_StyleNamesShort[style],
- RP_FILE_EXTENSION);
-}
-
-static void FormatBlockJumpReplayPath(char[] buffer, int maxlength, int client, int block, int jumpType, int mode, int style)
-{
- BuildPath(Path_SM, buffer, maxlength,
- "%s/%d/%s/%d_%d_%s_%s.%s",
- RP_DIRECTORY_JUMPS,
- GetSteamAccountID(client),
- RP_DIRECTORY_BLOCKJUMPS,
- jumpType,
- block,
- gC_ModeNamesShort[mode],
- gC_StyleNamesShort[style],
- RP_FILE_EXTENSION);
-}
-
-static int EncodePlayerFlags(int client, int buttons, int tickCount)
-{
- int flags = 0;
- MoveType movetype = Movement_GetMovetype(client);
- int clientFlags = GetEntityFlags(client);
-
- flags = view_as<int>(movetype) & RP_MOVETYPE_MASK;
-
- SetKthBit(flags, 4, IsBitSet(buttons, IN_ATTACK));
- SetKthBit(flags, 5, IsBitSet(buttons, IN_ATTACK2));
- SetKthBit(flags, 6, IsBitSet(buttons, IN_JUMP));
- SetKthBit(flags, 7, IsBitSet(buttons, IN_DUCK));
- SetKthBit(flags, 8, IsBitSet(buttons, IN_FORWARD));
- SetKthBit(flags, 9, IsBitSet(buttons, IN_BACK));
- SetKthBit(flags, 10, IsBitSet(buttons, IN_LEFT));
- SetKthBit(flags, 11, IsBitSet(buttons, IN_RIGHT));
- SetKthBit(flags, 12, IsBitSet(buttons, IN_MOVELEFT));
- SetKthBit(flags, 13, IsBitSet(buttons, IN_MOVERIGHT));
- SetKthBit(flags, 14, IsBitSet(buttons, IN_RELOAD));
- SetKthBit(flags, 15, IsBitSet(buttons, IN_SPEED));
- SetKthBit(flags, 16, IsBitSet(buttons, IN_USE));
- SetKthBit(flags, 17, IsBitSet(buttons, IN_BULLRUSH));
- SetKthBit(flags, 18, IsBitSet(clientFlags, FL_ONGROUND));
- SetKthBit(flags, 19, IsBitSet(clientFlags, FL_DUCKING));
- SetKthBit(flags, 20, IsBitSet(clientFlags, FL_SWIM));
-
- SetKthBit(flags, 21, GetEntProp(client, Prop_Data, "m_nWaterLevel") != 0);
-
- SetKthBit(flags, 22, isTeleportTick[client]);
- SetKthBit(flags, 23, Movement_GetTakeoffTick(client) == tickCount);
- SetKthBit(flags, 24, GOKZ_GetHitPerf(client));
- SetKthBit(flags, 25, IsCurrentWeaponSecondary(client));
-
- return flags;
-}
-
-// Function to set the bitNum bit in integer to value
-static void SetKthBit(int &number, int offset, bool value)
-{
- int intValue = value ? 1 : 0;
- number |= intValue << offset;
-}
-
-static bool IsBitSet(int number, int checkBit)
-{
- return (number & checkBit) ? true : false;
-}
-
-static int GetPlayerWeaponSlotDefIndex(int client, int slot)
-{
- int ent = GetPlayerWeaponSlot(client, slot);
-
- // Nothing equipped in the slot
- if (ent == -1)
- {
- return -1;
- }
-
- return GetEntProp(ent, Prop_Send, "m_iItemDefinitionIndex");
-}
-
-static bool IsCurrentWeaponSecondary(int client)
-{
- int activeWeaponEnt = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon");
- int secondaryEnt = GetPlayerWeaponSlot(client, CS_SLOT_SECONDARY);
- return activeWeaponEnt == secondaryEnt;
-}
-
-static void CreateReplaysDirectory(const char[] map)
-{
- char path[PLATFORM_MAX_PATH];
-
- // Create parent replay directory
- BuildPath(Path_SM, path, sizeof(path), RP_DIRECTORY);
- if (!DirExists(path))
- {
- CreateDirectory(path, 511);
- }
-
- // Create maps parent replay directory
- BuildPath(Path_SM, path, sizeof(path), "%s", RP_DIRECTORY_RUNS);
- if (!DirExists(path))
- {
- CreateDirectory(path, 511);
- }
-
-
- // Create maps replay directory
- BuildPath(Path_SM, path, sizeof(path), "%s/%s", RP_DIRECTORY_RUNS, map);
- if (!DirExists(path))
- {
- CreateDirectory(path, 511);
- }
-
- // Create maps parent replay directory
- BuildPath(Path_SM, path, sizeof(path), "%s", RP_DIRECTORY_RUNS_TEMP);
- if (!DirExists(path))
- {
- CreateDirectory(path, 511);
- }
-
-
- // Create maps replay directory
- BuildPath(Path_SM, path, sizeof(path), "%s/%s", RP_DIRECTORY_RUNS_TEMP, map);
- if (!DirExists(path))
- {
- CreateDirectory(path, 511);
- }
-
- // Create cheaters replay directory
- BuildPath(Path_SM, path, sizeof(path), "%s", RP_DIRECTORY_CHEATERS);
- if (!DirExists(path))
- {
- CreateDirectory(path, 511);
- }
-
- // Create jumps parent replay directory
- BuildPath(Path_SM, path, sizeof(path), "%s", RP_DIRECTORY_JUMPS);
- if (!DirExists(path))
- {
- CreateDirectory(path, 511);
- }
-}
-
-public void MYAWCheck(QueryCookie cookie, int client, ConVarQueryResult result, const char[] cvarName, const char[] cvarValue, any value)
-{
- if (IsValidClient(client) && !IsFakeClient(client))
- {
- playerMYaw[client] = StringToFloat(cvarValue);
- }
-}
-
-public void SensitivityCheck(QueryCookie cookie, int client, ConVarQueryResult result, const char[] cvarName, const char[] cvarValue, any value)
-{
- if (IsValidClient(client) && !IsFakeClient(client))
- {
- playerSensitivity[client] = StringToFloat(cvarValue);
- }
-}
-
-static int RecordingIndexAdd(int client, int offset)
-{
- int index = recordingIndex[client] + offset;
- if (index < 0)
- {
- index += recordedRecentData[client].Length;
- }
- return index % recordedRecentData[client].Length;
-}
-
-static void RemoveFromRunningTimers(int client, Handle timerToRemove)
-{
- int index = runningJumpstatTimers[client].FindValue(timerToRemove);
- if (index != -1)
- {
- runningJumpstatTimers[client].Erase(index);
- }
-}