mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-17 13:57:05 +02:00
Skater slot, menu and throwdown fixes; MY MODS rework; lobby parties Saved skaters (Extension/Customization, Extension/Skater) - The selected slot, and slots before it, came up as the standard skater after a start or map change. The game asks for a slot's saved outfit once per level, and that happened before the cosmetics catalog could be read. The first selection now waits for the catalog (60 s at most), and later map changes check against the catalog already read. - The retry fromabdda77is removed: the record it wrote to is a temporary on the game's stack, so it could never work. Pause menu (Extension/UI/NativeMenu) - ReSkate's entries stopped working after enough map changes: each level change took new slots in a fixed table of 1024 for the same buttons. A button now keeps its slot, and a full table hands out the slot that has gone longest unused. Throwdowns (Extension/Throwdowns, Runtime/runtime_init.cpp) - Multiplayer crashed when someone placed a throwdown after a map change. The event types are freed with the level but were cached for the process. They are now cleared when a level is left, checked before use, and looked for again in each level. MY MODS (Launcher/gui_mods.cpp, gui.cpp) - Search, a filter and an order; a switch per mod; a menu per row. - Multi-select with tick boxes, Shift-click and Ctrl+A, and a bar to enable, disable, move, update or uninstall everything ticked. Lobby parties (Extension/Multiplayer/Session) - A lobby is no longer one party. Its host keeps parties the way a dedicated server does: invite, accept, join, leave, kick, promote, open and close, with "/p" party chat. The "Lobby party" switch is gone. Protocol version unchanged (38). Mod merge (Engine/Vfs), follow-ups toe715b9b- Files rewritten into the merged patch are tracked by position, so another mod's added assets no longer shift the lookup. - Two mods adding different assets under one name is now noted, with whose copy the maps get. - mod_merge_unshift_tests compiles with the launcher tests on.
79 lines
3.5 KiB
C++
79 lines
3.5 KiB
C++
#pragma once
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
namespace dingosdk::multiplayer::menu_data {
|
|
// Native transitions also unload assets when no ReSkate load request exists.
|
|
// Suspend publication and queued actions before cleanup can dispatch callbacks.
|
|
// Only a new active world may resume them; shutdown remains permanent.
|
|
class MenuLifetime {
|
|
enum class Phase { loading, active, shutdown };
|
|
std::atomic<Phase> phase_{Phase::loading};
|
|
public:
|
|
bool blocked() const noexcept { return phase_.load() != Phase::active; }
|
|
template<class Cleanup> void before_transition(unsigned next, Cleanup&& cleanup) {
|
|
if (next == 24) {
|
|
if (phase_.exchange(Phase::shutdown) != Phase::shutdown) cleanup();
|
|
} else if (next == 14 || next == 22 || next == 3) {
|
|
auto expected = Phase::active;
|
|
if (phase_.compare_exchange_strong(expected, Phase::loading)) cleanup();
|
|
} else if (next == 13 || next == 21) {
|
|
auto expected = Phase::loading;
|
|
phase_.compare_exchange_strong(expected, Phase::active);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Model creation returns an independently owned root. Dropping the C++ handle
|
|
// does not destroy it. Track partial builds and old pause-menu generations too.
|
|
template<class Model> class OwnedMenuModels {
|
|
std::vector<Model> models_;
|
|
public:
|
|
bool empty() const { return models_.empty(); }
|
|
void track(Model value) {
|
|
if (std::none_of(models_.begin(), models_.end(), [&](const auto& existing) {
|
|
return existing.handle == value.handle && existing.type == value.type;
|
|
})) models_.push_back(value);
|
|
}
|
|
template<class Detach, class Destroy> void release(Detach&& detach, Destroy&& destroy) {
|
|
if (empty()) return;
|
|
detach();
|
|
while (!models_.empty()) {
|
|
destroy(models_.back());
|
|
models_.pop_back(); // Keep unfinished cleanup available on failure.
|
|
}
|
|
}
|
|
// Use only after the native manager itself has been replaced.
|
|
void manager_replaced() { models_.clear(); }
|
|
};
|
|
|
|
// A command one of a page's native buttons runs: the pause-menu generation it was
|
|
// last handed out in, and the render pass that last asked for it.
|
|
struct MenuAction { std::uint64_t generation{}; std::string command, argument; std::uint64_t pass{}; };
|
|
|
|
// The slot a button gets for a command. A slot is a native callback whose
|
|
// descriptor the game borrows for the rest of the process, so a page has a
|
|
// fixed number of them and they are reused, never added to: a command keeps
|
|
// the slot it has, whichever generation of the menu gave it out (every map
|
|
// load starts a new one); a new command takes the next unused slot; and with
|
|
// none left, the slot asked for longest ago. Every button asks on every render
|
|
// pass, so a slot not asked for in this pass or the one before is on no button.
|
|
// Empty when every slot is on a button.
|
|
template<class At> std::optional<std::size_t> action_slot(std::size_t used, std::size_t capacity, At&& at,
|
|
std::string_view command, std::string_view argument, std::uint64_t pass) {
|
|
std::optional<std::size_t> idle;
|
|
for (std::size_t i = 0; i < used; ++i) {
|
|
const MenuAction& action = at(i);
|
|
if (action.command == command && action.argument == argument) return i;
|
|
if (action.pass + 1 < pass && (!idle || action.pass < at(*idle).pass)) idle = i;
|
|
}
|
|
return used < capacity ? used : idle;
|
|
}
|
|
} // namespace dingosdk::multiplayer::menu_data
|