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>
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).
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.
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.
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.
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.
- 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.
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.
- 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.
The launcher read no controller at all. Started from Steam (Desktop or Game
Mode on a Steam Deck), Steam Input applies the game's gamepad layout to it,
where the right trackpad is the right stick and its click is R3, so the
trackpad did nothing; only the touch screen, which arrives as mouse
messages, worked.
- Every XInput pad is merged (Xbox, Steam Input's virtual pad), else a
DualShock 4 / DualSense is read over HID with the reader the in-game menu
already uses, now its own library (dingosdk_playstation_input).
- D-pad and left stick move ImGui's focus, outlined in the launcher's blue;
A / Cross presses. The hand-drawn tiles, switches and menu buttons take
part in navigation (ImGuiButtonFlags_EnableNav).
- B / Circle closes an open combo or menu and leaves a text field, and is
Escape otherwise, which the launcher's pages already go back on. Escape
now also closes the mod overviews and answers Cancel in the uninstall and
replace confirmations; closing an overview no longer also leaves the page.
- The right stick (the trackpad) moves a pointer that ImGui draws, and R3
(the trackpad's click) is the left button. The system cursor follows it;
a mouse move or a touch elsewhere takes the pointer back.
- The ImGui Win32 backend's own gamepad code (slot 0 only) is compiled out
so it cannot fight the launcher's events; nothing else used it.
Only replace a nested metadata file when the package supplies a matching
top-level file. Cover ZIP and folder installs with partial metadata and
case-insensitive package overrides.