mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-23 22:16:00 +02:00
Plugins register modes (card, replaces, order, flag, players) and publish documents through reskate.throwdowns.ui: the setup page, the waiting card, HUD slots themed on the game's HUD_<theme>_* widgets, the match panel, flags and match restrictions. SmokeyDawg58's native plumbing (throwdown_menu.cpp and its .inl files) renders them and posts throwdown_ui events back. kit::flag is the one native flag registration. General services added for it: reskate.files, reskate.ui.dialog, reskate.skater push/hold/bone_cam, reskate.camera.pad_flight, reskate.physics competition, reskate.session.world, reskate.tricks feed/gesture, and world entities. His ten modes are now Lua in his plugin: the rules, referee and wire (td/rules, referee, wire), and every page and HUD document (td/ui*.lua). Extension/Modes and reskate.modes are gone, along with every race and 1-UP branch in the SDK. The plugin test drives each mode through kit events. A native setup page is never written to while it closes into flag placement: its rows are not republished, the stock page is put back once the exit is over, and the stock Players slider is left alone while a plugin flag is out. This fixes the crashes on the Deathrace button and on 1-UP's Confirm.
104 lines
5.7 KiB
C++
104 lines
5.7 KiB
C++
#pragma once
|
|
#include "Engine/Game/Abi/native_data.h"
|
|
#include <cstring>
|
|
#include "Engine/Game/Build/fingerprint.h"
|
|
#include <map>
|
|
#include <span>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <cstring>
|
|
|
|
namespace dingosdk::multiplayer::menu_data {
|
|
using Address = std::uintptr_t;
|
|
using Handle = std::uint64_t;
|
|
struct Ref { Address record{}; Handle handle{}; };
|
|
struct Widget { Address blueprint{}; Ref data; };
|
|
struct Value { Handle handle{}; Address type{}; };
|
|
struct Field { unsigned index{}; std::uint16_t offset{}; Address type{}; };
|
|
struct Root { Value model; Address value{}; };
|
|
struct Schema { std::uint32_t hash{}; std::uint16_t size{}; };
|
|
// The native loader assumes DataModelObject at +18/+20. Passing a widget
|
|
// blueprint reads its name as TypeInfo and faults inside the model factory.
|
|
inline constexpr game::build::Fingerprint model_record_load_contract{0x1918520,
|
|
{0x48,0x89,0x5c,0x24,0x08,0x55,0x56,0x57,0x41,0x54,0x41,0x55,0x41,0x56,0x41,0x57,
|
|
0x48,0x83,0xec,0x50,0x41,0xbd,0x00,0x01,0x00,0x00,0x48,0x8d,0x6a,0xf0,0x66,0x44}};
|
|
using ModelRecordLoad=Handle (*)(Address,Address,bool);
|
|
extern ModelRecordLoad model_record_load_original;
|
|
Handle model_record_load_hook(Address manager,Address record,bool flag);
|
|
unsigned size(Address type);
|
|
inline constexpr Schema core{0xde615b97, 1392}, page{0x5844be65, 1968},
|
|
stack_item{0xbd3e10f0, 1424}, button{0x608f1aab, 1232}, input{0x9832b0a8, 384},
|
|
linear_list{0x48455d84, 464}, presenter{0x395d20a4, 24};
|
|
|
|
// Used only on the client thread while holding the native model write lock.
|
|
class Context {
|
|
mutable std::map<std::uint32_t, Address> types_;
|
|
public:
|
|
Address base{}, manager{};
|
|
explicit Context(Address image, Address models) : base(image), manager(models) {}
|
|
Address type(Schema schema) const;
|
|
Address address(Value value) const;
|
|
Address type_of(Handle handle) const;
|
|
std::vector<Root> roots(std::initializer_list<std::uint32_t> hashes = {}) const;
|
|
Field member(Address type, std::uint32_t hash) const;
|
|
Value field(Value value, std::uint32_t hash) const;
|
|
Value path(Value value, std::initializer_list<std::uint32_t> hashes) const;
|
|
Value element(Value array, unsigned index) const;
|
|
Value create(Schema schema, std::uint64_t id) const;
|
|
// Gives every null CString in an owned copy an engine-owned empty string:
|
|
// the VM's typed read calls strlen even for empty optional text.
|
|
void initialize_text(Value value) const;
|
|
void destroy(Value owned_root) const;
|
|
void publish(Value target, const void* bytes) const;
|
|
void copy(Value target, Address source) const;
|
|
template<class T> void set(Value target, const T& value) const {
|
|
static_assert(std::is_trivially_copyable_v<T>);
|
|
if (size(target.type) != sizeof(T)) throw std::runtime_error("Native menu value size differs.");
|
|
publish(target, &value);
|
|
}
|
|
template<class T> void set_if_changed(Value target, const T& value) const {
|
|
static_assert(std::is_trivially_copyable_v<T>);
|
|
if (size(target.type) != sizeof(T)) throw std::runtime_error("Native menu value size differs.");
|
|
T current{};
|
|
if (!memory::peek_bytes(address(target), ¤t, sizeof(T))) throw std::runtime_error("Native menu value is unavailable.");
|
|
if (std::memcmp(¤t, &value, sizeof(T))) publish(target, &value);
|
|
}
|
|
void text(Value target, const std::string& text) const;
|
|
std::string text(Value value, std::size_t maximum = 128) const;
|
|
// Snapshots copy row bytes but still borrow nested native values. Keep
|
|
// their owners live and publish synchronously under the model write lock.
|
|
std::vector<std::byte> array(Value value, unsigned maximum, unsigned& count, unsigned& stride) const;
|
|
// Retains the snapshot in a transient native array before resizing target.
|
|
// The temporary is destroyed before this call returns (including errors).
|
|
void array(Value target, std::span<const std::byte> bytes, unsigned count) const;
|
|
template<class T> void array(Value target, const std::vector<T>& values) const {
|
|
array(target, std::as_bytes(std::span(values)), static_cast<unsigned>(values.size()));
|
|
}
|
|
};
|
|
|
|
// Runs on the client thread for every menu page tick: peeked (a plain copy) rather than one
|
|
// ReadProcessMemory call per field, which profiled at ~16% of that thread on 2026-10-01.
|
|
template<class T> T read(Address at) {
|
|
T value{};
|
|
if (!memory::peek(at, value)) throw std::runtime_error("Native menu memory is unavailable.");
|
|
return value;
|
|
}
|
|
void require(bool condition, const char* message);
|
|
std::string string(Address at, std::size_t maximum = 256);
|
|
unsigned kind(Address type);
|
|
|
|
// Blueprint references are borrowed only until publication. Native model
|
|
// publication performs the engine's typed copy/retain operation.
|
|
std::map<std::string, Address, std::less<>> blueprints(const Context& context, Value core);
|
|
// A loaded asset by name, searched from `anchor`'s asset domain up its parents.
|
|
Address named_asset(const Context& context, Address anchor, const char* name);
|
|
// Keep the registry lock until native publication has retained the icon. Missing
|
|
// optional art leaves the caller's already-published fallback untouched.
|
|
bool publish_tab_icons(const Context& context, Value tab, std::string_view idle, std::string_view focused);
|
|
bool publish_texture(const Context& context, std::span<const Value> fields, std::string_view name);
|
|
// Publishes one loaded texture or image asset, by name, into an asset reference field.
|
|
bool publish_texture(const Context& context, Value target, std::string_view name);
|
|
// The same texture into several fields, each written only when it differs.
|
|
bool publish_texture(const Context& context, std::span<const Value> targets, std::string_view name);
|
|
} // namespace dingosdk::multiplayer::menu_data
|