From 574860da72615ea64afaf2186fb8a6c464c402c7 Mon Sep 17 00:00:00 2001 From: AyeZeeBB Date: Thu, 8 Oct 2026 22:39:05 -0500 Subject: [PATCH] Binds for first person, the HUD, voice chat, time of day, No Bail, challenges, nametags and dots Binds for first person, the HUD, voice chat, time of day, No Bail, challenges, nametags and dots Eight more actions can be put on a keyboard key, a chord or a controller combo, in the binds list beside the others. None is bound until the player binds it. - First person, Hide HUD, Voice chat on / off, Next time of day, No Bail, Show / hide challenges, Player nametags, Player dots. - Each bind runs the console switch for it, so it follows the same rules as typing it: No Bail still answers to a server that has it off, and the time of day goes to the server for an admin where the server sets it. - They are one table (action_binds): a bind is its name in the profile, the name "bind " takes, its label and its command. - "bind firstperson|hidehud|voicechat|tod|nobail|challenges|nametags| nametagdots " sets one from the console; 0 clears it. - They are not read while the menu, the console or the chat box is open. Console, which the binds use and anyone can type: - Every on/off switch takes "toggle" ("nobail toggle", "hideui toggle"). - "firstperson 0|1", which was only in the menu. - "tod next": the time of day after the current one. - "mp voice-chat on|off|toggle": the player's own voice chat, leaving the rest of their voice settings; it says which way it went in their chat. --- Engine/Core/Console/command_registry.h | 2 ++ Engine/Game/Input/controller_bindings.h | 16 ++++++++++++++++ .../Multiplayer/Session/session_commands.cpp | 14 +++++++++++++- Extension/Multiplayer/console_commands.cpp | 2 ++ Extension/Profile/local_profile.h | 4 ++++ Extension/Profile/local_profile_preferences.cpp | 13 +++++++++++++ Extension/Profile/local_profile_runtime.h | 2 ++ Extension/Profile/local_profile_store.cpp | 12 ++++++++++++ Extension/Profile/profile_update.cpp | 1 + Extension/Settings/input_bindings.cpp | 1 + Extension/Skater/console_commands.cpp | 17 +++++++++++++++++ Extension/UI/Overlay/skate_menu_settings.cpp | 4 +++- Extension/World/console_commands.cpp | 16 ++++++++++++++-- Runtime/client_tick.cpp | 13 ++++++++++++- Runtime/runtime_internal.h | 1 + 15 files changed, 113 insertions(+), 5 deletions(-) diff --git a/Engine/Core/Console/command_registry.h b/Engine/Core/Console/command_registry.h index 33be1c1..841aa0b 100644 --- a/Engine/Core/Console/command_registry.h +++ b/Engine/Core/Console/command_registry.h @@ -215,6 +215,8 @@ template class Registry { token += ' '; token += args[index++]; } + // "toggle" on an on/off switch: the other of what it is now. + if (arg.type == Type::boolean && equal(token, "toggle")) token = current.value && *current.value == "1" ? "0" : "1"; auto value = parse_value(arg.type, token); if (!value) { output("error: Invalid " + arg.name + ": '" + token + "'. Usage: " + usage(*entry)); diff --git a/Engine/Game/Input/controller_bindings.h b/Engine/Game/Input/controller_bindings.h index 6b5ca87..639ed1e 100644 --- a/Engine/Game/Input/controller_bindings.h +++ b/Engine/Game/Input/controller_bindings.h @@ -84,6 +84,21 @@ struct ControllerInput { ControllerStyle style{}; std::array keys{}; }; +// Switches a player can put on a button or key: each runs a console command when pressed. +// `key` is its name in the profile, `name` what "bind " takes. +struct ActionBind { + std::string_view key, name, label, command; +}; +inline constexpr std::array action_binds{{ + {"first_person", "firstperson", "First person", "firstperson toggle"}, + {"hide_hud", "hidehud", "Hide HUD", "hideui toggle"}, + {"voice_chat", "voicechat", "Voice chat on / off", "mp voice-chat toggle"}, + {"time_of_day", "tod", "Next time of day", "tod next"}, + {"no_bail", "nobail", "No Bail", "nobail toggle"}, + {"challenges", "challenges", "Show / hide challenges", "challenges toggle"}, + {"nametags", "nametags", "Player nametags", "mp nametags toggle"}, + {"nametag_dots", "nametagdots", "Player dots", "mp nametag-dots toggle"}, +}}; struct ControllerBindingsModel { bool available{}; bool freecam_controller{}; @@ -95,6 +110,7 @@ struct ControllerBindingsModel { std::uint32_t up_velocity_combo{}; std::uint32_t offboard_up_velocity_combo{}; std::uint32_t vote_yes_combo{}, vote_no_combo{}; // answering a dedicated server's vote + std::array action_combos{}; // by action_binds std::string status; }; inline std::string controller_combo_label(std::uint32_t value, ControllerStyle style = ControllerStyle::xbox) { diff --git a/Extension/Multiplayer/Session/session_commands.cpp b/Extension/Multiplayer/Session/session_commands.cpp index 2336a69..e497a3e 100644 --- a/Extension/Multiplayer/Session/session_commands.cpp +++ b/Extension/Multiplayer/Session/session_commands.cpp @@ -616,7 +616,7 @@ bool queue_command(std::string_view action, std::string_view argument, std::stri if ((action != "host" && action != "host-config" && action != "join" && action != "join-lobby" && action != "join-friend-lobby" && action != "stop" && action != "distances" && action != "object-placement" && action != "object-limit" && action != "kick" && action != "clear-objects" && action != "nametags" && action != "chat-visible" && action != "chat-filter" && - action != "nametag-distance" && action != "nametag-dots" && action != "nametags-friends" && action != "player-distance" && action != "direct-connections" && action != "pose-dump" && action != "vote" && + action != "nametag-distance" && action != "nametag-dots" && action != "nametags-friends" && action != "player-distance" && action != "direct-connections" && action != "pose-dump" && action != "vote" && action != "voice-chat" && action != "chat-bubbles" && action != "chat-bubbles-own" && action != "chat-bubbles-distance" && action != "chat-bubbles-duration" && action != "chat-bubbles-history" && !own_mark_command(action) && @@ -741,6 +741,18 @@ std::string command(std::string_view action, std::string_view argument, std::str publish(s); return value.enabled ? (value.open_mic ? "Open microphone enabled." : "Push-to-talk voice enabled.") : "Voice chat disabled."; } + if (action == "voice-chat") { + // The player's own voice chat, on or off; the rest of their voice settings stay. + if (argument != "on" && argument != "off" && argument != "toggle") return "Choose on, off or toggle."; + auto value = s.voice_settings; + value.enabled = argument == "toggle" ? !value.enabled : argument == "on"; + s.voice_settings = value; + s.voice.configure(value); + publish(s); + const std::string result = value.enabled ? "Voice chat on." : "Voice chat off."; + add_chat(s, 0, "ReSkate", result); + return result; + } if (action == "tp") return teleport_self(s, argument); if (action == "tpall" || action == "tphere") return teleport_players(s, action, argument); if (action == "voice-allow") { diff --git a/Extension/Multiplayer/console_commands.cpp b/Extension/Multiplayer/console_commands.cpp index 9a34114..2962ea0 100644 --- a/Extension/Multiplayer/console_commands.cpp +++ b/Extension/Multiplayer/console_commands.cpp @@ -33,6 +33,7 @@ void register_multiplayer_commands(Commands ®istry) { Command{"nametags", "Show or hide player nametags (on, off, toggle)"}, Command{"pose-dump", "Research: record your own poses for a number of seconds (1-600) to logs/poses-*.bin"}, Command{"vote", "Answer the vote a dedicated server is running (yes, no)"}, + Command{"voice-chat", "Your own voice chat (on, off, toggle)"}, Command{"direct-connections", "Connect straight to servers that offer it, not through Steam's relays (on, off, toggle)"}, Command{"player-distance", "How far away another player still gets a skater, in metres (50-1000; 1000: every player). Past it: a nametag or dot"}, Command{"nametag-distance", "How far away a player's name still shows, in metres (10-500); past it they are a dot"}, @@ -61,6 +62,7 @@ void register_multiplayer_commands(Commands ®istry) { std::string_view(c.name) == "chat-bubbles" || std::string_view(c.name) == "chat-bubbles-own" || std::string_view(c.name) == "nametag-dots" || std::string_view(c.name) == "nametags-friends" || std::string_view(c.name) == "direct-connections" || std::string_view(c.name) == "vote" || + std::string_view(c.name) == "voice-chat" || std::string_view(c.name) == "score-check") args.push_back(argument("choice")); if (std::string_view(c.name) == "chat-bubbles-distance" || std::string_view(c.name) == "nametag-distance") diff --git a/Extension/Profile/local_profile.h b/Extension/Profile/local_profile.h index a1752d2..2380032 100644 --- a/Extension/Profile/local_profile.h +++ b/Extension/Profile/local_profile.h @@ -117,6 +117,8 @@ bool freecam_controller(const Snapshot&); std::uint32_t freecam_binding(const Snapshot&); std::uint32_t tp_to_freecam_binding(const Snapshot&); std::uint32_t vote_yes_binding(const Snapshot&); +// One of action_binds, by its key; 0: not bound. +std::uint32_t action_binding(const Snapshot&, std::string_view key); std::uint32_t vote_no_binding(const Snapshot&); std::uint32_t noclip_binding(const Snapshot&); std::uint32_t forward_velocity_binding(const Snapshot&); @@ -195,6 +197,8 @@ public: void save_tp_to_freecam_binding(std::uint32_t); std::uint32_t vote_binding(bool yes) const; void save_vote_binding(bool yes, std::uint32_t); + std::uint32_t action_binding(std::string_view key) const; + void save_action_binding(std::string_view key, std::uint32_t); std::uint32_t noclip_binding() const; void save_noclip_binding(std::uint32_t); std::uint32_t forward_velocity_binding() const; diff --git a/Extension/Profile/local_profile_preferences.cpp b/Extension/Profile/local_profile_preferences.cpp index 6ec8d98..352ea2c 100644 --- a/Extension/Profile/local_profile_preferences.cpp +++ b/Extension/Profile/local_profile_preferences.cpp @@ -19,6 +19,7 @@ ControllerBindingsModel local_profile_controller_bindings() { result.offboard_up_velocity_combo = s.store->offboard_up_velocity_binding(); result.vote_yes_combo = s.store->vote_binding(true); result.vote_no_combo = s.store->vote_binding(false); + for (std::size_t i = 0; i < action_binds.size(); ++i) result.action_combos[i] = s.store->action_binding(action_binds[i].key); result.available = true; return result; } @@ -68,6 +69,18 @@ bool set_local_freecam_binding(std::uint32_t combo) { } catch (...) { binding_feedback = "Could not save the binding. See the console log."; return false; } } +bool set_local_action_binding(std::size_t index, std::uint32_t combo) { + auto& s = local_runtime(); std::lock_guard lock(s.native_mutex); + if (!s.active || !s.store || index >= action_binds.size() || !valid_action_binding(combo)) return false; + try { + const auto& slot = action_binds[index]; + s.store->save_action_binding(slot.key, combo); + binding_feedback = std::string(slot.label) + (combo ? " binding saved." : " binding cleared."); + dingosdk::logging::event(dingosdk::logging::Channel::profile, dingosdk::Json{{"event","controller_binding_saved"},{"action",std::string(slot.key)},{"combo",combo}}.dump().c_str()); + return true; + } catch (...) { binding_feedback = "Could not save the binding. See the console log."; return false; } +} + bool set_local_vote_binding(bool yes, std::uint32_t combo) { auto& s = local_runtime(); std::lock_guard lock(s.native_mutex); if (!s.active || !s.store || !valid_action_binding(combo)) return false; diff --git a/Extension/Profile/local_profile_runtime.h b/Extension/Profile/local_profile_runtime.h index 07d0b70..6f72898 100644 --- a/Extension/Profile/local_profile_runtime.h +++ b/Extension/Profile/local_profile_runtime.h @@ -67,6 +67,8 @@ bool set_local_freecam_binding(std::uint32_t); bool set_local_tp_to_freecam_binding(std::uint32_t); // The player's binding for Yes (or No) in a dedicated server's vote; 0 clears it. bool set_local_vote_binding(bool yes, std::uint32_t); +// One of action_binds (controller_bindings.h), by its place there; 0 clears it. +bool set_local_action_binding(std::size_t index, std::uint32_t); bool set_local_noclip_binding(std::uint32_t); bool set_local_forward_velocity_binding(std::uint32_t); bool set_local_up_velocity_binding(std::uint32_t); diff --git a/Extension/Profile/local_profile_store.cpp b/Extension/Profile/local_profile_store.cpp index c9d3b17..4df82de 100644 --- a/Extension/Profile/local_profile_store.cpp +++ b/Extension/Profile/local_profile_store.cpp @@ -217,6 +217,18 @@ void Store::save_forward_velocity_binding(std::uint32_t combo) { update.commit(); } +std::uint32_t Store::action_binding(std::string_view key) const { + std::lock_guard lock(mutex_); + return profile::action_binding(value_, key); +} +void Store::save_action_binding(std::string_view key, std::uint32_t combo) { + require(valid_action_binding(combo), "Unsupported controller combo or keyboard key"); + const std::string name(key); + std::lock_guard lock(mutex_); + Update update(*this); + update.json(value_.settings, {"bindings", name.c_str()}) = combo; + update.commit(); +} std::uint32_t Store::vote_binding(bool yes) const { std::lock_guard lock(mutex_); return yes ? profile::vote_yes_binding(value_) : profile::vote_no_binding(value_); diff --git a/Extension/Profile/profile_update.cpp b/Extension/Profile/profile_update.cpp index 12231dc..eed85b6 100644 --- a/Extension/Profile/profile_update.cpp +++ b/Extension/Profile/profile_update.cpp @@ -70,6 +70,7 @@ void Store::Update::encode(Json& patch) { (void)world_controls(value); (void)graphics_controls(value); (void)profile::freecam_controller(value); (void)profile::freecam_controller_binding(value); (void)profile::freecam_binding(value); (void)profile::tp_to_freecam_binding(value); (void)profile::noclip_binding(value); (void)profile::forward_velocity_binding(value); (void)profile::vote_yes_binding(value); (void)profile::vote_no_binding(value); + for (const auto& slot : action_binds) (void)profile::action_binding(value, slot.key); (void)profile::up_velocity_binding(value); (void)profile::offboard_up_velocity_binding(value); remove("player_settings", [](const Json& row) { return row[0] != "options"; }); diff --git a/Extension/Settings/input_bindings.cpp b/Extension/Settings/input_bindings.cpp index a8e9d6c..bab6567 100644 --- a/Extension/Settings/input_bindings.cpp +++ b/Extension/Settings/input_bindings.cpp @@ -95,6 +95,7 @@ static std::uint32_t vote_binding(const Snapshot& s, const char* key, std::uint3 "Unsupported controller or keyboard key in a vote binding"); return static_cast(mask); } +std::uint32_t action_binding(const Snapshot& s, std::string_view key) { return vote_binding(s, std::string(key).c_str(), 0); } std::uint32_t vote_yes_binding(const Snapshot& s) { return vote_binding(s, "vote_yes", default_vote_yes_binding); } std::uint32_t vote_no_binding(const Snapshot& s) { return vote_binding(s, "vote_no", default_vote_no_binding); } std::uint32_t offboard_up_velocity_binding(const Snapshot& s) { diff --git a/Extension/Skater/console_commands.cpp b/Extension/Skater/console_commands.cpp index b4c55fb..6e5de87 100644 --- a/Extension/Skater/console_commands.cpp +++ b/Extension/Skater/console_commands.cpp @@ -20,6 +20,8 @@ void register_movement_commands(Commands ®istry) { &overlay::DebugModel::no_bail, &overlay::DebugModel::no_bail_available}, {"freecam", "debug.freecam", "Move the camera independently of the skater", Debug::set_free_camera, &overlay::DebugModel::free_camera, &overlay::DebugModel::camera_available}, + {"firstperson", "debug.first_person", "See through the skater's eyes", Debug::set_first_person, + &overlay::DebugModel::first_person, &overlay::DebugModel::camera_available}, {"hideui", "debug.game_ui_hidden", "Hide the game's interface", Debug::set_game_ui_hidden, &overlay::DebugModel::game_ui_hidden, &overlay::DebugModel::ui_available}}; for (const auto &setting : settings) { @@ -247,6 +249,21 @@ void register_movement_commands(Commands ®istry) { }; registry.add(std::move(vote_bind)); } + // The switches a player can put on a button (action_binds): each bind runs its command. + for (std::size_t i = 0; i < action_binds.size(); ++i) { + const auto &slot = action_binds[i]; + auto entry = action("bind " + std::string(slot.name), + "Bind a controller combo or keyboard key to: " + std::string(slot.label) + " (" + std::string(slot.command) + "); 0 clears the binding", + Group::movement, {mask}); + entry.inspect = [](const Model &m) { + return State{m.bindings.available, {}, "Controller bindings are unavailable.", {}, false}; + }; + entry.run = [i](const Model &, const Values &args, const Output &out) { + const bool saved = set_local_action_binding(i, static_cast(std::get(args[0]))); + out(saved ? local_profile_controller_bindings().status : "error: Invalid binding or save failed."); + }; + registry.add(std::move(entry)); + } } void register_ai_commands(Commands ®istry) { const auto ready = [](const Model &m) { diff --git a/Extension/UI/Overlay/skate_menu_settings.cpp b/Extension/UI/Overlay/skate_menu_settings.cpp index ee55ddf..69ea8dd 100644 --- a/Extension/UI/Overlay/skate_menu_settings.cpp +++ b/Extension/UI/Overlay/skate_menu_settings.cpp @@ -72,7 +72,7 @@ void binds_page(SkateMenu& menu, const Model& model, const CallbacksV3& callback const auto save = [&](int action, std::uint32_t combo) { std::array result{}; const auto command = std::string("bind ") + - (action == 1 ? "freecamcontroller " : action == 2 ? "freecam " : action == 3 ? "noclip " : action == 4 ? "forwardvelocity " : action == 5 ? "upvelocity " : action == 6 ? "tptofreecam " : action == 8 ? "voteyes " : action == 9 ? "voteno " : "offboardupvelocity ") + std::to_string(combo); + (action >= 10 ? std::string(action_binds[static_cast(action - 10)].name) + " " : action == 1 ? "freecamcontroller " : action == 2 ? "freecam " : action == 3 ? "noclip " : action == 4 ? "forwardvelocity " : action == 5 ? "upvelocity " : action == 6 ? "tptofreecam " : action == 8 ? "voteyes " : action == 9 ? "voteno " : "offboardupvelocity ") + std::to_string(combo); const bool queued = callbacks.queue_console_command(callbacks.user, command.c_str(), result.data(), result.size()); result.back() = '\0'; feedback(menu, result[0] ? result.data() : queued ? "Saving binding..." : "Could not queue binding."); @@ -129,6 +129,8 @@ void binds_page(SkateMenu& menu, const Model& model, const CallbacksV3& callback row(7, "Off-board Up Boost", model.bindings.offboard_up_velocity_combo); row(8, "Vote yes", model.bindings.vote_yes_combo); row(9, "Vote no", model.bindings.vote_no_combo); + for (std::size_t i = 0; i < action_binds.size(); ++i) + row(10 + static_cast(i), action_binds[i].label.data(), model.bindings.action_combos[i]); ImGui::EndTable(); } ImGui::EndDisabled(); diff --git a/Extension/World/console_commands.cpp b/Extension/World/console_commands.cpp index 6831e49..2063679 100644 --- a/Extension/World/console_commands.cpp +++ b/Extension/World/console_commands.cpp @@ -190,8 +190,9 @@ void register_world_commands(Commands ®istry) { // and the rest off, or all seven go back to the level's own choice. static constexpr std::array times{"default", "morning", "noon", "afternoon", "evening", "night", "weatherday", "weathernight"}; - auto time = argument("default|morning|noon|afternoon|evening|night|weatherday|weathernight"); + auto time = argument("default|morning|noon|afternoon|evening|night|weatherday|weathernight|next"); time.choices.assign(times.begin(), times.end()); + time.choices.emplace_back("next"); // the one after the current: for a button that steps through them auto tod = variable("tod", "Time of day on the current map", Group::world, time); // The current map's time layers in slot order, or empty when it has none. const auto time_layers = [](const Model &m) { @@ -219,7 +220,18 @@ void register_world_commands(Commands ®istry) { "Saved for this map", value != "default"}; }; tod.run = [time_layers](const Model &m, const Values &args, const Output &out) { - const auto wanted = lower(std::get(args[0])); + auto wanted = lower(std::get(args[0])); + if (wanted == "next") { + // As inspect reads it: the one layer forced on, or the level's own. + const auto now_layers = time_layers(m); + std::size_t forced{}; + for (std::size_t i = 0; i < now_layers.size(); ++i) { + const auto &choice = m.world.choices[now_layers[i]]; + if (choice == "on") forced = forced ? times.size() : i + 1; + else if (choice != "off") forced = times.size(); + } + wanted = std::string(times[forced >= times.size() ? 1 : (forced + 1) % times.size()]); + } const auto found = std::find(times.begin(), times.end(), wanted); if (found == times.end()) { out("error: Choose default, morning, noon, afternoon, evening, night, weatherday or weathernight."); return; } const auto slot = static_cast(found - times.begin()); diff --git a/Runtime/client_tick.cpp b/Runtime/client_tick.cpp index 1de17b9..faa5fa2 100644 --- a/Runtime/client_tick.cpp +++ b/Runtime/client_tick.cpp @@ -385,6 +385,8 @@ void update_model(std::uintptr_t client, TickState& frame) { std::uint32_t freecam_controller_combo{}, freecam_combo{}, tp_to_freecam_combo{}, noclip_combo{}, forward_velocity_combo{}, up_velocity_combo{}, offboard_up_velocity_combo{}; // Yes and No in a dedicated server's vote: read only while one is running. std::uint32_t vote_yes_combo{}, vote_no_combo{}; + // The switches on buttons (action_binds): each runs its console command. + std::array action_combos{}; const bool vote_open = dingosdk::multiplayer::server_vote_open(); bool freecam_controller = dingosdk::local_freecam_controller(); { @@ -398,6 +400,7 @@ void update_model(std::uintptr_t client, TickState& frame) { forward_velocity_combo = r.model.bindings.available ? r.model.bindings.forward_velocity_combo : 0; up_velocity_combo = r.model.bindings.available ? r.model.bindings.up_velocity_combo : 0; offboard_up_velocity_combo = r.model.bindings.available ? r.model.bindings.offboard_up_velocity_combo : 0; + if (r.model.bindings.available) action_combos = r.model.bindings.action_combos; if (vote_open && r.model.bindings.available) { vote_yes_combo = r.model.bindings.vote_yes_combo; vote_no_combo = r.model.bindings.vote_no_combo; @@ -409,7 +412,15 @@ void update_model(std::uintptr_t client, TickState& frame) { DingoSDKOverlaySetFreecamInputCapture(dingosdk::client_free_camera_active() && freecam_controller); dingosdk::ControllerInput controller; if (freecam_controller_combo || freecam_combo || tp_to_freecam_combo || noclip_combo || forward_velocity_combo || up_velocity_combo || offboard_up_velocity_combo || - vote_yes_combo || vote_no_combo) DingoSDKOverlayReadControllerInput(&controller); + vote_yes_combo || vote_no_combo || std::ranges::any_of(action_combos, [](auto combo) { return combo != 0; })) + DingoSDKOverlayReadControllerInput(&controller); + for (std::size_t i = 0; i < action_combos.size(); ++i) + if (r.action_bind_latches[i].update(action_combos[i], controller, r.observer_failed)) { + std::array result{}; + const std::string command(dingosdk::action_binds[i].command); + if (queue_console_command(nullptr, command.c_str(), result.data(), result.size())) + record(("{\"event\":\"controller_binding_triggered\",\"action\":\"" + std::string(dingosdk::action_binds[i].key) + "\"}").c_str()); + } // (The input reads as nothing while the menu, the console or the chat box is open, so typing // a bound key answers no vote.) if (r.vote_yes_bind_latch.update(vote_yes_combo, controller, !vote_open) && dingosdk::multiplayer::queue_command("vote", "yes", "")) diff --git a/Runtime/runtime_internal.h b/Runtime/runtime_internal.h index 6afd809..61ce5ec 100644 --- a/Runtime/runtime_internal.h +++ b/Runtime/runtime_internal.h @@ -77,6 +77,7 @@ struct Runtime { dingosdk::ControllerComboLatch up_velocity_bind_latch; dingosdk::ControllerComboLatch offboard_up_velocity_bind_latch; dingosdk::ControllerComboLatch vote_yes_bind_latch, vote_no_bind_latch; + std::array action_bind_latches; ULONGLONG next_offline{}; bool transition_seen{}, initialized{}, catalog_logged{}, native_tick_ready{}, native_loading_logging{}, fixed_stop_entitlement_route_ready{}, menu_load_queued{}, startup_load_queued{};