Commit Graph
30 Commits
Author SHA1 Message Date
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
xThrasherrr ddd24c0f41 Pass empty launch arguments to the game as "" 2026-10-04 17:23:54 -06:00
AyeZeeBB 81913d8974 fix modlist 2026-10-04 16:07:48 -05:00
AyeZeeBB 76330e40bb Skater slot, menu and throwdown fixes; MY MODS rework; lobby parties
Skater slot, menu and throwdown fixes; MY MODS rework; lobby parties

Saved skaters (Extension/Customization, Extension/Skater)
- The selected slot, and slots before it, came up as the standard
  skater after a start or map change. The game asks for a slot's saved
  outfit once per level, and that happened before the cosmetics catalog
  could be read. The first selection now waits for the catalog (60 s at
  most), and later map changes check against the catalog already read.
- The retry from abdda77 is removed: the record it wrote to is a
  temporary on the game's stack, so it could never work.

Pause menu (Extension/UI/NativeMenu)
- ReSkate's entries stopped working after enough map changes: each
  level change took new slots in a fixed table of 1024 for the same
  buttons. A button now keeps its slot, and a full table hands out the
  slot that has gone longest unused.

Throwdowns (Extension/Throwdowns, Runtime/runtime_init.cpp)
- Multiplayer crashed when someone placed a throwdown after a map
  change. The event types are freed with the level but were cached for
  the process. They are now cleared when a level is left, checked
  before use, and looked for again in each level.

MY MODS (Launcher/gui_mods.cpp, gui.cpp)
- Search, a filter and an order; a switch per mod; a menu per row.
- Multi-select with tick boxes, Shift-click and Ctrl+A, and a bar to
  enable, disable, move, update or uninstall everything ticked.

Lobby parties (Extension/Multiplayer/Session)
- A lobby is no longer one party. Its host keeps parties the way a
  dedicated server does: invite, accept, join, leave, kick, promote,
  open and close, with "/p" party chat. The "Lobby party" switch is
  gone. Protocol version unchanged (38).

Mod merge (Engine/Vfs), follow-ups to e715b9b
- Files rewritten into the merged patch are tracked by position, so
  another mod's added assets no longer shift the lookup.
- Two mods adding different assets under one name is now noted, with
  whose copy the maps get.
- mod_merge_unshift_tests compiles with the launcher tests on.
2026-10-04 13:27:21 -05:00
AyeZeeBB 9c03edda34 Show each mod's size in the mod manager
Show each mod's size in the mod manager

A mod's size was only visible in the package overview and the install
prompt, so there was no telling from either list what a mod costs to
download or how much room the installed ones take.

- MY MODS: each row carries the size of the mod's folder on disk after
  the version ("v1.0.1 / 410 MB / by anton / 1 map"), and so does the
  line under the title in a mod's overview. The folders are measured
  when the Mods folder is scanned, so the figure follows an install, an
  update or Refresh. The game's merged patch hard links a mod's archives
  rather than copying them, so this is also what removing the mod gives
  back.
- GET MODS: each row carries the download size Thunderstore reports for
  the latest version, in the same place ("v1.0.0 / 101 MB / 0 downloads
  / 2026-10-04"), as the package overview already did.
- size_text moves out of gui_mods_browse.cpp's file-local namespace so
  both pages format a size the same way.

The two figures are not the same thing: one is the unpacked folder, the
other the zip, so a mod reads a little larger once installed.
2026-10-04 00:37:15 -05:00
AyeZeeBB d895c9e915 Say on PLAY when mods have updates, and ask before launching without
A mod update was only visible on the mod manager's own page, so the
usual way to find one was a mod behaving oddly in game.

- PLAY carries a pill in its corner while Thunderstore has newer
  versions: "1 MOD UPDATE", or the count. Dark on the blue tile, blue on
  a grey one.
- Pressing it asks first, listing each mod and the version it would move
  to, with three ways on: update them and play, open the mod manager, or
  play anyway. Playing on the versions you have is a real choice -- a map
  you are part-way through, an update you do not trust yet -- so it stays
  offered, and taking it is remembered for the session rather than asked
  again on every launch.
- Only from READY. INSTALL, DOWNLOAD, UPDATE and RETRY are untouched: a
  mod update has no business standing between someone and a 14 GB game
  download.
- The dialog re-checks as it draws. If the pending list emptied while it
  was open, it launches instead of showing an empty box.

"Update them and play" launches when the install finishes, which is what
it says and what it did not do:
- The promise is only made if the install really started, so a refused
  start cannot launch instantly with nothing updated.
- An update that failed stays on the page with the reason on screen and
  logs why it did not launch. Starting the game on a half-updated mod set
  is how the cosmetic crash in a30a838 happened.
- Leaving the page with BACK cancels it: that is a change of mind, not a
  request to be launched into the game a minute later.

collect_install takes the result under the panel's mutex at the top of
the frame, so once installing is false the outcome is settled and the
error check is reading the real answer.
2026-10-03 22:23:34 -05:00
AyeZeeBB a30a8384f3 Keep every mod's cosmetics when two presets share a short name
With several cosmetic mods installed the game crashed as soon as an
equipped custom item came into view, and so on every launch after it
was equipped.
Cosmetic presets (Engine/Resource/bundle_ref_table.*)
- The character bundle-reference table says which bundle holds each
  preset, under the preset's full path and under its leaf name, one row
  per hash. Mods number their presets alike in different folders
  (cust_tops/1_ap, cust_bottoms/1_ap), so two mods can share a leaf
  name. Adding the second preset threw "collides with an existing
  lookup", the merge fell back to the highest-priority mod's copy of
  the table, and every other cosmetic mod's presets were unregistered.
  The game then built the skater with a preset it could not find and
  called through a bad pointer.
- A preset whose leaf name is taken is now added under its full path
  only; the leaf name stays with the preset that had it. Only a clash
  of full paths still fails. MergedTable::shadowed lists each such
  preset with the one that holds its name.
Merge log (Engine/Vfs/mod_merge_combine.cpp)
- Each mod with shadowed presets gets a line naming how many and an
  example pair, so an item that shows the wrong preset can be traced to
  the mods that clash.
Checked against 39 installed mods, 15 of them cosmetic: the table
combines 274 presets where it used to keep one mod's, all reachable by
full path and 267 by leaf name, with the game's own 6264 rows unchanged.
The seven left without a leaf row are a naming clash between the mods
themselves (two Carhartt packs, a Zero tee and deck); whether the game
finds those by full path is not known.
bundle_ref_table_tests takes an optional second mod and checks that two
mods' presets are all added, in either order, and that a shared leaf
name is reported with the preset that holds it.
2026-10-03 21:43:24 -05:00
AyeZeeBB f815658bf1 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.
2026-10-03 19:21:51 -05:00
AyeZeeBB abdda77946 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.
2026-10-03 18:30:28 -05:00
AyeZeeBB 1004e012bf Write paths as UTF-8, not the ANSI code page
A player whose game sat under a folder named U+200E (left-to-right mark,
an invisible directory on their Desktop) could not launch: the status
tile read "No mapping for the Unicode character exists in the target
multi-byte code page". Nothing was wrong with their mods, and the merge
in their log had just succeeded.

fs::path::string() converts to the process's ANSI code page and throws
exactly that when a character has no mapping there. initfs.cpp called it
on the normal path, not in an error handler, so exporting loose files
threw on every launch. Their log stops right where that export begins.
Cyrillic, CJK or accented characters in an install path do the same on a
code page that cannot hold them.

- path_utf8() in Engine/Core/Platform/path_text.h: UTF-8 represents every
  path Windows allows, so it never throws.
- Every path that becomes a narrow string uses it -- 35 of them across 13
  files. Most are log lines and error messages, which were a second trap:
  reporting a problem with a path threw this instead of the problem.
- The ~45 .string() calls on Json values are untouched; those are not
  paths.

Nothing changes for an ASCII path, where both encodings agree, and a path
that is not ASCII had no working behaviour to preserve.

Reported with a log by a player on Discord. Moving the game to an ordinary
folder works around it on an unpatched build.
2026-10-03 17:36:12 -05:00
AyeZeeBB 201571c225 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.
2026-10-03 17:08:03 -05:00
AyeZeeBB e56db5d62b Merge branch 'main' of https://github.com/Dingo-Shenanigans/ReSkate 2026-10-03 16:21:14 -05:00
AyeZeeBB ff0633e2ef Detect Steam under Proton, where ActiveUser is never set
Under Steam + Proton on Linux the launcher always reported Steam as
offline, so the game ran on the offline stand-in and servers could not be
joined. steam_signed_in() needs ActiveProcess\ActiveUser, and Proton's
stand-in steam.exe only writes ActiveProcess\pid.
- When ActiveUser is missing and the launcher is running under Proton,
  the account comes from the native Steam client instead: the most recent
  entry in config/loginusers.vdf under STEAM_COMPAT_CLIENT_INSTALL_PATH,
  which Steam sets for compatibility tools and Wine's
  wine_get_dos_file_name maps to a DOS path.
- The Steam name on the launcher's plate and the SteamID that --offline
  reports are read from that directory too. Proton's
  C:\Program Files (x86)\Steam holds no account files, so offline mode
  fell back to 76561197960265729.
- loginusers.vdf's MostRecent key is matched in either case. Steam has
  also written it as "mostrecent", which picked the first account in the
  file instead of the most recent.
Windows is unchanged: the variable and the Wine export are both absent
there, so ActiveUser is still required.
Diagnosed, with a confirmed workaround and the suggested fix, by
BenjaminJAnderson.
Fixes Dingo-Shenanigans/ReSkate#5
Co-authored-by: BenjaminJAnderson <45207455+BenjaminJAnderson@users.noreply.github.com>
2026-10-03 16:21:10 -05:00
zeex64 4325f47acc Merge pull request #2 from Bortlesboat/fix/launcher-mod-install
Preserve nested mod metadata without package replacements
2026-10-03 16:18:57 -05:00
AyeZeeBB 4006d664a0 status row
- The supported build number appears once, on its own row, instead of in
  the advice line and the DOWNLOAD tile as well.
2026-10-03 16:12:48 -05:00
AyeZeeBB aef269497e Rebuild the launcher's mod manager, settings and status tile
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.
2026-10-03 16:10:55 -05:00
zeex64 62b99171db Merge branch 'main' of https://github.com/Dingo-Shenanigans/ReSkate 2026-10-03 01:26:18 -05:00
zeex64 b21448ee06 Update launch.cpp 2026-10-03 01:26:16 -05:00
ReGlitched 2fd92ba989 crash fix + launch arg 2026-10-03 00:23:44 -06:00
Bortlesboat c9a7dfce38 Preserve nested mod metadata without package replacements
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.
2026-10-03 00:31:05 -04:00
AyeZeeBB b763631b77 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.
2026-10-02 21:07:43 -05:00
AyeZeeBB f7fc909ca7 Revert "Keep launcher alive through Skate startup; trim per-frame client work"
This reverts commit b7932535e5.
2026-10-02 20:19:51 -05:00
AyeZeeBB b7932535e5 Keep launcher alive through Skate startup; trim per-frame client work
Launcher
- "Close the launcher when Skate starts" now hides the launcher and exits
  when Skate.exe does, instead of exiting as the splash opens. Outside a
  Steam library, Skate's protection closed the game (TerminateProcess 0x32)
  when the launcher exited during startup; players confirmed turning the
  setting off avoided it.
- STATUS tile warns when Steam isn't open or signed in that PLAY starts
  offline as Unknown Player (hidden when Offline mode is chosen or while
  launching).
- Setting description updated to match.
Startup interventions (20260929)
- exit_return 0x87755f2 -> 0x87fe7fb: the last LEA RSP,[RSP]; JMP stub
  before the PUSHFQ / LOCK CMPXCHG routine, as 0x87367cf was in 20260908,
  and the return address player logs show. Only enforced once a frame site
  is armed; this build's protector has no equivalent rep stosb frame clear.
Client frame cost (profiled 2026-10-02)
- Transform interpolate is inline plain arithmetic (~1% of an MP frame).
- Script gameplay-setting and native-setting gets are served from small
  per-thread caches keyed on asset/key address, name prefix and a new
  profile::Store::changes() counter, instead of a store lookup each call.
- Hot-path reads in client tick, native settings and population controls
  use memory::peek instead of ReadProcessMemory.
- Script error logging probes each argument pointer once.
2026-10-02 18:56:57 -05:00
AyeZeeBB ca1d82f636 1.0.0 Release! 2026-10-02 14:40:41 -05:00