A third ReSkate tab in the game's pause menu (native_menu.cpp slot 2, the Store's tab art), drawn by
the overlay as the other two are (mod_manager_page.inl, included into hub_page.cpp):
- BROWSE: search, categories in a panel of their own (shown only, hidden or either, kept in the
profile), sort, a featured carousel and rows (most popular, recently updated, new, the biggest
categories), a grid for results, and a mod's own page with its README, what it brings with it,
and INSTALL, switch on or off and uninstall. Works from a controller: the carousel's card is
one stop that left and right turn, and B goes back from a mod's page (the page's Back is held
meanwhile).
- MY MODS: the Mods folder with the states the ReSkate menu shows; UPDATES: Update all.
- The foot of the page shows the install's progress and the apply's, and Apply (View on a pad).
Leaving the menu with changes not applied asks first (a two-answer card in message_card.cpp,
answered by the runtime as the message is); LATER closes the menu.
Behind it, Extension/Assets/mod_store: Thunderstore's listing (cached on disk), the installed mods,
and a queue that downloads and installs packages with their dependencies, updates and uninstalls
them. An update or uninstall of a mod the game holds open waits in Mods/.reskate-pending and the
launcher makes it before the next start (launcher_mods::apply_pending). Package icons load into
their own SRV slots (store_icons.cpp), sharing the launcher's icon cache.
Tests: mod_store_view (search, categories, the front page's rows, install plans), the pending
updates and uninstalls in mod_manager_tests, and the third tab in native_menu_view_tests.
- Forgetting uninstalled packs' records cannot close the launcher: a
brought-in.json that cannot be rewritten (a locked or read-only Mods
folder) is logged as a warning; the mods are gone already.
- A dependency the player had before is matched ignoring case, as Windows
matches folders: one installed under another spelling counts as installed
(it is not downloaded again or renamed) and is never recorded as brought in.
- UNINSTALL waits while an install runs, so the two never rewrite
brought-in.json at once.
- A mod pack installs from GET MODS: a package with nothing to load of its
own is taken when its manifest.json lists dependencies, as the install
options already said; it was refused, so no pack ever got a record.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Thunderstore install was refused unless the package held layout.toc, reskate-levels.json or a
parks folder, so a package with only a plugin, or a pack whose manifest only lists dependencies,
could not be installed from GET MODS. reskate-plugin.json now counts as content (and finds the mod
inside a package like the other markers), and a manifest with dependencies is a mod pack, as
InstallOptions::require_content always said.
- The category menu on GET MODS, and a new one on MY MODS, take each category as shown only, hidden or
either (a click goes round them), with how many mods are in each; each page's choice is kept in the
launcher's settings. On MY MODS a mod Thunderstore does not list, or lists in no category, is under
"No category".
- Before a launch whose mods give the game other data than the last launch's (the mods with a layout,
their files and load order, and the variants in play), the game's own shader cache
(%LOCALAPPDATA%\ReSkate\Game\Skate\cache) is emptied: shaders kept from earlier mods were found to
break the picture after many changes. The shader_cache folder the game ships with is left alone.
A Thunderstore install now notes, per package asked for, the dependencies it
downloaded that were not installed before (Mods/brought-in.json), and the
uninstall offers only those, so a mod the player had already is never offered
(nor one the install only updated). A pack installed before this has no
record, so its uninstall offers nothing. Dependency names longer than a mod
folder's are matched to the cut folder thunderstore::folder_for installs.
The confirmation's checkbox reads "Uninstall N modpack dependencies?", with
its label spaced like the settings page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Installing a mod pack installs its dependencies, but uninstalling it left
them all behind. The uninstall confirmation now lists the mods the chosen
ones list as dependencies (and theirs in turn) that no mod staying
installed still lists, with "Also uninstall the N mods it brought in,
which nothing else needs" ticked. A dependency another mod needs is never
offered; unticked, only the chosen mods go, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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).
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.