From 55be92a56f244c1de271013290a2ec423d425a37 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Thu, 19 Mar 2026 10:37:39 -0700 Subject: [PATCH] add game duration to info window --- src/CommandFormats.hh | 8 ++++---- src/Lobby.cc | 1 + src/Lobby.hh | 2 ++ src/Main.cc | 2 +- src/ReceiveCommands.cc | 21 +++++++++++++++++++++ src/ServerState.cc | 3 ++- src/ServerState.hh | 3 ++- 7 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index b3c13967..47c27560 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -3738,10 +3738,10 @@ struct G_UpdateEnemyStateT_6x0A { // 00000800 = is dead (when set for most enemies, plays the death animation and then destroys the enemy) // 00001000 = unknown (TODO: see TObjEnemyV8048ee80_v1C, TObjEnemyV8048ee80_v3B, TObjEneDolmOlm_v3B) // 00002000 = unknown (TODO: see TObjGrass_v1E, 8011EA08, TObjEneIllGill_v1E, TObjEneIllGill_init) - // 00004000 = unknown (TODO: has status effect in slot 5; see TObjectV8047c128_v24_update_paralysis_effect; De Rol - // Le uses this; Vol Opt uses it too at TBoss3Volopt_update, TBoss3VoloptCore_update, - // TBoss3VoloptP01_update, TBoss3VolOptP02_update, TObjectV8047c128_v39, TObjectV8047c128_v38; related - // to paralysis somehow? see TObjectV8047c128_v24_update_paralysis_effect) + // 00004000 = unknown (TODO: has status effect in slot 5; De Rol Le uses this; Vol Opt uses it too at + // TBoss3Volopt_update, TBoss3VoloptCore_update, TBoss3VoloptP01_update, TBoss3VolOptP02_update, + // TObjectV8047c128_v39, TObjectV8047c128_v38; related to paralysis somehow? see + // TObjectV8047c128_v24_update_paralysis_effect) // 00008000 = immune to freeze (TODO: see TBoss3VolOptP02_init_inner; maybe other things use it too) // 00010000 = unknown (TODO: see 801BA1F8) // 00020000 = can't attack, cast techs, or use items (e.g. Vol Opt cage and Ruins falling traps set this) diff --git a/src/Lobby.cc b/src/Lobby.cc index 3a95055f..3d4d14e9 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -145,6 +145,7 @@ uint32_t Lobby::FloorItemManager::reassign_all_item_ids(uint32_t next_item_id) { Lobby::Lobby(shared_ptr s, uint32_t id, bool is_game) : server_state(s), log(std::format("[{}:{:X}] ", is_game ? "Game" : "Lobby", id), lobby_log.min_level), + creation_time(phosg::now()), lobby_id(id), random_seed(phosg::random_object()), rand_crypt(make_shared()), diff --git a/src/Lobby.hh b/src/Lobby.hh index 89f3a89c..da3419bf 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -91,6 +91,8 @@ struct Lobby : public std::enable_shared_from_this { std::weak_ptr server_state; phosg::PrefixedLogger log; + uint64_t creation_time; + uint32_t lobby_id; uint32_t min_level = 0; diff --git a/src/Main.cc b/src/Main.cc index aee51e97..7af63fed 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -3936,7 +3936,7 @@ Action a_run_server_replay_log( use_terminal_colors = true; } - auto state = make_shared(get_config_filename(args)); + auto state = make_shared(get_config_filename(args), !replay_log_filename.empty()); if (args.get("debug")) { state->is_debug = true; } diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 339f58fb..a2ee9615 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -2204,6 +2204,27 @@ static asio::awaitable on_09(shared_ptr c, Channel::Message& msg) // If page 1 is blank (there are no players) or we sent page 1 last time, send page 2 (extended info) if (info.empty()) { c->last_game_info_requested = 0; + + // Suppress the time field during replays, since it will not contain the same value as when the test was + // recorded + if (!s->is_replay) { + uint64_t seconds_ago = (phosg::now() - game->creation_time) / 1000000ULL; + uint64_t minutes_ago = seconds_ago / 60; + uint64_t hours_ago = minutes_ago / 60; + uint64_t days_ago = hours_ago / 24; + if (seconds_ago < 1) { + info = "Time: <1s\n"; + } else if (seconds_ago < 60) { + info = std::format("Time: {}s\n", seconds_ago); + } else if (minutes_ago < 60) { + info = std::format("Time: {}m{}s\n", minutes_ago, seconds_ago); + } else if (hours_ago < 24) { + info = std::format("Time: {}h{}m{}s\n", hours_ago, minutes_ago, seconds_ago); + } else { + info = std::format("Time: {}d{}h{}m{}s\n", days_ago, hours_ago, minutes_ago, seconds_ago); + } + } + uint8_t effective_section_id = game->effective_section_id(); if (effective_section_id < 10) { info += std::format("Section ID: {}\n", name_for_section_id(effective_section_id)); diff --git a/src/ServerState.cc b/src/ServerState.cc index 09fccf7c..7620e293 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -73,10 +73,11 @@ ServerState::QuestF960Result::QuestF960Result( } } -ServerState::ServerState(const string& config_filename) +ServerState::ServerState(const string& config_filename, bool is_replay) : creation_time(phosg::now()), io_context(make_shared(1)), config_filename(config_filename), + is_replay(is_replay), thread_pool(make_unique()), bb_stream_files_cache(new FileContentsCache(3600000000ULL)), bb_system_cache(new FileContentsCache(3600000000ULL)), diff --git a/src/ServerState.hh b/src/ServerState.hh index 46c61431..0531c1ac 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -91,6 +91,7 @@ struct ServerState : public std::enable_shared_from_this { std::shared_ptr config_json; bool one_time_config_loaded = false; bool default_lobbies_created = false; + bool is_replay = false; size_t num_worker_threads = 0; std::unique_ptr thread_pool; @@ -308,7 +309,7 @@ struct ServerState : public std::enable_shared_from_this { std::unordered_map proxy_persistent_configs; - explicit ServerState(const std::string& config_filename = ""); + explicit ServerState(const std::string& config_filename = "", bool is_replay = false); ServerState(const ServerState&) = delete; ServerState(ServerState&&) = delete; ServerState& operator=(const ServerState&) = delete;