diff options
Diffstat (limited to 'sourcemod/scripting/gokz-replays/recording.sp')
| -rw-r--r-- | sourcemod/scripting/gokz-replays/recording.sp | 990 |
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); - } -} |
