mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-27 23:07:08 +01:00
A server lists Thunderstore packages in plugins.required ("Owner-Name", or "Owner-Name-1.2.3"
for at least that version, at most 16). It checks each on Thunderstore as it starts and leaves
out one not listed as a plugin, and sends the rest with its map offer (protocol 55).
A joining player's game checks them before the server's map loads. Those missing, switched off
or too old go on one card with the map when that is missing too, so the player is asked once.
GET ALL downloads and installs each, switches on any that were off, runs one live merge when
any brings game data, and joins; CANCEL leaves. The game fetches a package only when
Thunderstore lists it as a plugin and it installs as one (a reskate-plugin.json naming Luau
code), whatever a server asks; anything else is taken out again, so a cosmetic can never be
required.
map_download handles a list of packages instead of one map, and the Thunderstore chooser takes
the category it needs. A map alone keeps its card as it was.
98 lines
6.3 KiB
C++
98 lines
6.3 KiB
C++
#pragma once
|
|
#include "Engine/Game/Multiplayer/session_model.h"
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
namespace dingosdk::multiplayer {
|
|
// Commands and tick execute on the game's client thread. model() is thread-safe.
|
|
std::string command(std::string_view action, std::string_view argument = {}, std::string_view password = {});
|
|
// A dedicated server's vote is running and this player may answer it (queue_command "vote" yes|no).
|
|
bool server_vote_open() noexcept;
|
|
// A dedicated server's poll is running and this player may answer it: how many answers it has
|
|
// (queue_command "vote" 1 to that many). 0: no poll.
|
|
unsigned server_poll_answers() noexcept;
|
|
// Private menu queue: passwords never pass through console history/logging.
|
|
bool queue_command(std::string_view action, std::string_view argument, std::string_view password);
|
|
// Called just before the host's validated native load is submitted, on the client thread.
|
|
void host_map_change(std::string_view destination);
|
|
enum class MapLoadResult { waiting, queued, failed, missing }; // missing: the map is not installed
|
|
// Runs on the client thread; only catalog-registered destinations may be queued.
|
|
using MapLoader = MapLoadResult (*)(std::string_view map, bool submitted, std::string &detail);
|
|
void tick(std::uintptr_t base, std::uintptr_t client, bool gameplay_ready, std::string_view map,
|
|
MapLoader loader = nullptr);
|
|
MultiplayerModel model();
|
|
// The player worked the game's controls just now (overlay.h controls_touched_at). While they do, a
|
|
// dedicated server is told they are at their game (PacketKind::present). Any thread.
|
|
void note_player_input() noexcept;
|
|
std::string take_leave_notice(); // why the session just ended over a missing map, once; client thread
|
|
// What the host or server said as it put the player out of the session (kicked, banned, away too
|
|
// long, a mod or a cheat it does not allow), or refused to let them in for it; once. Client thread.
|
|
std::string take_removed_notice();
|
|
// The same map when the host said which Thunderstore package it is from, so it can be offered
|
|
// for download instead (Extension/Assets/map_download.h): the session does not end. It tells
|
|
// the host the map is being fetched, which is given much longer than a load, and goes on
|
|
// trying the map, so it loads as soon as it is installed. Once per map; client thread.
|
|
// The downloader ends its own attempt (queue_command "stop-map-fetch", with its deadline)
|
|
// when the player says no or the map
|
|
// cannot be had. Should the session end meanwhile for another reason,
|
|
// queue_command("rejoin", "", "") goes back to it, with the password it was joined with.
|
|
struct MapNeed {
|
|
std::string package; // protocol.h: valid_map_package; empty when only plugins are needed
|
|
std::string map; // its name for people
|
|
bool server{}; // a dedicated server's map, not a player's lobby
|
|
bool moved{}; // the session changed to this map with the player in it (else: joining)
|
|
// The version of it installed here, when that is another than the host's: the host's is
|
|
// fetched in its place. Empty: the map is not installed.
|
|
std::string have;
|
|
// Local steady-clock deadline, shared with the downloader, never transmitted.
|
|
std::uint64_t deadline_us{};
|
|
// The plugins a dedicated server needs that this PC does not have ready (protocol.h:
|
|
// valid_required_plugins), asked for on the same card as the map; empty when only the map is.
|
|
std::vector<std::string> plugins;
|
|
};
|
|
std::optional<MapNeed> take_map_need();
|
|
// Whether a session is waiting on a map being fetched right now. Thread-safe.
|
|
bool fetching_map() noexcept;
|
|
std::uint64_t map_fetch_deadline() noexcept;
|
|
// Held for the named package while it is being downloaded, installed or applied: unrelated
|
|
// joins are not held. The session waits as it does for a missing map. A map that is being
|
|
// replaced by another version is one the game already knows, so without this it would load as
|
|
// soon as the new files are in place, before the mods are applied again. Thread-safe.
|
|
void hold_map_load(bool hold, std::string_view package) noexcept;
|
|
// How a host learns the package of the map it is on, to tell its guests: the level's asset in,
|
|
// the package (or nothing) out. Called on the client thread, often; it should be quick.
|
|
using MapPackageLookup = std::string (*)(std::string_view asset);
|
|
// The maps a dedicated server's vote for the next map will offer, as it said about a minute
|
|
// before the vote (protocol.h: next_maps): each one's name in the vote and its Thunderstore
|
|
// package (may be empty). None outside such a server, and after the map changed. Thread-safe;
|
|
// the serial changes whenever the list does.
|
|
struct NextMapWanted {
|
|
std::string name, package;
|
|
};
|
|
std::vector<NextMapWanted> next_maps();
|
|
unsigned next_maps_serial() noexcept;
|
|
void set_map_package_lookup(MapPackageLookup lookup) noexcept;
|
|
// A dedicated server's required plugins (Packet::required_plugins) are checked before its map
|
|
// loads. PluginCheck answers which of them this PC does not have ready: not installed, switched
|
|
// off, or older than the version named. LevelCheck answers whether an enabled mod here has a
|
|
// level (its asset), from a package or not, so a missing map goes on the same card. Both are
|
|
// called on the client thread every half second while joining; they should be quick.
|
|
using PluginCheck = std::vector<std::string> (*)(const std::vector<std::string> &required);
|
|
using LevelCheck = bool (*)(std::string_view asset);
|
|
void set_plugin_check(PluginCheck plugins, LevelCheck levels) noexcept;
|
|
// The session's chat lines for the overlay. Thread-safe and cheap: callable every frame.
|
|
MultiplayerChat chat();
|
|
// A chat line only this player sees, from a plugin: shown under the plugin's name with a
|
|
// "Plugin" tag, so it can't pass for a player or the server. Client thread; "" when shown,
|
|
// otherwise why not (no session).
|
|
std::string plugin_chat(std::string_view plugin, std::string_view text, std::uint32_t text_color = 0);
|
|
// A lobby host's plugins (their server copies): drops a player from the session (they cannot
|
|
// rejoin it), or moves their skater. "" when done, otherwise why not. Client thread.
|
|
std::string host_kick(std::uint64_t player, std::string_view why);
|
|
std::string host_teleport(std::uint64_t player, const std::array<float, 3>& at);
|
|
} // namespace dingosdk::multiplayer
|