Releases: Dingo-Shenanigans/ReSkate
Releases Β· Dingo-Shenanigans/ReSkate
Release list
v1.0.7
Show a mod's cosmetics as Collector, the game's sixth rarity A cosmetic a mod installs is in no retail catalogue, so it carries no rarity and its card shows none. Collector is the game's own sixth tier, already styled and already worn by 395 retail items: it reads as something apart without claiming to be Legendary, and invents nothing. - A rarity_id from the content cache or the profile still wins, so letting mod authors declare one later needs no change here. - Guarded on catalogs.available: with no pack installed reserved_cosmetic() answers false for everything, and every retail item would have turned Collector. - Build-kit objects share this message and are not cosmetics, so they are left alone.
v1.0.6
Draw the launcher on the best GPU; hide the CoreGameAssets layer A blank launcher window (Launcher/gui_renderer.cpp, Launcher/gui.cpp) Reported as a launcher that showed nothing, from someone with a GT 1030. The log named the card it had used: Intel HD Graphics 4600, the onboard chip beside it. - D3D12CreateDevice(nullptr, ...) takes whichever adapter DXGI lists first, which on a PC with onboard graphics beside a real card is the onboard one. It now walks EnumAdapterByGpuPreference with HIGH_PERFORMANCE, skips software adapters and takes the first that gives a device, as a game would. The old default adapter and WARP remain behind it, so no working setup loses its device. - That chip's Direct3D 12 driver cannot build a pipeline, and the ImGui backend creates one on the first frame while ignoring whether it worked (imgui_impl_dx12.cpp:792). Every draw then does nothing and the window shows only ClearRenderTargetView's colour, which is near black. Untextured shapes sample the font atlas's white pixel, so a failed atlas looks identical -- the drawn background disappears with the text. The pipeline and font texture are built in init now and checked, and a failure says to update the driver and that the card may be below what ReSkate and skate. need. - None of this was diagnosable, because the renderer logged nothing at all. It now records the adapter, its memory and whether it is the software fallback; the window size, scale, system DPI and work area; the font atlas size and whether it built; and the first frame drawn. Those four lines are what turned "blank window, healthy log" into the two bugs above, and they separate a missing device from a missing pipeline from a window that draws and is still black. World layers (Engine/Vfs/world_layer_scan.cpp) - CoreGameAssets had a row like any other, and switching it off took the world away with no clue which row had done it. essential_layer keeps its node, so its children are still found and still switchable, and drops its row -- which removes it from the menu, from "world <key> off" and from layer sync at once, rather than hiding it in one place and leaving it reachable from another. - Choices are stored by row key, not by index, so no saved setting shifts, and an "off" already stored for it has no row to apply to: a profile that turned it off fixes itself on the next launch. - The cache stamp goes to 2, so a cache written before this is rebuilt instead of keeping the row alive on existing installs. - The scan test asserted one row per node, which this breaks on purpose. It now checks the counts differ, that no row's key holds "coregameassets", and that the node is still there. Against a real install: 262 rows to 261, six maps, passing.
v1.0.5
Give each mod archive its own index; unblock launches and saves Mod archives (Engine/Vfs/mod_merge*) - A free archive index no longer has to sit at or below the highest index the layout already declares. The engine keeps archives in a table keyed by layer, install chunk and a 16-bit index, with no range to stay inside; what it cannot survive is a reference to an archive the layout never declared. Indices are taken lowest first, per package directory. - So a mod that collides takes an index of its own and its file is hard linked rather than concatenated, and no two mods share one archive's 4 GB of addressable bytes. Sharing is left for a mod added while the game runs, which has no declared index to take, and for a directory with no index left. - A placement addresses its payload with a 32-bit offset, so both append paths now refuse to cross 4 GB instead of wrapping round and pointing the game at some other payload: CasStore::write fails with the archive that is full, and the live-apply path checks archive 1's size plus the mod's before appending and asks for a restart, which gives the mod an archive of its own anyway. Injection through a hooked LoadLibraryW (Engine/Core/Platform) - A player could not launch at all: "Child LoadLibraryW entry bytes match neither Windows' kernel32 file nor this process". Anti-virus and overlays hook the entry with a jump to a per-process trampoline, so the bytes differ in the launcher and in the child and neither comparison could match. The guard already allowed the same hook when both processes carried it identically, so it refused on an accident of how the hook was written rather than on intent. - inline_jump_length recognises the forms a hooking engine writes (rel32 jmp or call, push/ret, jmp [rip], mov rax/jmp rax, short jmp). When one is present and every byte after it still matches the clean kernel32 file, the launch goes ahead: the body is Windows' own, and the provider image already matched on timestamp, checksum and size. A wholesale replacement is still refused, and now says so. - The failure names the modules loaded into Skate that came from neither Windows nor the game folder, instead of guessing at "security software". An accepted hook is logged as a warning with the same list. Launching (Launcher/launch.cpp) - Exporting loose scripts is a convenience, and a folder that would not take it was failing the whole launch: fs::create_directories threw and nothing caught it. The export is skipped, RESKATE_LOOSE_FILES goes to 0 so the runtime does not look for files that are not there, and Skate starts. Someone whose install is under Program Files can play; only editing loose scripts is lost. Failures worth reading (Launcher/problem.h) - A hooked LoadLibraryW, a refused write and ERROR_ELEVATION_REQUIRED each explain themselves now. Windows reports the last two in the user's own language -- "Zugriff verweigert", "La operaciΓ³n solicitada requiere elevaciΓ³n" -- so the rules key on the operation names and the error number, which are never translated. Saved skaters (Extension/Customization, Extension/Profile) - Outfits stopped saving and every slot showed the standard skater. The load hook marks a preset blocked on entry and clears it only on a full success, so a load refused for a catalog that was not ready yet left the preset blocked for the session and every later save was refused as previous_load_rejected. The game asks once per level load and does not ask again, so one lost race cost a skater until a restart. Logs show the catalog arriving three seconds after the refused load, at startup and again on a map switch 17 minutes later. - The record is remembered in CosmeticRuntime::pending_loads and applied by retry_pending_cosmetic_loads from the customization pump, which already waits for refresh_cosmetic_catalog. The preset stays blocked until a load really succeeds, so nothing is written over the outfit on disk. - Those records belong to the loadout manager's array, which can be replaced in between, so each pointer is checked against the live array exactly as a save checks one -- manager current, begin/end sane, 0x48 stride, at most 10 records, in range and aligned. A record that no longer belongs is dropped as retry_record_moved rather than written through.
v1.0.4
Merge mods before launch; name the mod that could not be merged 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.
v1.0.3
v1.0.2
Trim per-frame multiplayer client work; size mesh pool to the GPU 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.