61 Commits
Author SHA1 Message Date
AyeZeeBB 93528884b3 Park editor: a session with object scaling off keeps saved scales without resizing
On a dedicated server with object scaling off, a loaded park's scaled objects were resized anyway
(and after a refused resize the pose poll saved 1x over their scale), and a scaled paste, set or
undo ended in a resize the server would not take, so the edit waited on it. The placements now
know whether this game may resize (set by the session from the server's setting and the role, the
host and roles that may always build exempt, allowed outside a session): where it may not, objects
stay 1x in the world, nothing waits on a resize, and the layout keeps their scale for when scaling
is allowed again. A transaction test pastes a scaled object with scaling off.
2026-10-17 13:26:16 -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 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 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 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
AldoandClaude Opus 5.5 57d98307a7 Skater: FPV drone mode
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
2026-10-13 21:59:49 +01:00
copilot-swe-agent[bot]andJean-LucTch 80242c0ec8 Cover deadline boundaries, safe cancellation and fresh join races
Co-authored-by: Jean-LucTch <88255067+Jean-LucTch@users.noreply.github.com>
2026-10-13 11:27:08 +00:00
copilot-swe-agent[bot]andJean-LucTch 8eddcab142 Coordinate custom-map downloads with the multiplayer deadline
Co-authored-by: Jean-LucTch <88255067+Jean-LucTch@users.noreply.github.com>
2026-10-13 11:11:27 +00:00
AyeZeeBB 28118efc27 Objects: other players' objects are ghosts by default, and their card is a visitor's
Objects other players place are spawned locally, so the game took every one
for the local player's own: all were solid, and their card offered the
owner's buttons and named the wrong player.
- Other players' objects are ghosts the skater passes through, as in the
  game's own sessions. Each player's row in the session menu has an
  "Objects: solid / ghost" switch, remembered by Steam ID; a dedicated
  server's own objects (its parks) are solid unless switched off.
  `mp objects-solid <SteamID64|server> on|off` does the same.
- The card's own Disable and Enable on one object stand over that player's
  setting until the setting is changed.
- The card on an object someone else placed no longer offers Edit or
  Delete: the game's "is this mine" and "may this be edited" checks answer
  for who really placed it. It offers Disable instead.
- The card names the owner of the object it is for, read from the card's
  own script, instead of the owner of the object nearest the skater.
- A dedicated server has a player card of its own, "Server", shown on what
  it placed.
Server: the Mods folder is kept when the configuration is reloaded. A park
mod was reported as not being in the Mods folder after a reload.
2026-10-13 03:37:45 -05:00
AyeZeeBB 28231c0df2 Typing dots: none for commands and private messages, and no flood of stop notices
A line that starts with "/" is a command, a whisper or party chat, so typing
one no longer shows dots to the whole server. Dots already showing are cleared
when the line becomes one.

A "stopped typing" notice was relayed every time it arrived, while "typing"
was held to one per 200 ms, so a modified client could flood everyone through
the host. A lobby's host and a dedicated server now pass a stop on only when
that player's dots are up, which is at most once per accepted "typing".
2026-10-12 23:45:27 -05:00
wilu222 149da6f72e Show dots above a skater while they are typing a message 2026-10-12 19:50:45 -04:00
AyeZeeBBandClaude Opus 5.5 d2b8e18097 2.0.7 hotfix: map updates, vote bar, ReSkate Admin tag, servers on the newest map version
Map updates
- A map the game already has, being replaced by the host's version,
  could load as soon as the new files were in Mods, while the mods
  were still being applied: it ended on "This map is not loading". The
  session now holds the load while a map is downloading, installing or
  applying, as it waits for one that is missing.
- A player whose version of a map is later than the host's keeps it and
  loads. Only an older one is replaced by the host's. Versions are
  compared number by number (1.10.0 is later than 1.9.2).

Dedicated servers
- maps.latest_versions (off by default): the server asks Thunderstore
  for the newest version of each of its custom maps, at start and every
  maps.latest_check_minutes (5-1440, default 60), and writes it into
  the mod's manifest.json. Nothing is downloaded: the server reads only
  a map's level list and version, and players, who are held to the
  server's version, fetch it as they join. The map being played waits
  until nobody is on it. A folder that is not a Thunderstore package is
  not touched, and the level list is left as it is.
- Console: maps update, maps latest on|off, maps latest every <minutes>.

Overlay
- Vote card: the pass mark is always in the middle of the bar. Green
  grows over the left half up to the yes votes needed and over the
  right half past them, whatever share the vote needs.
- The "Staff" tag is "ReSkate Admin", shimmering from #2500FF to
  #B2F9FF.

No protocol change. Tests cover the version comparison and the
manifest write; the Thunderstore lookup was checked against the site.
The map update flow, the vote bar and the tag were not seen in game,
and the server setting has not run on a live server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:19:49 -05:00
AyeZeeBB e344d585f4 Vote for the next map when a map's time is up
A dedicated server that rotates its maps can now ask players where to
go next instead of taking the pool's next map.

Server
- maps.rotation_vote: when the timer runs out, a vote between three
  maps drawn at random from the pool, "Random map" and "Stay here".
  The most votes wins, a tie is drawn, and with no votes it is the
  pool's next map. It ends early once everyone has voted.
- maps.rotation_vote_seconds (10-120, default 30) and
  maps.rotation_extend_minutes (0-240, default 15; 0 leaves "Stay
  here" out).
- maps.rotation_announce: a card when the map has 10, 5 and 1 minutes
  left.
- The vote's maps are drawn a minute ahead and sent to games
  (PacketKind::next_maps), so their pictures are ready when it opens.
- /vote extend: a built-in vote that gives the map in play
  rotation_extend_minutes more. On by default (votes.extend_map), and
  only runs while the maps are on a timer. "extend" is now a reserved
  vote name.
- Console: rotation vote on|off, rotation announce on|off,
  rotation stay <minutes>|off, rotation extend [minutes],
  votes extend ..., vote extend.

Game
- The vote opens as a popup in the middle of the screen: a tile per
  answer with its picture, name, share of the votes and count. It
  holds input as the map download card does, and works with a
  controller (D-pad, A, B), the keyboard and the mouse.
- Pictures: a mod map's icon from its folder, or from Thunderstore
  when it is not installed; the game's own maps show their loading
  screen picture, cut to its middle square. "Random map", "Stay here"
  and a map without a picture are drawn as shapes.
- Older servers and polls are unchanged: the vote is a poll, so it can
  still be answered with /1, /2... in chat.

Protocol 50: games and servers both need this build.
2026-10-11 17:21:27 -05:00
AyeZeeBB 8351654222 Votes by those who vote, map versions, maps reload, and a crash leaving a map
Votes: a vote is decided by the votes cast, not by everyone on the server. It
passes when yes is its percentage of them, at the end of its time, or sooner
once the players still to vote could not change it. A kick still needs two
yes. The card's bar is yes in green from the left and no in red from the
right, with a mark where yes has to reach. No protocol change.

Map versions: a map installed at another version than the host's is no longer
loaded as it is. The download card offers the host's version (MAP VERSION,
UPDATE), which replaces the installed one and is applied while the game runs.
For that the live merge keeps each mod's fingerprint with where its archives
were placed, and places a mod again when its files have changed since: before,
a mod replaced while the game ran was read where the old one lay. When the
host's version is gone from Thunderstore, or the old one's files are in use,
the player is told and leaves the session.

Dedicated server: "maps reload" reads the Mods folder again, so a map copied
in while the server runs can be switched to at once. The server window reads a
copy of the map list.

Map names: a custom map's level is dingolevel_reskate_<map>, and "reskate" was
left in its name ("/map reskate badlands"). The map's own name is shown and
typed now; the old spelling still finds it.

as a level unloads, the game takes each mesh variation out of its registry by the
entry's mesh and reads it unchecked, and this map's meshes unload before
their databases. A guard on that one function has the read of a mesh that is
gone go on with the next entry, and logs how many were passed over.
2026-10-11 16:12:57 -05:00
AyeZeeBB c27d6cb053 Word filter: fewer false flags, servers' own word lists; protocol 49
The chat filter matched short listed words anywhere inside a longer one, so
entries like "gger", "spik", "crip" and "coon" in the backend's lists blocked
"bigger", "spike", "script" and "raccoon".

- A listed word of four or five letters now matches inside a longer word only
  when the rest of it is what goes around a swear word ("motherfuckers",
  "bullshit", "shithead", "shitty", "xxfuckxx"). Words of six letters or more
  still match anywhere.
- An ordinary word that equals a listed phrase without its spaces
  ("the rapist" / "therapist") no longer matches.
- bad_words.txt is gone: the filter has no words until the backend's lists
  arrive.

Dedicated servers:
- "use_global_word_filter" (default true) turns the backend's lists off for a
  server. Console: words global on|off.
- data/words_filtered.txt and data/words_blocked.txt hold a server's own
  words, read at start and by "words reload". Blocked words follow
  word_warnings. Filtered words are sent to players (new packet, kind 33),
  whose games star them for players with the chat filter on and drop them on
  leaving.

Multiplayer outfits: a player whose mods add or remove skater or board slots
no longer makes everyone look like the local player. A peer's items are put
in this game's slots by slot hash; slots the peer lacks stay empty and items
in slots this game lacks are left out.
2026-10-11 13:17:28 -05:00
zeex64 193dec2fe1 Merge pull request #154 from wilu222/fix/lobby-name-persist
Persist saved multiplayer lobby name across restarts
2026-10-11 11:18:05 -05:00
AyeZeeBB a1621ee19e Dedicated servers: roles, with their tags, commands and permissions
Dedicated servers: roles, with their tags, commands and permissions
A dedicated server had one list of admins who could do everything, and
everyone else could do nothing. It now has roles, kept in
data/roles.json beside the bans: each a name, the tag its players wear
in the game, the server commands it may use, what it may do besides
commands, and a rank.
A server starts with five: player (everyone given no role: chat, votes,
parties, private messages), vip (a reserved slot), moderator (kick, ban,
messages, giving out lower roles), admin (everything) and muted. The
default role is whoever was given none; a role has what the default has
as well as its own unless it says it does not inherit. A reserved slot
is a permission, so a role's players have one without being listed.
Nobody kicks, bans or changes the role of a player whose rank is theirs
or higher, or gives out a role that is; the console is above every role.
"*" is every command but role and reserved, which a role has only when
it lists them.
New commands: role <player or SteamID64> <role>|none, roles, and roles
reload. "admin add|remove" and "admins" are gone: the admins an older
config lists are moved into the admin role the first time the server
starts, and leave the config.
In the game, a player's role tag and colour show in chat and on their
nametag, and each control in the Multiplayer menu, the map, time of
day, world layers and parks follows the command it needs rather than
one admin flag, as do the commands chat offers.
Protocol 48: the roster carries each player's tag, and a new
permissions packet tells a player what their role gives them. Older
games and servers do not connect to newer ones.
Also fixes a name clash that kept the server config tests from
compiling.
2026-10-11 03:52:13 -05:00
AyeZeeBB 8044c88373 Hold skateboards to their size, and catch one kept on another player
A hacked game was resizing its skateboard and reporting it at another
player's position, so everyone saw an oversized board riding on them,
and with player collision on it knocked them about.

A skateboard is now held to within 10% of its size, apart from the limit
on resized body parts: on a dedicated server when its scale limit is on,
and in every player's game whatever sent the pose. Another player's
board is solid only within 3 m of its own skater; one further off is
seen and not felt.

Dedicated servers also check for a board kept on another player
(Server/board_check.h): one away from its own skater that stays beside
one other player while that player travels 8 m over at least 3 seconds.
The board is then no longer passed on to the others until it has behaved
for half a minute, and the log and the admins in the game are told who.
New setting anti_cheat.pinned_board and command board-check:
off | warn (default) | kick.
2026-10-11 03:07:32 -05:00
AyeZeeBB f289666c2c Entity limit 4x, replay crash guard, -map launch option, memory tools; pause-menu fixes
Memory
- The entity and pending-update pages are four times the game's own size
  (524,288 slots), down from eight times. At eight, the game kept a
  3.5 GB block for them that was fully in RAM on custom maps; the largest
  map load seen needs about 150,000 slots. The render-instance pool and
  spatial buckets stay at eight times.
- The log has a "Memory:" line when the game's memory, the players in the
  session or the skaters built change: committed, in RAM, players, skaters
  built, player distance and map.
- Console: `perf memory` lists the game's memory by block size.
  `perf memory mark` remembers it, and the next `perf memory` says what
  was added since. `perf memory trace` also records who asked for each
  block until `perf memory stop`. `perf memory big` lists the largest
  blocks with how much of each is in RAM and a sample of what is in them.
  RESKATE_MEMORY_TRACE=<megabytes|all> starts the trace with the game.

Replay crash guard
- The game's replay recorder crashes when its stream has no room for the
  next chunk: it writes to the chunk it was not given (Skate.exe+0x48efa34,
  crash reports of 2026-10-10). The function now runs under a handler
  that, for that one fault alone, returns as if no chunk was ended, and
  says so in the log. Any other fault still goes to the crash reporter.
  The skip path has not been triggered in testing.

Launcher
- `-map <name>` (also --map, -map=<name>) loads that map as soon as the
  game is up, named as the console's `load` names one. With --no-gui and
  no other options the launch still uses the saved launcher settings.

Pause menu
- The Multiplayer and Mod Options pages step aside while the game's side
  menu, or a screen opened from it such as Settings, is over them. They
  drew over both before.
- A button still held when a page comes back is not a press on it: the
  Back that closed the side menu no longer dismissed the page as well.

Multiplayer cosmetics
- A player whose mods add or remove cosmetic slots (stickers, say) no
  longer has the whole outfit refused. Their items go to this game's slots
  by slot: a slot they lack stays empty, an item in a slot this game
  lacks is left out, and the log says how many of each.
2026-10-10 16:15:59 -05:00
AyeZeeBB 0ae0e71f0f Map downloads on join, faster mod merging, connect cards in the server browser
Map downloader
- A host or dedicated server now tells joiners which Thunderstore package
  its map comes from (the Mods folder name plus manifest.json).
- A player without the map gets a card: icon, name, author, version and
  description, with DOWNLOAD and CANCEL. It works with mouse, keyboard and
  controller.
- Only packages listed as Maps in the ReSkate community are offered, and
  never deprecated, rejected or NSFW ones.
- On yes the package is downloaded, installed into Mods and applied as a
  live mod, then the session loads the map. A small card on the right
  shows the progress, so the player can keep skating.
- The player stays connected while it downloads: the host gives a player
  who says they are fetching the map up to 45 minutes instead of the
  usual load time.
- When the server changes map with the player in it, the card says so.
- Protocol version 47: map package on map_offer and world_state, and a
  "fetching the map" flag on map_request and world_ready.

Joining
- The pause-menu server browser shows a CONNECTING card over the page
  while a join is under way, and a COULDN'T CONNECT card with the reason
  when it does not get in.
- The session and voice tabs no longer show before the player is in.
- Leaving without the map (declined or cancelled download) no longer
  counts towards the failed-join limit, on servers and player hosts.

Input
- A card that asks something holds the game's input until the buttons
  are released.
- Steam's overlay hooks XInput in front of ReSkate and answers the game
  itself, so a controller reached the game behind menus. ReSkate now
  hooks that hook as well.

Mod merging
- Mods are read on several threads, at launch and for live merges. A live
  merge runs below the game's priority.
- The paid-cosmetics check reads the game's files and the mods on several
  threads.
- A live merge reuses the paid-cosmetics verdicts it already has and
  reports its progress and timings.

Launcher
- NSFW mods and their categories are not shown in the mod browser.
2026-10-10 13:15:04 -05:00
wilu222 34f181305c Persist lobby name across restarts 2026-10-10 01:50:33 -04:00
AyeZeeBB e98169a2b6 Host bans, and a quieter direct-connection log
Host bans: the backend's new banned_hosts list (players the team has
stopped from hosting lobbies) is read with the other identity lists.
A listed player's own game refuses to host and stops a lobby already
running. Since that is theirs to change, everyone else's game does the
rest: the lobby browser leaves their lobbies out, and the game will not
join one by code or stay in one. They can still play on servers and in
other players' lobbies. The browser now also leaves out lobbies hosted
by globally banned players.

Server log: "Direct connection asked for by ..." is written once a
minute per player, with a count of the repeats. A player waiting for a
place on a full server asks again about every second, and each ask was
a line.
2026-10-09 20:31:05 -05:00
AyeZeeBB 20bd777b4c Anti-cheat: limit how far a skater's bones can be moved
A hacked client could move its skater's bones anywhere and stretch the
skater across the map for everyone: the server limited a bone's scale
but not its place.

Server: new anti_cheat setting bone_reach_limit, default 1 metre (0.5
to 20, 0 for off; console bone-reach <0.5-20>|off). A body or board
bone further than that from its parent is pulled back before the pose
is passed on. On the game's rig these bones never move (the longest, a
thigh, is 0.44 m). The fourteen skater bones that do move by metres
(they follow the board and the body in a fall) are held to 100 m, and
the board's rig to 5 m from the board.

Client: every pose a game is shown is held to 2 m reach and 8x bone
scale, whoever sent it, which covers servers that have not updated and
sessions without a dedicated server. A client used to accept up to
100x scale.

The limits come from two recorded play sessions; the exempt bones are
picked by index for the current build's 395-bone rig. The pose codec
bench now prints each recording's bone reach, and reads recordings made
by earlier versions.
2026-10-09 17:11:16 -05:00
AyeZeeBB c733816d62 Guard a missing shader preset, end a stuck close, unload guard off
Costume crash: the skater scripts' item:addEsShaderPreset uses the
preset it is handed without checking that it is loaded, and the game
crashed in the material's construction shortly after arriving on a
server's map while wearing a modded costume. The call is now checked
first: an item whose preset is not there is shown without it, and the
log says so.

Stuck close: once the player closes the game, a watch starts. If the
window has gone or stopped answering and the process is still there
8 seconds later, the process is ended. A window that still answers
(the close was turned down) is left alone, and the watch stands down
after two minutes.

Level unload guard: switched off. Skipping the entry that crashed left
its reference registered, so leaving the level never finished and the
game hung with the world unloaded. The crash it covered is back until
the reference is released properly.
2026-10-09 17:03:35 -05:00
Robin MöllerandClaude Opus 5.5 c996fbda13 Server votes: custom votes, polls, more vote settings and announcements
Builds on the 2.0.0 vote card. Protocol 46: a server's vote can now be a
poll, and the roster carries who may start polls, the owner's own votes
and the current announcement.

Custom votes (votes.custom): the server owner defines /vote <name> [choice]
in ReSkateServer.json. When one passes, the server runs its command as the
console; {map} is the current map and {arg} the player's choice, which must
be one of the vote's "choices", so players never put free text into a
command. They show in /help, /vote list and the "/" menu.

Polls: /poll <question> | <answer> | ... with 2 to 6 answers. A poll runs
nothing. Players answer by clicking a row on the vote card or with /1, /2...;
the card shows each answer's count, then the result for a few seconds.
votes.polls says who may start one (off, admins, everyone), poll_seconds how
long it runs, and /poll end ends it early (its starter or an admin).

More vote settings: each vote may have its own seconds, cooldown_seconds and
min_players; starter_votes_yes says whether starting a vote counts as a yes.
The "votes" admin command sets all of them.

Announcements: "announce <text>" posts a chat line and shows a card at the
top of every player's screen. The new "announcements" section holds messages
the server posts in turn every interval_minutes while players are on;
"card": false keeps them to chat. Managed with the "announcements" command.

Older configs gain the new settings with their defaults on first start.
Server/README.txt documents them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WbSRfNf5T9wX4XMMQ5kwia
2026-10-09 21:01:40 +00:00
AyeZeeBB b3b067a356 The creator tag reads "Content Creator"
The badge a content creator gets before their name, in chat, on their nametag
and on their own tag in the menu, said "Creator". It now says
"Content Creator". Tests and comments that quoted the old text follow.
2026-10-09 10:04:49 -05:00
AyeZeeBB b229062400 Skater effects on other players: costume and board trails, and contact sparks and dust
Other players' skaters and skateboards showed none of the game's skater
effects: not the trail or fire a costume or deck comes with, and not the
sparks, dust and puffs of a skater touching the world.

Costume and skateboard effects. The game gives another player's skater and
skateboard the right effect sets by itself, but builds them before their
outfit is on, so nothing showed. They are now built again half a second after
the outfit is applied. An always-on effect that a skater's item puts on the
skateboard's wheel joints (the Ghost Skater trail) follows the skater's own
copy of those joints, which only this player's skater keeps up to date: for
other players it is taken out of the skater's sets and built on their
skateboard, which has the same joints.

Contact effects. They come from a skater touching the world, which only
happens in that player's own game. Each game now captures its skater's
contacts (position, velocity, surface normal, surface material) and sends up
to three a tick in a new unreliable packet, PacketKind::effects. Other games
play each through the game's own function, so the effect is still chosen from
the surface and the speed. A contact is shown as late as that player's poses
are, only within 30 m of their skater, and only for a material the level's
table has: the game reads that table without a bound.

Server. A dedicated server relays effects packets to players within 150 m of
the sender, at most one packet a tick from each player. New setting
players.sync_effects (default true), console and admin command
"effects on|off". Off: the server relays none, and the roster tells games
neither to send their contacts nor to build other players' costume and
skateboard effects. Player-hosted lobbies relay through the host and always
share them.

The roster carries one more byte for the setting. Still protocol 45, which
has not been released.

New console command "mp effects": what has been captured and played, and why
the last contact was refused if one was.
2026-10-09 00:10:11 -05:00
AyeZeeBB 9ab985e778 Server: object scaling can be turned off
Server: object scaling can be turned off

A dedicated server can stop players resizing the objects they place.

- "allow_object_scaling" in the config's "players" section (default true),
  and "object-scaling on|off" from the console or an admin. A change is
  saved and applies at once.
- Off: the server shares every player's objects at their own size, whatever
  that player's game sent. Admins can still resize theirs, as with the
  object limit.
- The roster tells each game. In the park editor of a player on such a
  server the SCALE mode is not offered, its Y shortcut does nothing, the
  scale box is disabled with a note saying why, and what is placed or edited
  is its own size.
- An object resized before the setting was turned off is its own size for
  everyone else straight away; its owner sees it resized until they edit it.
2026-10-08 22:21:00 -05:00
AyeZeeBB 5967124f3a Vote card above chat with rebindable Yes and No; fix full servers breaking on extra slots
Votes on a dedicated server show as a card above chat instead of being
followed through chat lines.

- The card has what is voted on, the Yes and No counts, how many are needed,
  a bar of how far Yes has got, and the seconds left. When the vote ends it
  shows Passed, Failed or Cancelled for four seconds.
- Yes and No are two buttons, labelled with the player's binds. The answer
  given is highlighted and can be changed. They can be clicked whenever the
  cursor is free (chat, menu or console open).
- Two new binds, "Vote yes" and "Vote no", in the binds list: F1 and F2
  unless changed, to keys or controller combos like the others. They are
  read only while a vote is running, and not while typing.
- /yes and /no still work and show on the card. The player a kick vote is
  about sees the card and has no vote in it.
- Console: "mp vote yes|no", "bind voteyes <n>", "bind voteno <n>".

Server:
- The roster carries the running vote (id, kind, tally, seconds left,
  starter, target, label) and the one just finished with how it ended.
- A chat line is no longer sent for each change of the tally; the lines for
  a vote starting and ending stay.

Fix: a dedicated server's roster may list more players than its limit.
The extra slots for reserved players and admins (33 of 32) made a roster the
protocol refused to encode, so with one in use the server logged
"Server error" every pass and stopped part-way through each one until
somebody left. Lobbies hosted by a player are still held to their size.
2026-10-08 21:54:45 -05:00
zeex64 7cdd2eaede Merge pull request #132 from VexFlint/fix/mesh-tests-direct-join
Build the mesh tests again: their fake transport takes the direct address
2026-10-08 21:31:38 -05:00
zeex64 2c90fbec36 Merge pull request #43 from ComputerKWasTaken/codex/random-community-parks
Add random community park loading and launch randomization
2026-10-08 21:20:47 -05:00
VexandClaude Opus 5.5 57c440507a Build the mesh tests again: their fake transport takes the direct address
38d86e1 gave SteamTransport::join a direct IP and port and added
take_direct_notes(), but the fake transport in mesh_tests.cpp kept the old
join and had no take_direct_notes, so the Windows test build failed. The
fake ignores the direct address and has no notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:53:11 -03:00
AyeZeeBB 38d86e1b72 New pose and sound formats for dedicated servers; direct connections; protocol 44
Poses and skater sound between a dedicated server and its players use new
formats. Lobbies and the links between games are unchanged.

Poses (pose_codec.h, pose_batch.h):
- A pose is rounded once (rotations to 0.08 degrees, positions to a
  millimetre) and sent as its differences from a pose of the same player
  the receiver has acknowledged. Several players' poses share one message.
- The receiver acknowledges messages once a tick; the sender builds only on
  acknowledged poses, or on the whole pose it sent reliably. Loss costs the
  poses in that packet and nothing more.
- Fingers are not sent to players further than "finger_distance" (25 m).
- Games upload their own poses the same way, and the server acknowledges.
- About 300 B a pose at full rate against about 660 B before.
- The server no longer rounds rotations by distance: it saved little.

Sound (sound_codec.h):
- Each sample is sent as what changed since the one before, numbers rounded
  to 1/1024. One reliable message a tick carries every skater a player can
  hear. A stream is sent whole at its start and every two seconds.
- About 106 B a message against 368 B before, on a recording of real play.

Direct connections:
- "connection": "relay" (default) or "direct", with "direct_address" and
  "direct_port". A direct server listens on UDP and is listed with its
  address; players still sign in through Steam.
- Games connect straight to servers that offer it and fall back to the
  relays after 5 s. Setting and "mp direct-connections" to turn it off.

Server:
- "bone_scale_limit" is 1 by default: resized body parts are off unless a
  server allows them.
- Logs Steam's relay network status at start-up and in "net", and watches
  the Steam sign-in.
- "pack_ms" (10): how long Steam may hold a message to pack it with others.
- "steam_debug": logs what Steam's networking says, for connection faults.
- "relay_everything": sends poses and sound through the server even between
  games that have linked, for testing with few players.
- "net" shows pose sizes by rate, the share sent without fingers, and whole
  poses sent.

Game:
- Only ReSkate's nametags are shown; the game's own are removed.
- "mp pose-dump <seconds>" records the local skater's poses and sound.

Tests: pose_codec_tests covers exact round trips, chains of differences,
held fingers, damaged input, the sender and receiver over a link that
delays, loses and reorders, and the sound format; with a recording as its
argument it prints sizes on real play.

Protocol 44: servers and games must both update.
2026-10-07 23:10:13 -05:00
AyeZeeBB 91c939ea4f Server: limit how far a mod may resize part of a skater for other players
- bone_scale_limit in ReSkateServer.json, and the bone-scale command:
  the most a bone may be scaled in the poses the server passes on, 1 to
  8, and its inverse the least. 1 shows every skater at the game's own
  proportions; 0 (the default, "bone-scale off") is no limit
- A "big head" mod sets one joint's scale in the body's bind pose
  (animation/dingo/cas_rsp_body_bind, the head from 1 to 4). The game
  applies it to the bone, and the bone's scale travels in every pose,
  which is how players without the mod see it. The game's own skeleton
  has no scaled joint, so 1 changes nothing for anyone else
- The player with the mod still sees it on their own screen: the server
  decides only what the others are sent
- Changing it sends whole states again, so nobody keeps a reference
  with the old sizes in it
- Dedicated servers only; a lobby's host has no such setting
2026-10-07 19:42:48 -05:00
AyeZeeBB 7ee82b2771 Player distance and nametag settings; far players sent less and more coarsely; profiler addresses
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
2026-10-07 16:43:18 -05:00
AyeZeeBB d04920c793 Server: the nearest players always at the full rate; whole states once a minute; relay routes in net
- The crowd limit always sends a player the nearest 8 others at the
  full rate and the next 12 at ten a second, whatever the budget. It
  gave everyone the low rate first and the nearest what was left, and
  with enough players on (about 120 at the default budget, fewer at a
  lower one) nothing was left: nobody was at the full rate, and since a
  skater's sound follows the full rate, nobody was heard either
- Whole pose and sound states are sent every minute instead of every 8
  seconds. A whole pose measured 3.7 KB against 0.7 KB for a difference
  from one, however old the reference, and at every 8 seconds they were
  about a seventh of what a busy server sent
- "net" lists which of Steam's relay locations the connections go
  through, and "net <player>" that player's: Steam reports pings of
  several hundred ms on a server measured at 55-84 ms directly, and
  this says which way the route went
2026-10-07 16:02:26 -05:00
AyeZeeBB c98c3f3207 Keep busy dedicated servers playable; hold stale skaters instead of rebuilding them
No protocol change: a server built from this still takes 1.1.4 players.

Server: what each player is sent
- A crowd limit: each player is sent at most crowd_budget poses a second
  (default 600, 0 for none; "crowd <n>|off"). Everyone at 5 a second at
  least, the nearest at the full rate, the next nearest at 10. Before,
  everyone within full-rate distance was sent at the full rate, which a
  crowd of 40 turned into more than a connection carries
- A skater's sound goes only to players within the full-rate distance,
  and in a crowd only from the nearest; it went to everyone
- A player who joins, or arrives after a map change, is shown the others
  about seven a second, nearest first, and none while what they were
  already sent is still on its way. Before, every outfit and every
  stream went at once (and to everyone at once after a map change)
- Whole pose and sound states every 8 seconds instead of 2, on the
  source's own clock, and slower pose rates sent on that clock too, so
  recipients share one patch and compression (DeltaCache) instead of one
  each. The settings survive a map change
- Object updates wait for a connection that is behind

Server: Steam connections
- send_rate (default 900 KB/s, "rate <KB/s>") is set as both Steam's
  minimum and maximum, for open connections and each new one. With only
  the maximum raised a connection stayed at Steam's 256 KB/s; set high,
  what was lost was resent until the connection was full of resends
  (about 1.1 MB/s reaches a player through the relays)
- A 4 MB send buffer, and a refused must-arrive message no longer ends
  the player's session at once: 15 seconds of refusals do (30 for object
  updates). A full queue used to drop everyone who was behind together

Server: seeing what is going on
- "net [player]": every connection's ping, delivery, queue and rate, the
  traffic by kind (poses, sound, voice, outfits, objects), who uploads
  most, and how busy the server's loop is
- A [network] line in the log every minute
- A leave or failed join says how it ended: closed by the player's game
  with Steam's reason, connection lost, or closed by the server and why
- The queue time no longer prints Steam's "unknown" as a number

Server: other
- Reserved slots: reserved_slots and reserved in ReSkateServer.json, and
  the "reserved" command; admins always have one
- The speed check flags a game at about 1.5x and over (was 1.06x), and
  tells only that player; for the admins it is in the log

Game
- A remote skater with no pose for a second stays where it was for up to
  eight seconds instead of being destroyed and rebuilt three seconds
  later. On a busy server one slow frame did that to many players at
  once, and the rebuilding made the next frames slower still
- Every waiting message is read each frame (at most 128 were)
- On an official server the role tags (Dev, Staff, Homie, Creator) show
  for every player, not only the few the game is connected to directly;
  community servers keep the old rule
- The server browser searches Steam at most every 15 seconds (45 after an
  empty answer) and keeps the last list for 15 minutes when Steam returns
  nothing, up from 2
- The game's own connections send at up to 900 KB/s
2026-10-07 14:43:45 -05:00
ComputerKWasTaken fbad6a1609 Merge remote-tracking branch 'origin/main' into codex/random-community-parks 2026-10-07 11:23:01 -05:00
AyeZeeBB 07fef3820d Limit how many objects each player can place (protocol 43)
Limit how many objects each player can place (protocol 43)

- Servers and lobby hosts set how many objects each player may have
  placed: object_limit in ReSkateServer.json and the "objects" command,
  "Objects per player" in the host options. Default 100; 0 is no limit
- The host and a server's admins are not limited
- At the limit a player's game refuses another object, from the native
  object tool and the park editor, and the menu shows how many they have
  placed
- Enforced by the host or server, which shows everyone no more than the
  limit of each player's layout; objects already shown keep their place
- The limit travels in the roster, which takes the protocol to version
  43: builds before this cannot join sessions with builds after it
- The protocol tests link again (the server browser needs the identity
  lists since the official servers change)
2026-10-07 10:03:54 -05:00
AyeZeeBB 101e1af6e4 Official, staff and friends in the server browser; server tokens; Linux self-update
Server browser
- Official servers (the backend's "official_servers") sort first and are
  drawn in ReSkate blue
- Servers Steam friends are skating in sort next, drawn in green with the
  friends' names; searching a friend's name finds their server
- A player in a session they joined can join another server straight from
  the list; hosts still end their session first, and a Steam friend's join
  offer never replaces a session
- Joining a dedicated server from the list records it as the player's
  public session, so friends see it and the row shows HERE
- Blocked servers (the backend's "blocked_servers") are hidden and cannot
  be joined, by code either
- When the backend sets "server_tokens_required", dedicated servers that
  signed in without a login token are hidden, except on the player's own
  network
- A search that gets no internet list from Steam keeps the last list for
  two minutes, and waits six seconds before treating it as empty
Roles
- New Staff tag from the backend's "staff" list: green name, badge and
  item animation, ranked after Dev
- Staff can pick a rainbow for their items on the Special page; it is sent
  as a solid colour older builds can show, so the protocol is unchanged
Dedicated server
- steam_token: sign in with a Steam game server login token and keep the
  same Steam ID every start; empty signs in anonymously as before
- A listed server without a token says so at startup, and when the token
  rule starts or stops hiding it
- Linux servers update themselves from the release's "server_linux"
  tarball, with the machine's curl and tar, pinned by SHA-256, and carry
  on in the same process
Launcher
- Only create the MiniDumpWriteDump API-set shim under Wine/Proton
2026-10-07 00:19:56 -05:00
ComputerKWasTaken 8aaa4b75fc Merge main and preserve random park command help 2026-10-06 16:40:56 -05:00
AyeZeeBB 2cdc5ee8df Fix costume merging, restrict server names, add Centrix tag
- Mod merge: rebuild a merged bundle's chunk metadata the way the game
  writes it (one record per chunk, in chunk GUID order), so costumes
  from different mods built on the same game costume keep their
  textures and meshes
- Merge audit checks that layout, and there is a new unit test for it
- Server names: only letters, numbers, spaces and - _ [ ] ( ),
  1 to 64 characters; the server browser hides servers with other names
- New Centrix tag from the backend's "centrix" list: blue and white
  name, badge and item animation, ranked after Dev and before Creator
2026-10-05 23:39:16 -05:00
Juan 23151496f1 Add chat bubbles above each skater
A player's recent chat lines show as speech bubbles above their skater: newest nearest the head, older lines stacking upward, each popping in with a slight overshoot before it fades. Emotes render as their images, a message that is nothing but a few emotes shows them jumbo, and a message longer than a handful of lines is cut with an ellipsis so one line can never wall the screen off.

Local display options live under Settings > Interface > Multiplayer (on/off, own messages, distance, duration and how many recent lines stack) and the console gains mp chat-bubbles, chat-bubbles-own, chat-bubbles-distance, chat-bubbles-duration and chat-bubbles-history.

The chat panel and the bubbles now share one rich-text module (chat_rich), and a filtered message keeps its emote names: the bad-word mask is applied, but emote tokens are put back from the original before they are parsed.
2026-10-05 22:46:40 -03:00
zeex64 022900d9fc Merge pull request #74 from xThrasherrr/feat/server-map-pool
Let server hosts choose the map pool and rotate through it
2026-10-05 16:53:52 -05:00
AyeZeeBB de631ad9e6 leave out mods that copy store items, chat badge check
- Mods that add copies of store items under new names are no longer
  loaded. The launcher, mod manager and game say why.
- A chat line passed on by the host only shows a Dev/Creator/Homie
  badge when the player's own copy of it arrives too, so a host
  can't put one on a line that player never wrote.
2026-10-05 16:29:37 -05:00
xThrasherrr 7007ad0389 Name the missing map when leaving a session over it
The host sends the map's name with map offers and changes, since a player without the map's mod has no manifest to name it from.
2026-10-05 15:09:30 -06:00
ComputerKWasTaken 1715dd38d0 Add controller park randomization and cancel guest startup rolls 2026-10-05 15:44:58 -05:00
xThrasherrr 206885fa62 Add a map pool card to the Multiplayer menu for server admins
The server now sends admins its pool and rotation with the map list, which bumps the protocol to 42: players and servers need this build to connect.
2026-10-05 14:43:12 -06:00
AyeZeeBB 5bfe4f7bfa tag preview, separate switches and per-item colors
- Split the single switch into "Show my tag" and "Show my items", and
  show the tag as chat and nametags draw it.
- Color whatever is equipped instead of the one hoodie and pink board:
  top, bottoms, shoes, socks, hat, glasses, outfit, costume, deck, grip
  tape, trucks and wheels, each set on its own.
- Each item can use its list's animation (rainbow, red or gold), a
  gradient between two picked colors, one solid color, or be off.
- The choices travel with the player's appearance so everyone sees them.
  Protocol version 41.
2026-10-05 00:00:45 -05:00
AyeZeeBB c930b512e6 Backend lists for tags, cosmetics and bans
- Read Dev / Creator / Homie Steam IDs from api.reskate.dev instead of the hardcoded list
- Creator (red) and Homie (gold) tags and hoodie/board colours; Admin tag is now pink
- Global bans: lobby hosts and dedicated servers turn banned players away
  ("global_bans": false in ReSkateServer.json opts a server out)
- SPECIAL menu page for listed players to hide their own tag and items
- Fix hoodie and board keeping the animated colour after the animation turns off
- Multiplayer protocol 39 -> 40
2026-10-04 22:33:57 -05:00