From 6c45e9666dcdea6577248f6ab7f3111de48b6039 Mon Sep 17 00:00:00 2001 From: AyeZeeBB Date: Tue, 13 Oct 2026 18:45:37 -0500 Subject: [PATCH] World layers: a server's catalog must be the supported build's, and a player is not put out over one that is not 2.0.8's dedicated server was released with a world-layers.json scanned from a game Steam had updated: 263 layers, where every player's game has 261. A server that shares its layers (world_layer_sync) sends one choice for each, the player's game turned the message down as unreadable, and nobody could join such a server ("Join code, sender identity, or multiplayer protocol did not match", and "Disconnected." in the server's log). - supported_build::world_layer_count (261): how many layers the supported build's levels have. - Server: a world-layers.json of another count is not used; the log says why and layer sync stays off, where before every player was turned away. - --export-world-layers refuses a game folder whose count differs, so a release cannot be built from an updated game again. - Player's game: a layer list of another length is left unread and every layer stays as the player has it; the rest of the message is taken and they join. --- Engine/Game/Build/supported_build.h | 7 +++++++ Extension/Multiplayer/Net/protocol.cpp | 13 +++++++++---- Server/main.cpp | 16 +++++++++++++++- Server/server_engine.cpp | 8 +++++++- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/Engine/Game/Build/supported_build.h b/Engine/Game/Build/supported_build.h index 88c658d2..56b15ba5 100644 --- a/Engine/Game/Build/supported_build.h +++ b/Engine/Game/Build/supported_build.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -27,6 +28,12 @@ inline constexpr std::string_view steam_build_id = "25414733"; // what mods made for it carry too. Data of another number beside a supported Skate.exe is a // game that was updated or copied over in part, and has to be verified. inline constexpr std::string_view game_data_changelist = "29544144"; +// How many world layers this build's levels have: the catalog players scan from their game and +// a dedicated server reads from world-layers.json. A session's layers are sent as one choice for +// each, in the catalog's order, so both ends must have the same catalog. (2.0.8's server was +// released with the catalog of a game Steam had updated, 263 layers, and no player could join a +// server that shared its layers: 2026-10-13.) +inline constexpr std::size_t world_layer_count = 261; // The live game's HTTP content cache for this build (catalogue chunks and CDN // images), published as a ReSkateCache release and installed by the launcher. inline constexpr std::wstring_view content_cache_url = diff --git a/Extension/Multiplayer/Net/protocol.cpp b/Extension/Multiplayer/Net/protocol.cpp index 1861d45f..c1f68070 100644 --- a/Extension/Multiplayer/Net/protocol.cpp +++ b/Extension/Multiplayer/Net/protocol.cpp @@ -1181,13 +1181,18 @@ std::optional decode(std::span bytes) noexcept { if (forced > 1) return {}; p.force_world_layers = forced != 0; const auto layer_count = r.integer(2); - if (layer_count != world_layers().size() && (layer_count || p.force_world_layers)) return {}; - p.layers.resize(world_layers().size()); + // A list that is not one choice for each of this game's layers is of another catalog + // (a server whose world-layers.json is of another version of the game): which layer + // each choice is for cannot be told, so none is taken and every layer stays as the + // player has it. Turning the message down put the player out of the session as they + // joined, with nothing to say why (2.0.8's servers, 2026-10-13). + const bool same_catalog = layer_count == world_layers().size(); + if (!same_catalog) p.force_world_layers = false; + p.layers.assign(world_layers().size(), 0); for (std::uint64_t i = 0; i < layer_count; ++i) { - auto &choice = p.layers[static_cast(i)]; const auto mode = r.integer(1); if (mode >= world_layer_modes.size()) return {}; - choice = static_cast(mode); + if (same_catalog) p.layers[static_cast(i)] = static_cast(mode); } const auto voice_allowed = r.integer(1); p.voice_policy.revision = static_cast(r.integer(4)); diff --git a/Server/main.cpp b/Server/main.cpp index c575e4e2..13e78ae6 100644 --- a/Server/main.cpp +++ b/Server/main.cpp @@ -2,6 +2,7 @@ // in-game server browser. Runs from its own folder, next to steam_api64.dll and // the Steam client files (steamclient64.dll, tier0_s64.dll, vstdlib_s64.dll). // On Linux the Steam files are libsteam_api.so and steamclient.so. +#include "Engine/Game/Build/supported_build.h" #include "global_bans.h" #include "server_map_update.h" #include "server_config.h" @@ -276,6 +277,10 @@ int run(int argc, wchar_t **argv, bool skip_update) { try { const auto catalog = world_layer_scan::scan(argv[2]); if (catalog.layers.empty()) throw std::runtime_error("no world layers found; is that the Skate folder?"); + if (catalog.layers.size() != supported_build::world_layer_count) + throw std::runtime_error("that game has " + std::to_string(catalog.layers.size()) + " world layers, and the build ReSkate supports has " + + std::to_string(supported_build::world_layer_count) + + ": it is another version of skate. (one Steam has updated?). Give the folder ReSkate's own game is in"); std::ofstream out(argv[3], std::ios::binary | std::ios::trunc); out << world_layer_scan::to_json(catalog, "server"); if (!out) throw std::runtime_error("cannot write the file"); @@ -303,6 +308,10 @@ int run(int argc, char **argv, bool skip_update) { try { const auto catalog = world_layer_scan::scan(std::filesystem::path(argv[2])); if (catalog.layers.empty()) throw std::runtime_error("no world layers found; is that the Skate folder?"); + if (catalog.layers.size() != supported_build::world_layer_count) + throw std::runtime_error("that game has " + std::to_string(catalog.layers.size()) + " world layers, and the build ReSkate supports has " + + std::to_string(supported_build::world_layer_count) + + ": it is another version of skate. (one Steam has updated?). Give the folder ReSkate's own game is in"); std::ofstream out(std::filesystem::path(argv[3]), std::ios::binary | std::ios::trunc); out << world_layer_scan::to_json(catalog, "server"); if (!out) throw std::runtime_error("cannot write the file"); @@ -383,7 +392,12 @@ int run(int argc, char **argv, bool skip_update) { // World layers are optional: without the players' catalog every player keeps their own. if (const auto catalog = here / "world-layers.json"; std::filesystem::exists(catalog)) { try { - install_world_layer_catalog(world_layer_scan::read(catalog)); + auto layers = world_layer_scan::read(catalog); + // (Players would turn down every message that carries the layers, and could not join.) + if (layers.layers.size() != supported_build::world_layer_count) + throw std::runtime_error("it has " + std::to_string(layers.layers.size()) + " layers and players' games have " + + std::to_string(supported_build::world_layer_count) + ": it is of another version of skate. Take the one from this server's release"); + install_world_layer_catalog(std::move(layers)); write_log("World layers: " + std::to_string(world_layers().size()) + " from world-layers.json."); } catch (const std::exception &e) { write_log(std::string("world-layers.json is unreadable; world layer sync is off: ") + e.what()); diff --git a/Server/server_engine.cpp b/Server/server_engine.cpp index 25a08590..a95fc4c0 100644 --- a/Server/server_engine.cpp +++ b/Server/server_engine.cpp @@ -1,3 +1,4 @@ +#include "Engine/Game/Build/supported_build.h" #include "server_engine.h" #include "global_bans.h" #include "server_map_update.h" @@ -198,7 +199,12 @@ bool ServerEngine::load_environment(const std::filesystem::path& folder, const s if (const auto catalog = folder_ / "world-layers.json"; std::filesystem::exists(catalog)) { try { - install_world_layer_catalog(world_layer_scan::read(catalog)); + auto layers = world_layer_scan::read(catalog); + // (Players would turn down every message that carries the layers, and could not join.) + if (layers.layers.size() != supported_build::world_layer_count) + throw std::runtime_error("it has " + std::to_string(layers.layers.size()) + " layers and players' games have " + + std::to_string(supported_build::world_layer_count) + ": it is of another version of skate. Take the one from this server's release; world layer sync is off"); + install_world_layer_catalog(std::move(layers)); log("World layers: " + std::to_string(world_layers().size()) + " from world-layers.json."); } catch (const std::exception &e) { log(std::string("world-layers.json error: ") + e.what(), LogLevel::warning);