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);