Throwdowns: the throwdown kit, so a mode is a plugin and the SDK knows no particular mode

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.
This commit is contained in:
AyeZeeBB committed 2026-10-20 01:04:54 -05:00
1 parent e14064250c
commit 8444553402
186 files changed
+28015 -2747

No files matched your search

+24 -2
View File
@@ -4,6 +4,7 @@
#include "Engine/Game/Build/20260929/engine.h"
#include "Engine/Game/Build/20260929/native_menu.h"
#include "Engine/Game/Build/20260929/native_party.h"
#include "Engine/Core/Log/logging.h"
#include <algorithm>
#include <atomic>
#include <cstring>
@@ -12,6 +13,24 @@
namespace dingosdk::multiplayer::menu_data {
namespace engine = addr::engine;
namespace menu = addr::native_menu;
ModelRecordLoad model_record_load_original{};
Handle model_record_load_hook(Address manager,Address record,bool flag) {
const auto base=reinterpret_cast<Address>(GetModuleHandleW(nullptr));
Address record_type{};
if(!record || !memory::peek(record+8,record_type) || record_type!=base+menu::asset_record_type) {
// Reject at the loader, before either of its TypeInfo reads. Returning
// from the constructor alone would still fault in the loader's tail.
static std::atomic<unsigned> reports{};
if(reports.fetch_add(1)<12) {
std::string name="unavailable";
try {name=string(read<Address>(record+0x18),192);}catch(...) {}
logging::log(logging::Level::error,logging::Channel::ui,
"Native model loader rejected non-record {:#x}, type {:#x}, asset '{}'.",record,record_type,name);
}
return 0;
}
return model_record_load_original(manager,record,flag);
}
void require(bool condition, const char* message) { if (!condition) throw std::runtime_error(message); }
unsigned kind(Address type) { return (read<std::uint16_t>(read<Address>(type) + 4) >> 5) & 31; }
unsigned size(Address type) { return kind(type) == 3 ? 8 : read<std::uint16_t>(read<Address>(type) + 6); }
@@ -43,7 +62,8 @@ Address Context::type(Schema schema) const {
types_.emplace(schema.hash, candidate);
return candidate;
}
throw std::runtime_error("Native menu schema has not loaded yet.");
throw std::runtime_error("Native menu schema has not loaded yet (hash " +
std::to_string(schema.hash) + ", size " + std::to_string(schema.size) + ").");
}
Address Context::type_of(Handle handle) const {
const auto record = reinterpret_cast<Address (*)(Address, Handle, std::uint8_t)>(base + addr::native_party::model_record)(manager, handle, 0);
@@ -117,7 +137,8 @@ Field Context::member(Address type, std::uint32_t hash) const {
require(result.offset + size(result.type) <= size(type), "Native menu field exceeds its structure.");
return result;
}
throw std::runtime_error("Native menu field schema differs.");
throw std::runtime_error("Native menu field schema differs (field " + std::to_string(hash) +
", model " + std::to_string(read<std::uint32_t>(meta)) + ").");
}
Value Context::field(Value value, std::uint32_t hash) const {
address(value);
@@ -189,6 +210,7 @@ void Context::text(Value target, const std::string& text) const {
Address native{};
game::native_data().values.assign(&native, text.c_str(), static_cast<std::uint32_t>(text.size()));
struct Release { Address base; Address& value; ~Release() { if (value) reinterpret_cast<void (*)(Address*)>(base + engine::native_text_release)(&value); } } release{base, native};
require(native!=0,"Native menu string construction returned null.");
set(target, native);
}
std::string Context::text(Value value, std::size_t maximum) const {