Game
- Player distance (Settings > Interface, "player-distance"): how far
away another player is still built as a skater, 50 to 1000 m, default
120. Past it they keep their nametag or dot. All the way up shows
everyone. A player already shown stays until 15% further out, so one
skating along the edge is not built and taken down over and over. The
game ran a full skater for every player on the server wherever they
were, which is most of a frame on a full one
- Nametags are always ReSkate's own. The "ReSkate nametags" toggle, the
"Nametag style" button and the nametag-style command are gone; the
game's own nametags are never shown
- Nametag distance ("nametag-distance"): how far away a name still
shows before it becomes a dot, 10 to 500 m. Default 120, was a fixed
150, so by default the name and the skater go at the same distance
- Nametag dots ("nametag-dots"): off hides the dots for far players and
the ones at the screen's edge for players off screen
- Friends' nametags only ("nametags-friends"): only Steam friends get a
name or dot; chat bubbles still show for everyone
Server
- A player out of sight of another (half as far again as the low rate
starts, 255 m by default) is sent to them once a second instead of
five times. On a full server most players are out of each other's
sight, and at five a second each they were most of what was sent
- Rotations are rounded before a pose is passed on, more coarsely the
further away the recipient is: 0.04 degrees within the full rate's
reach, 0.16 within the half rate's, a third of a degree past that,
about 5 degrees out of sight. The pose is still an ordinary one. A
bone that turned by less than a step is the same bytes as before, so
the difference from the reference leaves it out, and the low bits of
the ones that did turn pack away. By distance only: a player the
crowd limit slowed keeps the fine rotations
- A recipient crossing from one precision to another is sent a whole
state of that player, since a difference from a reference of another
precision would carry every bone again
- "net" shows the size of the poses sent at each precision
Profiler
- A function in ReSkate.dll is reported by its address when the DLL has
no symbols. A release build named every one of ours as the nearest of
three exports, so a report could not say which was slow; the
release's symbols name the address afterwards
"Cannot open file for SHA-256" was all a player saw when the launcher
could not read ReSkate.dll, at startup and again right after each
download. That is an antivirus holding or blocking the file.
- A sharing violation is waited out for up to five seconds: a file that
was just written is often held by the scanner for a moment.
- Otherwise the error names the file and its folder, says to add the
folder to the antivirus exclusions where that is the likely cause, and
carries the Windows error code.
- Conflict in launcher_support.h: kept Linux guards, took upstream's
validated_remote_load_library(note) signature + comment.
- Upstream's path_utf8() helper is portable; merged cleanly into the
Linux closure. Build + 8/8 portable ctest green.
Mod archives (Engine/Vfs/mod_merge*)
- A free archive index no longer has to sit at or below the highest index
the layout already declares. The engine keeps archives in a table keyed
by layer, install chunk and a 16-bit index, with no range to stay
inside; what it cannot survive is a reference to an archive the layout
never declared. Indices are taken lowest first, per package directory.
- So a mod that collides takes an index of its own and its file is hard
linked rather than concatenated, and no two mods share one archive's
4 GB of addressable bytes. Sharing is left for a mod added while the
game runs, which has no declared index to take, and for a directory
with no index left.
- A placement addresses its payload with a 32-bit offset, so both append
paths now refuse to cross 4 GB instead of wrapping round and pointing
the game at some other payload: CasStore::write fails with the archive
that is full, and the live-apply path checks archive 1's size plus the
mod's before appending and asks for a restart, which gives the mod an
archive of its own anyway.
Injection through a hooked LoadLibraryW (Engine/Core/Platform)
- A player could not launch at all: "Child LoadLibraryW entry bytes match
neither Windows' kernel32 file nor this process". Anti-virus and
overlays hook the entry with a jump to a per-process trampoline, so the
bytes differ in the launcher and in the child and neither comparison
could match. The guard already allowed the same hook when both
processes carried it identically, so it refused on an accident of how
the hook was written rather than on intent.
- inline_jump_length recognises the forms a hooking engine writes (rel32
jmp or call, push/ret, jmp [rip], mov rax/jmp rax, short jmp). When one
is present and every byte after it still matches the clean kernel32
file, the launch goes ahead: the body is Windows' own, and the provider
image already matched on timestamp, checksum and size. A wholesale
replacement is still refused, and now says so.
- The failure names the modules loaded into Skate that came from neither
Windows nor the game folder, instead of guessing at "security
software". An accepted hook is logged as a warning with the same list.
Launching (Launcher/launch.cpp)
- Exporting loose scripts is a convenience, and a folder that would not
take it was failing the whole launch: fs::create_directories threw and
nothing caught it. The export is skipped, RESKATE_LOOSE_FILES goes to
0 so the runtime does not look for files that are not there, and Skate
starts. Someone whose install is under Program Files can play; only
editing loose scripts is lost.
Failures worth reading (Launcher/problem.h)
- A hooked LoadLibraryW, a refused write and ERROR_ELEVATION_REQUIRED
each explain themselves now. Windows reports the last two in the user's
own language -- "Zugriff verweigert", "La operación solicitada requiere
elevación" -- so the rules key on the operation names and the error
number, which are never translated.
Saved skaters (Extension/Customization, Extension/Profile)
- Outfits stopped saving and every slot showed the standard skater. The
load hook marks a preset blocked on entry and clears it only on a full
success, so a load refused for a catalog that was not ready yet left
the preset blocked for the session and every later save was refused as
previous_load_rejected. The game asks once per level load and does not
ask again, so one lost race cost a skater until a restart. Logs show
the catalog arriving three seconds after the refused load, at startup
and again on a map switch 17 minutes later.
- The record is remembered in CosmeticRuntime::pending_loads and applied
by retry_pending_cosmetic_loads from the customization pump, which
already waits for refresh_cosmetic_catalog. The preset stays blocked
until a load really succeeds, so nothing is written over the outfit on
disk.
- Those records belong to the loadout manager's array, which can be
replaced in between, so each pointer is checked against the live array
exactly as a save checks one -- manager current, begin/end sane, 0x48
stride, at most 10 records, in range and aligned. A record that no
longer belongs is dropped as retry_record_moved rather than written
through.
A player whose game sat under a folder named U+200E (left-to-right mark,
an invisible directory on their Desktop) could not launch: the status
tile read "No mapping for the Unicode character exists in the target
multi-byte code page". Nothing was wrong with their mods, and the merge
in their log had just succeeded.
fs::path::string() converts to the process's ANSI code page and throws
exactly that when a character has no mapping there. initfs.cpp called it
on the normal path, not in an error handler, so exporting loose files
threw on every launch. Their log stops right where that export begins.
Cyrillic, CJK or accented characters in an install path do the same on a
code page that cannot hold them.
- path_utf8() in Engine/Core/Platform/path_text.h: UTF-8 represents every
path Windows allows, so it never throws.
- Every path that becomes a narrow string uses it -- 35 of them across 13
files. Most are log lines and error messages, which were a second trap:
reporting a problem with a path threw this instead of the problem.
- The ~45 .string() calls on Json values are untouched; those are not
paths.
Nothing changes for an ASCII path, where both encodings agree, and a path
that is not ASCII had no working behaviour to preserve.
Reported with a log by a player on Discord. Moving the game to an ordinary
folder works around it on an unpatched build.