The update check reads GitHub's release list either way (one call, as before) and notes whether a
pre-release newer than the latest release is there; the setting is offered only then, or while it
is on so it can be turned off. Unreachable GitHub keeps what the last check found.
Flipping "Install ReSkate updates" or "Test pre-releases" saves and checks again straight away, so
PLAY or the update button changes to match: updates off while one is offered goes back to PLAY,
pre-releases on offers the pre-release, off the latest release. A flip while a check is running is
checked as soon as it ends (each frame asks; it only compares two settings).
Reported in #265 by Kayyo321; first fixed in #267 by Jean-LucTch, whose
approach this keeps to the folder being installed.
"Install folder" named each file with fs::relative, which follows links:
a file link inside the folder to a file outside it came out as
../../<file>, and the copy wrote over that file (ReSkate.dll among what
it could reach).
- Files are named by where they sit in the chosen folder
(lexically_relative), never by where a link leads, and go through
safe_entry like a zip's entries.
- A symbolic link, junction or mount point inside the folder refuses the
install, saying which. Links on the way to the folder or to the Mods
folder (a library moved to another drive) stay fine, as do cloud
placeholders such as OneDrive's, which name nothing else: #267 refused
any reparse point on those paths, so a game reached through a junction
could install nothing.
- Tests: a junction inside a mod folder is refused and the file it leads
to is untouched, with no staging left behind (this fails without the
fix); a Mods folder and a picked folder reached through junctions still
install; a file symbolic link is refused (skipped without Developer
Mode, which creating one needs).
Co-authored-by: Jean-Luc T. <88255067+Jean-LucTch@users.noreply.github.com>
- page_banner: each page opens with its heading, what it is for, and how
things stand at the right (a status pill and a caption). card: the
panel the pages draw on, lighter edged on hover, blue edged when marked.
- MY MODS and GET MODS rows are cards with a gap between them (list_row),
in lists without a border. MY MODS' banner counts problems and waiting
updates and holds the load-order explanation; GET MODS' says whether
the listing loaded and what is installed or waiting. The ticked bar on
GET MODS is a card.
- OFFICIAL: a card per mod with its status and version pills, its
Thunderstore facts, and THUNDERSTORE and OPEN FOLDER.
- PROFILES: new-profile and import cards side by side; a card per profile
with a picture of its first mods' icons, pills for what it holds, LOAD
and UPDATE, and the rest in a menu.
- The borderless window has a frame style for resizing, snapping and its
shadow, with WM_NCCALCSIZE keeping the whole window client. Its edges
and corners resize it; maximised, it is inset by the frame Windows puts
past the screen.
- WM_GETMINMAXINFO holds it to the size it opens at.
- The swap chain's buffers follow the client size (Renderer::resize).
- Frames are drawn from WM_SIZE too, so dragging an edge (Windows' own
message loop) does not stretch the last frame.
Every update, and every install over an installed mod, sent the old
folder to the Recycle Bin, which filled up with old versions.
- commit() moves the installed copy aside into the staging folder, moves
the new one in, then deletes the old for good. A new copy that cannot
go in puts the old one back; a copy with a file in use is not replaced
and stays whole.
- Uninstalling still goes to the Recycle Bin. The Replace dialog says the
installed copy is deleted.
The launcher reads the official list from the ReSkate backend (or the last one it read, or its own)
and keeps each mod in Mods/.reskate-official at its latest version; a copy the player installed is
moved in, the deprecated Board Break retired. The game loads them always on and leaves out Mods
folder copies; the browser no longer lists them. The built-in zips are gone (FPV drone too), and
built-in folders no longer carried are removed.
Fourth Studio's Board Break pack (ten decks and every sound) and Road Rash wounds are built into
ReSkate.dll beside the FPV drone, unpacked under Mods/.reskate-builtin and always on.
- The Thunderstore copies (Fourth_Studio-Board_Break_Pack, Fourth_Studio-Road_Rash and the
deprecated Fourth_Studio-Board_Break, or the same put in by hand) are moved to the Recycle Bin
as the launcher starts and dropped from mods.json; the merge leaves them out should one still
be there, and the Thunderstore browser no longer lists, updates or installs them, a dependency
on one included.
- Board Break's deck list, sounds and deck settings look in the built-in packs too.
- Names with spaces inside them (Board Break's "Body Impact 1.wav") unpack.
- The build makes each zip in assets/builtin into one zstd stream (ReSkateBuiltinPacker,
Engine/Vfs/builtin_pack_format.h), which the DLL carries: repeated files across a pack cost next
to nothing. Board Break 15.0 -> 5.4 MB, the drone 6.0 -> 5.4 MB, Road Rash 36.1 -> 34.2 MB;
ReSkate.dll 86 -> 70 MB. Plain zips still unpack.
- Tests: the mods taken for built-in ones and their retirement, and a pack unpacking to exactly
its files, refusing one that leaves its folder or is cut short (RESKATE_BUILTIN_PACKS also
unpacks the packs a build made).
path::string() uses the ANSI code page and throws on a character it lacks (a Cyrillic folder on
an English Windows). The remaining places it was used now go through path_utf8:
- Mods: a deck folder or a mod folder named outside the code page no longer leaves the Board Break
decks empty, or the Mods folder ignored altogether.
- Game and mod archives: a file under Win32 with such a name is passed over instead of stopping
the read.
- pose-dump no longer ends the multiplayer session when the game folder is not ASCII.
- The item browser's status line and a Board Break warning no longer fail on such a path.
- Launcher: system fonts are named to ImGui in UTF-8, which it reads.
Sentry's minidump endpoint streams every attachment but the dump to an object store, which this
self-hosted Sentry does not run: ReSkate.log and report.json were dropped from every crash
(outcome upload_failed). Each crash now goes to the envelope endpoint as sentry-native sends one:
the event, the minidump, report.json and the log, all kept.
- The event carries the log's last 100 lines as breadcrumbs (debug lines only among the last 25),
the mods the game ran with (enabled, outdated, excluded), how long it had run, and an anonymous
install ID as its user. Its time is the crash's, however late it is sent.
- crash.site tags where it crashed ("Skate.exe+0x3d9ed9d"), as Sentry names game code by the
nearest export; mods.count how many mods were on.
- An accepted report is kept in sent/ (the newest 10) instead of deleted, so the player still has
their crashes.
- multipart.* is report_envelope.*; the receipt check also reads the envelope endpoint's
{"id": ...}.
Most crashes are caught by the game's own fatal handlers, which write a
dump of thread stacks only; the launcher could only copy it after the
game had exited. A crash on a freed or overwritten object then showed
the faulting registers but not the object or the list it came from.
- game_fatal_dump: the game's MiniDumpWriteDump import points at
ReSkate. Before the game writes its dump, the crash reporter takes its
own of the still-running game for the faulting thread; the game's dump
follows as before. Reports say capture.source "game-fatal-handler".
- The reporter's dumps include the memory the stacks point at
(MiniDumpWithIndirectlyReferencedMemory), a few MB more a report.
- The shared report says who asked for the dump (protocol version 4).
- Tests: a game-handler crash is captured with a heap block only its
stack points at; ReSkate's own capture keeps it too.
Turning "Install ReSkate updates" or "Test pre-releases" on or off took
effect at the next start. Closing settings after changing either now runs
the update check again: a pre-release is offered as soon as it is wanted,
the latest release when it no longer is, and a pending update is dropped
when updates are turned off.
- Not while a download, an update or a launch is under way, while the game
runs, or while a merge waits on an answer about mods; nor as the launcher
closes. The next check or start picks the change up then.
- The "Test pre-releases" help says the check happens when settings close.
Settings > Launcher > "Test pre-releases" (off by default). On, the update
check takes the newest GitHub release that is not a draft and carries a
launcher.json, pre-release or not, in place of the latest full release:
a pre-release newer than the last release is installed, pinned by size
and SHA-256 as any release is. Turned off, the next check goes back to
the latest release, whose files then replace the pre-release's.
- The update notice says "Pre-release" for one, and the log names which
kind of release was taken.
- The changelog shows the release the setting picks, marked as a
pre-release, and is fetched again when the setting changes.
- --no-gui launches follow the same setting.
The renderer gives each frame 131,072 shader-visible descriptors past a persistent 120,000, in
heaps of 333,333. Dense custom maps (the Los Santos City sublevels, San Andreas) draw more than
that in a frame, and the renderer wrote past the end of the heap: shaders read garbage
descriptors, which faulted or hung the GPU (page faults at nonsense addresses, in draws, texture
fetches and ray tracing alike). ReSkate now patches the device setup at boot so each heap is
D3D12's 1,000,000 and a frame may use 524,288; heaps are 644,288 descriptors. LS-B2, LS-B3 and
San Andreas (210,000 a frame at its peak) now hold.
Also in this change:
- Memory: perf memory owners/heap/gpu/report, a ledger of who commits the game's RAM; the
render-resource pool no longer allocates its 115 MB twice; the EadpErrorsData arena is
capped at 32 MiB (EA's error reporting is blocked anyway).
- Crash reports: tagged with the map, destination, graphics card and driver version (protocol
v3), so Sentry can group GPU hangs by map and card.
- GPU diagnostics (--gpu-diagnostics): a fuller DRED report, the device-removal and video
memory watches, a large-draw watch, a record of which game code made and freed each GPU heap
and resource, checks of every GPU address bound, and a meter of each descriptor heap's use.
- World pools: peak use of entity pages, render instances and spatial blocks in the log.
- dingosdk_mod_mesh_check: checks every MeshSet a mod ships against its geometry; the MeshSet
reader now knows static meshes' layout.
After Steam updated skate., some installs had the supported Skate.exe over
the new build's data (a copied game, or a download that stopped part-way).
The launcher called that ready, every mod was left out as "made for game
build 29544144, not the installed 29663206; rebuild it with ReSkate Studio",
and the mods stayed out after the game files were put right.
Launcher
- Ready now also needs the game's data to be the supported build, read from
Data\layout.toc against supported_build::game_data_changelist (29544144).
Data of another build, or none, reads "Game files need verifying".
- DOWNLOAD in that state checks every file (-validate) instead of fetching
only what is missing. A download that fails part-way no longer ends on
ready because Skate.exe arrived.
- Data whose build cannot be read does not block.
Mods
- A mod left out because the game was of another build is no longer
remembered in .reskate-excluded.json: it is checked on every launch. One
already remembered for it is looked at again.
- When it is the game that is not the supported build, the mod's message
says to verify the game files in the launcher, not to rebuild the mod.
"The Steam download did not finish" is the launcher's message for any
failed download, and what DepotDownloader said (the account does not own
the game, Steam refused the sign-in, the disk is full) was only written at
the debug level, so Open logs showed nothing to go on.
When a download fails, its last 40 lines are now written to the log as
warnings, after the exit code. The Steam login name is still left out, and
so are the rows of a QR code and the progress lines.
- The launcher refuses to start from a Steam library folder under
Wine/Proton as well; the check is no longer skipped there.
- README, Linux (Proton): the launcher and ReSkate.dll go in a new, empty
folder, and the Steam launch option swaps in the launcher's full path in
place of EA's anti-cheat launcher. Noted as not yet tested on Proton; the
earlier test was with the launcher beside Skate.exe.
Launcher: ReSkate lives in a folder of its own, not Steam's skate. folder
- The launcher refuses to start from a Steam library folder
(...\steamapps\common\<game>): it shows an error saying to move
ReSkateLauncher.exe and ReSkate.dll to a new, empty folder, then closes.
Checked before anything is read or written there. Not under Wine: on
Proton, Steam starts the launcher from the game's folder.
- The "not in your skate. folder" error no longer tells players to put the
files beside Skate.exe in Steam's folder; it now says to keep them together
in a folder of their own.
- README: Getting started says to extract into a new, empty folder, where the
launcher installs its own copy of the game, so a Steam update cannot break
it. The Proton section says why it is the one exception.
The Mods page has a third tab, PROFILES. A profile is a saved list of which
mods are on, in what order and at what version; every mod stays installed
once, in the one Mods folder. Loading a profile rewrites mods.json, switches
off what it does not name, and downloads from Thunderstore what it names
that is missing. Profiles live in Mods/profiles.json.
A profile travels the way r2modman's do, so the two managers read each
other's: export.r2x inside a .r2z file, and a code on Thunderstore's
profile service. Config files in an r2modman profile are ignored.
Installing or updating a Thunderstore package now brings the packages it
depends on, needed ones first, leaving out any already installed at a new
enough version. A mod pack is a package that is nothing else.
A kick, by an admin or by a passed vote, used to keep the player out until
the server restarted. "kick_minutes" in the players section of the config
(1-1440; 0, the default, is still until the restart) and the console
command "kick-time <minutes>|restart" now set how long it lasts.
The player is told how long when they are kicked, and how many minutes are
left if they try to join early. A kick keeps the time it was given, so
changing the setting is for later kicks. Bans are unchanged.
Crash dumps now go to the project's own Sentry server. The launcher's
out-of-process helper is unchanged; only where it posts and what it expects
back are:
- The default endpoint is the Sentry project's minidump URL. Report
attributes travel as Sentry tags, with the release as reskate@<version>.
- An upload counts as accepted when the server answers with an event ID.
- "backtrace" is "crash_report" throughout: the namespace, files, CMake
targets, DINGOSDK_CRASH_REPORT_URL, DINGOSDK_BUILD_CRASH_REPORT_TESTS and
the RESKATE_CRASH_REPORT_URL override.
Performance timings are new, and off unless "Send performance timings" is
ticked in the launcher's settings (RESKATE_TRACING=1). A small tracer
(Engine/Core/Debug/trace.h) posts each finished step to Sentry as a
transaction from a background thread; the launcher and the game it starts
share one trace. Timed: the update check, the Steam download, Thunderstore
installs, the mod merge, starting the game, the launch up to the first
playable level, each level load, joining a session, fetching the identity
lists, and the time spent in each level with its average frame rate and
long frames. Turning crash reports off turns timings off too.
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>
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.
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.
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.
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.
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.
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.
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.
The game shows on the player's Discord profile where they are skating.
It talks to the Discord app on the same PC over its local pipe, with no
SDK; without Discord running nothing happens.
- Solo: "On <map>", "Solo skating", and how long they have been in.
- In a session: "On <map>", "Server: <name>" or "Lobby: <name>", the
player count, and how long since they joined. A session with a
password shows "Private server" or "Private lobby" and no name.
Joining: a status carries the session's join code when anyone may walk
in (no password, not full, a dedicated server or a publicly listed
lobby). Discord shows that as "Ask to Join"; the game answers yes for
the player at once, and the other player's game is handed the code and
joins. If their game is not running, Discord starts the ReSkate
launcher, which the game registers for under the player's own
discord-<application id> URL scheme.
Switches: "Discord status" in the launcher's settings (off starts the
game with --no-discord, and the game never opens Discord's pipe), and
the same in the in-game Multiplayer settings, kept in the profile.
The model gains join_code: the code of the session a player is in as
host or guest (invite was a host's only).
Reject malformed, non-finite, and out-of-range progress values while preserving them as ordinary messages. Add parser regression tests for progress and QR-code output, and register them with CMake.
The overview that opens when a mod is clicked, on both MY MODS and GET MODS,
now shows the mod's README and has more room for it.
README:
- GET MODS fetches a package's README from Thunderstore the first time its
overview opens (/api/experimental/package/<owner>/<name>/<version>/readme/)
and keeps it while the launcher runs.
- MY MODS shows the mod's own README.md from its folder, or the package's
from Thunderstore when the folder has none.
- The markdown is laid out as plain text: headings in bold, lists, rules and
code blocks kept; links keep their text; images and HTML are dropped; a
table row shows its cells side by side. A README is cut at 400 lines, with
a note that the rest is on the mod's page, and one over 512 KB is not read.
Layout:
- The overview is up to 1080 x 740, from 680 x 560.
- The header has a larger icon with the name, the author and the short
description beside it. An installed mod shows its Thunderstore icon.
- Under the buttons the README has the wide column and the facts (version,
downloads, updated, size, categories, folder, maps and so on) have a panel
of their own on the right.
- The trackpad pointer follows the whole swipe; Steam stops the tilt when
the thumb lifts, so nothing has to be cut off.
- Up and down stay inside the mod lists and leave a row from any of its
parts (tick, switch, buttons) in one press.
- Left from anywhere in a list reaches the nearest rail tile, BACK included.
- The focus no longer falls back to a tab when ImGui lets go of a row the
list only partly shows.
ReSkate.log only said "Steam is not running or not signed in", even with
Steam open and signed in. steam_offline_reason() names the check that
failed (ActiveProcess pid or ActiveUser is 0, the process cannot be
opened or identified, has exited, or is not steam.exe) and start_game
appends it to that line. steam_signed_in() behaves as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VNYF3aYa2q3MkFXc5xYnDd
- The focus starts on PLAY when the launcher opens with a pad connected, and
on the open tab (GAME, MY MODS) when the pad opens Settings or the Mod
manager.
- List rows act as one item: up and down go from row to row in one press,
and a row's buttons take the focus only from the row (D-pad right).
- D-pad left and right between the rail and a list: ImGui looks to the side
only along a thin band through the focus and missed items level with the
gap between two rows, so a side move that finds nothing looks again across
the focus's full height.
- The left stick scrolls the panel under the pointer directly instead of
through wheel events, which queued behind the pointer's moves.
- On a Steam Deck (SteamDeck=1) the right stick gets a trackpad's feel: small
swipes move the pointer, and the glide after the thumb lifts is cut.
- The MODS tile shows the skate tool again, faded like the wheel on
SETTINGS. Thunderstore's mark is no longer drawn there
- The mod manager's Thunderstore button is laid out as a link sign, the
name, then Thunderstore's mark at the far end, so it is plainly a way
out to a website. The sign is drawn (a box with an arrow leaving its
corner); the name is no longer in capitals
- It keeps Thunderstore's green on its dark blue and its height
- The MODS tile shows Thunderstore's mark in its own green instead of
the skate tool, which stays as the fallback when the mark is not
built in
- "Open Thunderstore" in the mod manager is a branded button: the mark
and name in Thunderstore green on its dark blue, taller than the
plain buttons beside it
- The mark is a new asset, assets/launcher/icon_thunderstore.png,
embedded like the other tile icons
- A disabled mod's update is no longer asked about before PLAY or
counted on the MODS tile, and "UPDATE AND PLAY" no longer installs
it. A disabled mod does not load, so its update stays on the Mods
page, where it can still be updated by choice
Server browser
- Official servers (the backend's "official_servers") sort first and are
drawn in ReSkate blue
- Servers Steam friends are skating in sort next, drawn in green with the
friends' names; searching a friend's name finds their server
- A player in a session they joined can join another server straight from
the list; hosts still end their session first, and a Steam friend's join
offer never replaces a session
- Joining a dedicated server from the list records it as the player's
public session, so friends see it and the row shows HERE
- Blocked servers (the backend's "blocked_servers") are hidden and cannot
be joined, by code either
- When the backend sets "server_tokens_required", dedicated servers that
signed in without a login token are hidden, except on the player's own
network
- A search that gets no internet list from Steam keeps the last list for
two minutes, and waits six seconds before treating it as empty
Roles
- New Staff tag from the backend's "staff" list: green name, badge and
item animation, ranked after Dev
- Staff can pick a rainbow for their items on the Special page; it is sent
as a solid colour older builds can show, so the protocol is unchanged
Dedicated server
- steam_token: sign in with a Steam game server login token and keep the
same Steam ID every start; empty signs in anonymously as before
- A listed server without a token says so at startup, and when the token
rule starts or stops hiding it
- Linux servers update themselves from the release's "server_linux"
tarball, with the machine's curl and tar, pinned by SHA-256, and carry
on in the same process
Launcher
- Only create the MiniDumpWriteDump API-set shim under Wine/Proton
Toolhelp returns module paths through the ANSI code page, so a folder like "Skate Тест" came back with '?' in it, the injected DLL was never matched, and the launcher killed the game. The loader's own list keeps the real path.
- Mods that add copies of store items under new names are no longer
loaded. The launcher, mod manager and game say why.
- A chat line passed on by the host only shows a Dev/Creator/Homie
badge when the player's own copy of it arrives too, so a host
can't put one on a line that player never wrote.
ImGui 1.91 moves the focus with the D-pad only; its left stick scrolls the
focused window, which under a pointer is usually the page itself. The stick
also handed the screen from the pointer to the focus, so scrolling flipped
between the two.
The launcher now scrolls itself: the nearest scrollable panel around the
focus while the focus is shown, else the panel under the pointer or the
mouse (as wheel notches). Scrolling no longer switches between them.
begin_panel and begin_page focused their window again every frame. With a
controller that pulled the focus out of the page's child panels, so in
Settings and the Mod manager (GET MODS included) the D-pad and left stick
could reach nothing. The window is now focused only when the focus is not
already inside it.
While the pad moves the focus the mouse cursor hides, and the next mouse
move brings it back.
- Settings' and Mods' rails, pages and lists are NavFlattened children, so
the D-pad crosses between them instead of treating each as one item that
only B could leave.
- The pad's pointer no longer moves the system cursor. Under Proton that
move came back late as a mouse move and dropped the pointer; now a mouse
move counts only when it lands somewhere new.
- The first R3 only shows the pointer, so a stray click does not press
whatever the mouse last rested on.