Commit Graph
539 Commits
Author SHA1 Message Date
AyeZeeBB e6de1dfd64 Plugins: the merge moves plugins' old files out before it deletes its folder
Plugins' files from before Mods/.reskate_plugin_data were still in Mods/.reskate/data, and the
launcher's merge deletes Mods/.reskate before the game ever looks for a plugin's folder: a saved
Deathrace route went at the next launch that rebuilt the merge. mods::keep_plugin_data moves each
plugin's old folder to Mods/.reskate_plugin_data first (whole, or the files a folder there lacks,
its own kept), at the start of every merge; the game still runs it once for a merge that kept its
patch. Checked in the build stamp test.
2026-10-20 14:09:09 -05:00
AyeZeeBB 39f322097a Plugins: Dual Stick, the Dual Stick Controls companion's settings as a mod
Made from GoalTrains' pull request 254 without its C++: a page in the PLUGINS section with the
Session-style and XL-style presets, the five custom slots the companion's own menu shares, stance,
stick layout, steering angle, stock ramp controls, the startup choice and every other setting, with
the companion's keys and fallbacks. It edits session_controls.ini in the plugin's own data folder
(reskate.files), keeping every line it does not change, and never creates the file; the README says
what the companion needs to change to read it there. `dualstick` in the console, and its own checks
(`dualstick test`) in the plugin tests.

ReSkate.dll exports ReSkateOverlayOwnsInputV1, from the same pull request: 1 while ReSkate's menu, a
free camera on the pad or a prompt has the input, for any native tool that runs beside the game.
2026-10-20 13:55:53 -05:00
AyeZeeBB 4d0182eef9 Plugins: a plugin's saved files live in Mods/.reskate_plugin_data, which no merge deletes
reskate.files kept each plugin's folder in Mods/.reskate/data/<plugin>/, inside the merge's own
output, which every full merge deletes: saved Deathrace routes went whenever a mod changed. It is
Mods/.reskate_plugin_data/<plugin>/ now (mod_list.h plugin_data_folder), and what is still in the
old folder moves across the first time the plugin looks for its folder. Deathrace's routes keep
SmokeyDawg58's <map>/<name>.json layout, so routes from his fork copy straight into routes/.
2026-10-20 13:48:21 -05:00
AyeZeeBB 253ba73d67 Merge branch 'plugins' of https://github.com/Dingo-Shenanigans/ReSkate into plugins 2026-10-20 13:30:35 -05:00
AyeZeeBB bf693dbe5c Throwdowns: fixes the docs review found in the kit and its services
- match.teleports lets through only the publishing plugin's own teleports, and only while its
  match runs (its HUD is active, or the native flag is one of its modes); it was merged across
  every plugin and checked no match.
- A mode's timer of 0 or past an hour no longer makes its card do nothing: the native flag takes
  it as 1 to 3600 s. flag.begin's seconds are checked as 1 to 3600, as the flag takes them.
- skater.push and hold(true) return false with a reason without the stock physics, where they
  were dropped unseen.
- telemetry's slam and struck are news since each plugin's own last read; reading them cleared
  them for every plugin.
- A banner or callout of a new match shows whatever serial it starts from, and a document without
  the slot no longer resets its serial.
- SmokeyDawg58's placement no longer divides input.wheel's whole notches by WHEEL_DELTA again.
2026-10-20 13:30:27 -05:00
zeex64 5da1344805 Merge pull request #317 from ValveTextureFile/server-lua-api
Plugins: server plugins ban, unban; player permissions; reskate.Enums
2026-10-20 13:24:17 -05:00
wagu e134c089e6 Merge branch 'server-lua-api' of https://github.com/ValveTextureFile/ReSkate into server-lua-api 2026-10-20 12:13:25 -04:00
waguandClaude Opus 5.5 cd36529865 Merge plugins into server-lua-api
The new services and throwdown kit API files use add_functions and the shared
helpers; add_functions opens a read-only parent for reskate.throwdowns.flag.
throwdowns.release_flag stays removed, as plugins has it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 12:13:19 -04:00
wagu ded49756ea Docs: project structure for clarity 2026-10-20 09:59:50 -04:00
wagu 70228af281 Docs: project structure for clarity 2026-10-20 09:54:45 -04:00
waguandClaude Opus 5.5 67e8e439f7 Server: lock ollies for a player
A lock tool like noclip and boosts. The player's game enforces it: the roster
carries guest_ollies (protocol 53), and the jump hook gives a locked ollie no
upward speed. Plugins get player.permissions.ollies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 09:53:31 -04:00
waguandClaude Opus 5.5 c02b4001a7 Plugins: add_functions, one line to add to the reskate table
The shared API helpers (plugin_of, need, checked_text, position_of) are in
plugin_api.h, defined once. The API plugins see is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 09:53:25 -04:00
AyeZeeBB efeae7e07d Docs: the throwdown kit's warning and panel prompts as the HUD reads them, and where the full reference is 2026-10-20 08:30:35 -05:00
AyeZeeBB 91b754bfb0 Docs: the throwdown kit for Lua, and Board Break and Road Rash as plugins inside their packs
contrib/throwdown-kit.md is the reference for making a throwdown mode in Lua: registering modes,
the documents (setup page, waiting card, HUD, match panel, flags, match), the throwdown_ui events,
the native flag, and the services a mode uses. Board Break's doc says how the deck is chosen now
(the pack's decks as variants, reskate.package.choose, reskate.board.fit); Road Rash's that it ships
inside its item pack. The plugin list and the README name the throwdown plugins.
2026-10-20 01:46:18 -05:00
AyeZeeBB e246acd24b Plugins: Road Rash ships inside its item pack, as Board Break does in its deck pack
The pack's manifest.json is the mod's, so the plugin's own is gone; its README says how to install
it beside the pack's files.
2026-10-20 01:31:50 -05:00
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 456be60e0e Launcher: no official mod is known by name until ReSkate's list is read
The launcher fell back on a list of its own (Board Break's pack and Road Rash) before it had ever
read one from ReSkate. Official mods now come only from that list.
2026-10-20 01:31:49 -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 e14064250c Merge: a plugin mod's resident assets stay when a map replaces their bundle, and the merge never writes into a mod's own folder
reskate-plugin.json's "resident" list names the assets a mod's bundles keep (world objects and HUD
widgets the game picks at run time, and their shader block depots), so they survive another mod
replacing that bundle. Scanning now uses lexically_relative, so a mod folder that is a junction no
longer yields ../ paths, and every output path is checked to be inside the merge output. Chunk
metadata audit failures are fixed; the full merge audit passes.
2026-10-20 01:04:37 -05:00
vtf 02d34012ae Plugins: server plugins ban, unban; player permissions; reskate.Enums 2026-10-19 20:09:30 -04:00
AyeZeeBB ae05341bb8 Plugins: SmokeyDawg58's ReSkate_Throwdown plugin, his ten modes on the Throwdowns page
His mod ships its assets and this plugin together. Each mode has its name, description,
logo, flag, players, timer and setup rows as his fork had them; lobby events are said in
chat. Their gameplay is ported mode by mode; until then Start says the mode is not
playable yet. The mod's assets stay out of the repository.
2026-10-19 19:04:26 -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 89281f62fb Plugins: each card on a plugin's page opens and closes outside its widgets' ImGui IDs
A card began inside one widget's PushID and ended inside the next card's, so
ImGui reported PopID called too many times and missing. Cards now open and
close between widgets, each with its own id.
2026-10-19 17:04:13 -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 fc4308b682 Plugins: a command ReSkate does not know goes to the game thread, where plugins have theirs
The console answered every command missing from ReSkate's own list itself, as unknown, so a
plugin's command (ptest) never reached the plugins on the game thread. Now only ReSkate's local
commands run there; anything else is queued, and the game thread tries the plugins first and
answers unknown as before when none has it. Checked in game: ptest all, 45 passed, 0 failed.
2026-10-19 12:19:39 -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
AyeZeeBB fffd34ca80 Mods: conflicts resolved in MY MODS, official mods live in Mods, and big maps merge faster
MY MODS: a table with an animated drag for the load order, a conflicts bar with Check conflicts, and a Resolve popup whose choices are kept. The check runs at startup and after changes; conflicts no longer show as problems, block Play, or hold mod files during an update.
Conflicts: only game EBX that cannot be combined, or Lua scripts with different versions; colourways and same-name additions are not conflicts.
Official mods: installed in Mods/<Namespace-Name> and turned on or off through mods.json, so they show in MY MODS; the old .reskate-official folder is moved over.
Merge: a scan cache (Mods/.reskate-scan.txt) keeps each unchanged mod's repair, mesh and texture scans, so big maps are not re-read every merge.
Launcher: an announcement from the backend shows under STATUS, with HIDE.
2026-10-18 16:33:53 -05:00
AyeZeeBB 8017c2feb3 Multiplayer: other players' objects are solid by default
A player made ghosts by the local player stays a ghost.
2026-10-18 16:33:52 -05:00
AyeZeeBB f92d405979 GPU: after the GPU stops, the next three launches record its breadcrumbs (DRED) for the crash report
The device-removal watcher always runs, and the fatal handler notes a DXGI stop in gpu-breadcrumbs.txt. Crash reports carry a gpu.breadcrumbs tag (protocol 5).
2026-10-18 16:33:52 -05:00
AyeZeeBB db99ed4d72 Mods: two mods that conflict load only the higher one, found before the merge
Where two mods change one of the game's assets in ways that cannot both be
in the game (a resource or chunk changed differently, an EBX whose changes
cannot be combined, different assets added under one name), the one lower
in the load order is left out whole, so neither loads half its changes.
The merge used to keep the higher copy of each asset and lose the rest of
the lower mod's change silently, which broke Skaterize beside deckorate.

A check before the merge (find_conflicts) reads only the bundles two or
more mods ship and applies the merge's rule: about 2 s for 296 mods, on
several threads, each mod's copy of a bundle by itself. When it finds
conflicts nothing is built, the catalog leaves the losing mods out and
merges once. The merge keeps its own check behind it, tracking per asset
the last change a mod made itself, so a change carried through other mods'
copies is still caught. A conflict is remembered in .reskate-excluded.json
with the mod it lost to, and stays out while both are unchanged and the
winner still loads above it.

The launcher says "Checking the mods" and "Checking the mods for
conflicts" during those steps. Its mod problems screen says MOD CONFLICT
when that is all there is, and the Mod Manager shows a CONFLICT badge, an
N CONFLICTS banner, a Conflicts filter and, on the mod, which mod it lost
to and how to load it instead.
v2.1.1
2026-10-18 01:35:31 -05:00
AyeZeeBB 99467f4b31 Official mods: an installed one can be uninstalled on the OFFICIAL tab
UNINSTALL, under OPEN FOLDER, takes the mod's stamp away first (a folder
without it is no mod to the game), then its folder, and clears whether it
was turned off so an install brings it back on. The card says UNINSTALLING
meanwhile, and UNINSTALL FAILED when Windows keeps the files (Skate still
running). Cards are as tall as their buttons.
2026-10-17 21:42:44 -05:00
AyeZeeBB ceb21e0b92 Official mods: installed by the player on the OFFICIAL tab instead of downloaded on every launch
The launcher still reads the list at start and looks up each mod's latest
version, moves in a copy installed from Thunderstore and takes away retired
mods, but it downloads nothing by itself. Each card on the OFFICIAL tab says
NOT INSTALLED, INSTALLED, UPDATE AVAILABLE, DOWNLOADING or UPDATING, with an
INSTALL or UPDATE TO vX button (official_mods::install, on a thread of its
own), OPEN FOLDER once installed, and the On / Off switch beside its pills.
The banner counts how many are installed. Play waits for an install under
way.

Board Break and Road Rash start only when their official mod is installed
and on (mods::official_in_use), and say in the log which it is not; the
menus leave them out otherwise, as when turned off.

The live test (RESKATE_OFFICIAL_LIVE) checks that nothing downloads until
asked, then installs each.
2026-10-17 21:39:50 -05:00
AyeZeeBB 129dc637be Drone: the motor hum is quieter by default (volume 0.09 from 0.5)
Other players' drones go by the same setting. A volume the player set
themselves is kept.
2026-10-17 21:03:53 -05:00
AyeZeeBB a65f94b55a Customization: a mod item's card sits under the info sign
The game's info sign shows on the first of the notice's three reserved
lines, where the mod's icon covered it. The icon, tag, name and details
now take the two lines under it, the icon 40 instead of 46 so it still
ends on the card's bottom text margin.
2026-10-17 21:03:53 -05:00
AyeZeeBB 675ad087f2 Official mods: each can be turned off in the launcher, and then the game leaves it and its feature out
The OFFICIAL tab has an on/off switch on each mod's card, with an OFF pill
while it is off. The list of those turned off is
Mods/.reskate-official/official-off.txt, one folder name a line, which the
launcher's sync never removes. A mod turned off stays downloaded and kept
up to date, but official_packs() leaves it out, so nothing merges it, no
deck comes from it and the in-game Mods page does not list it.

Board Break and Road Rash do not start while their official mod is off,
and say so in the log. The menu model carries that as `off`, and the side
rail, the addon tabs and tiles, the home tiles, the pause menu's settings
and the native tools menu leave them out without a gap. category_tabs
takes a list built at run time for that.

Tests: an official mod turned off stays in place and is left out, comes
back when turned on, and only a plain folder name is written.
2026-10-17 20:55:52 -05:00
AyeZeeBB a64cd93d82 Drone: other players' drones are markers only, without a model
A model made for another player's drone crashed the game a few seconds
later in the per-frame update of its shader sections (Skate.exe+0x505b04b,
2.1.0): 28 of 29 reports there came right after one was made. Real
players' drones show as markers and are heard as before until that is
found; the pretend one from dronemodeltest remote still gets its model.
2026-10-17 20:32:28 -05:00
AyeZeeBB 2b5772bdf5 Customization: a mod item's card shows the info sign instead of an empty icon
The warning sign on a mod item's description notice was cleared by writing
an empty IconOverride. The game resolves that override again later in its
UI update and crashed on the empty one (Skate.exe+0x3e897fb, a lookup that
finds nothing): about a quarter of 2.1.0's crash reports, all of them after
the sign was cleared.

The notice now gets the game's own info sign (img_generic_info_64), set the
way the favorites star is. When that texture is not loaded the warning sign
stays and it is looked for again two seconds later; an empty override is
never written. Texture assets are matched by name ignoring case.
2026-10-17 20:32:28 -05:00
AyeZeeBB c7bf7a1edf Launcher: the agreement's summary wraps, and reads "skate" without the period
BulletText does not wrap, so the crash-report line ran past the window; each line is now a bullet
and wrapped text beside it.
2026-10-17 18:29:52 -05:00
AyeZeeBB 4f1f5a882e Launcher: the privacy policy and terms of service, and an "I agree" before playing
Both documents come from the ReSkate backend (GET /api/v1/legal/privacy and /terms, edited on its
Legal page), so a change reaches players without a release. Settings has a button for each, opening
a panel like the changelogs': title, version and date, the Markdown drawn as the README viewer draws
it, and a switch to the other one.

Each launch looks up the current versions (GET /api/v1/legal) in the background. One newer than
the player agreed to, or none agreed to yet, puts up an agreement over the home screen: a short
summary, a way into each document, QUIT and I AGREE, which saves the versions agreed to
(accepted_privacy_version, accepted_terms_version in the launcher's settings). A backend that
cannot be reached holds nobody up. Launches that skip the window (--no-gui, Discord joins) are
not asked.
2026-10-17 18:25:22 -05:00
AyeZeeBB d15e51e558 Custom maps: the mesh pool follows the map's own meshes, coarser LODs when it is short, and old maps' textures stream
The merge measures each custom map's own meshes, every level of detail of every MeshSet in its
level bundles, into Mods/.reskate/reskate-map-meshes.json. A custom map's mesh streaming pool is
the game's 520 MB and half as much again as those meshes, at most half the video memory free (as
before): most maps have 25 to 70 MB of meshes and reserved 1 to 2 GB for them. When the map needs
more than the card has room for, Mesh.GlobalLodScale goes from the quality preset's 1.25 or 1.5
to 1, so coarser levels take over nearer; the game's value comes back for the next map that fits.

A map's world textures (texture group Worlds_*) flagged to stream that keep every mip with the
bundle, as an older Studio built them (404 MB of PQS Cordoba's), are made to stream as the game's
own do: the bundle keeps the mip tail from a 64 KB block, both first-mip fields of the header and
the chunk record say so, and the sharper mips stream from the chunk the map's TOC lists in full.
Only in a map's own level bundles (Studio keeps cosmetics whole; changing those left a load at
Starting server), and only for chunks whose guid marks them compressed (the streamer reads any
other as raw pixels and copied past what it read).
2026-10-17 17:04:18 -05:00
AyeZeeBB 7fd2563087 Mesh streaming: a custom map's pool is measured again once the map it leaves has gone
The pool was sized from the video memory free when the load was asked for, while the map being
left still held its textures and meshes. From one custom map to the next it came out smaller each
time (1846 MB down to 1253 MB over five maps on an 8 GB RTX 5050), until The Mashup loaded to a
black screen. A custom map's pool is now measured again at Starting level load, after the old map
has left and before the pools are re-created from the new level's data (FUN_14507a2d0), and raised
when more is free then; it is never lowered there. The log says which measurement the load used.
2026-10-17 15:08:51 -05:00
AyeZeeBB ba1245a5af Menu: the window's side rail lists every addon in EXTRAS, with no ADDONS overview page
The window listed one ADDONS row that opened an overview of tiles, and showed the addons under it
only while that page was open. EXTRAS now lists Drone, Board Break, Road Rash, Hall of Meat, Grind
Exits and Music as rows of their own, always; a window left on the overview (switched from full
screen on ALL ADDONS) shows Drone. Every row of the rail is as tall as fits it without scrolling,
from 28 to 36. Full screen keeps its ADDONS page and ALL ADDONS tab.
v2.1.0
2026-10-17 14:47:35 -05:00