74 Commits
Author SHA1 Message Date
AyeZeeBB 36ce7cc263 Launcher: "Test pre-releases" only shows when there is one, and either update switch checks again at once
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).
2026-10-17 01:50:50 -05:00
AyeZeeBBandJean-Luc T. f6f1052237 Mods: a link inside a mod folder refuses its install instead of writing outside Mods
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>
2026-10-16 20:16:33 -05:00
AyeZeeBB b63e2d62bd Launcher: the mod manager's pages redesigned, with a banner and cards
- 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.
2026-10-16 20:01:53 -05:00
AyeZeeBB 72e6ba0301 Launcher: the window can be resized, no smaller than it opens
- 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.
2026-10-16 20:01:46 -05:00
AyeZeeBB 181e02454b Mods: an update deletes the copy it replaces instead of recycling it
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.
2026-10-16 20:01:41 -05:00
AyeZeeBB a2d6daffbc Official mods: Board Break and Road Rash downloaded from Thunderstore and kept at their latest, with an Official tab
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.
2026-10-16 18:59:41 -05:00
AyeZeeBB 43b998f285 Built-in content: Board Break and Road Rash, replacing their Thunderstore mods, packed with zstd
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).
2026-10-16 18:38:46 -05:00
AyeZeeBB 320086381c Paths: the last few written as text go through UTF-8, so a non-ASCII name no longer throws
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.
2026-10-16 18:12:34 -05:00
AyeZeeBB 571d57f603 Crash reports: sent as envelopes so their logs arrive, with the log's last lines, crash site and mods
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": ...}.
2026-10-16 17:59:40 -05:00
AyeZeeBB 290089e16d crash fix 2026-10-16 17:34:06 -05:00
AyeZeeBB 3249f747dc Crash reports: the game's own fatal crashes come with the memory their stacks point at
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.
2026-10-15 20:01:55 -05:00
AyeZeeBB 228ce65f84 Launcher: changing the update settings checks again without a restart
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.
2026-10-15 18:43:11 -05:00
AyeZeeBB 310922556c Launcher: a setting to test pre-releases
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.
2026-10-15 14:23:06 -05:00
zeex64 e7c6df0df6 Merge pull request #225 from Jean-LucTch/dev
Fix multiplayer custom map download and join timeout handling
2026-10-15 14:03:06 -05:00
AyeZeeBB 164fdffbd0 Descriptor heaps: dense custom maps no longer overflow the GPU's descriptor heap and crash it
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.
2026-10-15 13:57:11 -05:00
AyeZeeBB 2e40aeb6e2 Game data of another build: the launcher asks for a verify, and mods are looked at again
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.
2026-10-13 16:31:09 -05:00
AyeZeeBB 9124869ba4 Launcher: a failed Steam download writes DepotDownloader's last lines to the log
"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.
2026-10-13 15:53:55 -05:00
AyeZeeBB 8cad9f2c13 Launcher: no exception for Proton, it has a folder of its own there too
- 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.
2026-10-13 15:14:51 -05:00
AyeZeeBB 375605b9fe Launcher: ReSkate lives in a folder of its own, not Steam's skate. folder
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.
2026-10-13 15:00:19 -05:00
copilot-swe-agent[bot]andJean-LucTch 80242c0ec8 Cover deadline boundaries, safe cancellation and fresh join races
Co-authored-by: Jean-LucTch <88255067+Jean-LucTch@users.noreply.github.com>
2026-10-13 11:27:08 +00:00
copilot-swe-agent[bot]andJean-LucTch 8eddcab142 Coordinate custom-map downloads with the multiplayer deadline
Co-authored-by: Jean-LucTch <88255067+Jean-LucTch@users.noreply.github.com>
2026-10-13 11:11:27 +00:00
AyeZeeBB 2e186b9839 Launcher: mod profiles, sharing them by code or file, and Thunderstore dependencies
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.
2026-10-13 00:55:29 -05:00
AyeZeeBB 4ac1343b95 Crash reports: self-hosted Sentry instead of Backtrace, and opt-in performance timings
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.
2026-10-12 23:24:33 -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 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.
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
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
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
AyeZeeBB 8a4980c26a Discord Rich Presence, with joining from Discord
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).
2026-10-09 22:51:22 -05:00
Jean-Luc T. e031571e1a Validate DepotDownloader progress output
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.
2026-10-09 18:14:35 +02:00
AyeZeeBB c8c6cf16f2 Launcher: mod overviews show the README, in a larger two-column layout
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.
2026-10-08 22:12:48 -05:00
zeex64 63ee53382a Merge pull request #63 from northdroplet/feat/gamepad-touchpad
Launcher: controller support and the Steam Deck trackpad as a mouse
2026-10-08 21:04:34 -05:00
NorthDroplet f66c1e74db Launcher: the focus goes back to the mod after an install instead of the window buttons 2026-10-08 08:12:37 +00:00
NorthDroplet 93ff021d7e Launcher: the trackpad pointer measures from where the stick rests, so it does not creep 2026-10-08 08:04:48 +00:00
NorthDroplet 7a34ead2d5 Launcher: steadier trackpad pointer and list navigation
- 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.
2026-10-08 07:21:05 +00:00
AlexandeeerandClaude Opus 5.5 6edb666011 Launcher: log why Steam counts as offline
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
2026-10-08 00:30:52 +02:00
NorthDroplet 7b093429a2 Launcher: controller starts on PLAY and the open tab; rows and side moves in lists
- 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.
2026-10-07 20:57:08 +00:00
AyeZeeBB 7050fad406 Launcher: trucks back on MODS; Thunderstore button reads as a link out
- 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
2026-10-07 15:12:35 -05:00
AyeZeeBB 56b687b058 Launcher: Thunderstore's mark on MODS and its button; ask about enabled mods' updates only
- 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
2026-10-07 15:01:55 -05:00
AyeZeeBB 101e1af6e4 Official, staff and friends in the server browser; server tokens; Linux self-update
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
2026-10-07 00:19:56 -05:00
xThrasherrr c646906b0b Find the injected DLL in a game folder outside the ANSI code page
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.
2026-10-06 13:11:51 -06:00
AyeZeeBB a9742673da log changes 2026-10-05 17:27:50 -05:00
zeex64 1466359a14 Merge pull request #45 from xThrasherrr/fix/launcher-empty-arguments
Pass empty launch arguments to the game as ""
2026-10-05 17:05:05 -05:00
AyeZeeBB de631ad9e6 leave out mods that copy store items, chat badge check
- 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.
2026-10-05 16:29:37 -05:00
NorthDroplet 21d5a0ba4b Launcher: the left stick scrolls under the focus or the pointer
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.
2026-10-05 15:26:41 +00:00
NorthDroplet deaa041f24 Launcher: controller focus stays inside pages; mouse cursor hides while it moves
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.
2026-10-05 15:20:22 +00:00
NorthDroplet 8c7c30e3bb Launcher: controller reaches every panel; pad pointer without cursor warps
- 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.
2026-10-05 14:38:48 +00:00