329 Commits
Author SHA1 Message Date
AyeZeeBB 314d31fe7b Update main.cpp v2.0.6 2026-10-11 14:09:29 -05:00
AyeZeeBB 6f1dae531c Server browser: favorite servers
A dedicated server can be marked as a favorite: F on the chosen server, or
ADD TO FAVORITES in the panel beside the list (the same to take it away).
Favorites carry a star, are listed first whatever the sort, and have a
FAVORITES filter beside FRIENDS.
2026-10-11 13:56:50 -05:00
AyeZeeBB 8c9c6cff13 Board Break and Road Rash are off until turned on
Both started on for a player who had never set them. They now start off;
a choice a player already made is kept.
2026-10-11 13:48:54 -05:00
AyeZeeBB f7beec83c5 Server window: word filter toggle and a roles editor
Settings tab: a Chat Word Filter card with "Use Global Word Filter"
(use_global_word_filter), buttons that open data/words_filtered.txt and
data/words_blocked.txt, and one that has a running server read them again.

Moderation tab:
- ROLES: create, edit and delete roles (tag, tag colour, rank, default role,
  inheriting from it, permissions, commands). Saving checks the roles, writes
  data/roles.json and has a running server reload it. A role's name is fixed
  once made, since players are given roles by name, and a save keeps who has
  which role as the server has it then.
- PLAYER ROLES (was SERVER ADMINISTRATORS): giving a role picks any of the
  server's roles, not only admin.

The two word files are now made empty when they are missing, without the
explanation that was written into them.

Also: the README's Chat words section sat inside the Roles section, and the
server parks test did not link without server_words.cpp.
2026-10-11 13:39:01 -05:00
zeex64 0b28dbd7c9 Merge pull request #163 from moelrobi/server-park-mods
Dedicated server: park mods as server-owned objects
2026-10-11 13:20:30 -05:00
AyeZeeBB 1d73fdc1de Merge branch 'main' of https://github.com/Dingo-Shenanigans/ReSkate 2026-10-11 13:17:36 -05:00
AyeZeeBB c27d6cb053 Word filter: fewer false flags, servers' own word lists; protocol 49
The chat filter matched short listed words anywhere inside a longer one, so
entries like "gger", "spik", "crip" and "coon" in the backend's lists blocked
"bigger", "spike", "script" and "raccoon".

- A listed word of four or five letters now matches inside a longer word only
  when the rest of it is what goes around a swear word ("motherfuckers",
  "bullshit", "shithead", "shitty", "xxfuckxx"). Words of six letters or more
  still match anywhere.
- An ordinary word that equals a listed phrase without its spaces
  ("the rapist" / "therapist") no longer matches.
- bad_words.txt is gone: the filter has no words until the backend's lists
  arrive.

Dedicated servers:
- "use_global_word_filter" (default true) turns the backend's lists off for a
  server. Console: words global on|off.
- data/words_filtered.txt and data/words_blocked.txt hold a server's own
  words, read at start and by "words reload". Blocked words follow
  word_warnings. Filtered words are sent to players (new packet, kind 33),
  whose games star them for players with the chat filter on and drop them on
  leaving.

Multiplayer outfits: a player whose mods add or remove skater or board slots
no longer makes everyone look like the local player. A peer's items are put
in this game's slots by slot hash; slots the peer lacks stay empty and items
in slots this game lacks are left out.
2026-10-11 13:17:28 -05:00
zeex64 49f971da41 Merge pull request #190 from FourthStudio/board-break/native
Board Break: a hard slam can snap the board
2026-10-11 13:15:04 -05:00
Fourth Studio bd628abc76 Board Break: a hard slam can snap the board
A slam can break the local skater's board at the nose, in the middle or at
the tail. Half the time the grip tape holds the pieces together and the board
folds or an end hangs; otherwise a piece comes off, flies and lies where it
lands. The board is whole again in hand, on riding and on a respawn.

The pieces are four stretches of the deck in a content mod's copy of the
game's one deck model, each on a spare bone of the board's rig. The boards'
render pose hook only moves bones, because the replay records where a bone is
and not its size: a broken board's deck goes away and its stretches go to
where the pieces lie, and a whole board's stretches go away.

The deck the player chose loads first and other mods that bring a deck model
are set aside for the run, without touching mods.json. Another deck is chosen
while the game runs, through a live apply of the mods.

contrib/board-break.md says how it works and how a deck pack is made.
2026-10-11 20:07:00 +02:00
Robin MöllerandClaude Opus 5.5 dbb5cf8caa Park mods: fit to roles
- park-mod is a command a role can list: the admin role's "*" gives it, the
  default moderator role does not (spawning parks and changing the saved
  setup are the owner's call); command() checks the caller's role as for
  every other command, and custom votes still run it as the console
- The client's "/" menu shows the park-mod rows by role like the other
  server commands there, by the row's first word (resolved in the rebased
  client commit)
- README-roles.txt lists park-mod; README.txt says who may use it
- Tests: the admin role has park-mod and the moderator and player roles do
  not; in game a moderator is refused and a role listing park-mod is not

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2G4APEkhHgVs59RDL3XWD
2026-10-11 17:52:49 +00:00
Robin MöllerandClaude Opus 5.5 f8b01dc3d5 Park mods: reload and set up parks while the server runs
- park-mod reload <name> [<name>...] reads a spawned park's file again and
  swaps its objects in place, keeping its timer; all or none, within
  park_limits, and players are told the park was updated
- park-mod config add <name> | <mod folder> | <map> [| <key>] and
  park-mod config remove <name> change maps.park_mods without a restart,
  validated like the startup config and saved like the map pool; removing a
  spawned park's entry removes the park too
- The client's admin "/" menu lists reload, config add and config remove
- README.txt and help cover the new commands; server_park_mods tests reload
  (in place, timer kept, refused when not spawned or broken) and config
  add/remove (saved, refusals, by an admin)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2G4APEkhHgVs59RDL3XWD
2026-10-11 17:46:45 +00:00
Robin MöllerandClaude Opus 5.5 c3751b9714 Park mods: name the Mods folder, not the server's path
- A missing mod folder reads "The mod folder "x" is not in the Mods folder."
  instead of printing where the server is installed; the reason can reach chat
  through an admin's command or a vote
- server_park_mods checks the exact message

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2G4APEkhHgVs59RDL3XWD
2026-10-11 17:46:32 +00:00
Robin MöllerandClaude Opus 5.5 6a8cbe8156 Client: park-mod in the admin "/" command list
- A dedicated server's admins see /park-mod list, add, remove and clear with
  their usage in the chat's "/" menu, beside the server's other admin commands
- No Tab completion for the park names: the roster does not carry them; the
  park/unpark votes already show with their choices as custom votes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2G4APEkhHgVs59RDL3XWD
2026-10-11 17:46:32 +00:00
Robin MöllerandClaude Opus 5.5 0193e4fadc Server tests: park mods
- server_config: park_mods and park_limits are new settings with defaults,
  round-trip (folders with spaces), and refuse bad names, folders, maps, keys
  and limits; a missing folder is not a config error
- server_park_mods: reads the fixture parks (a shortened copy of a player's
  street park, objects to skip, a missing key, broken files) and drives the host
  over a fake transport: a player's game takes the park from the server's own
  SteamID through routed_source and ObjectState, several parks at once, the
  limits (all or none), expiry with its warning, admin and vote spawns, late
  joiners, clear, and removal on a map change

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2G4APEkhHgVs59RDL3XWD
2026-10-11 17:46:05 +00:00
Robin MöllerandClaude Opus 5.5 6909fe3a2e Dedicated server: park mods as server-owned objects
- maps.park_mods binds a Park Editor park mod (Mods/<folder>/parks/<key>.park.json)
  to a name and the map it was built on; maps.park_limits caps the parks and
  objects spawned at once (3 and 512 by default)
- park-mod list | add <name> [<name>...] [minutes] | remove <name> [<name>...] | clear,
  from the console, as /park-mod by admins, and by players through custom votes
- A spawned park is the layout of the server's own SteamID and epoch, sent like a
  player's: games since 1.0.0 already show it, it counts against no player's
  object limit, nobody can move or delete it, and it stays when players leave
- Objects players' games would not take (not Build Kit items, broken values) are
  skipped and logged; a missing or broken park is an error line when spawning
- Optional expiry (1-1440 min) with a warning in chat a minute before; every
  park goes on a map change and none survive a restart
- [parks] log lines name who did it: console, the admin, or "a vote"; a vote's
  refusal reaches chat
- park-mod is reserved for custom commands; README.txt documents the settings,
  commands and a park/unpark vote

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2G4APEkhHgVs59RDL3XWD
2026-10-11 17:45:49 +00:00
zeex64 f4292d6b73 Merge pull request #148 from Vebjorhk/feat/r3-first-person-camera-cycle
First person as a step in the R3 camera height cycle (opt-in)
2026-10-11 11:21:26 -05:00
zeex64 193dec2fe1 Merge pull request #154 from wilu222/fix/lobby-name-persist
Persist saved multiplayer lobby name across restarts
2026-10-11 11:18:05 -05:00
AyeZeeBB 1f5fe75b55 Server: teleport command, and the server window builds with roles
eleport <players> <player|x y z>, from the console or for a role with
the command: one player (a name's start or a SteamID64) or up to 64
SteamID64s joined by commas, to a player or a place. Several stand in
tpall's ring round the target; one sent to a place lands on it. Those
not connected or not in the world yet are skipped and named, and each
use is logged. tpall and tphere share the ring code. The name is
reserved, and it is in help, the chat list, README.txt and
README-roles.txt.
The server window merged in #170 did not compile on main. Its admin
card listed the admins a config no longer has: it lists the players
given a role, and gives or takes the admin role with the role command.
The "autostart" setting it reads is added to the config (off unless
set). A second read of Steam's debug lines is removed: they already
come with the direct notes.
Co-Authored-By: Robin Möller <moelrobi@gmail.com>
2026-10-11 11:12:54 -05:00
zeex64 43e88fa433 Merge pull request #177 from ValveTextureFile/shader-macos-fixing
Launcher/overlay: fall back to ImGui's stock pixel shader when the HDR one can't be built
2026-10-11 11:04:09 -05:00
AyeZeeBB aa08050c8b Music: Favorites takes playlist 0 on the music player's channels only
The Favorites order fix gave Favorites to any channel that asked for
playlist 0. That is what the music player asks for when Favorites is
chosen, but nothing said another channel could not ask for 0 and mean
something else. The channels the music player's own queues pick songs
for are now remembered as they are seen, and only those are given
Favorites for 0; on any other channel the request goes through as it
came. The channel numbers are still the game's: none is written down.

A channel's binding was logged once per change, except for a channel
past the end of the table, which was logged on every call. Those are no
longer logged at all. The binding line now says whether the channel is
one of the music player's.

The hook on the game's candidate check is removed, with its address: it
changed nothing and only logged.
2026-10-11 10:59:08 -05:00
zeex64 a95c542a72 Merge pull request #183 from DeckardDetribine/fix/favorites-playback-order
fix(music): play favorites sequentially in visible order
2026-10-11 10:53:42 -05:00
zeex64 ade2d6ed24 Merge pull request #170 from wxndr/feature/dedicated-server-gui
Server: add native DirectX 12 graphical server GUI & live telemetry
2026-10-11 10:43:08 -05:00
zeex64 5e0b0fe121 Merge pull request #168 from dubsector/fix/pterodactyl-egg-config
Fix the Pterodactyl egg's config writes and Release rule
2026-10-11 10:42:04 -05:00
DeckardDetribine 63b747fd4a fix(music): play favorites sequentially in visible order 2026-10-11 11:32:30 +01:00
AyeZeeBB a1621ee19e Dedicated servers: roles, with their tags, commands and permissions
Dedicated servers: roles, with their tags, commands and permissions
A dedicated server had one list of admins who could do everything, and
everyone else could do nothing. It now has roles, kept in
data/roles.json beside the bans: each a name, the tag its players wear
in the game, the server commands it may use, what it may do besides
commands, and a rank.
A server starts with five: player (everyone given no role: chat, votes,
parties, private messages), vip (a reserved slot), moderator (kick, ban,
messages, giving out lower roles), admin (everything) and muted. The
default role is whoever was given none; a role has what the default has
as well as its own unless it says it does not inherit. A reserved slot
is a permission, so a role's players have one without being listed.
Nobody kicks, bans or changes the role of a player whose rank is theirs
or higher, or gives out a role that is; the console is above every role.
"*" is every command but role and reserved, which a role has only when
it lists them.
New commands: role <player or SteamID64> <role>|none, roles, and roles
reload. "admin add|remove" and "admins" are gone: the admins an older
config lists are moved into the admin role the first time the server
starts, and leave the config.
In the game, a player's role tag and colour show in chat and on their
nametag, and each control in the Multiplayer menu, the map, time of
day, world layers and parks follows the command it needs rather than
one admin flag, as do the commands chat offers.
Protocol 48: the roster carries each player's tag, and a new
permissions packet tells a player what their role gives them. Older
games and servers do not connect to newer ones.
Also fixes a name clash that kept the server config tests from
compiling.
2026-10-11 03:52:13 -05:00
AyeZeeBB 8044c88373 Hold skateboards to their size, and catch one kept on another player
A hacked game was resizing its skateboard and reporting it at another
player's position, so everyone saw an oversized board riding on them,
and with player collision on it knocked them about.

A skateboard is now held to within 10% of its size, apart from the limit
on resized body parts: on a dedicated server when its scale limit is on,
and in every player's game whatever sent the pose. Another player's
board is solid only within 3 m of its own skater; one further off is
seen and not felt.

Dedicated servers also check for a board kept on another player
(Server/board_check.h): one away from its own skater that stays beside
one other player while that player travels 8 m over at least 3 seconds.
The board is then no longer passed on to the others until it has behaved
for half a minute, and the log and the admins in the game are told who.
New setting anti_cheat.pinned_board and command board-check:
off | warn (default) | kick.
2026-10-11 03:07:32 -05:00
AyeZeeBB b9ffd72409 Replay drafts work, and the profiler lists the slowest frames
Replay drafts: the replay editor saves a draft as a pin on the recording
and a thumbnail in the game's media catalogue, which the Drafts screen
lists from. The game only makes that catalogue for a user signed in to
its backend, so under ReSkate a saved draft had nowhere to be listed.
ReSkate now registers the catalogue service and signs in a local user,
both with the game's own functions, so the game's Drafts screen works as
it was built: save, list, open and delete within a session.

A draft's footage stays in the recorder's memory, as it does in the
game, and does not outlive the session. Thumbnails of earlier sessions'
drafts are removed from the catalogue folder at startup; only folders
whose manifest is a replay draft's are touched.

Profiler: `perf sample` on the client thread now reports the slowest
client frames ahead of the function table, each with what it spent its
time in. An average over thirty seconds hides a single long frame.
2026-10-11 02:31:38 -05:00
AyeZeeBB 63c9e39c2a Teleport to players further away than skaters are shown
/tp refused anyone without a skater built here ("is not in the world
right now"). Since skaters are only built within the player distance
(120 m by default), that left only players already nearby: a player far
from everyone could teleport to nobody. The Social menu's teleport failed
for the same players, as the game was only told where a player was while
their skater was built.

- /tp goes to where the player was last heard to be, skater or not. Poses
  keep arriving for far players (their name or dot is placed by them), so
  distance no longer matters. Only a player no pose has placed yet is
  refused.
- The game's party position is kept up to date for far players too, for
  the Social menu's teleport.

No protocol or server change. /tp is not tested in game; the Social menu
path follows from the code and is not tested either. Spectate from the
Social menu may now accept far players and show the place without a
skater.
2026-10-10 22:43:09 -05:00
vtfandClaude Opus 5.5 8c85d54e3d Launcher/overlay: fall back to ImGui's stock pixel shader when the HDR one cannot be built
Since 2.0.4 the DX12 backend compiles only the HDR-aware pixel shader at
runtime with D3DCompile(). Wine's builtin d3dcompiler (macOS, Apple's Game
Porting Toolkit) cannot build it, so ImGui_ImplDX12_CreateDeviceObjects()
failed and the launcher refused to open on every Mac.

Try the HDR-aware shader first, then the stock one 2.0.3 shipped (on a
failed compile or a failed pipeline). ImGui_ImplDX12_HasOutputTransfer()
tells the launcher and overlay which one is drawing, and they log it.
Windows keeps the HDR shader and HDR output as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 21:04:39 -04:00
AyeZeeBB d1e6afc951 Pause menu: the mouse's resting place is not lit beside the D-pad's focus
On the Multiplayer and Mod Options pages an item was lit whenever the
mouse was over it. After moving the focus with the D-pad or the keys, the
item the mouse had been left on stayed lit as well, so two looked chosen
at once.
The pointer lights an item only while the pointer is in charge. Moving
the mouse puts it back in charge, and a click where it rests still counts
without moving it first.
2026-10-10 18:26:10 -05:00
AyeZeeBB b042bf253a Overlay on Intel GPUs: watch the swapchain under Streamline's wrapper
On Intel GPUs the overlay never drew: no Insert menu, and the pause
menu's Multiplayer and Mod Options tabs were empty frames, since the
overlay draws what is in them. The log stopped at "Registered DX12
swapchain" with nothing after it.

The game is given Streamline's wrapper (sl.interposer.dll) around its
swapchain, and the overlay hooked that wrapper's Present. On AMD and
NVIDIA the game presents through the wrapper. On Intel the frames are
presented on DXGI's own swapchain underneath, the wrapper is passed by,
and no presentation reached the overlay.

- The swapchain under a wrapper is hooked as well (Present, Present1,
  ResizeBuffers, ResizeBuffers1), found through Streamline's
  base-interface query.
- It is registered beside the wrapper with the queue the wrapper was made
  with: it is the same chain, and it gives no queue of its own when asked.
- A swapchain that presents without having been seen at creation is
  registered then, when it gives its queue.
- The log says whose Present was hooked, when a wrapper was found, when
  the first presentation arrives, and why a presenting swapchain cannot
  be drawn on.

Confirmed from an Arc A380 log up to "the first presentation reached the
overlay". The last step, registering the underlying chain with the
wrapper's queue, has not been run on Intel yet. Not yet checked on AMD or
NVIDIA either, where both chains are now registered.
2026-10-10 17:53:27 -05:00
wxndr 4ea92f0258 Add native DirectX 12 graphical server management GUI & live telemetry (Windows)
### Overview
Hey! This PR adds an optional native DirectX 12 graphical interface to the dedicated server (`ReSkateServer.exe`), making it easier to monitor and manage a server without having to rely solely on the raw terminal.

### What's included:
- **DirectX 12 / ImGui Management GUI**:
  - Live console with color coding and command input.
  - Connected players list with real-time ping, position coordinates (click to copy), and kick/ban controls.
  - Live map switcher and world layer manager.
  - Ban list & admin manager.
  - Real-time tick rate / latency graph and live telemetry table.
  - Server configuration & release update checker in Settings.
- **In-Game Badges & Roles**: Synced with official nametags/role badges (`DEVELOPER`, `CREATOR`, `HOMIE`, etc.) just like in-game HUDs.
- **Headless CLI Support**: Kept full compatibility for headless/VPS setups — running with `--headless`, `--cli`, or `--no-gui` (or on Linux) runs standard terminal mode as usual.
- **Thread Safety & Performance**: The GUI runs on the main Win32/DX12 thread while server host/networking ticks run asynchronously on worker threads so tick rate is never blocked.

Tested on Windows Server 2022 with Release x64 builds against local and Steam SDR sessions. Let me know if you'd like any adjustments!
2026-10-10 18:25:29 -04:00
AyeZeeBB 39897037be Fix the crash when a PlayStation controller is first read (2.0.4)
2.0.4 told a USB DualSense from a Bluetooth one by asking Windows for the
pad's report length, the first time the game read from it. That call was
made inside the ReadFile hook with the HID lock held, and hid.dll's
HidD_GetPreparsedData itself calls GetOverlappedResult and CloseHandle,
which ReSkate also hooks and which take the same lock. Locking it twice
on one thread throws, through hid.dll and the game, and the process ends
without a crash dump: as a DualSense or DualShock 4 was plugged in, or at
startup with one connected.

Nothing is asked of the game's handle now. Windows pads every HID read to
the device's own report length, so the number of bytes read says USB (64)
or Bluetooth (longer). The session-marker fix that the query was for
works from that instead.
2026-10-10 17:20:50 -05:00
AyeZeeBB 6b3d2bad97 Launcher: mods with an update waiting are listed first
In MY MODS, mods that have an update on Thunderstore come first, in every
sort order and with any filter or search. They keep their order among
themselves, and so do the rest.

Only the list changes: the load order, and so which mod wins where two
change the same thing, is as it was.

The arrows that move a mod in the load order are off while an update is
waiting, since the list is not in load order then; the tooltip says so.
v2.0.4 v2.0.5
2026-10-10 16:38:37 -05:00
AyeZeeBB 34da0d008d Overlay in HDR, DualSense marker bug, launcher mouse takeover, item search outside menus
Overlay in HDR
- ImGui draws sRGB colours, and the overlay wrote them straight into the
  game's HDR buffer, where they came out far too bright and washed out.
  The DX12 backend's pixel shader now converts them: to scRGB for an FP16
  buffer, and to HDR10 (BT.2020, PQ) for a 10-bit buffer while the display
  is in HDR, with white at 200 nits. An SDR buffer is drawn as before.
- The log says which was chosen ("Overlay output: ..."), and now also says
  when the game presents in a format the overlay does not draw to, which
  left its menus hidden with no reason given.
- Not seen on an HDR display. The changed shader compiles with the D3D
  compiler.
DualSense
- While a menu holds the game's input, the reports the game reads from a
  PlayStation pad are rewritten as untouched. USB and Bluetooth lay a
  report out differently, and the two were told apart by the size of the
  buffer the game read into. A USB DualSense read into a larger buffer had
  its d-pad byte written as "up", which placed a session marker whenever T
  or Insert opened something. The device's own report length decides now.
- The log line for the first rewritten report gives the pad, USB or
  Bluetooth, the report and the two lengths.
- Not tested with a DualSense.
Launcher
- The controller pointer learned where the right stick rests while no pad
  was being read, which includes the launcher being in the background, and
  took the middle for it. A stick that rests off the middle, or a wheel or
  throttle on that axis, then steered the pointer for good once the
  launcher had the focus again, and the mouse stopped responding until a
  restart. Nothing is learned while no pad is read, and a pad that goes
  away gives the pointer back. Two tests for it.
Item search
- The cosmetic search bar showed in the world: a grid keeps its focused
  item after its screen has gone. It needs the game's menu to be up, and
  to have been for a second, as well.
2026-10-10 16:34:39 -05:00
AyeZeeBB f289666c2c Entity limit 4x, replay crash guard, -map launch option, memory tools; pause-menu fixes
Memory
- The entity and pending-update pages are four times the game's own size
  (524,288 slots), down from eight times. At eight, the game kept a
  3.5 GB block for them that was fully in RAM on custom maps; the largest
  map load seen needs about 150,000 slots. The render-instance pool and
  spatial buckets stay at eight times.
- The log has a "Memory:" line when the game's memory, the players in the
  session or the skaters built change: committed, in RAM, players, skaters
  built, player distance and map.
- Console: `perf memory` lists the game's memory by block size.
  `perf memory mark` remembers it, and the next `perf memory` says what
  was added since. `perf memory trace` also records who asked for each
  block until `perf memory stop`. `perf memory big` lists the largest
  blocks with how much of each is in RAM and a sample of what is in them.
  RESKATE_MEMORY_TRACE=<megabytes|all> starts the trace with the game.

Replay crash guard
- The game's replay recorder crashes when its stream has no room for the
  next chunk: it writes to the chunk it was not given (Skate.exe+0x48efa34,
  crash reports of 2026-10-10). The function now runs under a handler
  that, for that one fault alone, returns as if no chunk was ended, and
  says so in the log. Any other fault still goes to the crash reporter.
  The skip path has not been triggered in testing.

Launcher
- `-map <name>` (also --map, -map=<name>) loads that map as soon as the
  game is up, named as the console's `load` names one. With --no-gui and
  no other options the launch still uses the saved launcher settings.

Pause menu
- The Multiplayer and Mod Options pages step aside while the game's side
  menu, or a screen opened from it such as Settings, is over them. They
  drew over both before.
- A button still held when a page comes back is not a press on it: the
  Back that closed the side menu no longer dismissed the page as well.

Multiplayer cosmetics
- A player whose mods add or remove cosmetic slots (stickers, say) no
  longer has the whole outfit refused. Their items go to this game's slots
  by slot: a slot they lack stays empty, an item in a slot this game
  lacks is left out, and the log says how many of each.
2026-10-10 16:15:59 -05:00
dubsector 7db99b57c3 Fix the Pterodactyl egg's config writes and Release rule
- The Release rule's regex held a "|", which Pterodactyl reads as a rule
  separator, so creating a server failed with a 500. Same check, no "|".
- The panel wrote port, query_port and steam_token at the top level, where
  the sectioned config ignores them. They now go to server.*.
- Add a Listed variable. The panel's JSON parser writes "false" as a
  string, so the startup command sets server.listed with sed instead.
- The first install writes its settings into the server section.
2026-10-10 16:20:05 -04:00
AyeZeeBB 5b7395c3ac merge v2.0.3 2026-10-10 13:28:08 -05:00
zeex64 d6775ae6c8 Merge pull request #166 from FourthStudio/road-rash/native
Road Rash: bails leave marks on the skater that build up
2026-10-10 13:19:15 -05:00
zeex64 fcd7435f4f Merge pull request #162 from FourthStudio/favorites/native-item-grids
Item grids: favorites, search and filters (Item Favorites, native)
2026-10-10 13:18:42 -05:00
zeex64 c36e210767 Merge pull request #161 from DeckardDetribine/investigate/music-favorites-persistence
Persist music favorites and playlist likes
2026-10-10 13:16:39 -05:00
AyeZeeBB 0ae0e71f0f Map downloads on join, faster mod merging, connect cards in the server browser
Map downloader
- A host or dedicated server now tells joiners which Thunderstore package
  its map comes from (the Mods folder name plus manifest.json).
- A player without the map gets a card: icon, name, author, version and
  description, with DOWNLOAD and CANCEL. It works with mouse, keyboard and
  controller.
- Only packages listed as Maps in the ReSkate community are offered, and
  never deprecated, rejected or NSFW ones.
- On yes the package is downloaded, installed into Mods and applied as a
  live mod, then the session loads the map. A small card on the right
  shows the progress, so the player can keep skating.
- The player stays connected while it downloads: the host gives a player
  who says they are fetching the map up to 45 minutes instead of the
  usual load time.
- When the server changes map with the player in it, the card says so.
- Protocol version 47: map package on map_offer and world_state, and a
  "fetching the map" flag on map_request and world_ready.

Joining
- The pause-menu server browser shows a CONNECTING card over the page
  while a join is under way, and a COULDN'T CONNECT card with the reason
  when it does not get in.
- The session and voice tabs no longer show before the player is in.
- Leaving without the map (declined or cancelled download) no longer
  counts towards the failed-join limit, on servers and player hosts.

Input
- A card that asks something holds the game's input until the buttons
  are released.
- Steam's overlay hooks XInput in front of ReSkate and answers the game
  itself, so a controller reached the game behind menus. ReSkate now
  hooks that hook as well.

Mod merging
- Mods are read on several threads, at launch and for live merges. A live
  merge runs below the game's priority.
- The paid-cosmetics check reads the game's files and the mods on several
  threads.
- A live merge reuses the paid-cosmetics verdicts it already has and
  reports its progress and timings.

Launcher
- NSFW mods and their categories are not shown in the mod browser.
2026-10-10 13:15:04 -05:00
Fourth Studio 1b0bb2b12f Road Rash: bails leave marks on the skater that build up
After 10 bails the leg of the side that took more of them shows road dust
and a graze, its arm one bail later; at 14 the marks are bigger and the
other side starts; at 20 they are raw wounds and the face is bruised. A
mark never gets smaller until the player heals. The worst wounds bleed,
or are dry and scabbed with the blood switch off.

The marks are cosmetic items of a content mod (tattoo items for the limbs,
moles items for the face, named RoadRash_<Set>_[<design>_]<level>). Road
Rash puts them into the local skater's recipe with the game's own recipe
copy, after the validation the multiplayer code runs on a peer's outfit.
Nothing is written to the outfit or the profile's skater, and without the
item pack nothing shows and nothing is changed.

A bail is the physics state selector choosing Wipeout (No Bail's watch).
Its side comes from the ragdoll's contacts through Hall of Meat's reader:
impact and slide of the arm and leg bodies that touch something other than
the skater's own board, summed per side while the body is a ragdoll.

Outfit menus. The game feeds a skater's items either from the player's
outfit or from a recipe of the skater's own: after the recipe copy the
skater in the world no longer gets what the player changes in the menus.
So the skater is given back to the game while a game menu is up, with
nothing to show, on healing, when a switch changes and when switched off:
the four words of the item component the copy had changed go back to what
they were and the component is told its feed changed, and the game puts
the outfit's own items back and feeds the skater again. The marks go back
on, over the outfit as it is by then, a second after the menu has gone.
Whose recipe a skater wears is read from the skater at every look: a
respawn hands over the same skater, items and all, as if it were new.
The player's own tattoos and moles stay unless roadrashtattoos is on, and
come back the same way.

The recipe handed to the copy names no item, only hashes, like the game's
own for the local skater: the game changes that skater's items where they
lie, and a name left beside one would stop every later read of the
recipe, the multiplayer code's too.

Console: roadrash, roadrashblood, roadrashtattoos, roadrash heal,
roadrash bails <count>. Menus: Road Rash, Road Rash blood and Heal Road
Rash beside Hall of Meat (Mod Options > Player, the overlay's skater page,
the native tools view). The three choices are saved with the profile.

No new hooks. The rules (side of a fall, levels per count, item names,
decal placement, comparing parameters the game has rounded) are in
road_rash_model.h with their own test target,
DINGOSDK_BUILD_ROAD_RASH_TESTS. Notes and what is not tried yet:
contrib/road-rash.md.
2026-10-10 19:55:29 +02:00
Fourth StudioandClaude Opus 5.5 6f41a16f1b Item grids: favorites, search and filters
In the skater's item grids (Apparel, Accessories, Board, Gestures) F favorites
the highlighted item, - (or Ctrl+F) opens a search box that filters as you
type, and X steps a filter (All, Favorites, Mods, Game items). Favorites sit
right after the worn item, carry the game's star where the tile shows its
rarity icon, and are kept in ReSkate.favorites.json beside Skate.exe.

The grids stay the game's own: their tile rows are reordered and filtered
through the native model functions, as the music screen's Mods shelf does.
Nothing is hooked for it and no file of the game or of a mod is changed.
See contrib/native-item-browser.md for how it works and what is not tried yet.

This is the Item Favorites mod done natively, so it no longer needs a DLL
loaded through a custom script.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 18:23:33 +02:00
DeckardDetribine 1b203d06c3 docs(music): record playlist persistence validation 2026-10-10 17:05:21 +01:00
DeckardDetribine 4312a08111 fix(music): persist favorites and playlist likes 2026-10-10 17:03:08 +01:00
AyeZeeBB b7936a36c6 Steam IDs in the player list for the team; custom scripts switched off
Player list. A player on the developer, staff or homie list sees each
player's Steam ID in the overlay's PLAYERS list for the session they are
in, after "Host" / "Guest" and the distance, on their own row too. A click
copies it. The lists are the backend's, the same ones the tags come from;
every client in a session already knew the IDs, so this changes who is
shown them and nothing else.

Custom scripts. Nothing in scripts/Custom is read or run any more, whatever
the launcher's "Loose files" setting says: nothing used them, and a mod was
using the folder to load a DLL into the game (package.loadlib from its
init.lua). The loader is kept behind one switch (custom_scripts_enabled in
custom_script_loader.cpp) for the day they come back.

For that day, the loader now also takes Lua's ways of running native code
away before the first custom script runs: package.loadlib, require through
the two C searchers and package.cpath, os.execute and io.popen. If that
does not hold, no custom script runs.

The overlay's Mods page no longer has the INSTALLED / SCRIPTS tabs; it is
the installed mods. Editing the game's own Lua and config through loose
files is unchanged.
2026-10-10 10:52:59 -05:00
AyeZeeBB 94ce8fddd7 Launcher: edit skate.'s own settings (graphics, audio, camera, controls, replay)
Settings has five more pages, under "SKATE. SETTINGS" in the rail, for the
settings the game saves per PC:

- GRAPHICS: window mode, resolution, HDR, brightness, UI brightness, HUD
  display, then the game's graphics options (frame rate limit, VSync,
  dynamic resolution, upscaler and its quality, antialiasing, textures,
  lighting, shadows, mesh, effects, post processing, global illumination,
  ambient occlusion, depth of field, motion blur) with buttons that set
  every low-to-ultra option at once.
- AUDIO: volumes, voice chat, text and voice language, subtitles, party
  menu narration.
- CAMERA: sensitivity, and the on board, off board and climbing options.
- CONTROLS: vibration, trigger force feedback, show controller on HUD,
  highlight indicator, the experimental toggles.
- REPLAY: editor speeds and sensitivity, effects, first-person keyframes.

They are written straight into the game's save containers under
%LOCALAPPDATA%\ReSkate\Game\Skate\data (game_settings.h): a DATA file of
sections and an INDEX naming them, the body one binary object of named
values. Only the values changed are rewritten, in both of the game's
copies; every other byte is kept. A container that cannot be walked whole
is left alone. The first change copies the untouched files to
%LOCALAPPDATA%\ReSkate\game-settings-backup.

Keys, kinds, ranges, steps and choices are the game's own
(DingoUISettingAsset in its data, build 20260929). The labels are ours.
Resolution is saved by the game as a place in the PC's list of display
sizes; the launcher lists Windows' sizes smallest first, which matches the
one PC it was checked on.

A change is stored once the control is let go. The pages are disabled while
a game this launcher started is running, and say so when the game has not
saved its settings on the PC yet.

Not offered: refresh rate, fullscreen monitor and audio devices (places in
lists the game builds at run time), the mobile-only settings, and settings
the game saves to the account rather than the PC.
2026-10-10 02:30:50 -05:00
AyeZeeBB 63955e51ff Launcher changelogs, download total, Discord joins skip the launcher window; Mod Options
Changelogs. The launcher's main screen has a CHANGELOGS tile in the
bottom-left corner. It opens a panel with the newest GitHub release: its
tag, title, date and notes. The notes are the release's description, read
as markdown and drawn like a mod's README (headings, bullets, code, rules).
A release published without a description shows what its GitHub page shows,
the tagged commit's message, taken from the releases feed. Fetched once,
the first time the panel opens; "Open on GitHub" goes to the releases page.

Download total. On GET MODS, the bar for ticked mods now gives the size of
what installing them downloads: "3 mods selected / 1.27 GB to download".
It is the sum of each mod's newest version as Thunderstore lists it.

Discord joins. The command the game registers for Discord to start ReSkate
now passes --no-gui, so pressing Join with the game closed goes straight
into the game instead of stopping at the launcher window.

--no-gui with no other launch flags now starts the game with the launcher's
saved settings (window size, menu and console keys, loose files, GPU
diagnostics, Discord status, offline, log level, extra launch options), as
pressing PLAY does. With flags of its own it behaves as before.

Mod Options. The pause menu's "Custom Stuff" page is renamed "Mod Options",
in the menu, the READMEs and the comments.

Not tested in game: the Discord join with the game closed, and the renamed
page. The changelog panel has only been seen with the earlier API-only
fetch.
2026-10-10 01:42:36 -05:00
AyeZeeBB 6a02550c7e Launcher: a mod that fails to install no longer stops the rest
Installing or updating a list of mods from Thunderstore stopped at the
first one that failed, so everything queued after it was never tried.
Each mod is its own download: a failure is now skipped and the rest
carry on.

The message at the end says how many could not be installed and how
many were, and names the first three with their reasons; the log has
every one. Cancelling still stops the whole list, and a single install
reports its error as before.
2026-10-10 00:56:38 -05:00