462 Commits
Author SHA1 Message Date
zeex64 ece3aa5f0d Merge pull request #277 from DreadHeadHippy/plex-link
Music: link a Plex account, with now-playing, album art and transport controls
2026-10-17 01:53:14 -05:00
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
AyeZeeBB d0f76bfd61 GPU diagnostics: a resource creation that takes 20 ms or more is said in the log
With what was made (heap type and flags, size, format, flags) and the game code that asked, at
most 40 times. Small buffers and textures took 120 to 170 ms each, two threads at the same moment,
which showed the 5 s stall was the GPU or driver held up by something outside the game, not the
size of what the game made.
2026-10-17 01:44:22 -05:00
AyeZeeBB d6b56fa1e8 Profiler: a present sample lists its slowest frames, from one present to the next
A hitch on the render side never showed in a report: the client report lists slow client frames
only, and a present sample had no frames at all. While a present sample runs, the present hook
notes when each frame is presented; the report then lists the longest gaps between presents, each
with what the present thread's samples in it spent their time in and how many were waiting. This
is what found a 170 ms stall every 5 s: the game's batched GPU uploads blocking in the driver.
2026-10-17 01:44:22 -05:00
AyeZeeBB 1456c1fa51 Customization: a preset with a parameter type the game cannot read is left out, not applied
Scrolling shirts onto Yudi-Yuddispack's philippleinshirt_ap crashed the game (Skate.exe+0x3dc07ed):
one of the preset's set parameters has a type of 0x0c07010304040502 (null in another crash). The
game type-checks every set parameter when it next applies a preset to a material, reading the
type unchecked, and catching that crash inside addEsShaderPreset was too late: the preset was on
the material already and the next update crashed on it.

addEsShaderPreset's guard now checks the preset first, as that type check reads it: the TypeInfo,
its data, a word at +4. A preset with a parameter whose type cannot be read is left out as a
preset that is not loaded is (the item shows without it), and named in the log once with that
parameter's key. Many of the game's own types are built at run time, so the test is that the
type reads, not that it lies in the game's image.
2026-10-17 00:48:40 -05:00
AyeZeeBB 21d9b0d993 Profile: cosmetic ownership is a rule, not a list of every item ever seen
With "unlock all cosmetics" (and objects) on, the default, ReSkate wrote every installed item into
the profile as owned and never removed one, so every mod a player ever tried stayed in the save:
8765 cosmetics and 520 objects in one profile, past the limit that left a player with no items.

What is owned is now worked out from what is installed (installed_item_owned): a paid store copy
never, every other item while the kind's unlock option is on, and with it off what the profile
saved. The catalog given to the game, the owned list, saved outfits, the player card, the Build
object browser and the park editor all ask it. Nothing per item is saved any more, and a profile
with an option on drops the rows earlier versions saved, once, on the next start.

profile_migration_tests checks that drop on a copy of a real save: the rows stay with the options
off, are gone after a restart with them on, and nothing else in the profile changes.
2026-10-17 00:29:27 -05:00
AyeZeeBB 8fc28a19da Profile: a big mod collection no longer leaves the player with no cosmetics
The profile keeps every cosmetic it has seen, a removed mod's too, and refused to save past
8192 of them (or of Build objects). A player who had tried enough mods passed that, every save
was refused, and the cosmetic catalog failed with it: no items in the customization menu and
no playcard. The saved limit is now 65536 (saved_item_limit), a bound against a damaged file;
the game's own catalog of installed items keeps its 8192.

Also: one item the catalog cannot hold (two items with one identity hash, a record the game
will not read) is left out and named in the log instead of failing the whole catalog, and a
catalog that still fails logs why.
2026-10-17 00:04:21 -05:00
AyeZeeBB 4c07f2493b Menu: a full screen layout for controllers beside the window, and the pages regrouped
The ReSkate menu now has two styles, switched with View / F11 or the buttons in its top bar
and saved with the profile (`ui fullscreen`):
- Window: the mouse menu as before, with a side rail of pages in groups.
- Full screen: made for a controller, in the pause menu's look. Pages across the top (LB / RB),
  each page's sections as the ONLINE page's tabs (LT / RT), button prompts at the bottom, the
  pause menu's sounds, and the pause menu's own Multiplayer pages as ONLINE.

The pages are regrouped: HOME (quick switches and a tile for each page), MAP, BUILD, SKATER
(freecam, first person, cheats), SANDBOX (once Trainer), ADDONS (drone, Board Break, Road
Rash, Hall of Meat, grind exits, music), ONLINE, PROGRESS, MODS, SETTINGS, DEVELOPER.

Full screen, every shared control is drawn for a pad: switches and option rows changed with
left / right one step a press, sliders as steppers that repeat when held, rows of buttons,
and pages that scroll with the right stick. Search boxes and typed values stay in the window.
A pad press takes the menu's focus back and hides the pointer until the mouse moves.

Also: map pictures in the map picker (official loading screens, a mod's icon) with a search
box in the window, Splash and Tutorial Island left out of it; number boxes beside the
window's sliders; the binds in groups, with an action bind to open the menu (`menu`); a menu
size for each style (`ui scalefull`); the time of day stepping from the one last asked for.
2026-10-16 23:37:33 -05:00
DreadHeadHippy 4e4ff152e2 Music: link a Plex account, with now-playing, album art and transport controls 2026-10-16 23:26:16 -04:00
zeex64 20d58a4b73 Merge pull request #262 from Kalmarv/pr/tod-v3
Time of day: the new one loads only after the old one has unloaded
2026-10-16 20:35:04 -05:00
zeex64 50b8c6a26c Merge pull request #274 from Badger-Field/ebx-boxed-values-fix
EBX writer: keep the arrays and strings boxed values point at (data loss in mod merge)
2026-10-16 20:26:40 -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
zeex64 3aebb38832 Merge pull request #271 from worthlessnorms/merge-carried-once
Mod merge: write each carried asset once, not once per map
2026-10-16 20:10:11 -05:00
zeex64 7000db22b0 Merge pull request #275 from worthlessnorms/merge-history
Mod merge: a mod's copy equal to the game's is not recorded for combining
2026-10-16 20:06:43 -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 a14a6f44b5 In-game Mods page: official mods are listed as official, not as deleted folders
They load from Mods/.reskate-official, outside the Mods folder's list, so
the page took the ones in effect for mods whose folder was gone: "Folder
deleted / APPLY TO UNLOAD", a change pending, and an Apply that reloaded
the level for nothing.

- Each official mod has a tile of its own after the installed mods:
  OFFICIAL while in effect, SET ASIDE while Board Break has another deck
  in the game, APPLY TO LOAD for one put in place since the game started.
  No Enable/Disable: the launcher keeps them.
- live_mods::preview leaves built-in and official content out of what
  applying would unload.
2026-10-16 20:01:30 -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 30a18edd34 Board Break: its sounds play through the game's sound, at the skater
In a level, the cracks, the board's metal and the body's hits play through the game's own
positioned sound where the skater is, as the drone's motor does, so the game's mix and echo apply
and a break heard from afar sounds far off. Several still play over each other.

- Each wave file is also made into the 16 kHz mono that sound takes, as it loads (8, 16, 24, 32
  bit and float, any rate and channels, averaged down so a crack's highs do not alias).
- A tick on the client thread, beside the drone sounds, streams them in at the skater's root.
- Outside a level, or until the game's sound is ready, they play straight to the speakers as
  before.
2026-10-16 18:24:15 -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 8f92b41a78 Drone: follow mode films sessions, keeps off walls and the ground, and has distance and aggression
- Riding back and forth in one place (a halfpipe, a bowl, laps of a spot) is filmed from a spot
  off to the side, high enough for the airs, instead of chasing each turn; riding off or no turn
  back for a few seconds goes back to following.
- It looks along where it flies and slows in time to stop short of a wall, sliding along it;
  short probes push it off anything close and keep it from climbing into a ceiling. A spot behind
  the skater that is inside a wall comes in to this side of it.
- It keeps its clearance over the ground under it and just ahead of it, climbing as fast as a rise
  needs, and only sinks slowly near the ground whatever its spot asks.
- FOLLOW ME has Follow distance (3-12 m) and Aggressiveness (0-1, how quickly the pilot reacts,
  how hard it leans, closes gaps and turns); the middle is the tuning it had. Also dronetune
  followdistance / followaggression.
- Tests: a halfpipe session, turning round at a wall, a slope and a spot under the ground, and the
  settings' middle being the usual tuning.
2026-10-16 17:32:20 -05:00
worthlessnorms 6e1ba2938d Mod merge: a mod's copy equal to the game's is not recorded for combining
combine() recorded every source's copy of every EBX and resource asset, so that conflicting edits could be combined later. Map mods carry near-complete copies of the game's root-level bundles, so most of those copies are the game's own bytes. A mod's copy equal to the game's first copy of the asset, the copy edits are combined onto, is no longer recorded; the game's copy, recorded first, stands for it. A contribution points at its source's folder instead of copying the path. The merge report counts the copies left out.
2026-10-16 16:48:45 -05:00
AyeZeeBB 9a011c0417 Drone: the pilot's own motor plays through the game's sound
In a level the pilot's drone is played with the other players' drones through the
game's positioned voice sound, just under its camera, so it gets the game's mixing,
reverb and obstruction. The Windows output only plays it where the game's sound is
not there to take it.
2026-10-16 16:43:56 -05:00
AyeZeeBB 5d37d0bc50 Drone: follow mode flies the drone behind the skater
An autopilot flies the real flight model behind the skater while they skate: it
works the sticks a pilot would (angle mode with height hold), reacts a moment late,
eases toward the lie of the ground instead of every ollie, closes gaps no faster than
it can slow down gently, and climbs a little over what blocks the shot. Its camera
sits on a tilt gimbal so leaning to speed up or brake keeps the skater in frame.

Started from FOLLOW ME on the DRONE page or the dronefollow command; it hides the
game's interface like Start flying does. Tests cover straight, turns, hard stops,
ollies and catching up.
2026-10-16 16:43:56 -05:00
Dave Johnson 9dbcef7557 EBX writer: a new array takes the hash of its own field
An array record's hash is one per field type in a document (every
array the base game's files hold follows this, and each such hash is a
key of the document's reflection group table, now read). A new array
takes the hash of an existing array of the same holding type and field;
callers can ask the writer to refuse when there is none, and the mod
merge keeps the old fallback. Checked on the game's own
level documents.
2026-10-16 20:58:02 +00:00
Dave Johnson db0d1f348c EBX writer: rewritten arrays keep their own record hash
Each array record carries a hash the original files make specific to
the field or element type; the writer used to give every array the hash
of the first record of the same element type, so a rewrite changed
hundreds of them in bam's level root. Arrays read from a document now
keep their record's hash; only arrays with no source record borrow as
before.
2026-10-16 20:58:02 +00:00
Dave Johnson f22adf2348 EBX writer: keep the arrays and strings boxed values point at
A rewrite copied a boxed value's bytes, pointers and all, but wrote the
arrays and strings those pointers reach only when a field also reached
them. Rewritten documents lost that data and the boxed pointers aimed
at whatever moved into its place; the mod merge rewrites real documents
this way. Read documents now keep their EBXD bytes, and the writer
carries every array, string, instance and boxed value a boxed pointer
reaches, repointed. Found by a round-trip check on bam's level root (one string and
about 1,120 bytes of arrays lost).
2026-10-16 20:58:02 +00:00
AyeZeeBB 291d547c34 Drone: other players' drone markers are smaller and sit above the model
The icon is about half its old size and is drawn just above the drone's
model, with the distance and name stacked above it, so the marker no
longer covers the model.
2026-10-16 15:55:31 -05:00
worthlessnorms 3854942f96 Tests: a carried payload is in its archive once; the patch is read the way the merge reads it
The tests read the merged patch through CasStore and list_bundle, report a missing archive as a failure, and cover a mod archive numbered like a placeholder.
2026-10-16 14:00:35 -05:00
worthlessnorms 3feeaa3893 Mod merge: placeholder archive indexes are reserved; carry() keeps one list
A mod archive numbered 65534 or 65535 is placed under a free index, so no real placement is read as a placeholder. carry() records every payload in one list (settle() still writes each once per install chunk) and keeps one archive index, as write() does.
2026-10-16 14:00:34 -05:00
worthlessnorms cec00f412e Tests: a carried TOC chunk lands on its audio in every map's TOC
The music mod's song names its audio chunk; each map that takes the song carries the chunk too, and every chunk entry in the patch points at bytes inside its archive.
2026-10-16 12:14:32 -05:00
worthlessnorms 5ef0e64e02 Mod merge: a carried asset with no package fails alone
A carried asset whose install chunk no package names fails when it is carried, and is noted, as it was before it was written once; it no longer fails the whole merge when it settles.
2026-10-16 11:02:15 -05:00
worthlessnorms b981813ec8 Tests: a carried asset's placement holds that asset's bytes 2026-10-16 11:01:57 -05:00
worthlessnorms 89927bf90f Mod merge: an asset carried into many maps is written once
A carried asset was written again for every superbundle that carried it: 1.4 GB of 2.2 GB with 96 mods, which left no room for a live apply. Each one is now written once per install chunk, when its first superbundle settles.
2026-10-16 11:01:57 -05:00
AyeZeeBB eeeb751f1b Drone: other players' drones are heard where they fly
Each remote drone's motor is synthesised and played through the game's positioned voice sound,
the one proximity chat uses, so it is placed in the world and muffled behind walls. The throttle
is worked out from how the drone accelerates, so the drone packet and protocol stay the same.
Full volume only within 1.5 m, heard out to 120 m; the DRONE page's sound volume sets the level.
2026-10-15 22:39:55 -05:00
AyeZeeBB 0dcbcb3a89 Drone: other players' drones shown as a 3D model
A model is spawned at each other player's flying drone and moved every frame. It is the drone's
own model (FPV Drone by PinaPipe, CC BY 4.0, credited on the DRONE page), built by ReSkate Studio
into a pack that ReSkate.dll carries and unpacks into Mods/.reskate-builtin before the merge.
Without the pack the game's traffic cone stands in. dronemodeltest spawns one in front of you.
2026-10-15 22:39:49 -05:00
AyeZeeBB 26ec1da5eb Merge branch 'main' of https://github.com/Dingo-Shenanigans/ReSkate 2026-10-15 21:01:07 -05:00
zeex64 9bc59bccb4 Merge pull request #263 from Kalmarv/pr/perf-textures-alias
Console: "perf textures" is "textures"
2026-10-15 20:56:45 -05:00
AyeZeeBB 90ad073e31 Drone: gamepad mode, a truer flight model and crashes
Gamepad: the drone flies with a gamepad by default (Xbox or PlayStation,
read directly; with none plugged in, the keyboard and mouse fly it), Mode
2. Its throttle stick springs back and its middle is hover power, so a
resting stick no longer means 50% throttle; the sticks are scaled down
(60%, which in angle mode slows turning only), softened in the middle and
given a round dead zone, and the right stick is inverted by default (up
tips the nose up; padinvert 0 for a radio's way). A arms and disarms, Menu
puts the drone back; while the ReSkate menu is open the pad is the menu's
and the drone holds still. An RC radio is still a choice.

Height hold is a toggle (off by default): the middle of a springing
throttle holds the height, adding throttle as the drone leans; partway it
climbs at the climb speed (20 m/s), and full stick is always full or no
throttle. Angle (self-levelling) mode, leaning 55 degrees, joins acro.

Flight: thrust grows with motor output to the power 1.5, so the hover
throttle sets thrust to weight (22% is about 10:1); the motors spool up
and down and the turn rates catch up with the sticks in about 30 ms.
Drag is quadratic and larger across the props, set by the top speed
setting so flat out settles near it (no more hard cap); inflow, ground
effect and prop wash. Defaults: gravity 9.81, camera tilt 0; the drone
starts level.

Crashes: collisions sweep to the surface met, find its normal with two
more rays, and slide along it; hard hits bounce and tumble the drone,
and one at the crash speed (7 m/s) cuts the motors until it is armed
again, with a warning and a broken-up picture.

- overlay.h: the gamepad reader is declared in the overlay namespace,
  where it is defined; nothing could call it before.
- Tests (drone_flight_tests): hover and height hold, full stick past the
  hold, thrust, motor and rate lag, top speed, angle mode, wall crash and
  re-arm, crashes off, a gentle landing, and the gamepad's sticks.
2026-10-15 20:47:03 -05:00
AyeZeeBB 46e18900a1 Multiplayer: a sender can no longer flood a player's memory with pose and sound streams
A whole pose from a dedicated server opened a stream under any of its
65,536 numbers, and each stream kept its last 32 poses. Streams were only
dropped once their player had left, so a sender that opened one under
every number for a player who was there (itself) made the receiver keep
millions of poses. Sound streams were open the same way.

- pose_batch::open_stream: a player's new stream ends their old one, and
  no more than 512 streams are held (twice the player limit); both the
  pose and sound receivers open streams only through it.
- A stream is opened only for a player in the session. A refused whole
  pose is not acked, so a real server sends it again; sound is sent whole
  every couple of seconds.
- Tests: the reported flood leaves one stream for one player and 512 for
  65,535; the same for sound, and only admitted players open a stream.
- mesh_tests: HookBatch stubbed, which the session uses since the freeze
  fixes; the test had stopped linking.
2026-10-15 20:02:03 -05:00
AyeZeeBB 869daed0f6 Replay export: a long export no longer drops the player to the splash screen
The game's own connection is a loopback to the world it hosts in the same
process, but it keeps the game's in-game timeouts (15 s in Game.cfg,
5 s in Branch.cfg). A replay export holds every frame to the encoder's
pace, and a long one stalled the game past that: the game left the map
with SecureReason_TimedOut and went back to the splash screen while the
export carried on.

- Client.IngameTimeout and Server.IngameTimeout are set to -1 (off) at
  startup, as the game's own development config does; ReSkate's
  multiplayer runs beside that connection and keeps its own timeouts.
- Set apart from the other startup settings and tried for a minute, so
  a name the game does not take cannot hold those back; the result is
  logged.
2026-10-15 20:02:03 -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 ea3f081367 World: big maps no longer run out of spatial cull IDs while loading
A spatial transaction queue gives every object it tracks a cull ID from a
free list made once, when the queue is, at the game's
InitialCullIdListSize (40,000 in its config), and never grown. A custom
map with more objects (Cardiff) emptied it while loading and the next
insert read entry 0xffffffff of the list (crash at 0x489cfe9).

- spatial_cull_ids: as each queue makes its list, its size setting is
  raised to 262,144, one ID for every entity slot the entity pages hold;
  a larger configured value is kept. 12 bytes an ID per queue.
- The contract checks the list maker and how it reads the setting; it is
  installed at startup with the other world pools.
2026-10-15 20:01:48 -05:00
KalmarvandClaude Opus 5.5 c22feca912 Console: "perf textures" is "textures"
The streamed-texture reading is one of the perf readings, and perf textures is where it gets
looked for. It is now an alias, so Tab under perf offers it too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 18:29:08 -06:00
AyeZeeBB 5e43dc2871 Multiplayer: a chain of resized bones is held to the bone-scale limit as a whole
Each bone's scale was held to the limit (a server's bone-scale, 2x by
default; 8x on every client), but a bone shows at its own size times its
parents' and the whole skater's. A modified client resized each bone of
a chain to the limit: four neck bones at 2x showed a neck 16 times as
long, within every per-bone check.

- skater_bone_parents.h: the parent of each of the skater rig's 386 bones,
  read from the game's data (animbase_default_renderskeleton), which the
  pose's skater bones follow.
- limit_bone_scale then holds each bone to what its parents leave of the
  limit: the size it shows stays within 1/limit to limit. A single resized
  bone (a bigger head) is unchanged; bones past the rig's have no parent
  and keep the per-bone limit.
- Servers and clients already hold every pose to their limit with it, so
  the combined limit follows each server's bone-scale setting, and every
  client's 8x where a server allows more.
- Tests: a neck chain at 2x each shows at 2x; a resized skater's bones are
  held to what its own size leaves them.
2026-10-15 19:00:37 -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