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.
- 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.
- 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.
- 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.
- 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.
- 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.
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.