From 3edae98df2c603a1ad102ca9f4f516c63754c28a Mon Sep 17 00:00:00 2001 From: drjaska Date: Sat, 7 May 2022 15:21:20 +0300 Subject: [PATCH] Finish savestate implementation for now, hooked up savestate saving for each checkpoint triggered --- .../common/gamemodes/gamemode/ctscup/TODO.txt | 7 +- .../common/gamemodes/gamemode/ctscup/_mod.inc | 1 - .../common/gamemodes/gamemode/ctscup/_mod.qh | 1 - .../gamemodes/gamemode/ctscup/savestate.qc | 69 ------------------- .../gamemodes/gamemode/ctscup/sv_ctscup.qc | 22 +++++- .../gamemodes/gamemode/ctscup/sv_ctscup.qh | 3 +- qcsrc/server/_mod.inc | 1 + qcsrc/server/_mod.qh | 1 + qcsrc/server/mutators/events.qh | 5 ++ qcsrc/server/race.qc | 2 + qcsrc/server/savestate.qc | 66 ++++++++++++++++++ .../gamemode/ctscup => server}/savestate.qh | 6 +- 12 files changed, 101 insertions(+), 83 deletions(-) delete mode 100644 qcsrc/common/gamemodes/gamemode/ctscup/savestate.qc create mode 100644 qcsrc/server/savestate.qc rename qcsrc/{common/gamemodes/gamemode/ctscup => server}/savestate.qh (72%) diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/TODO.txt b/qcsrc/common/gamemodes/gamemode/ctscup/TODO.txt index 581582f8c..898d1db1a 100644 --- a/qcsrc/common/gamemodes/gamemode/ctscup/TODO.txt +++ b/qcsrc/common/gamemodes/gamemode/ctscup/TODO.txt @@ -6,15 +6,14 @@ Round handler: - Clear times upon round reset Savestates: -- Finish implementating savestates -- Hook up savestate saving to touching a checkpoint -- Delete savestates upon round reset +- Softlock prevention +- FIXME?: Splat damage is dealt AFTER health restoring of savestate loading if the player were to smash into something and die from another source during I think the same frame - I hope that loading a savestate from Damage_Calculate hook when player is about to die and setting incoming damage to 0 can still save them and dying (for example by suicide bind) isn't a hardcoded death :) HUD: - Display current round's players -- Display splits +- Display checkpoint splits - Display how many players are about to lose this round and show that on splits - Maybe show eliminated players on leaderboard, there should be a difference between 2nd place and Nth place for bragging rights - Medals for placements? diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/_mod.inc b/qcsrc/common/gamemodes/gamemode/ctscup/_mod.inc index d57a6255e..89d6a2c7a 100644 --- a/qcsrc/common/gamemodes/gamemode/ctscup/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/ctscup/_mod.inc @@ -6,4 +6,3 @@ #ifdef SVQC #include #endif -#include diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/_mod.qh b/qcsrc/common/gamemodes/gamemode/ctscup/_mod.qh index 6c08b79f4..e9459a017 100644 --- a/qcsrc/common/gamemodes/gamemode/ctscup/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/ctscup/_mod.qh @@ -6,4 +6,3 @@ #ifdef SVQC #include #endif -#include diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/savestate.qc b/qcsrc/common/gamemodes/gamemode/ctscup/savestate.qc deleted file mode 100644 index e07c55e3a..000000000 --- a/qcsrc/common/gamemodes/gamemode/ctscup/savestate.qc +++ /dev/null @@ -1,69 +0,0 @@ -//these files can be moved but while CTS Cup is WIP I'll keep them in CTS Cup's gamemode folder -//files to update if moving: sv_ctscup.qh, ctscup folder's _mod files - -#include "savestate.qh" - -void SaveSaveState(entity player) -{ - if (player.savestate) - { - player.savestate.origin = player.origin; - player.savestate.v_angle = player.v_angle; - player.savestate.angles = player.angles; - player.savestate.velocity = player.velocity; - //SetResource(player.savestate, RES_ROCKETS, GetResource(player, RES_ROCKETS)); - //SetResource(player.savestate, RES_BULLETS, GetResource(player, RES_BULLETS)); - //SetResource(player.savestate, RES_CELLS, GetResource(player, RES_CELLS)); - //SetResource(player.savestate, RES_PLASMA, GetResource(player, RES_PLASMA)); - //SetResource(player.savestate, RES_SHELLS, GetResource(player, RES_SHELLS)); - //SetResource(player.savestate, RES_FUEL, GetResource(player, RES_FUEL)); - //SetResource(player.savestate, RES_HEALTH, max(1, GetResource(player, RES_HEALTH))); - //SetResource(player.savestate, RES_ARMOR, GetResource(player, RES_ARMOR)); - //STAT(WEAPONS, player.savestate) = STAT(WEAPONS, player); - //StatusEffects_copy(player.statuseffects, player.savestate, 0); - player.savestate.items = player.items; - player.savestate.pauserotarmor_finished = player.pauserotarmor_finished; - player.savestate.pauserothealth_finished = player.pauserothealth_finished; - player.savestate.pauserotfuel_finished = player.pauserotfuel_finished; - player.savestate.pauseregen_finished = player.pauseregen_finished; - player.savestate.teleport_time = time; - } -} - -bool LoadSaveState(entity player) -{ - if (player.savestate) - { - player.origin = player.savestate.origin; - player.v_angle = player.savestate.v_angle; - player.angles = player.savestate.angles; - player.fixangle = true; - player.velocity = player.savestate.velocity; - //SetResource(player, RES_ROCKETS, GetResource(player.savestate, RES_ROCKETS)); - //SetResource(player, RES_BULLETS, GetResource(player.savestate, RES_BULLETS)); - //SetResource(player, RES_CELLS, GetResource(player.savestate, RES_CELLS)); - //SetResource(player, RES_PLASMA, GetResource(player.savestate, RES_PLASMA)); - //SetResource(player, RES_SHELLS, GetResource(player.savestate, RES_SHELLS)); - //SetResource(player, RES_FUEL, GetResource(player.savestate, RES_FUEL)); - //SetResource(player, RES_HEALTH, GetResource(player.savestate, RES_HEALTH)); - //SetResource(player, RES_ARMOR, GetResource(player.savestate, RES_ARMOR)); - //STAT(WEAPONS, player) = STAT(WEAPONS, player.savestate); - //StatusEffects_copy(player.savestate, player.statuseffects, player.savestate.teleport_time); - //StatusEffects_update(player); - player.items = player.savestate.items; - player.pauserotarmor_finished = time + player.savestate.pauserotarmor_finished - player.savestate.teleport_time; - player.pauserothealth_finished = time + player.savestate.pauserothealth_finished - player.savestate.teleport_time; - player.pauserotfuel_finished = time + player.savestate.pauserotfuel_finished - player.savestate.teleport_time; - player.pauseregen_finished = time + player.savestate.pauseregen_finished - player.savestate.teleport_time; - - return true; - } - return false; -} - -void DeleteSaveState(entity player) -{ - if (player.savestate) - delete(player.savestate); -} - diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qc b/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qc index f801ebb4c..80808ea56 100644 --- a/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qc +++ b/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qc @@ -287,8 +287,15 @@ MUTATOR_HOOKFUNCTION(ctscup, PlayerSpawn) SaveSaveState(player); } - // upon spawning for a new round within 1s of the round starting, save a savestate - if ((time - 1) < (round_handler_GetEndTime() - autocvar_g_ctscup_maxroundlength)) + //debug print + //print(sprintf("%f", time), " time \n"); + //print(sprintf("%f", round_handler_GetEndTime()), " round_handler_GetEndTime() \n"); + //print(sprintf("%f", (roundFirstFinisherTime + autocvar_g_ctscup_finishwait)), " (roundFirstFinisherTime + autocvar_g_ctscup_finishwait) \n\n"); + + // upon spawning for a new round, save a savestate instead of loading an old one + // for whatever reason on the frame players are reset and spawned for a new round to start + // round_handler_GetEndTime() still gives the end time for last round and time should be bigger than that + if ((time > round_handler_GetEndTime()) || (roundFirstFinisherTime && (time > (roundFirstFinisherTime + autocvar_g_ctscup_finishwait)))) { SaveSaveState(player); } @@ -377,6 +384,8 @@ bool CTSCUP_CanRoundStart() void CTSCUP_RoundStart() { + roundFirstFinisherTime = 0; + CTSCUP_AliveParticipants(); } @@ -425,7 +434,7 @@ void CTSCUP_TournamentStart() MUTATOR_HOOKFUNCTION(ctscup, reset_map_players) { - roundFirstFinisherTime = 0; + // roundFirstFinisherTime = 0; if (tournamentStarted) roundCounter++; else CTSCUP_TournamentStart(); @@ -465,6 +474,13 @@ MUTATOR_HOOKFUNCTION(ctscup, ForbidSpawn) return canSpawn; } +MUTATOR_HOOKFUNCTION(ctscup, Race_Checkpoint) +{ + entity player = M_ARGV(0, entity); + + SaveSaveState(player); +} + MUTATOR_HOOKFUNCTION(ctscup, ClientCommand_Spectate) { entity player = M_ARGV(0, entity); diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qh b/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qh index 943db58d9..4378c20dd 100644 --- a/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qh +++ b/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qh @@ -1,9 +1,8 @@ #pragma once -#include "savestate.qh" - #include #include +#include #include void ctscup_Initialize(); diff --git a/qcsrc/server/_mod.inc b/qcsrc/server/_mod.inc index c82e892f7..eb02899b1 100644 --- a/qcsrc/server/_mod.inc +++ b/qcsrc/server/_mod.inc @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/server/_mod.qh b/qcsrc/server/_mod.qh index 52574efec..716594c24 100644 --- a/qcsrc/server/_mod.qh +++ b/qcsrc/server/_mod.qh @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index 6feb81132..5f4010e78 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -905,6 +905,11 @@ MUTATOR_HOOKABLE(DecodeLevelParms, EV_NO_ARGS); /**/ MUTATOR_HOOKABLE(GetRecords, EV_GetRecords); +#define EV_Race_Checkpoint(i, o) \ + /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /**/ +MUTATOR_HOOKABLE(Race_Checkpoint, EV_Race_Checkpoint); + #define EV_Race_FinalCheckpoint(i, o) \ /** player */ i(entity, MUTATOR_ARGV_0_entity) \ /**/ diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index 24a25f87f..f3af86b48 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -790,6 +790,8 @@ void checkpoint_passed(entity this, entity player) } fclose(fh); } + + MUTATOR_CALLHOOK(Race_Checkpoint, player); } else if(player.race_checkpoint == race_NextCheckpoint(this.race_checkpoint)) { diff --git a/qcsrc/server/savestate.qc b/qcsrc/server/savestate.qc new file mode 100644 index 000000000..ef08083bb --- /dev/null +++ b/qcsrc/server/savestate.qc @@ -0,0 +1,66 @@ +#include "savestate.qh" + +void SaveSaveState(entity player) +{ + if (player.savestate) + { + player.savestate.origin = player.origin; + player.savestate.v_angle = player.v_angle; + player.savestate.angles = player.angles; + player.savestate.velocity = player.velocity; + SetResource(player.savestate, RES_ROCKETS, GetResource(player, RES_ROCKETS)); + SetResource(player.savestate, RES_BULLETS, GetResource(player, RES_BULLETS)); + SetResource(player.savestate, RES_CELLS, GetResource(player, RES_CELLS)); + SetResource(player.savestate, RES_PLASMA, GetResource(player, RES_PLASMA)); + SetResource(player.savestate, RES_SHELLS, GetResource(player, RES_SHELLS)); + SetResource(player.savestate, RES_FUEL, GetResource(player, RES_FUEL)); + SetResource(player.savestate, RES_HEALTH, max(1, GetResource(player, RES_HEALTH))); + SetResource(player.savestate, RES_ARMOR, GetResource(player, RES_ARMOR)); + STAT(WEAPONS, player.savestate) = STAT(WEAPONS, player); + StatusEffects_copy(player.statuseffects, player.savestate, 0); + player.savestate.items = player.items; + player.savestate.pauserotarmor_finished = player.pauserotarmor_finished; + player.savestate.pauserothealth_finished = player.pauserothealth_finished; + player.savestate.pauserotfuel_finished = player.pauserotfuel_finished; + player.savestate.pauseregen_finished = player.pauseregen_finished; + player.savestate.teleport_time = time; + } +} + +bool LoadSaveState(entity player) +{ + if (player.savestate) + { + player.origin = player.savestate.origin; + player.v_angle = player.savestate.v_angle; + player.angles = player.savestate.angles; + player.fixangle = true; + player.velocity = player.savestate.velocity; + SetResource(player, RES_ROCKETS, GetResource(player.savestate, RES_ROCKETS)); + SetResource(player, RES_BULLETS, GetResource(player.savestate, RES_BULLETS)); + SetResource(player, RES_CELLS, GetResource(player.savestate, RES_CELLS)); + SetResource(player, RES_PLASMA, GetResource(player.savestate, RES_PLASMA)); + SetResource(player, RES_SHELLS, GetResource(player.savestate, RES_SHELLS)); + SetResource(player, RES_FUEL, GetResource(player.savestate, RES_FUEL)); + SetResource(player, RES_HEALTH, GetResource(player.savestate, RES_HEALTH)); + SetResource(player, RES_ARMOR, GetResource(player.savestate, RES_ARMOR)); + STAT(WEAPONS, player) = STAT(WEAPONS, player.savestate); + StatusEffects_copy(player.savestate, player.statuseffects, player.savestate.teleport_time); + StatusEffects_update(player); + player.items = player.savestate.items; + player.pauserotarmor_finished = time + player.savestate.pauserotarmor_finished - player.savestate.teleport_time; + player.pauserothealth_finished = time + player.savestate.pauserothealth_finished - player.savestate.teleport_time; + player.pauserotfuel_finished = time + player.savestate.pauserotfuel_finished - player.savestate.teleport_time; + player.pauseregen_finished = time + player.savestate.pauseregen_finished - player.savestate.teleport_time; + + return true; + } + return false; +} + +void DeleteSaveState(entity player) +{ + if (player.savestate) + delete(player.savestate); +} + diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/savestate.qh b/qcsrc/server/savestate.qh similarity index 72% rename from qcsrc/common/gamemodes/gamemode/ctscup/savestate.qh rename to qcsrc/server/savestate.qh index eac6517a9..720ce4eb2 100644 --- a/qcsrc/common/gamemodes/gamemode/ctscup/savestate.qh +++ b/qcsrc/server/savestate.qh @@ -1,6 +1,6 @@ -//#include -//#include -//#include +#include +#include +#include .entity savestate; -- 2.39.2