From 58252950d4382a957b0d6488e69ae1f2a974c1e0 Mon Sep 17 00:00:00 2001 From: Andrei Alexeyev <0x416b617269@gmail.com> Date: Wed, 18 Apr 2018 02:17:28 +0300 Subject: [PATCH] add replay verification mode: taisei --verify-replay file.tsr --- src/cli.c | 25 +++++++++++++++++++------ src/cli.h | 1 + src/env.h | 2 ++ src/framerate.c | 7 ++++++- src/global.c | 6 +++++- src/global.h | 4 +++- src/main.c | 38 +++++++++++++++++++++++++++++--------- src/replay.c | 13 ++++++++++--- 8 files changed, 75 insertions(+), 21 deletions(-) diff --git a/src/cli.c b/src/cli.c index e0aa6102..b009398b 100644 --- a/src/cli.c +++ b/src/cli.c @@ -41,8 +41,9 @@ static void print_help(struct TsOption* opts) { } int cli_args(int argc, char **argv, CLIAction *a) { - struct TsOption taisei_opts[] = - {{{"replay", required_argument, 0, 'r'}, "Play a replay from %s", "FILE"}, + struct TsOption taisei_opts[] = { + {{"replay", required_argument, 0, 'r'}, "Play a replay from %s", "FILE"}, + {{"verify-replay", required_argument, 0, 'R'}, "Play a replay from %s in headless mode, crash as soon as it desyncs", "FILE"}, #ifdef DEBUG {{"play", no_argument, 0, 'p'}, "Play a specific stage", 0}, {{"sid", required_argument, 0, 'i'}, "Select stage by %s", "ID"}, @@ -105,6 +106,10 @@ int cli_args(int argc, char **argv, CLIAction *a) { a->type = CLI_PlayReplay; a->filename = strdup(optarg); break; + case 'R': + a->type = CLI_VerifyReplay; + a->filename = strdup(optarg); + break; case 'p': a->type = CLI_SelectStage; break; @@ -162,10 +167,18 @@ int cli_args(int argc, char **argv, CLIAction *a) { } if(stageid) { - if(a->type != CLI_PlayReplay && a->type != CLI_SelectStage) { - log_warn("--sid was ignored"); - } else if(!stage_get(stageid)) { - log_fatal("Invalid stage id: %X", stageid); + switch(a->type) { + case CLI_PlayReplay: + case CLI_VerifyReplay: + case CLI_SelectStage: + if(stage_get(stageid) == NULL) { + log_fatal("Invalid stage id: %X", stageid); + } + break; + + default: + log_warn("--sid was ignored"); + break; } } diff --git a/src/cli.h b/src/cli.h index 77b659be..df034979 100644 --- a/src/cli.h +++ b/src/cli.h @@ -14,6 +14,7 @@ typedef enum { CLI_RunNormally = 0, CLI_PlayReplay, + CLI_VerifyReplay, CLI_SelectStage, CLI_DumpStages, CLI_DumpVFSTree, diff --git a/src/env.h b/src/env.h index 8ef22e7a..eb6649bc 100644 --- a/src/env.h +++ b/src/env.h @@ -31,6 +31,7 @@ void env_set_double(const char *var, double val, bool override) const char* : env_get_string, \ char* : env_get_string, \ void* : env_get_string, \ + bool : env_get_int, \ int8_t : env_get_int, \ uint8_t : env_get_int, \ int16_t : env_get_int, \ @@ -47,6 +48,7 @@ void env_set_double(const char *var, double val, bool override) const char* : env_set_string, \ char* : env_set_string, \ void* : env_set_string, \ + bool : env_set_int, \ int8_t : env_set_int, \ uint8_t : env_set_int, \ int16_t : env_set_int, \ diff --git a/src/framerate.c b/src/framerate.c index 8866ae10..7b9893fb 100644 --- a/src/framerate.c +++ b/src/framerate.c @@ -71,6 +71,11 @@ void loop_at_fps(LogicFrameFunc logic_frame, RenderFrameFunc render_frame, void bool uncapped_rendering_env = env_get("TAISEI_FRAMELIMITER_LOGIC_ONLY", 0); bool late_swap = config_get_int(CONFIG_VID_LATE_SWAP); + if(global.is_replay_verification) { + uncapped_rendering_env = false; + delay = 0; + } + uint32_t frame_num = 0; // don't care about thread safety, we can render only on the main thread anyway @@ -153,7 +158,7 @@ begin_frame: fpscounter_update(&global.fps.logic); } - if(!uncapped_rendering && frame_num % get_effective_frameskip()) { + if((!uncapped_rendering && frame_num % get_effective_frameskip()) || global.is_replay_verification) { rframe_action = RFRAME_DROP; } else { rframe_action = render_frame(arg); diff --git a/src/global.c b/src/global.c index 791a99b3..0d3c0602 100644 --- a/src/global.c +++ b/src/global.c @@ -25,7 +25,11 @@ void init_global(CLIAction *cli) { global.replaymode = REPLAY_RECORD; global.frameskip = cli->frameskip; - if(global.frameskip) { + if(cli->type == CLI_VerifyReplay) { + global.is_headless = true; + global.is_replay_verification = true; + global.frameskip = 1; + } else if(global.frameskip) { log_warn("FPS limiter disabled. Gotta go fast! (frameskip = %i)", global.frameskip); } diff --git a/src/global.h b/src/global.h index b6187edb..b7dadf1b 100644 --- a/src/global.h +++ b/src/global.h @@ -123,7 +123,9 @@ typedef struct { StageInfo *stage; - bool is_practice_mode; + uint is_practice_mode : 1; + uint is_headless : 1; + uint is_replay_verification : 1; } Global; extern Global global; diff --git a/src/main.c b/src/main.c index 40d30f34..07689e64 100644 --- a/src/main.c +++ b/src/main.c @@ -31,8 +31,11 @@ static void taisei_shutdown(void) { log_info("Shutting down"); - config_save(); - progress_save(); + if(!global.is_replay_verification) { + config_save(); + progress_save(); + } + progress_unload(); free_all_refs(); @@ -208,6 +211,7 @@ int main(int argc, char **argv) { Replay replay = {0}; int replay_idx = 0; + bool headless = false; init_log(); @@ -233,7 +237,7 @@ int main(int argc, char **argv) { free_cli_action(&a); return 0; - } else if(a.type == CLI_PlayReplay) { + } else if(a.type == CLI_PlayReplay || a.type == CLI_VerifyReplay) { if(!replay_load_syspath(&replay, a.filename, REPLAY_READ_ALL)) { free_cli_action(&a); return 1; @@ -245,6 +249,10 @@ int main(int argc, char **argv) { free_cli_action(&a); return 1; } + + if(a.type == CLI_VerifyReplay) { + headless = true; + } } else if(a.type == CLI_DumpVFSTree) { vfs_setup(true); @@ -269,14 +277,23 @@ int main(int argc, char **argv) { free_cli_action(&a); vfs_setup(false); - init_log_file(); + + if(headless) { + env_set("SDL_AUDIODRIVER", "dummy", true); + env_set("SDL_VIDEODRIVER", "dummy", true); + env_set("TAISEI_RENDERER", "null", true); + env_set("TAISEI_NOPRELOAD", true, false); + env_set("TAISEI_PRELOAD_REQUIRED", false, false); + } else { + init_log_file(); + } + log_info("%s %s", TAISEI_VERSION_FULL, TAISEI_VERSION_BUILD_TYPE); log_system_specs(); + log_lib_versions(); config_load(); - log_lib_versions(); - init_sdl(); time_init(); init_global(&a); @@ -286,11 +303,14 @@ int main(int argc, char **argv) { init_resources(); r_post_init(); draw_loading_screen(); - audio_init(); + + if(!headless) { + audio_init(); + } + load_resources(); gamepad_init(); progress_load(); - r_shader_standard(); set_transition(TransLoader, 0, FADE_TIME*2); @@ -298,7 +318,7 @@ int main(int argc, char **argv) { atexit(taisei_shutdown); - if(a.type == CLI_PlayReplay) { + if(a.type == CLI_PlayReplay || a.type == CLI_VerifyReplay) { replay_play(&replay, replay_idx); replay_destroy(&replay); return 0; diff --git a/src/replay.c b/src/replay.c index 65ed3f55..999bdf1c 100644 --- a/src/replay.c +++ b/src/replay.c @@ -716,15 +716,22 @@ void replay_stage_check_desync(ReplayStage *stg, int time, uint16_t check, Repla if(mode == REPLAY_PLAY) { if(stg->desync_check && stg->desync_check != check) { - log_warn("Replay desync detected! %u != %u", stg->desync_check, check); + log_warn("Frame %d: replay desync detected! 0x%04x != 0x%04x", time, stg->desync_check, check); stg->desynced = true; + + if(global.is_replay_verification) { + // log_fatal("Replay verification failed"); + exit(1); + } + } else if(global.is_replay_verification) { + log_info("Frame %d: 0x%04x OK", time, check); } else { - log_debug("%u OK", check); + log_debug("Frame %d: 0x%04x OK", time, check); } } #ifdef REPLAY_WRITE_DESYNC_CHECKS else { - // log_debug("%u", check); + // log_debug("0x%04x", check); replay_stage_event(stg, time, EV_CHECK_DESYNC, (int16_t)check); } #endif