Commit Graph
437 Commits
Author SHA1 Message Date
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
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
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
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
AyeZeeBB 3faa58bb42 Render-resource pool: the other renderer is allocated room for its metadata pointers
719cba1 stopped writing the metadata pointers into the other renderer
(0x2daa00 bytes, Skate.exe+0x30c64cc, kept at 0x75f2700), taking them for
the state owner's alone. They are not: the other renderer's own code reads
them through the patched view (0x030e6bff, called from 0x30a7e18) and
append (0x03e94fe6, called from 0x30d8ebb). Without the write, it read
whatever lay 18 MB past its end. With it, as in every release before, that
memory was overwritten, or the write faulted when nothing was mapped there
(the store_field crash).

- The other renderer's allocation is patched to the state owner's
  0x11ab080 bytes (0x030c64cc, mov ecx), so +0x11aae28/+0x11aae30 are
  inside it.
- Both renderers' metadata pointers are written again, before and after
  the native constructor. The resource-state and relation-mask vectors stay
  the state owner's alone: the other renderer keeps fields of its own at
  +0x2da920.
- The contract's comment says which renderer reads what, and where.
2026-10-15 18:16:22 -05:00
AyeZeeBB 719cba1166 Freezes and crashes: the game's threads no longer wait on ReSkate's slow work
Players reported the game hanging for about a second while skating. A
profile showed the memory line in the log walking every heap
(HeapSummary) on the client thread, 0.5 to 2 s every two minutes. An
audit of everything that runs on the game's update and render threads
found more of the same, and Sentry's top crashes had causes of ours.

Freezes:
- Memory log line: no heap walk or thread snapshot; the GPU part and the
  write happen on a thread of their own.
- Profile database: synchronous=NORMAL in WAL mode. A save is still kept
  when the game crashes; every play event no longer waits for the disk
  inside the game's hooks.
- ReSkate's own preferences and values (menu clicks, session sliders,
  Discord) are saved by a background writer in order; reads see a value
  until it is saved.
- Input: the foreground window and the keyboard are sampled on a thread
  of their own (GetAncestor and GetAsyncKeyState stalled the client tick
  40-50 ms when another program held Windows' lock); flight input uses
  the sample; ClipCursor at most twice a second while a menu has the
  pointer.
- Hooks installed while the game runs go in one transaction per place
  (HookBatch): throwdowns, remote players, the party, Hall of Meat.
- Off the update thread: the host's map package lookup (one table read in
  the background, not the Mods folder per offer), the trainer's map file
  and settings save, Board Break's settings save, mod music playlists and
  their covers, the Spotify station covers.
- Off the render thread: the Modding page's and the deck card's Mods
  folder reads. The overlay's three startup waits share five seconds.
- Less work per frame: Steam names reused while one a frame is asked
  again, 2 friends a tick, steam_api64.dll checked once per process,
  cosmetic names read in place rather than a system call per character,
  an outfit load no longer copies the inventory per slot, the throwdown
  type scan runs at background priority over resident memory, the Board
  Break deck label is kept a minute.

Crashes:
- Render-resource pool: the metadata fields are written to the state
  owner only. The other renderer is 0x2daa00 bytes, and +0x11aae28 is far
  past its end (store_field crash; heap corruption in every release).
- Other players' skateboard effects: a stale skateboard component is no
  longer followed, and the joint lookup is walked with a bound first (an
  endless loop in reused memory ended in GameStall).
- Launcher: Oodle decodes into the buffer it asks for with corrupt data
  possible, and its assertions no longer stop the process (heap corruption
  in the store-copy scan; a breakpoint in oo2core).

Timings (Send performance timings):
- A frame of half a second or more in a level is sent as game.freeze:
  where the time went (ReSkate's tick, the game's frame, or outside it),
  whether the renderer kept presenting, and ReSkate's busiest zones.
- Closing the game sends the level session it was in.

Music after a connection drop:
- Spotify device: an account Spotify could not be reached with is kept and
  tried again (3 s doubling to a minute); a session that drops is taken
  up again. Only an account Spotify refuses is asked for anew.
- YouTube playlist pictures that could not be fetched are tried again,
  from 30 s to 10 minutes apart.
2026-10-15 18:10:53 -05:00
AyeZeeBB 310922556c Launcher: a setting to test pre-releases
Settings > Launcher > "Test pre-releases" (off by default). On, the update
check takes the newest GitHub release that is not a draft and carries a
launcher.json, pre-release or not, in place of the latest full release:
a pre-release newer than the last release is installed, pinned by size
and SHA-256 as any release is. Turned off, the next check goes back to
the latest release, whose files then replace the pre-release's.

- The update notice says "Pre-release" for one, and the log names which
  kind of release was taken.
- The changelog shows the release the setting picks, marked as a
  pre-release, and is fetched again when the setting changes.
- --no-gui launches follow the same setting.
2026-10-15 14:23:06 -05:00
AyeZeeBB 94a616feb9 Map downloads: one that has begun goes on when the session stops waiting
Follow-up to #225. The session still waits 30 minutes in all for a map
(checking, the two-minute question, download, install, apply, join), and
servers and lobbies still let a player go at 30 minutes. But a download
that has begun is no longer cut off at 27 minutes: a large map on a slow
connection never came at all that way.

- Once the download has begun, the session's deadline is the session's
  alone. The download goes on (up to its own four hours, as before #225),
  installs and is applied, and the player is told to join again; no
  rejoin is tried past the deadline, where the server would turn them
  away. "Map still downloading" says so when the session stops waiting.
- Before the download, the deadline still ends the check and the question.
  Declining or cancelling still stops it and removes the partial file.
- A download whose session has gone is not cancelled by joining another
  session; a new map need is turned away until it is done.
- The 27-minute transfer budget and its completion reserve are gone from
  map_fetch_policy.h, with their tests; the map download tests cover the
  new behaviour.
- The HTTPS timeout test compiles with warnings as errors (a signed and
  unsigned comparison), and get_text's stale-file removal is indented again.
- README: map downloads described as they now are.
2026-10-15 14:23:06 -05:00
zeex64 e88853add8 Merge pull request #242 from Kalmarv/pr/help-v2
Console: "help perf" lists the perf commands
v2.1.0-pre
2026-10-15 14:15:45 -05:00
zeex64 b6f3ea3c30 Merge pull request #228 from davad00/fix/playstation-report-age
Fix PlayStation reports briefly dropping after HID reads
2026-10-15 14:14:56 -05:00
zeex64 1094e4dc86 Merge pull request #237 from AlexanderPuglisi/fix/unreadable-script-keeps-toc
Mod merge: an unreadable script no longer drops the rest of its mod's TOC from other mods' copies
2026-10-15 14:14:01 -05:00
zeex64 e7c6df0df6 Merge pull request #225 from Jean-LucTch/dev
Fix multiplayer custom map download and join timeout handling
2026-10-15 14:03:06 -05:00
AyeZeeBB 164fdffbd0 Descriptor heaps: dense custom maps no longer overflow the GPU's descriptor heap and crash it
The renderer gives each frame 131,072 shader-visible descriptors past a persistent 120,000, in
heaps of 333,333. Dense custom maps (the Los Santos City sublevels, San Andreas) draw more than
that in a frame, and the renderer wrote past the end of the heap: shaders read garbage
descriptors, which faulted or hung the GPU (page faults at nonsense addresses, in draws, texture
fetches and ray tracing alike). ReSkate now patches the device setup at boot so each heap is
D3D12's 1,000,000 and a frame may use 524,288; heaps are 644,288 descriptors. LS-B2, LS-B3 and
San Andreas (210,000 a frame at its peak) now hold.

Also in this change:
- Memory: perf memory owners/heap/gpu/report, a ledger of who commits the game's RAM; the
  render-resource pool no longer allocates its 115 MB twice; the EadpErrorsData arena is
  capped at 32 MiB (EA's error reporting is blocked anyway).
- Crash reports: tagged with the map, destination, graphics card and driver version (protocol
  v3), so Sentry can group GPU hangs by map and card.
- GPU diagnostics (--gpu-diagnostics): a fuller DRED report, the device-removal and video
  memory watches, a large-draw watch, a record of which game code made and freed each GPU heap
  and resource, checks of every GPU address bound, and a meter of each descriptor heap's use.
- World pools: peak use of entity pages, render instances and spatial blocks in the log.
- dingosdk_mod_mesh_check: checks every MeshSet a mod ships against its geometry; the MeshSet
  reader now knows static meshes' layout.
2026-10-15 13:57:11 -05:00
zeex64 67d84d5bd7 Merge pull request #227 from neekgate/fpv-drone-pr
ReSkate FPV Drone
2026-10-15 13:20:11 -05:00
zeex64 1acde3b9b3 Merge pull request #232 from Belcross-Dev/grind-exit-playstation-pads
Grind exits: fix triggers not registering on DualShock and DualSense controllers and improve curved grind exits
2026-10-15 13:19:59 -05:00
zeex64 32a83d429e Merge pull request #241 from Kalmarv/pr/command-crash-fix-v2
Console: mp map-markers, mp puppet-savings, throwdown-hud and throwdown-offboard no longer crash when typed alone
2026-10-15 13:19:12 -05:00
zeex64 dc43a50a47 Merge pull request #243 from Kalmarv/pr/lights-v2
Lights: a level with more lights of one kind than the game holds no longer crashes
2026-10-15 13:17:39 -05:00
zeex64 de272c1439 Merge pull request #257 from FourthStudio/board-break/wear-and-tear
Board Break: wear and tear, rail snaps, break and body sounds, settings
2026-10-15 13:14:03 -05:00
Fourth Studio b999f37e7b Board Break: wear and tear, rail snaps, break and body sounds, settings
Boards wear: hard landings, hits onto rails and ledges, grinding and
slams wear the board of the deck in the game, kept per deck. From a fifth
worn it steers lazier and its trucks go soft (the game's own physics
tuning, outside multiplayer sessions only); a worn board breaks from less
and, worn out, can give way under the rider. A board that broke is a new
one once it is whole again, and the game's own board wear is cleared with
it.

A boardslide, lipslide or darkslide that comes down too hard onto a rail
can snap the board, and the rider goes down with it: No Bail can now be
asked for a wipeout, which goes in as a cause before the selector picks.

Sounds play through XAudio2: a crack by the size of the break, metal when
the board broke on metal (the material comes from the game's contact
effects; which are metal is marked in play or listed by a pack), and the
body's hits in a bail by how hard they land.

The settings are a card on the overlay's Skater page, two rows in Mod
Options and console verbs, kept in BoardBreak.settings. A deck may name
itself and set its wheels wider apart in a deck.ini; Old School and Pig
join the decks the feature knows.
2026-10-15 19:22:21 +02:00
AyeZeeBB 6df4f9a7bd Replay export: videos are encoded at 100 Mbps whichever quality is chosen
he export dialog's quality setting picks 8, 12 or 18 Mbps for the H.264
encoder (three mov eax immediates at Skate.exe+0x62c24f, +0x62c256 and
+0x62c25d in the 20260929 build). All three are now 100000 kbps.
- The code around them must match the supported build's bytes, and each
  value must be the game's own or already 100000; otherwise nothing is
  written and the log says the change was skipped.
- The log says whether the bitrate change or the frame-pacing hook was
  skipped. start() reports success if either one is in place, so
  replay_export_initialized can say active while the hook is off.
- The fix that encodes every rendered frame is unchanged.
2026-10-14 20:11:01 -05:00
AldoandClaude Opus 5.5 301b7b220f Drone: turning the goggle display off hides it
The `osd` setting (Show the goggle display) was saved but never read, so
the readouts stayed. Off now hides every readout: battery, armed, timer,
throttle, altitude, speed and signal. The range warnings (NO SIGNAL,
FAILSAFE) still show, and the sticks, crosshair and analogue look keep
their own switches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MArBL6UbYZPsp9rMnq2Ks5
2026-10-14 19:45:05 +01:00
KalmarvandClaude Opus 5.5 3468f556b0 Lights: a level with more of one kind than the game's table holds no longer crashes it
The game keeps each kind of light (PbrSpotLight, PbrSphereLight, ...) in a
table of 8,192 and crashes at Skate.exe+0x4ea4094 when a level makes more.
- Each kind's light factory is hooked: once the kind's table is nearly full
  it gives no entity, as the game's own factory does for a light it should
  not make. The light is not drawn and the game goes on.
- Said in the log: the kind of light and how many were left out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 09:40:06 -06:00
KalmarvandClaude Opus 5.5 99e79f123a Console: "help perf" lists the perf commands, and a wrong word after a command says where to look
help <command> showed only that command when it was also the first word of others: help
perf showed the HUD toggle and not perf hud, perf status, perf sample and the rest, and the
same held for help log, music, reset, boardbreak and roadrash. help listed a family only
when no command had the family's name. It now lists them after the command's own details,
by name or by alias (Registry::under).
"perf hdu" answered "Too many arguments. Usage: perf"; when the command has others under
it, the error now ends "Use help perf for the commands under it."

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 09:36:32 -06:00
KalmarvandClaude Opus 5.5 e5557d914e Multiplayer console: map-markers, puppet-savings and the throwdown switches work by themselves
mp map-markers, mp puppet-savings, throwdown-hud and throwdown-offboard take an optional
argument and, without one, say what they are set to. Each read values[0] unchecked, and an
optional argument that is not given adds no value, so typed by itself each indexed an empty
vector. They check now, as mp collision and challenge-invite beside them do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 08:56:47 -06:00
AldoandClaude Opus 5.5 76c0fd0903 Drone: settings move into the ReSkate menu (DRONE page)
The drone's own floating window becomes a DRONE page in the ReSkate menu
(Insert), after TRAINER, built from the menu's cards, ON/OFF rows, fields
and option strips like every other page. Its tabs are the window's:
FLY, HANDLING, RATES, DISPLAY, CONTROLLER and LOBBY. `dronemenu` now opens
the menu on that page.

The FLY tab ends with a small credit line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MArBL6UbYZPsp9rMnq2Ks5
2026-10-14 13:33:52 +01:00
AlexandeeerandClaude Fable 5.1 eacfbd72dd Mod merge: an unreadable script no longer drops the rest of its mod's TOC from other mods' copies
Since the mods are read on several threads (0ae0e71), read_mod reads every
script a mod replaces: whether a higher mod's version stands is only known
once the mods are settled. Before, a script a higher mod had replaced was
never read. When such a read fails, the TOC's catch noted it and gave up the
rest of that TOC, so the mod's later changes and additions were left out of
other mods' copies, with a note blaming the mod where nothing used to be read.

The read now has a try of its own. A failure is kept with the script and
settled with the rest: where a higher mod's version stands it goes, note and
all, as if never read; where the script is wanted, the note stands and only
that script is missing, the rest of the TOC having been read on.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VNYF3aYa2q3MkFXc5xYnDd
2026-10-14 06:24:19 +02:00
AyeZeeBB 84940ee571 Stations: songs get their own names back before a map change, and the names shown no longer pile up
While a Spotify, YouTube Music or External Audio station is on, a song of
the game's is made to read as the track that is playing.
Crash on changing map
- The game keeps each song in its registry under its artist and title, and
  takes it out by them when its bundle unloads. A song that read as
  Spotify's was looked for under Spotify's words and not found, so its own
  entry stayed behind, pointing at an asset that was freed. Registering the
  song again for the next map read that asset (Skate.exe+0x3dd1db0 from
  0x43e260, joining a session with a station on).
- Before a level transition every such song is given its own artist and
  title back, and none is held on to: they are the old map's.
Memory
- The artist and title a song is made to read were copied afresh on every
  call, and kept. That is once a track, but every frame while the station's
  song cannot be made to read (its asset gone after a map change, or 64
  songs shown already): two copies a frame for as long as the station was
  on. Each text is now kept once.
Memory log
- The "Memory:" line also says how much is in the process's heaps (what
  ReSkate, the Spotify library and Windows' libraries allocate, apart from
  the game's own pools) and how many threads and handles there are, and it
  is written every two minutes as well as when memory moves by a quarter of
  a gigabyte.
2026-10-13 19:08:38 -05:00
Belcross 72aa7b87c0 Grind exits: work with DualShock 4 and DualSense pads read natively
skate. reads a DualShock 4 or DualSense without Steam Input through its
own HID reports, not XInput, and the gamepad filter only ran on XInput
reads. With such a pad grind exit triggers never saw a trigger: no side
was picked, and the grab and lean guards did nothing.

The filter now also runs on the game's own native PlayStation reads that
the capture already follows (the ones it neutralises while a menu holds
the input): the report is parsed into XInput's layout, filtered, and the
changed triggers (with their L2/R2 digital bits) and left stick X are
written back in the report's own layout, the Bluetooth CRC refreshed.
The CRC code is shared with the neutral report's.
2026-10-14 01:04:38 +01:00