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).
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.
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.
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.
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.
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.
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.
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).
- 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.
A mod the merge could not use was left out silently. The game started
without it, three minutes into a loading screen, and only ReSkate.log said
which one.
Merging before launch
- PLAY runs the merge the game would have run behind its splash, on the
launcher's worker, with the step and progress on the STATUS tile
(Phase::merging). The launcher links dingosdk_mods for it, which costs
about 600 KB of exe.
- When every mod merges, the launch goes ahead and the game's own
load_catalog finds the patch already built, so the splash is shorter
rather than the work being done twice.
- When mods were left out, the launch stops at Phase::mods_broken and a
dialog names each one and its first problem, with three ways on: switch
them off and play (which writes mods.json, so the next launch does not
rediscover it the hard way), open the mod manager, or play anyway.
- Mods built for another game version do not block. They already have
their own notice and message box, and blocking on them would stop play
after every Steam update.
- The launcher resolves -dataPath out of the extra-arguments setting, so
it merges against the root the game will read.
One SDK identity for both binaries
- Two places recorded "which ReSkate did this" as the module the code was
running in, which is ReSkate.dll in the game and ReSkateLauncher.exe in
the launcher now that both merge. merge_fingerprint hashes it, so the
game matched no stamp the launcher wrote and merged everything a second
time; sdk_identity keys the exclusions file, so each would have called
the other's exclusions stale.
- Both now name ReSkate.dll beside the running executable. Skate.exe and
ReSkateLauncher.exe sit beside it and launch.cpp enforces that, so the
two agree, and unlike the data root it does not move under -dataPath.
The value is unchanged from what the runtime computed before, so nothing
re-merges on upgrade.
Naming the mod
- In game, a mod left out of the merge altogether said nothing on screen:
only a mod that merged partially did, which is the lesser failure. Both
are named now.
- The MOD MANAGER tile says which mod did not load, in the detail line's
colour rather than a pill, clipped so a long name cannot run under the
tile's artwork.
- MY MODS carries a line naming them above the list, and NOT LOADED and
OUTDATED are badges on the row instead of words at the front of its
summary.
MY MODS reaches Thunderstore parity
- Rows carry the package's icon, the same one the store shows, through a
shared mod_icon(); a mod that never came from Thunderstore keeps the
placeholder.
- An update is a button on the row, not a badge you had to open the mod to
act on, and every row has Uninstall. Rows grew to fit, and "Remove" is
"Uninstall" throughout.
- No pill for a mod that is up to date: the absence of an update button
says it, and eight green pills said nothing eight times.
Fixes
- The install panel was drawn on the Mods page's own draw list after its
children, and ImGui renders a child after its parent, so it was painted
behind the mod rows along with its Cancel button: installs ran with no
sign of it. It is its own window over the page now.
- pump_icons, which uploads decoded icons, only ran on the store page, so
MY MODS showed placeholders until a visit to GET MODS uploaded them for
it. The page does it for both lists.
- The merge dialog's closing advice overlapped its buttons.
The mod browser was a 960x620 panel inside a 1180x680 window, reached from
a tile the same size as SETTINGS. People could not find it, and once they
did there was room for about six mods.
Window
- The design size is 1440x840. Everything is laid out in design units and
multiplied by g_scale, which now scales down when that would not fit the
screen's work area, so a small laptop or a high DPI no longer gets a
window running off under the taskbar.
- The MODS tile is MOD MANAGER: taller than SETTINGS, directly above PLAY,
with the pending update count as a pill on its subtitle line.
- background.jpg is 1920x1027, so the bigger window is not upscaling a
1280x720 photo.
Mod manager, a page rather than a panel
- begin_page fills the window and draws its own minimise and close, since
it covers the main screen's. BACK or Escape leaves it.
- A nav rail holds MY MODS and GET MODS (count badges, blue for the open
one), the install actions and BACK. Refresh sits with what it refreshes,
top right of the content.
- Every mod is a full-width row: icon, name, author, description, version
and downloads, with a blue INSTALL or UPDATE and a tick box on the right.
Ticking several installs them in one run.
- Clicking a row opens that mod's overview in a popup, replacing the
side-by-side details pane that halved the list's width.
- virtual_rows draws only the rows a scrolling child actually shows.
ImGuiListClipper cannot do this, as it assumes every row is a fixed
height.
- Installing is a centred panel over a dimmed page with the activity, a
striped bar and Cancel, instead of a progress bar in the footer.
Settings
- The same page and rail: GAME, DISPLAY, KEYS and ADVANCED are nav tiles
rather than a row of tab buttons, and the content keeps to a 760 px
column so the explanations stay a readable measure.
- The Steam sign-in, Steam Guard and QR dialogs stay centred panels. They
are prompts, not pages.
Status and failures
- The STATUS tile carries its state in a coloured edge and a pill (WORKING,
READY, ACTION NEEDED, PROBLEM), its headline is 22 pt over muted detail,
and the standing facts (game build, ReSkate version) sit under a rule
instead of competing with the live status as equal rows.
- Launcher::fail keeps the raw message for the log, and problem.h turns it
into a headline and the next thing to try. The injection and PE-identity
failures ("VirtualAllocEx failed", "Injected ReSkate.dll is not present
in the child module list") all become "ReSkate could not attach to
Skate", with the anti-virus and overlay advice that actually fixes it;
the Steam download, a missing or damaged ReSkate.dll, the content cache
and a shared menu/console key each get their own. Anything unmatched
keeps its raw text.
- A failed tile grows Open logs and Copy details. Copy details puts the raw
message and the versions on the clipboard, ready to paste into Discord.
Mesh streaming pool (fixes loading crashes on older GPUs)
- 1.0.0 forced MeshStreaming.PoolSize to 3.5 GiB on every card. An RX 580
crashed while loading with DXGI_ERROR_DEVICE_RESET, and the oversized pool
is the likely cause. The pool is now a quarter of the largest hardware
adapter's dedicated video memory, capped at 3.5 GiB; cards of 6 GB or less
keep the game's own 520 MB. The card, its memory and the chosen size are
logged once at startup.
Remote players (busy servers)
- Transform interpolate is inline plain arithmetic: 22 -> 5.8 ns per joint.
It was 4% of a busy server's client frame, since every player within
200 m has the whole skeleton interpolated every frame.
- Players out of the camera's view and more than 8 m away are sampled at
20 Hz, like far players. Their position still updates every frame for
collision, sound and nametags, and a player coming into view is sampled
that frame.
Expression hooks (run for every expression the game executes)
- No profiler zones in the expression hooks: taking a sample switched them
on, inflating the very samples that measured the hooks.
- The throwdown lab pump does one sorted lookup of the running graph and
skips everything else for graphs it doesn't watch (graph_throttle gets
throttled_graph for this).
Settings and memory reads
- Script gameplay-setting and native-setting gets are served from small
per-thread caches, keyed on the asset/key address, its first 8 name
bytes and a new profile::Store::changes() counter, instead of a store
lookup each call.
- Per-frame reads in the client tick, native settings and population
controls use memory::peek instead of ReadProcessMemory.
- Script error logging probes each argument pointer once.