Commit Graph
13 Commits
Author SHA1 Message Date
AyeZeeBB af83281b8d Mods: variants, one of several takes on the same game data; Board Break's decks leave the SDK
A mod's reskate-plugin.json can declare "variants" {folder, order, displaces}: each <folder>/<name>/
is a variant, the one chosen (else the first of its order) loads, and every other mod whose layout
lists a displaced bundle is set aside for the run. Choices are kept in
%LOCALAPPDATA%\ReSkate\variants.json. Plugins read and choose their own mod's variants with
reskate.package.variants and reskate.package.choose (permission "variants"), and fit every board
for a deck that is not the game's own with reskate.board.fit {plates, axle}.

The board-break plugin, which ships inside the Board Break deck pack, declares the pack's decks as
variants, picks the deck on its own page and with `boardbreak deck`, reads each deck.ini and fits
the boards. Gone from the SDK: breakable_deck, the deck catalog, the MODS page's deck card, the
boarddeck command and the Board_Break_<Name> folder naming.
2026-10-20 01:31:50 -05:00
AyeZeeBB 8444553402 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.
2026-10-20 01:04:54 -05:00
AyeZeeBB 37516e24fc Throwdowns: a plugin mode's flag is offered to the session, joined with the game's own Join
When a plugin mode's flag goes down in a session, the relay offers it to the other players
as that mode's: a new mode message (kind 17) names it, its flag and its player count, and
its offer's series is one older versions do not show. A player whose plugin has the same
mode gets a copy with the mode's own flag mesh and joins it natively; the copies are held
at ten players so none fills and starts a Spot Battle, and the mode's player count decides
who may still join. throwdown_mode events now carry host, leader, player and players: the
host hears joined and left, a player in another's flag hears joined, left, closed and
start, and start names who plays, the leader first. The plugins run the match from there.

A mode can take a stock throwdown's place on the picker (replaces = "skate", "spot_battle"
or "jam"), so his R.E.S.K.A.T.E. sits where S.K.A.T.E. does.
2026-10-19 19:04:11 -05:00
AyeZeeBB 0ff96018d0 Throwdowns: plugin modes on the native Throwdowns page, which is SmokeyDawg58's logo grid
reskate.throwdowns.mode{...} adds a mode to the game's own Throwdowns page: a card on
the picker, the real Spot Battle setup page relabelled with the mode's name, logo,
description, players, timer and rows (sliders, choices, buttons), and the native start
flag. The plugin hears throwdown_mode events (opened, setting, button, placed with the
flag's position and heading, start, leave, cancelled) and runs the mode itself;
release_flag takes the flag down. The flag's queue never starts a Spot Battle: its
waiting card's Start goes to the plugin, MaxPlayers is held at 10 and it is never
relayed as a drop. After SmokeyDawg58's one_up_menu and one_up_placement
(reskate-custom-mods), generalised over a mode registry.

The page is his 4-column logo grid whether or not a plugin has a mode. Its widgets and
grid records, and the game assets it uses that only some levels load, ship in
ReSkate.dll as the throwdown_menu built-in pack, taken from his asset mod's FBMOD
(dingosdk_ebx_pick trims the asset list in place). A mode's logo is a texture path in
its own mod, so no mod edits the shared list. The FPV drone is no longer a built-in pack.

Native menu data: text initialization, change-only writes, retained array publication,
owned-model rollback. Tests: registry and setup helpers, the plugin API, a test plugin
with two modes.
2026-10-19 18:38:10 -05:00
AyeZeeBB 64a06aa5a7 Plugins: a PLUGINS section in the menu, with a page for each plugin
Each plugin gets its own page instead of sharing the ADDONS page's PLUGINS
tab: a row per plugin in the window's side rail under PLUGINS, a tab per plugin
full screen. reskate.menu.page(title, build) makes it; the plugin's panels show
as cards under it, and a plugin with panels only gets a page named after it.
A page is built ten times a second, only while the menu shows it.

The menu's own widgets for pages: ui:card(title, subtitle), ui:tabs, ui:note,
ui:warn, ui:info, help text on toggles and dropdowns, a slider's format
("%d%%") and a card's main button. Full-screen screens draw them too.

Road Rash, Board Break, the FPV drone (a tab each for fly, flight, rates,
display and controller), Hall of Meat and grind exits have pages, worded as
their built-in pages were. MUSIC is ADDONS' only addon now.
2026-10-19 17:01:28 -05:00
AyeZeeBB db52508bdb Plugins: Hall of Meat and grind exits are plugin mods now; their C++ is gone
Hall of Meat (contrib/plugins/hall-of-meat) and grind exit triggers
(contrib/plugins/grind-exits) are Lua plugins: the bail model, its Meat card,
each map's best, the trigger rules, settings, console commands and menu panels
are theirs, with their own checks that dingosdk_plugin_tests runs.

What has to run in the game stays in core as plugin services:
- every physics step of the skater (skater_physics, the physics_step event,
  permission events.physics);
- the skater's x-ray (xray, xray_render, xray_skeleton; reskate.skater.xray and
  the draw handler's d:skeleton);
- skate.'s bottom left HUD corner (hud_corner; reskate.hud.corner);
- the game's slow motion, never in a session (game_speed; reskate.time.slow and
  speed, permission game.speed);
- the side of the next pop out of a grind and pad controls kept from the game
  (grind_exit; reskate.skater.grind_exit, the grind_exit event, reskate.input.hide);
- skate.'s own pictures by name (game_images; d:game_image), d:gradient and a
  font for drawn text.

Removed: Extension/HallOfMeat, grind_exit_control and its commands, the HALL OF
MEAT and GRIND EXITS add-on pages, home tile, hub and native menu rows, their
menu models, and the hallofmeat and grindexit built-in commands.
2026-10-19 16:51:28 -05:00
AyeZeeBB 2d3b77701d Plugins: drone, Board Break and Road Rash are plugin mods now; their C++ is gone
The three features are Lua plugins in contrib/plugins (fpv-drone, board-break,
road-rash), built on what this adds to the plugin API: flight input, gamepad and
joystick reads with input capture, a rolled camera, a package's own sounds and a
synthesised motor (plugin_sound, plugin_wave, motor_synth), the skater's physics
events (skater_events), items worn over the outfit (outfit_overlay, skater_items),
the board's bones (board_pose), skater animation (anim_core), plugin images,
throwdowns started for chosen players, and server copies of plugins on a lobby
host and the dedicated server (server_plugins).

Removed from core: Extension/RoadRash, Extension/BoardBreak, the drone's flight,
radio, follow, OSD, beacon, outfit and sound code, its prop models (native_prop),
the DRONE, BOARD BREAK and ROAD RASH menu pages, home tiles and native menu rows,
their console commands, the drone follow debug action, the drone marker packet
(kind 36 stays unused; the roster keeps its byte) and the server's drones setting.

Kept in core, since every board needs it whichever plugins run: the breakable
deck packs (breakable_deck: which deck, its label and wheel spacing, the
boarddeck command and the BREAKABLE DECK card on the Mods page), and board_pose
keeping a deck pack's stretches out of sight, the baseplates on the deck and the
wheels at the deck's spacing on every board.
2026-10-19 16:22:15 -05:00
AyeZeeBB 5c0993afea Plugins: the wider set (phase 3), behind one Game interface the tests fake
- Skater: skater.state() and the skater_state and bail events (events.skater); noclip and boosts
  through the console's own queue, under the host's rules (skater.control); a save point of the
  plugin's own (trainer); the game's HUD hidden while any plugin asks (hud).
- World: park editor objects listed, placed, moved and removed through the editor's own edits and
  the session's object rules (world.objects); time of day, world controls and park rotation as
  the World menu sets them, refused where the host chooses (world.environment); fast travel and
  levels, refused for guests (travel); AI skaters, outside sessions only (world.ai).
- Session: other players' skaters as last heard (session.skaters, a feed the session publishes
  each frame, plugin_session_feed.h); a plugin's tag above a player's nametag (session.nametags);
  voice volume (voice.control); chat lines as events (chat.read) and sending one, one every 3 s
  (chat.send); throwdown state and events (throwdowns).
- Music from outside players (music), progress (progression.read) and game settings
  (settings.read), read only; named actions on keys or pad combos, pressed while the game has the
  input (input); full-screen screens with the input, closed by Esc (ui.screen).
- Libraries and events between plugins: expose, require_plugin, emit, on_plugin; plain data is
  copied across VMs and a plugin in a call cannot be called back into.
- net.http: HTTPS to the hosts reskate-plugin.json's new "domains" lists; no redirects, cookies
  or Windows sign-in; private and local addresses refused; 10 a minute, 4 waiting, 1 MB back.
- Left for engine work (listed in the design doc): respawn and freeze, manuals, the camera, trick
  animations, replays, starting throwdowns, the game's own soundtrack, overlay images, rebinding
  plugin keys in the Binds page, local-only chat lines.
- reskate-test has a wider module (ptest noclip, boost, hidehud, object, night, day, ai, screen,
  bind, music, chat, web by hand); reskate-hostile has chat-spam, edit-spam, travel-spam,
  web-elsewhere, bind-spam and screen-loop.

All host tests pass; not yet tried in game.
2026-10-19 13:49:29 -05:00
AyeZeeBB d884c1c161 Plugins: the rest of the core set (overlay, menu panels, sounds, mods, session and messages)
- overlay: reskate.on("draw", function(d) ...) each tick, with text, rect, line, circle, poly
  and world_text, world_line, world_circle (projected through the camera as the frame draws),
  size and world_to_screen; 4000 shapes a frame. reskate.ui.toast through ReSkate's notices, three
  in 5 s. Images are left for later: they need texture slots in the overlay's renderer.
- menu: reskate.menu.panel(title, build) on a new PLUGINS tab of the ADDONS page, with heading,
  label, separator, button, toggle, slider and dropdown, built ten times a second while it shows.
- audio.ui: the game's menu sounds by name, ten a second. mods.read: installed(name) and list(),
  from the discovery thread's scan.
- session.read: active, is_dedicated, players, me, host, and player_joined / player_left.
  net.session: send(to, message) to the same plugin on other games (by its manifest.json name) in
  the session's throwdown packets, tagged 0xD1 (plugin_wire.h); 4 KB each, 16 KB a second.
- The overlay and the host share plugin_ui.h (shapes, panels, actions) and plugin_camera.h.
- Fixed: the overlay asked ImGui for the window's size before its context existed, which crashed
  on the first frames; it now reads the size from the context.
- reskate-test has overlay (ptest hud), menu, audio, mods and session (ptest ping) modules;
  reskate-hostile has draw-flood, toast-spam, sound-spam, message-flood and panel-loop.

Checked in game: ptest all 62 passed, 0 failed; the test HUD, the PLUGINS tab and its panel; every
hostile attack stopped or refused, trick-loop and panel-loop included. Not yet: ptest ping with a
second player.
2026-10-19 13:20:43 -05:00
AyeZeeBB f37abef426 Plugins: trick events (trick_started, trick_landed, line_broken) and reskate.score.current
- With events.tricks a plugin hears the local skater's tricks: trick_started (where), trick_landed
  (points, multiplier, where it started and landed, how long it took) and line_broken (a slam or
  a wipeout); reskate.score.current() is the score HUD's line, multiplier and whether a trick is
  going on. Without the permission the events cannot be registered.
- The score HUD is read into the trick watch (Extension/Skater/score_hud.h, trick_watch.h) on the
  client tick only while a running plugin has events.tricks; a new level starts a new line.
  plugins::start takes the image base for it.
- reskate-test has a tricks module (skipped until a trick lands) and ptest trick, which waits 30 s
  for one and prints what its events said; reskate-hostile has trick-loop.

Not yet checked against real tricks in game.
2026-10-19 12:43:44 -05:00
AyeZeeBB 373c141381 Plugins: skater.read, skater.teleport and world.raycast
- skater.read: reskate.skater.get() (position, heading, speed, vertical speed, airborne, air
  time) and position, heading, speed and on_ground on their own; nil without a skater. From the
  trainer's telemetry. The heading is radians, -pi to pi: 0 faces +Z, pi/2 faces +X, and a higher
  heading is turned to the skater's left (checked in game).
- skater.teleport: reskate.skater.teleport(at, heading?) through teleport_local_skater_heading;
  false and why when the host has turned teleporting off (as the console's tp), there is no
  skater, or it is asked more than twice a second. Positions must be finite and within 100 km.
- world.raycast: reskate.world.raycast(from, to) is where the line first meets the world's
  collision (local_surface_hit), or nil; 64 a tick, 1000 m at most.
- The host passes the game to plugins through Environment, so the tests fake a skater and flat
  ground. reskate-test has a skater module (skipped without a skater; the harness counts skips)
  and ptest tp; reskate-hostile has teleport-spam and raycast-flood.

Checked in game: teleporting, the two-a-second cap and the raycast cap. ptest skater's heading
check failed there (0 to 2 pi from the trainer) and is fixed, not yet rerun in game.
2026-10-19 12:34:18 -05:00
AyeZeeBB a3e75ba10e Plugins: the reskate-test and reskate-hostile plugins, and trick reading and a facing teleport in the core
- contrib/plugins/reskate-test calls every plugin API and says what worked: ptest all, ptest
  <module>, ptest list. Its 45 checks cover the base set, timers, the level, package files, JSON,
  require, the console, storage and the sandbox. It is the example to copy for a plugin.
- contrib/plugins/reskate-hostile attacks the sandbox, one attack per command (hostile list):
  loops, memory and buffer bombs, caught errors, error and log floods, timers, recursion, an
  unload that never ends, and 17 ways out. Each must end stopped or refused.
- dingosdk_plugin_tests runs both from the repository: every reskate-test check passes, and every
  hostile attack is stopped or refused with nothing escaping.
- Taken from Domination (PR 279) for the plugin API to use: the trick watch (when a trick starts,
  lands and what it scored, or a slam breaks it; Domination's spots left out) in
  Extension/Skater/trick_watch.h with its tests, the score HUD reader in score_hud.h, and
  teleport_local_skater_heading, whose hunks are the PR's own. Neither is wired to anything yet.
2026-10-19 12:08:26 -05:00
AyeZeeBB 6d542536e1 Plugins: sandboxed Luau plugins in mods, phase 1 (the host, and the base, console and storage APIs)
A mod with a reskate-plugin.json is a plugin: Luau scripts that run in their own VM, with only
the reskate API ReSkate registers for them. Luau 0.741 is vendored in External/luau (the VM and
compiler only: no native code generation, no loadstring, io or C modules).

- Limits: 64 MB of memory, 50 ms a call, 2 ms a tick on average (over it for 3 s stops it),
  more than 5 errors in 10 s stops it, 50 log lines a second. A plugin over a limit cannot catch
  it in a pcall and carry on.
- API: reskate.log, on (load, unload, tick, level_loaded, level_unloading), timers, the level,
  its own package's files and JSON, require inside the package; console.command and print
  ("console"); storage get, set, add and keys ("storage"), 1 MB in
  %LOCALAPPDATA%\ReSkate\Plugins\<mod>\storage.json.
- Found every 2 s among the mods mods.json leaves on: switching a mod, or changing a plugin's
  files, loads, unloads or reloads it without a restart. A package holding a program is refused.
- plugin list, commands, reload, off and on in the console. A plugin cannot take a name of
  ReSkate's own commands.
- Tests: DINGOSDK_BUILD_PLUGIN_TESTS, including plugins written to break the sandbox.
2026-10-19 11:55:27 -05:00