Commit Graph
54 Commits
Author SHA1 Message Date
Robin MöllerandClaude Opus 5.5 6909fe3a2e Dedicated server: park mods as server-owned objects
- maps.park_mods binds a Park Editor park mod (Mods/<folder>/parks/<key>.park.json)
  to a name and the map it was built on; maps.park_limits caps the parks and
  objects spawned at once (3 and 512 by default)
- park-mod list | add <name> [<name>...] [minutes] | remove <name> [<name>...] | clear,
  from the console, as /park-mod by admins, and by players through custom votes
- A spawned park is the layout of the server's own SteamID and epoch, sent like a
  player's: games since 1.0.0 already show it, it counts against no player's
  object limit, nobody can move or delete it, and it stays when players leave
- Objects players' games would not take (not Build Kit items, broken values) are
  skipped and logged; a missing or broken park is an error line when spawning
- Optional expiry (1-1440 min) with a warning in chat a minute before; every
  park goes on a map change and none survive a restart
- [parks] log lines name who did it: console, the admin, or "a vote"; a vote's
  refusal reaches chat
- park-mod is reserved for custom commands; README.txt documents the settings,
  commands and a park/unpark vote

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2G4APEkhHgVs59RDL3XWD
2026-10-11 17:45:49 +00:00
AyeZeeBB 1f5fe75b55 Server: teleport command, and the server window builds with roles
eleport <players> <player|x y z>, from the console or for a role with
the command: one player (a name's start or a SteamID64) or up to 64
SteamID64s joined by commas, to a player or a place. Several stand in
tpall's ring round the target; one sent to a place lands on it. Those
not connected or not in the world yet are skipped and named, and each
use is logged. tpall and tphere share the ring code. The name is
reserved, and it is in help, the chat list, README.txt and
README-roles.txt.
The server window merged in #170 did not compile on main. Its admin
card listed the admins a config no longer has: it lists the players
given a role, and gives or takes the admin role with the role command.
The "autostart" setting it reads is added to the config (off unless
set). A second read of Steam's debug lines is removed: they already
come with the direct notes.
Co-Authored-By: Robin Möller <moelrobi@gmail.com>
2026-10-11 11:12:54 -05:00
zeex64 ade2d6ed24 Merge pull request #170 from wxndr/feature/dedicated-server-gui
Server: add native DirectX 12 graphical server GUI & live telemetry
2026-10-11 10:43:08 -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
wxndr 4ea92f0258 Add native DirectX 12 graphical server management GUI & live telemetry (Windows)
### Overview
Hey! This PR adds an optional native DirectX 12 graphical interface to the dedicated server (`ReSkateServer.exe`), making it easier to monitor and manage a server without having to rely solely on the raw terminal.

### What's included:
- **DirectX 12 / ImGui Management GUI**:
  - Live console with color coding and command input.
  - Connected players list with real-time ping, position coordinates (click to copy), and kick/ban controls.
  - Live map switcher and world layer manager.
  - Ban list & admin manager.
  - Real-time tick rate / latency graph and live telemetry table.
  - Server configuration & release update checker in Settings.
- **In-Game Badges & Roles**: Synced with official nametags/role badges (`DEVELOPER`, `CREATOR`, `HOMIE`, etc.) just like in-game HUDs.
- **Headless CLI Support**: Kept full compatibility for headless/VPS setups — running with `--headless`, `--cli`, or `--no-gui` (or on Linux) runs standard terminal mode as usual.
- **Thread Safety & Performance**: The GUI runs on the main Win32/DX12 thread while server host/networking ticks run asynchronously on worker threads so tick rate is never blocked.

Tested on Windows Server 2022 with Release x64 builds against local and Steam SDR sessions. Let me know if you'd like any adjustments!
2026-10-10 18:25:29 -04: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
zeex64 1bd1d3437e Merge pull request #147 from moelrobi/claude/peaceful-bardeen-ode7bj
Server: announcements to one player, card only, and custom chat commands
2026-10-09 23:03:16 -05:00
AyeZeeBB b5230d728c Words not allowed in chat are blocked whatever word_warnings is
A dedicated server with word_warnings set to 0 passed such messages on.
It now never does: at 0 the message is dropped, its player is told it
was not sent, and nobody is warned or kicked. The team's lists are read
on every server for this, including one with use_global_bans off, which
still lets globally banned players in.
2026-10-09 21:59:10 -05:00
AyeZeeBB 70128cf2d7 Chat word lists from the backend, and warnings for words not allowed
The bad-word list now comes from the ReSkate backend's Words page, in
the same /api/v1/steam-ids answer the game and servers already read.
It has two lists: filtered words, and words not allowed in chat at
all. Until the backend has any words the built-in list stays in use;
once it does, its lists replace the built-in one.

Filtered words work as before: masked for players with the chat filter
on, and not allowed in server or lobby names. Words not allowed at all
are filtered the same way, and a chat message with one is not passed
on. Its player is warned each time, and after three warnings the next
one gets them kicked.

Dedicated servers: new players setting word_warnings (default 3, 1 to
10, 0 switches the rule off). The count lasts while the server runs,
so a kicked player who joins again is kicked at their next one. Party
chat and commands are checked too, and each warning and kick is logged
under [words]. The lists are read whenever global bans or word
warnings are on.

Player lobbies: the host's game applies the same rule to its guests,
with three warnings.
2026-10-09 21:54:40 -05:00
Robin Möller 5e9fafd48b Merge branch 'main' into claude/peaceful-bardeen-ode7bj 2026-10-10 01:50:53 +02:00
Robin MöllerandClaude Opus 5.5 9ce56cbdcf Server: announcements to one player, card only; owner-defined chat commands
- announce-to <player> <text>: the announcement card goes to that player only.
- Announcements are no longer posted in chat, only shown as the card; the
  now pointless "card" setting is gone (old files' value is ignored).
- ReSkateServer.json "commands": the owner's own /commands, each with a reply
  and/or server commands run as the console ({player}, {map}, {arg}), an
  optional admin-only flag, and never listed in /help.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NYHTXYAUyibUbfztfcriYB
2026-10-09 22:43:05 +00: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
zeex64 b2b434e622 Merge pull request #143 from moelrobi/claude/loving-goodall-98qb8j
Server votes: custom votes, polls, more vote settings and announcements
2026-10-09 16:06:15 -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 e533dadccb Server: shed stale poses when behind, and send each pass on several threads
A full server (about 120 players) slowed down at random until everyone lagged:
one pass a second, each taking over six seconds, 115% busy, and whole poses
going out by the thousand.

Why it stuck. After any stall the next pass had everything that arrived
meanwhile to pass on, to every player, so it took longer still. By then the
poses each update would have built on had gone stale, so poses went out whole,
ten times the size and more work again. Each pass carried the whole of the
last one's arrivals and the server never caught up.

Shedding. A pass normally reads one or two poses from each player. When more
than three of one player's are waiting, only the newest is passed on. Contact
effects are held to two packets a player a pass. Each pass then has a bounded
amount to do whatever the backlog, so the server catches up; players see the
others at a lower rate for a moment. The log says when it happens (at most
every 30 s) and `net` has a "shed while behind" count.

Threads. Building each player's update of everyone else was 80% of a pass. It
reads the other players and writes only that player's own state, so it is now
done for several players at once (worker_pool.h); the server's own counts and
the marks on whole poses are added up afterwards, and sends into the transport
are made one at a time. New setting network.threads: 0 (default) is one thread
for each processor but one, up to 8; 1 is the single thread as before. The
server says how many it uses when it starts.

Load test. Server/Test/server_load_tests.cpp drives the host with simulated
players over a transport that is not Steam's (built with the multiplayer tests
on Windows, not run by ctest). With 120 players at 20 TPS and 27 MB/s out, the
loop's share of one core went from about 50% on one thread to 26% on four and
22% on eight, and every player was sent byte for byte the same on 1, 2, 4 and
8 threads. A simulated 3 s stall shed about 7,000 poses, took one 169 ms pass
to recover and lost nobody.

The test's transport has none of Steam's send cost, so a real server's loop
sits higher than these figures. Receiving, choosing who is sent which pose and
the sends themselves still run on one thread.
2026-10-09 16:00:02 -05:00
AyeZeeBB f02a39a3f0 Hall of Meat toggle in the overlay; objects placed in a burst are deleted, not held
Overlay. Hall of Meat could only be switched from the game's own menu and the
console. The Insert overlay's skater page now has a "Hall of Meat" switch on
the Movement card, under No Bail. It runs the same "hallofmeat 0|1" command, so
both menus agree and the choice is saved with the profile. It is greyed out
when Hall of Meat did not start.

Server. A player who is not an admin and places more than twice the object
limit plus 100 in a minute (a modified game animating objects by respawning
them) had their objects held as they were for a minute. They are now deleted
for everyone, and nothing that player places is shared for the next minute:
without that their game would upload them again at once. The log names the
player once with their SteamID and how many objects went, and they are told in
chat. After the minute only what they place anew is shared.

The server cannot delete the objects in that one player's own game, so they
still see them there; nobody else does.
2026-10-09 10:30:31 -05:00
AyeZeeBB ccfd05e0df Dedicated servers: everything goes through the server; object burst hold; names for bans by ID 2026-10-09 09:59:45 -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 46cf65df0a Server: remove players who have been away too long
Server: remove players who have been away too long

A dedicated server can remove players who are away from their game.

- "afk_kick_minutes" in the config's "players" section: 1 to 1440, or 0
  (the default) for never. "afk-kick <minutes>|off" from the console or an
  admin; a change is saved and applies at once.
- Away is doing nothing a player at their game does: not moving or turning,
  not speaking, not sending chat, and not placing, moving or removing an
  object.
- A minute before the limit the player is told in chat; at the limit they
  are removed with a message saying why and that they can join again. The
  log has an "[afk]" line.
- Admins are never removed for it. The time starts once a player's game has
  loaded the map, and starts over after a map change and when the setting is
  changed.
2026-10-08 22:25:17 -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
AyeZeeBBandClaude Opus 5.5 29c42911ff Merge pull request #130: server admin commands in their own file; one update loop
Conflicts in Server/server_host.cpp: main changed the command code after this
branch moved it to Server/server_commands.cpp. The moved copy now has what
main added since: the "chat-color" command, "park random", and its line in
the help text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:24:32 -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
AyeZeeBB 0b58163de5 Server: its chat lines in colours the owner chooses; protocol 45
Server: its chat lines in colours the owner chooses; protocol 45

A dedicated server's own lines in chat (its welcome, console messages and
answers) were made violet in #129. The owner can now pick the colours.

- "chat_color": the "Server" badge and name, as "#RRGGBB" (default
  "#8E5CFF", violet).
- "chat_text_color": the text of those lines (default "#D9C8FF", lavender).
- Both are in the config's "server" section; a value that is not a colour
  stops the server from starting and names the setting.
- Console: chat-color <#badge> [<#text>]. Saved, and sent to everyone on
  the server, so the next line uses it.
- With the defaults, chat looks as it did.

The colours travel in the roster, so this is protocol 45: servers and
games must both update. Lobbies hosted by a player are unchanged.
2026-10-08 20:42:51 -05:00
AyeZeeBB f13995dfe8 Fix players freezing on dedicated servers; reserved slots are extra slots; bone scale limit 2
Fix players freezing on dedicated servers; reserved slots are extra slots; bone scale limit 2

Players stopping and jumping (1.1.6):
- The new sound messages begin with the same four bytes ("RMS1") as the
  pose updates games send each other over their direct links. The game
  took those pose updates for sound and discarded them, so a directly
  linked player moved only when a whole state arrived.
- Game: only messages from the dedicated server are read as its sound,
  pose or acknowledgement messages; anything from another player is
  decoded as what it is.
- Server: poses and sound go through the server to every player, whether
  or not two games have linked to each other. This fixes games still on
  1.1.6 without an update, at the cost of more outbound on small servers.

Reserved slots:
- "reserved_players_slots" no longer holds slots back. Anyone joins until
  max_players are on; the listed players and the admins can still join
  after that, each adding one slot beyond the limit (33/32 and so on).
- An admin who is also listed counts once, and the total never exceeds
  what a session can hold.
- The list holds up to 1024 players again. "reserved" in the console shows
  how many extra slots there are.

Server:
- "bone_scale_limit" is 2 by default. The game's own skater height is a
  bone scale, so 1 showed everyone at one height. A config that already
  has 1 written in it keeps it until changed.
2026-10-08 17:12:41 -05:00
AyeZeeBB 2833ab1f88 config in sections
Server config:
- Written in sections, in reading order: server, access, maps, players,
  anti_cheat, network, votes. A config from an older version, flat and
  under older names, is read as it is and written back this way on first
  start; only settings that are really new are reported as new.
- Renamed: no_bail, noclip, boosts, parties, voice_chat to allow_*;
  global_bans to use_global_bans; welcome to welcome_message; speed_check
  to speed_hack; score_check to modified_scoring; score_allow to
  allowed_scoring_mods; map_pool and map_rotation_minutes to maps.pool and
  maps.rotation_minutes. Console and admin commands are unchanged.
- Bans are kept in data/bans.json beside the config. A config that holds
  them has them moved there; a bans file that cannot be read stops the
  server from starting.
- Reserved slots are one list, "reserved_players_slots": a slot is kept
  for each listed player who is not on. "reserved_slots" and the
  "reserved slots <n>" command are gone; admins may take a kept slot but
  none is kept for them.
- "tps" is no longer in the file: dedicated servers run at 20.
- "bone_scale_limit" is 1 by default: resized body parts are off unless a
  server allows them.
- New: "pack_ms" (10), how long Steam may hold a message to pack it with
  others; "finger_distance" (25); "steam_debug", which logs what Steam's
  networking says, for connection faults.
2026-10-08 16:18:29 -05: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 d540b88640 Dedicated servers run at 20 TPS; still players at the low rate; message commands in the "/" menu
Server
- Dedicated servers run at 20 network updates a second, fixed for now:
  whatever tps says in ReSkateServer.json is read as 20, and the tps
  command no longer changes it. A busy server's traffic, in and out, is
  its players' poses, and 20 a second is a third less than 30. Lobbies
  keep their own choice
- A player who has not moved or turned for three seconds is sent on at
  five poses a second; the full rate is back with their first movement
- "net" shows what a pose costs as sent, and what it would cost against
  a reference renewed every second, every quarter second, and against
  the pose before, measured on every fourth pose that arrives. It only
  measures: a nearer reference packs smaller, and this says by how much
  before the server is made to send them that way
Game
- The "/" menu on a dedicated server lists /w for everyone, and /msg,
  /msg-party and /msg-admins for admins. The server already answered
  them; the menu is the game's own list and never had them
2026-10-07 15:27:05 -05:00
AyeZeeBB 737c1611af Server: only change to a map the server has
- The map command, from the console or an admin, refuses a map the
  server does not have: the game's own maps are always allowed, a
  custom one only when its mod folder is in the server's Mods. It
  accepted any level path before, so a server could move everyone to a
  map it had no mod for
- A map setting in ReSkateServer.json that names a map the server does
  not have now stops the server at start, saying to add the map's mod
  or change the map
- The map change itself checks too, whatever asks for it. Votes,
  rotation and the map pool were already limited to known maps
2026-10-07 15:16:53 -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
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
AyeZeeBB dcc5e31a67 Update server_host.cpp 2026-10-05 18:57:42 -05: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
Robin MöllerandClaude Sonnet 5.5 3b3dbf8cd7 Add server direct messages: msg, msg-party, msg-admins and /w
Admins and the console can DM one player, a player's party or all online
admins. Players can whisper with /w. Messages carry a [DM from ...] prefix.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhQ3AZZ7KQddTqV67tZMLc
2026-10-05 21:19:38 +00: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
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
xThrasherrr b643f76c2f Let server hosts choose the map pool and rotate through it 2026-10-05 14:40:25 -06:00
ComputerKWasTaken 4adeee310e Merge remote-tracking branch 'origin/main' into codex/random-community-parks 2026-10-05 15:29:00 -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
AyeZeeBB ecb083f417 Don't drop every player after the server is held up 2026-10-04 17:56:24 -05:00
ComputerKWasTaken 13a64c2e91 Add random community park loading and launch randomization 2026-10-04 16:40:57 -05:00
AyeZeeBB 3fddd1433a Trainer: quiet by default, host's setup for guests; drop the out-of-view cut
Follow-ups to the trainer (PR 37), and one multiplayer change.

Off until asked for (Extension/Trainer)
- The jump read-out and the LB+RB controller shortcuts start off. Jumps
  are logged only while the read-out or telemetry recording is on, and
  trick launches only when the trainer scaled them. A saved trainer.json
  keeps what the player chose.

Memory search on demand (trainer.cpp, trainer_classes.cpp)
- The search for the game's tuning classes reads all writable memory.
  It ran for every player after a level loaded, and up to seven times
  when it found nothing. It now runs only when a class value or the flip
  speed is not the game's own (the player's, or a host's they skate
  with) or the EVERYTHING list is open, and at most three times a level.
- For those players it runs again on each level, which brings fresh
  copies; a search from an earlier level used to count as done.

Guests under enforced physics
- A guest is locked by the session's enforce setting, from the moment
  of joining (Engine/Game/Multiplayer/session_physics.h), not by whether
  the enforcement status text was set yet.
- A guest's class values no longer come back after a map change.

The host's whole setup reaches its guests (protocol 39)
- Only Gameplay/SkatePhysicsTuning travelled, so a host could push,
  sprint or jump further than the guests it held to the game's own.
- New packet physics_extras (30): the host's class values, trick
  multipliers and auto push (trainer_session.h), sent when they change
  and to players who join later, taken from the host only. A dedicated
  server's are the game's own and it ignores the packet.
- Under enforcement a guest's own trick multipliers stand down for the
  host's, whatever the boosts switch says. With enforcement off nothing
  changes.
- A guest refuses bytes that are not an encoding, holds values to the
  trainer's own ranges, and leaves out class values written for another
  game build's table.
- protocol_version 38 -> 39: released versions disconnect from a peer
  that sends a packet they cannot decode.

Out-of-view players are posed every frame again (Session/session.cpp)
- Players outside the camera's view and past 8 m were sampled at 20 Hz
  (b763631). The game's replays record each skater as it stood every
  frame and their camera looks anywhere, so those players moved at
  20 Hz in replays. Removed; the distance rule (20 Hz past 200 m, 10 Hz
  past 350 m) stays. This gives back the client-tick time it saved on
  busy servers.
2026-10-04 14:19:30 -05:00
AyeZeeBB a7d95057ac Harden multiplayer against disruptive and spoofed traffic
Cap outfit packets and stop dropping recipients when a relay cannot be
built. Add per-player relay limits for outfits, skater sound and voice,
and a back-off for Steam IDs that keep failing to join.

Throwdowns: only participants can write rows, turns and scores; a copy
that fails to spawn no longer blocks the others.

Throttle remote skater and skateboard re-creation, cap remote objects
per session, and show developer and friend marks only for identities
Steam vouches for. Route all connections through Steam's relays.

Dedicated server: validate map changes, clean player names, keep vote
cooldowns across reconnects, and cut ban labels on a character boundary.
2026-10-04 13:49:11 -05:00