#pragma once #include "roles.h" #include "Engine/Game/Multiplayer/distance_settings.h" #include "Engine/Game/Multiplayer/object_placement.h" #include "Engine/Game/Multiplayer/session_model.h" #include "Engine/Game/Multiplayer/tick_settings.h" #include "Engine/Game/Multiplayer/voice_settings.h" #include "Engine/Game/World/park_rotation.h" #include #include #include #include #include #include #include namespace dingosdk::server { // One kind of player vote: whether players may start it, and the share of the votes cast // (percent, 1-100) whose yes passes it. struct VoteSetting { bool enabled{}; unsigned percent = 60; unsigned seconds{}; // how long it runs; 0: VoteSettings::seconds unsigned cooldown{}; // seconds before its starter may start another vote; 0: VoteSettings::cooldown unsigned min_players = 1; // players on before anyone may start it }; // A vote the server's owner defines: "/vote [choice]" runs `command` (any server // command, as the console) when it passes. In the command {map} is the current map and {arg} // the choice the starter picked, one of `choices`; without choices the vote takes no argument. struct CustomVote { std::string name; // 1-16 of a-z 0-9 - _ std::string description; // shown in /help and the "/" menu std::string command; // "map {map}", "noclip {arg}"... std::vector choices; VoteSetting setting{true, 60}; }; struct VoteSettings { VoteSetting map, kick, time{false, 50}; // "/vote extend": the map in play gets maps.rotation_extend_minutes longer. It only runs // while the maps rotate on a timer. VoteSetting extend{true, 50}; std::vector custom; unsigned seconds = 30; // how long a vote runs unsigned cooldown = 60; // seconds before the same player may start another // How long a player kicked by a vote is kept out, in minutes; 0: until the server restarts. // (An admin's kick goes by ServerConfig::kick_minutes.) unsigned kick_minutes = 5; bool starter_votes_yes = true; // whoever starts a vote has voted yes // Polls: questions with up to six answers that run nothing. "off", "admins" or "everyone". std::string polls = "admins"; unsigned poll_seconds = 60; }; inline constexpr std::size_t max_announcements = 32; inline constexpr unsigned max_announcement_interval = 1440; // minutes // Messages the server posts by itself, one every `interval` minutes in turn while players are on. struct Announcements { std::vector messages; unsigned interval{}; // minutes; 0: off }; inline constexpr std::size_t max_custom_commands = 32; inline constexpr std::size_t max_custom_command_runs = 8; // The owner's own chat commands ("/discord"), never listed in /help. Each answers whoever typed // it with `reply`, and/or runs `commands` as the console: {player} is their SteamID64, {map} // the current map, {arg} the rest of what they typed. struct CustomCommand { std::string name; // 1-16 of a-z 0-9 - _ std::string reply; std::vector commands; bool admin{}; // only admins may use it }; // A park built in the Park Editor and shipped as a mod: \\parks\.park.json. // Its objects only fit the map it was built on, so it is bound to a map by name (custom maps // share a base map's key, "bam" for many). It is spawned with "park-mod add " while that // map runs, as objects the server owns. struct ParkMod { std::string name; // 1-16 of a-z 0-9 - _: what commands and votes use std::string folder; // the mod folder in Mods (may contain spaces) std::string map; // the map it belongs to, named as "map" names one std::string key; // which base map's park in the mod (bam, grom...); empty: the only one }; // How much of the parks can be spawned at once: parks, and their objects in all. struct ParkLimits { unsigned parks = 3, objects = 512; }; inline constexpr std::size_t max_park_mods = 32; inline constexpr unsigned max_spawned_parks = 16; // ReSkateServer.json. Every setting an admin or the console changes is saved // back, so a restart keeps it. inline constexpr unsigned dedicated_tps = 20; // The tools the server can take away from single players (lock, throwdown_locks): noclip with // teleporting, No Bail, the boosts, their own physics tuning, and placing objects. "all" is the // first four, as throwdown_locks has always read it: objects are only ever locked by name. inline constexpr std::uint8_t lock_noclip = 1, lock_nobail = 2, lock_boosts = 4, lock_tuning = 8, lock_objects = 16, lock_ollies = 32; inline constexpr std::uint8_t lock_movement = lock_noclip | lock_nobail | lock_boosts | lock_ollies; // what the role's "movement" gives inline constexpr std::uint8_t lock_all = lock_movement | lock_tuning; struct ServerConfig { std::filesystem::path file; std::string name = "ReSkate server"; // The map everyone skates, named like the game's `load` command: "San Vansterdam", // "Isle of Grom", or a custom map in Mods\ such as "bbcity". std::string map = "San Vansterdam"; std::vector map_pool; // maps for votes and the rotation, in order; empty: every map unsigned map_rotation = 0; // minutes per map before the next pool map (0: off) // When the rotation's time is up, players vote for the next map: three of the pool's, any // map of it, or (rotation_extend minutes, 0: not offered) staying on this one for longer. // Off: the next map of the pool, in order. bool rotation_vote = false; unsigned rotation_vote_seconds = 30; // how long that vote runs, 10 to 120 unsigned rotation_extend = 15; // minutes, 0 to 240 // Players are told on a card when the map has 10, 5 and 1 minutes left. bool rotation_announce = true; // The custom maps in Mods that are Thunderstore packages are kept at Thunderstore's newest // version (server_map_update.h), looked up at start and every so many minutes. bool latest_versions = false; unsigned latest_check_minutes = 60; unsigned max_players = 16; // players; the server itself is not one // The most poses a second one player is sent (crowd_limits); 0: no limit. unsigned crowd_budget = crowd_pose_budget; // The most a mod may resize part of a skater for the other players, as a factor (and its // inverse the least). The game's own skater height is a scale as well, so 1 shows every // skater at one height and build; 2, the default, leaves height alone. 0 is no limit. float bone_scale_limit = 2; // How far (metres) a bone of a skater's body or board may be from the one it hangs from // for the other players (limit_bone_reach). 0: no limit. float bone_reach_limit = 1; // How players reach the server: true, through Steam's relay network only; false, straight // to `port` (UDP). A direct server still answers through the relays, for a player the port // does not reach, one who has turned direct connections off, or an older game. bool use_steam_relay = true; // Logs what Steam's networking says it is doing, to find out why connections fail. bool steam_debug = false; // How long a message to a player may wait to share a packet with the next ones, in // milliseconds (0: each goes at once in a packet of its own). Fewer, fuller packets: // less sent for the same updates, and less work sending it. unsigned pack_ms = 10; // How many threads share the sending of each pass, this one included (1: the one thread, // as before 2.0.2). 0: one for each of the machine's processors but one, up to 8. unsigned threads = 0; // Past this many metres a player's fingers are not sent moving (0: always). A skater's // forty finger bones turn in nearly every pose and are half of what a pose carries. unsigned finger_distance = 25; // What the server may send each player, in KB/s (128-16384). unsigned send_rate = 900; // The players with a reserved slot: they can join a full server, as the admins can. std::vector reserved; std::string password; // empty: anyone may join std::string welcome; // sent to each player as they join // The colours of the server's own lines in chat, as "#RRGGBB": its badge and name, and the // text after them. std::string chat_color = "#8E5CFF", chat_text_color = "#D9C8FF"; bool listed = true; // shown in the in-game server browser // A Steam game server login token (steamcommunity.com/dev/managegameservers, app 3354750). // With one the server signs in to its own account and keeps the same Steam ID every start, // which is how the ReSkate team's list of official servers knows it. Empty: anonymous. std::string steam_token; bool auto_update = true; // install new releases when nobody is on // The server window (Server/GUI) starts the server as soon as it opens, without waiting // for Start to be pressed. The console server always starts at once and does not read it. bool autostart{}; bool global_bans = true; // turn away players the ReSkate team has banned (global_bans.h) bool activity_log = true; // console lines for throwdowns, objects and loading bool announce_throwdowns = true; // tell everyone in chat when a throwdown drop is placed // The tools locked for the players of a throwdown while it runs (lock_* bits); 0: none. std::uint8_t throwdown_locks{}; // Players form parties (/party, the game's Social menu): 2-8 players each (the game's Party // panel has eight rows). Off, nobody can be in one. bool parties = true; unsigned party_size = 8; // Minutes a player may be away (not moving, talking, typing or building) before the server // removes them, 1 to 1440; 0: never. Admins are never removed for it. unsigned afk_kick = 0; // Minutes a kicked player (by an admin or by a vote) is kept out, 1 to 1440; 0: until the // server restarts. A kick keeps the time it was given: changing this is for later kicks. unsigned kick_minutes = 0; // A chat message with a word the ReSkate team does not allow at all (word_lists.h) is never // passed on. Its player is warned; after this many warnings, 1 to 10, the next gets them // kicked. 0: nobody is warned or kicked, and the message is still not passed on. unsigned word_warnings = 3; // The ReSkate team's chat word lists hold here. Off: only this server's own words // (server_words.h) are filtered and refused. bool global_words = true; // Players whose game runs fast (a speedhack; Server/speed_check.h): "warn" takes them out of // throwdowns and coop challenges and tells the admins, "kick" also removes them, "off" does not check. std::string speed_check = "warn"; // Players whose skateboard stays on another player as they move (a hacked game; Server/ // board_check.h): "warn" stops showing their board to the others and tells the admins, // "kick" removes them, "off" does not check. std::string board_check = "warn"; // Players whose mods change how tricks score (Engine/Vfs/mod_scoring.h): "warn" takes them out // of throwdowns and coop challenges (the server stops relaying theirs) and tells everyone, // "kick" removes them, "off" does not check. std::string score_check = "warn"; // Scoring fingerprints accepted besides the game's own (a server that runs on a scoring mod // everyone installs). Each player's fingerprint is in the console when they are flagged. std::vector score_allow; std::uint16_t port = 27015, query_port = 27016; // Fixed for dedicated servers for now (dedicated_tps): a busy one's traffic, in and out, // is its players' poses, and at 20 a second that is a third less than at 30. Whatever // the file says is read as this; lobbies keep their own choice. unsigned tps = dedicated_tps; bool voice_chat = true; float voice_range = default_voice_range; MultiplayerDistances distances; // everyone, admins (only the admins below may build) or nobody. ObjectPlacement object_placement = ObjectPlacement::everyone; // Objects each player may have placed (object_placement.h); 0: no limit. Admins are not held to it. unsigned object_limit = default_object_limit; // Players may place objects at another size than their own. Off: every player's objects // are shared at their own size; admins may still resize theirs. bool object_scaling = true; // Players see each other's skater effects: sparks and dust where a skater touches the world, // and the trails and fire of costumes and skateboards. Off: nothing of them is relayed and // players' games show each other without them. bool sync_effects = true; // Whether players may use noclip (and teleport) / No Bail / the boosts (admins always may). bool noclip = true, no_bail = true, boosts = true; // Players skate with the game's own physics tuning, not copies they edited. bool enforce_tuning = true; VoteSettings votes; // all off until the owner turns them on Announcements announcements; std::vector commands; ParkChoices parks{"skatepark_01", "megapark_05", "flumppark_08"}; // Forced on every player while world_layer_sync is on: layer key -> mode. // Needs world-layers.json (the players' catalog) next to the server. bool world_layer_sync{}; std::map layers; std::vector park_mods; ParkLimits park_limits; // Where the park mods' folders are: Mods next to the server. Not part of the file. std::filesystem::path mods; Roles roles; // data/roles.json std::vector bans; }; // Reads `file`, writing a default one first when it does not exist. Settings this version // has that the file lacks (added by an update) are written back with their defaults, and // their names go to `added` ("votes.seconds" for a nested one). ServerConfig load_config(const std::filesystem::path &file, std::vector *added = nullptr); void save_config(const ServerConfig &config); // Where the bans are kept: data/bans.json, beside the config. std::filesystem::path bans_file(const ServerConfig &config); // Where the roles are kept: data/roles.json. A server without the file starts with // default_roles(); the admins an older config listed are moved into its admin role. std::filesystem::path roles_file(const ServerConfig &config); // The roles a file's text holds (throws what is wrong with it), and the text for these. Roles read_roles(std::string_view text); std::string roles_text(const Roles &roles); // How many players may be on beyond max_players: one for each reserved player and each player // whose role has a reserved slot. std::size_t extra_slots(const ServerConfig &config) noexcept; // Whether a player who is not yet on may join a server with `on` players on it. Anyone, until // max_players are on; the reserved players and those roles after that too, in the extra slots // (a full server of 32 shows 33/32 with one of them on). bool may_join(const ServerConfig &config, std::uint64_t id, std::size_t on) noexcept; // "#RRGGBB" (or "RRGGBB") as a colour in the layout the protocol and the overlay use, or nothing. std::optional parse_colour(std::string_view text) noexcept; // Why `config` cannot run, or empty. std::string config_error(const ServerConfig &config); // Why the owner's custom votes cannot run, or empty; and whether a custom vote may be called // `name` (not a word "/vote" already takes: map, kick, tod, yes, poll...). std::string custom_votes_error(const std::vector &votes); // Why the owner's custom chat commands cannot run, or empty; and whether a chat command may be // called `name` (not one players or admins already type: help, party, vote, kick, map...). bool custom_command_name_free(std::string_view name) noexcept; std::string custom_commands_error(const std::vector &commands); bool custom_vote_name_free(std::string_view name) noexcept; // Why the park mods or their limits cannot run, or empty. A folder that is not there is not one: // it is reported when the park is spawned. std::string park_mods_error(const ServerConfig &config); // A mod folder's name as the game's mod list allows it: no path, no leading dot. bool valid_mod_folder(std::string_view folder) noexcept; // A base map's key as park files are named by it: bam, grom, stadium_1... (1-32 of a-z 0-9 _). bool valid_park_key(std::string_view key) noexcept; // A scoring fingerprint as the config and console write it (16 hex digits), and read back // (nothing for text that is not one, or for 0: the game's own scoring needs no entry). std::string scoring_text(std::uint64_t fingerprint); // Lock tools by name: "noclip" (or "teleport"), "nobail", "boosts", "tuning", "all", several // joined by commas. Nothing for an unknown name or an empty list. std::optional parse_lock_tools(std::string_view text); // "noclip, nobail, tuning"; "none" for 0. std::string lock_tools_text(std::uint8_t tools); std::optional parse_scoring(std::string_view text); // Maps. The game always has its root level (DingoLevel_Root) loaded, and a map // is the level loaded into it, named as the game's own `load` command names it: // "San Vansterdam", "Isle of Grom", a custom map's "bbcity"... struct ServerLevel { std::string asset, name; // The Thunderstore package a custom map's mod folder is (protocol.h: map_package_name), from // the folder's name and its manifest.json, so players without the map can be offered it. std::string package; }; // The retail maps, and the custom maps in `mods`\\reskate-levels.json // (players' map mods, copied next to the server). Returns why a mod was skipped. std::vector load_levels(const std::filesystem::path &mods); const std::vector &levels(); // A copy of them, for a thread that is not the server's (the server window). std::vector level_snapshot(); // Like the game's `load`: a level path, a name or short name, or the unique start of one. const ServerLevel *find_level(std::string_view map); // Whether the server has this map (a name, level path or destination): one of the game's own, // or one a mod folder in its Mods lists. It only moves players to a map it has itself. bool installed_map(std::string_view map); // "a.b.c.d" as a number (a the highest byte), or 0 when it is not an IPv4 address. std::uint32_t direct_ipv4(std::string_view text) noexcept; // What players load for a map, as the protocol carries it ("|"). // Empty when the map is unknown (a full level path is always accepted). std::string map_destination(std::string_view map); // The name to store for a map, a level path or a destination (an in-game admin // sends destinations). std::string map_setting(std::string_view map); // A map's name for people: "San Vansterdam". std::string map_label(std::string_view map); // The Thunderstore package that map comes from; empty for the game's own and when unknown. std::string map_package(std::string_view map); std::vector pool_levels(const ServerConfig &config); // known pool maps once each; all when empty bool in_map_pool(const ServerConfig &config, std::string_view map); const ServerLevel *next_pool_map(const ServerConfig &config, std::string_view map); // after `map`; null if no other } // namespace dingosdk::server