- 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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>
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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>
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>
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>
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
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
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.
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.
2.0.8's dedicated server was released with a world-layers.json scanned from
a game Steam had updated: 263 layers, where every player's game has 261. A
server that shares its layers (world_layer_sync) sends one choice for each,
the player's game turned the message down as unreadable, and nobody could
join such a server ("Join code, sender identity, or multiplayer protocol
did not match", and "Disconnected." in the server's log).
- supported_build::world_layer_count (261): how many layers the supported
build's levels have.
- Server: a world-layers.json of another count is not used; the log says
why and layer sync stays off, where before every player was turned away.
- --export-world-layers refuses a game folder whose count differs, so a
release cannot be built from an updated game again.
- Player's game: a layer list of another length is left unread and every
layer stays as the player has it; the rest of the message is taken and
they join.
Shader tables
- Reverts a61f82c ("Shader crash fix"). Since 2.0.8 a mod's own shader
programs were copied into the merged table, and the tables that cosmetic
bundles carry were merged across mods. Each such bundle then held other
mods' programs, and the game built them as the bundle loaded, before the
files with their compiled variants were there. That was 2.0.8's most
reported crash (Skate.exe+0x3f86da6, some forty machines in ten minutes).
2.0.9's guard passed such a program over, and the same players then
crashed a little later in cosmetic loading (+0x3f7bb65, +0x16fe1a6,
+0x3dc07ed).
- Whatever crash a61f82c fixed comes back with this for the mods that
needed it.
- The guard stays.
Music
- The game's read of one song for its scorer (0x14f23a0) is replaced by the
same read with the check it lacks: a place past the end of the playlist
asset's songs has no song, which the game already answers with 0. 2.0.9's
check in music_select_next_hook looked at what the queue held before the
game was asked and did not see these; the game asked for places such as
0xd570 and crashed reading them (7 reports on 6 machines).
- Said in the log the first eight times.
Not confirmed in game: neither change has been run by a player who was
crashing.
nder Wine and Proton the Spotify device signed in but was never taken up
by Spotify as a device to play on: librespot never logged "active device
is ...", nothing could be started on it, and after a minute and a half its
control connection failed ("Websocket connection failed: Remote sent after
having closed"). Its HTTPS and websocket went through Windows' security
layer, which there is Wine's own.
librespot and reqwest are now built with rustls and its built-in
certificate authorities (rustls-tls-webpki-roots, ring) in place of
native-tls, so the same code makes the connections on Windows and under
Proton. This covers the Spotify device, online radio and anything else
fetched through the helper. Not yet confirmed on Proton.
Cargo.lock changes with it. ReSkate.dll is about 2.5 MB larger.
Music screen: large playlists no longer empty it, and two logs say why things fail
Music screen
- The screen's list was turned down whole, and came up empty (the game's own
Featured row too), once it held more than 1,024 songs: a few large YouTube
Music, Spotify or local playlists went over that. The most is now 2,000
songs and 16,384 places in playlists (music_ui_most_songs,
music_ui_most_places); 2,000 because the screen's own table of songs is
read only up to 2,048.
- The rows of the player's playlists are counted as they are registered and
held under that: with no room left, the rest of a playlist is left out of
its list (it still plays) and the screen is never empty for it.
- A row whose "artist - title" is longer than the screen takes is left out;
one such row turned the whole list down as well.
- Said in the log: how many songs were left unlisted, and, should the list
still be turned down, what it held.
Logs
- Spotify device: librespot's own warnings and errors are written at the
warning level again; only the rest of what it says stays at debug. A
player whose Spotify connects and stays silent had nothing to send.
- Multiplayer: a message turned down with "Join code, sender identity, or
multiplayer protocol did not match" now says which of the three it was,
its size and first bytes, and whether it came from a dedicated server.
After Steam updated skate., some installs had the supported Skate.exe over
the new build's data (a copied game, or a download that stopped part-way).
The launcher called that ready, every mod was left out as "made for game
build 29544144, not the installed 29663206; rebuild it with ReSkate Studio",
and the mods stayed out after the game files were put right.
Launcher
- Ready now also needs the game's data to be the supported build, read from
Data\layout.toc against supported_build::game_data_changelist (29544144).
Data of another build, or none, reads "Game files need verifying".
- DOWNLOAD in that state checks every file (-validate) instead of fetching
only what is missing. A download that fails part-way no longer ends on
ready because Skate.exe arrived.
- Data whose build cannot be read does not block.
Mods
- A mod left out because the game was of another build is no longer
remembered in .reskate-excluded.json: it is checked on every launch. One
already remembered for it is looked at again.
- When it is the game that is not the supported build, the mod's message
says to verify the game files in the launcher, not to rebuild the mod.
An FPV drone flown from the free camera: real quad physics (thrust,
gravity, drag, Betaflight "Actual" rates), collisions with the world, and
a goggle-style display. Open it with `dronemenu` (or the drone window's
Start flying).
Flying
- Keyboard and mouse, gamepads, or USB RC radios (EdgeTX etc. in Joystick
mode). "Set up controller" learns each stick and switch by asking for
one at a time, reversed axes and springing gamepad throttles included.
While a controller flies the drone, the game is kept off it, as
freecam's controller mode does.
- Presets (Tinywhoop, Toothpick, 5 inch), arm/disarm, back to takeoff,
and every setting live through `dronetune`, saved in the profile.
- Optional range: the picture breaks up and is lost with distance from
the takeoff point; past it, failsafe cuts the motors.
- OSD, on-screen sticks, an analogue video look, synthesised motor sound.
- Goggles: the pilot's skater wears eyewear from the game's catalog while
flying, and gets their own back on landing (Road Rash aware).
- Start flying hides the game UI, Stop flying brings it back.
Multiplayer
- New packet `drone` (36): where a player's drone is and which way it
faces, 15 times a second, unreliable, relayed like effects. Markers
glide between updates. Protocol version 52.
- FLYING DRONE above the name of a player who is flying.
- Players choose whether to share their drone and whether to see others'.
- Dedicated servers: `sync_drones` (players section) and `drones on|off`.
Off: no drone packets are relayed, and players are told in the roster.
Changes to existing code
- `flyspeed` takes any speed from 0.6 to 1500, not only the seven listed
(the drone's presets and top speed slider use speeds of their own).
- Overlay: begin/end_input_access, so the drone's controller read sees
the device while the game is kept off it.
- Profile: local_surface_hit, the park editor's ray from one point to
another, for the drone's collisions.
Tests: Skater/Test/drone_radio_tests (controller setup), and the drone
packet and roster rule in Multiplayer/Test/protocol_tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MArBL6UbYZPsp9rMnq2Ks5
"The Steam download did not finish" is the launcher's message for any
failed download, and what DepotDownloader said (the account does not own
the game, Steam refused the sign-in, the disk is full) was only written at
the debug level, so Open logs showed nothing to go on.
When a download fails, its last 40 lines are now written to the log as
warnings, after the exit code. The Steam login name is still left out, and
so are the rows of a QR code and the progress lines.