#pragma once #include "Engine/Game/Multiplayer/chat_rate.h" #include "session.h" #include "Extension/Multiplayer/developer_identity.h" #include "Extension/Multiplayer/word_lists.h" #include "Engine/Core/Text/word_filter.h" #include "Extension/Customization/developer_hoodie.h" #include "Extension/Customization/developer_board.h" #include "Extension/Multiplayer/Remote/native_skater.h" #include "Extension/Multiplayer/Voice/voice_chat.h" #include "Extension/Multiplayer/Steam/steam_transport.h" #include "Extension/Throwdowns/throwdown_relay.h" #include "Extension/Multiplayer/Steam/steam_lobbies.h" #include "Extension/Multiplayer/Steam/steam_server_browser.h" #include "room.h" #include "party_book.h" #include "Extension/Multiplayer/Net/delta_codec.h" #include "Extension/Multiplayer/Net/pose_batch.h" #include "Extension/Multiplayer/Net/sound_codec.h" #include "password.h" #include "Engine/Game/World/world_names.h" #include "client_timing.h" #include "monotonic_clock.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Session state shared by session.cpp and the session_*.cpp files. namespace dingosdk::multiplayer::session_detail { enum class Mode { off, host, join, echo }; // Flood control for throwdown messages (chat has ChatRate, Engine/Game/Multiplayer/chat_rate.h). struct ChatBudget { std::uint64_t since{}; unsigned messages{}; bool accept(std::uint64_t now, unsigned burst = 6) noexcept { if (now < since || now - since >= 5000000) { since = now; messages = 0; } return ++messages <= burst; } }; // Throwdown messages per sender per 5 s: offers, joins, starts and ~4 scores a second. inline constexpr unsigned throwdown_burst = 60; // Plugin messages (Extension/Plugins/plugin_wire.h) ride the same packets on a budget of their own: // a plugin may send a few a second for a whole session (a drone's place, a game mode's state). inline constexpr unsigned plugin_burst = 200; // The party the echo test shows its mirrored player in (a real lobby has none until its // players form one). inline constexpr std::uint32_t lobby_party = 1; struct Peer { // Guests: this owner's layout as relayed by the host. Host: the owner's own // upload, which is never forwarded directly. The host publishes `shared` // instead, under its own revisions, so it alone decides what others see. ObjectState objects, shared; std::uint64_t shared_from{}; // Host: object IDs removed by "delete all guest objects". Re-uploads of // them stay hidden; newly placed objects get fresh IDs and sync normally. std::set cleared; struct ObjectDelivery { std::map> sent; std::vector chunks; std::uint64_t source{}, epoch{}; std::size_t next{}, cursor{}; } object_delivery; struct PendingCosmetics { Packet packet; std::uint64_t received{}; }; std::vector pending_cosmetics; Member member; DeltaSender sender; DeltaReceiver receiver; std::uint64_t password_challenge{}; bool handshaken{}, render_failed{}, visible{}, direct_ready{}, map_authorized{}; std::uint64_t last_map_offer{}; bool world_ready = true; std::uint64_t travel_since{}; std::uint64_t fetching_since{}; // a guest's: when they first said they are fetching the map (0: never, this world) std::uint32_t ready_sequence{}; std::uint64_t next_dial{}, last_direct_hello{}, last_direct_pose{}, route_reported{}; std::uint32_t route_sequence{}; std::vector direct_routes; std::uint64_t pose_arrival{}, pose_count{}, rate_at{}, rate_count{}; float pose_hz{}; std::uint64_t native_rate_count{}, board_rate_count{}; float native_pose_hz{}, native_board_hz{}; NativeAnimationStats animation_rate_base; float native_animation_hz{}, native_animation_ms{}, pose_apply_ms{}; std::uint32_t received_pose_interval = 50000; std::optional latest_root; bool player_collision{}; // the player has the game's party collision on (from their poses) // One entry per pose source relayed or sent to this recipient (at most max_players). std::vector pose_delivery; std::uint64_t connected_at{}, last_packet{}, last_cosmetic_apply{}; // A skater removed for lack of poses is not spawned again before this: a spawn is a heavy // native pass, and a player's poses may stop and start again and again. std::uint64_t next_spawn{}; // Near enough to be shown as a skater; and whether a pose has placed them yet. bool shown_wanted = true, placed{}; ReceiveBudget budget; PoseBuffer poses; Pose render_pose; // Far from the local skater and the camera, render_pose is sampled every far_interval // (0: every frame) and the skater keeps it in between. std::uint64_t far_interval{}, next_far_sample{}; AppearanceBuffer appearance; // Bumped per accepted outfit; render compares it with the one its actor wears // (0 after a spawn) to spread native recipe applies over frames. std::uint64_t cosmetic_revision{}, applied_cosmetics{}; DeveloperHoodieState developer_hoodie; DeveloperBoardState developer_board; AudioBuffer audio; std::uint32_t voice_sequence{}; bool received_voice{}; VoiceBudget voice_budget; OutfitBudget outfit_budget; SoundBudget sound_budget; EffectBudget effect_budget; // Their skater's contacts with the world (effects.h), each with when to show it: as late // as their poses are shown, so the sparks are where their skater is. std::deque> impacts; ChatRate chat_rate; // Dots above this player until this time (0: they stopped). typing_notice is the last // accepted "still typing", so a flood is not relayed. std::uint64_t typing_until{}, typing_notice{}; // Chat proofs (session_receive.cpp): the copies of their lines this player sent us over // their own Steam connection, and the lines the host passed on as theirs that wait for one. struct ChatProof { std::uint32_t sequence{}; std::string text; std::uint64_t at{}; }; std::vector chat_proofs, chat_waiting; ChatBudget throwdown_budget, party_budget, plugin_budget; // Host: how the guest's mods change trick scoring, as they reported it (Engine/Vfs/mod_scoring.h): // nothing until the report arrives, 0 for the game's own. member.scoring is the verdict. std::optional scoring; std::string scoring_mods; ChatBudget scoring_budget; std::optional presented_audio; std::uint64_t next_audio_update{}, next_ui_update{}; bool ui_visible{}; std::vector cosmetic_packet; std::string native_status, cosmetic_status; }; struct PrivateRequest { std::string action, argument, password; std::uint64_t queued{}; ~PrivateRequest() { erase_password(password); } }; struct Session { VoiceChat voice; VoiceSettings voice_settings; VoicePolicy voice_policy; unsigned tps = multiplayer_default_tps; ObjectState local_objects; std::uint64_t next_object_update{}; ClientTiming client_timing; std::uint64_t next_publish{}, last_client_log{}, next_party_update{}; MultiplayerDistances distances; ObjectPlacement object_placement = ObjectPlacement::everyone; unsigned object_limit{}; // objects each player may have placed; 0: no limit bool object_scaling{true}; // the dedicated server lets players resize what they place bool sync_effects{true}; // the dedicated server has players see each other's skater effects // What guests may use: the host's choice, or the host's roster for a guest. bool guest_noclip = true, guest_no_bail = true, guest_boosts = true; bool guest_ollies = true; // a dedicated server's lock only: a lobby host has no such choice // Host: guests skate with its physics tuning. Guest: the host's roster says so (a // dedicated server's guests skate with the game's own). bool enforce_tuning = true; // Host: players whose mods change trick scoring (itself included) are kept out of linked // throwdowns and coop challenges. Guest: the report last sent to the host. Both: whether the // roster flags the local player. bool score_check = true; std::optional> scoring_sent; bool local_scoring{}; std::uint64_t next_scoring_check{}; // The local skater has the game's party collision on; sent with its poses. bool local_player_collision{}; // Guest: the host's physics tuning differences, once it sent them. std::optional> host_tuning; // Host: the differences last sent, the packet that carried them (for players who join // later) and when to look at its tuning again. std::optional> sent_tuning; std::vector tuning_packet; std::uint64_t next_tuning_check{}; // The physics the tuning does not carry (Engine/Game/Multiplayer/session_physics.h). // Guest: the host's, once it sent them. Host: which of its own were last sent, the packet // that carried them (for players who join later) and when to look again. std::optional> host_extras; std::uint64_t sent_extras{}; std::vector extras_packet; std::uint64_t next_extras_check{}; std::uint8_t server_votes{}; // guest of a dedicated server: the votes it runs std::uint8_t server_polls{}; // ... who may start a poll there (ServerPolls) std::vector server_custom_votes; // ... and its owner's own votes // Host: bumped per "delete all guest objects". Guest: the last value seen // (unset until the first roster) and whether a local wipe is outstanding. std::optional object_clears; bool clear_pending{}; bool force_world_layers{}; // Players the host kicked. They cannot reconnect until the session ends. std::set banned; // Warnings each guest of this lobby has had for words that are not allowed at all (word_lists.h). std::map word_warnings; // Host: Steam IDs whose attempts to join keep failing wait longer each time (room.h). JoinBackoff join_backoff; // Players banned for good (every session this PC hosts), from the local profile. std::vector bans; bool bans_loaded{}; // Sorted IDs of `bans` for the per-frame connection check; rebuilt when marked dirty. std::vector ban_ids; bool ban_ids_dirty = true; WorldLayerChoices layers{default_world_layers()}; ParkChoices parks; std::uint64_t next_park_update{}; std::optional password; std::string lobby_password, lobby_name; std::mutex request_mutex; std::deque> requests; SteamTransport transport; SteamLobbies lobbies{make_steam_lobby_api(), [](std::uint64_t host) { return banned_host(host) || reskate_banned(host); }}; SteamServerBrowser servers; // When the lobby and server lists were last searched. Every search in the game, asked for // or automatic, waits multiplayer_browse_interval_us after it (take_browse). std::uint64_t browsed{}; // Guest of a dedicated server: whether the roster lists us as an admin, // and the server's voice range from it. // On a dedicated server: what the player's role gives them (its permissions packet), and the // tag it wears (the roster's). server_admin: it has at least one of the server's commands. bool server_admin{}; std::string role, own_tag; std::uint32_t own_tag_color{}; bool role_all_commands{}; std::vector role_commands; std::uint32_t role_permissions{}; float roster_voice_range = default_voice_range; // The colours of the dedicated server's own chat lines, as its roster gives them. std::uint32_t server_chat_badge = default_server_chat_badge, server_chat_text = default_server_chat_text; // The dedicated server's vote as its roster last gave it, when it ends by this game's clock, // and what this player answered in it (0 nothing yet, 1 yes, 2 no; in a poll 1 + the answer). ServerVote vote; std::uint64_t vote_ends{}; std::uint8_t vote_mine{}; // The dedicated server's announcement as its roster last gave it, and when it goes by this game's clock. ServerAnnouncement announcement; std::uint64_t announcement_ends{}; // The server's ban list, as sent to us while we are one of its admins. std::vector server_bans; std::uint32_t server_ban_total{}; std::vector server_maps; // level assets the dedicated server allows std::vector server_map_pool; // admins: the pool's assets in rotation order (empty: every map) unsigned server_map_rotation{}; // minutes per map (0: off) std::string map_label; // the host's name for join_destination (may be empty) std::string map_package; // the Thunderstore package it is from (may be empty) // The map is not installed and is being fetched (take_map_need): since when, 0 for not. // The host is told with what is sent while waiting (Packet::map_fetching). std::uint64_t fetching_since{}; std::uint64_t map_fetch_deadline{}; std::optional map_need; // take_map_need() // The session a missing map ended, for "rejoin": who hosted it and the password key it was joined with. struct Rejoin { Invite invite; std::optional password; std::uint64_t deadline_us{}; }; std::optional rejoin; std::string leave_notice; // take_leave_notice() std::string removed_notice; // take_removed_notice() std::uint64_t joined_public_lobby{}; std::array peers; // Players take the lowest free slots, so every one sits below this mark // (note_slot raises it, trim_slots lowers it). Loops stop here, not at max_players. std::size_t used_slots{}; bool public_host{}, gameplay_ready{}, started_map{}, roster_dirty{}; bool awaiting_map{}, join_map_authorized{}, map_load_submitted{}; std::uint64_t join_started{}, last_map_request{}, last_map_load_check{}; std::string join_destination; std::uint64_t world = 1, travel_started{}, last_world_state{}, last_world_ready{}; std::uint32_t world_state_sequence{}; bool travelling{}, host_world_ready = true; unsigned capacity = max_players; // Local display preferences, loaded once from the profile. Never sent to peers. bool nametags = true, chat_visible = true, display_preferences_loaded{}; float nametag_distance = 120.f; // names within this many metres, dots past it // Only players within this many metres have a skater (session.cpp); checked a few times a second. float player_distance = 120.f; bool prefer_direct = true; // straight to servers that offer it, not through the relays // Poses from a dedicated server (pose_batch.h): each player's stream as this game holds it, // and which of the server's messages were read in full, which it is told once a tick. std::unordered_map pose_streams; pose_batch::Ack pose_ack; bool pose_ack_due{}; // This game's own poses go to a dedicated server the same way: what was sent and what the // server acked, and the last poses sent, to build the next on. pose_batch::Sender pose_upload; std::deque own_poses; // Skaters' sound to and from a dedicated server (sound_codec.h). std::unordered_map sound_streams; sound_codec::Sender sound_upload; // pose-dump: this player's own poses, as encoded for sending, written to a file for a // while. Real poses to measure encodings against (tools/research). std::ofstream pose_dump; std::uint64_t pose_dump_until{}; std::size_t pose_dump_count{}; std::uint64_t next_shown_rank{}; bool nametag_dots = true, nametags_friends{}; bool chat_filter = true; // bad words in chat show as **** (Engine/Core/Text/word_filter.h) // Chat bubbles above each skater's head (Hud/custom_nametags.h, nametag_overlay.cpp). bool chat_bubbles = true, chat_bubbles_own{}; // Grey dots above this skater until this time. The overlay sets it while the chat // input is changing, and clears it after three seconds idle or when the box closes. std::uint64_t local_typing_until{}; float chat_bubbles_distance = 40.f, chat_bubbles_duration = 5.f; int chat_bubbles_history = 3; bool game_menu{}; // a game menu is up or the game's UI is hidden: no chat on screen bool hooks_prepared{}; // the remote-player hooks were installed (once per process) // Host settings remembered between sessions and game restarts. struct HostPreferences { bool loaded{}, public_lobby{true}, password_required{}, world_layer_sync{}; unsigned capacity = max_players, tps = multiplayer_default_tps; std::string lobby_name; MultiplayerDistances distances; ObjectPlacement placement = ObjectPlacement::everyone; unsigned object_limit = default_object_limit; float voice_range = default_voice_range; bool guest_noclip = true, guest_no_bail = true, guest_boosts = true; bool enforce_tuning = true; bool score_check = true; } host_preferences; // Host: how far proximity voice is forwarded at all (listeners fade it by their own distance). float voice_range = default_voice_range; DirectUploadBudget direct_upload; Mode mode = Mode::off; std::uintptr_t base{}, context{}, parent{}; std::uint64_t secret{}, epoch{}, map{}, host_id{}, next_send{}, last_hello{}, last_roster{}, last_cosmetic_capture{}, last_routes{}, last_network_log{}, network_now{}; std::uint32_t sequence{}, roster_sequence{}; // Whose placed objects are solid for this player (the rest are ghosts): see objects_solid(). std::map objects_solid; std::uint64_t local_pose_count{}, logged_local_pose_count{}; std::size_t last_skater_bones{}, last_board_bones{}; std::optional sent_appearance; std::optional local_root; std::vector cosmetic_packet; std::string available_map, cosmetic_capture_status, map_name, invite, status = "Multiplayer is off.", native_status; MultiplayerModel view; // Text chat: the log lives on the client thread; chat_view is its copy for // the overlay, guarded by `mutex` like `view`. std::deque chat; std::uint64_t chat_sequence{}; ChatRate local_chat_rate; MultiplayerChat chat_view; std::optional chat_signature; // of everything chat_view shows (publish_chat) // Masked names and text of the lines in `chat`, by sequence, while the filter is on. std::map> chat_masked; // Steam friends as sorted IDs, from the social snapshot revision they were read from. std::optional friend_revision; std::vector friend_ids; // What the throwdown relay reads each tick, kept between ticks: its player list is // rebuilt when the admitted players change, and names are refreshed once a second. ThrowdownRelayInput throwdown_input; std::uint64_t throwdown_names_at{}; // The remote object owners (id, epoch, revision) and map last handed to the native // runtime; unchanged ones skip the rebuild (session_send.cpp, sync_objects). std::vector> object_owners_sent; std::string object_map_sent; bool object_owners_valid{}; std::uint64_t object_owners_at{}; // Throwdown messages received since the last relay tick: sender, encoded message. std::vector>> throwdown_inbox; // Parties (session_party.cpp): the local player's own roster entry, and the invites the // host passed on (newest last). Whoever hosts owns the parties: a dedicated server, or in // a lobby the host's game, in `parties`. Nobody is in one until they form it. PartyBook parties; std::uint64_t parties_revision{}; // the book's revision the last roster was built from std::uint32_t local_party{}; bool local_party_leader{}, local_party_open{}; bool local_speeding{}; // the dedicated server flagged our game speed: no linked activities struct PartyInvite { std::uint64_t from{}, received{}; }; std::vector party_invites; std::uint64_t party_revision{}; // bumped when any party membership or invite changes std::mutex mutex; }; std::uint64_t nonce(); Session &session(); // The slots below used_slots: every one that may hold a player. inline std::span active_peers(Session &s) { return {s.peers.data(), s.used_slots}; } inline std::span active_peers(const Session &s) { return {s.peers.data(), s.used_slots}; } inline void note_slot(Session &s, std::size_t slot) { s.used_slots = std::max(s.used_slots, slot + 1); } // Lowers used_slots past free trailing slots. Freed slots were reset by reset_peer. inline void trim_slots(Session &s) { while (s.used_slots && !s.peers[s.used_slots - 1].member.id) --s.used_slots; } // Runs fn(peer) for each slot below used_slots with that slot selected for the native adapters. template void each_active_peer(Session &s, F &&fn) { for (std::size_t slot = 0; slot < s.used_slots; ++slot) { const PeerScope scope(slot); fn(s.peers[slot]); } } // A guest whose host is a dedicated server (a Steam game server, not a player). inline bool dedicated_host(const Session &s) { return s.mode == Mode::join && game_server_steam_id(s.host_id); } // Whether Steam itself vouches for this player's identity to this PC: a host's guests and a // guest's host are connected directly, and so is another guest once the direct handshake is // done. Anyone else is known only from the host's roster, which a host can fill as it likes, // so what rests on who a player is (the developer and friend marks) waits for this. An // official server is the exception: Steam vouches for it to this PC and for each player to // it, and it is ours, so its roster is taken at its word. On a busy one a player is // connected directly to only the few nearest, and the rest would show no tag. inline bool steam_vouched(const Session &s, const Peer &peer) { return s.mode != Mode::join || peer.member.id == s.host_id || peer.direct_ready || official_server(s.host_id); } // Whether a player shows the tag the backend gives them, and whether the items that come with // it animate: not until their appearance has arrived, and not when it says they turned that // off (Appearance::hide_tag, Appearance::hide_items). inline bool shows_tag(const Peer &peer) { const auto &look = peer.appearance.value(); return look && !look->hide_tag; } inline bool shows_items(const Peer &peer) { const auto &look = peer.appearance.value(); return look && !look->hide_items; } // How a player has each of their marked cosmetics animate: what their appearance says. inline const MarkStyles &mark_styles(const Peer &peer) { static const MarkStyles standard{}; const auto &look = peer.appearance.value(); return look ? look->marks : standard; } Peer *find_peer(Session &s, std::uint64_t id); unsigned player_count(const Session &s); void reset_peer(Session &s, std::size_t slot); void stop(Session &s, std::string reason); void clear_world(Session &s, std::uint64_t now); bool world_playing(const Session &s, const NativeFrame &local); // session_view.cpp void load_host_preferences(Session &s); void save_host_preferences(const Session &s); void publish(Session &s, const NativeFrame *local = nullptr); void publish_chat(Session &s); void load_bans(Session &s); void save_bans(const Session &s); bool is_banned(Session &s, std::uint64_t id); // Re-reads friend_ids when the Steam social snapshot has changed. void refresh_friends(Session &s); // On a dedicated server games do not send each other anything: all of it goes through the // server, which holds it to its rules. The one thing a server cannot do for a game is prove // who another player is, which the marks rest on (steam_vouched). So two games still link, // for that proof alone, when one of the two players has a mark the other would show: either is // on one of the backend's lists, or they are Steam friends. Both games decide the same. bool identity_link(Session &s, std::uint64_t other); // `marks`: whether the line may show the badge the backend gives its sender (player_role). void add_chat(Session &s, std::uint64_t sender, std::string name, std::string text, bool local = false, bool marks = true); // The colour and badge of one of the backend's categories. std::pair mark_role(IdentityList list); // A player's role colour and badge ("Dev", "ReSkate Admin", "Content Creator", "Centrix", "Homie", "Admin", "Host", "Friend" or none), shown in chat // and on their nametag. `local`: the local player. std::pair player_role(Session &s, std::uint64_t id, bool local, bool marks = true); // Sends one line from this player; returns why not when it cannot. std::string send_chat(Session &s, std::string_view typed); // A "/" command for a dedicated server (votes, and any server command for its admins): sent // like chat but never shown as a line; the server answers in chat. std::string send_chat_command(Session &s, std::string_view typed); // Answers the dedicated server's running vote, as /yes or /no in chat does. std::string cast_server_vote(Session &s, bool yes); // Answers the dedicated server's running poll (0: its first answer), as /1, /2... in chat does. std::string answer_server_poll(Session &s, std::size_t answer); // Whether a vote the local player may answer is running: read by the game thread for the binds. inline std::atomic server_vote_open_flag{}; inline std::atomic map_package_lookup{}; inline std::atomic fetching_map_flag{}; inline std::atomic map_fetch_deadline_flag{}, map_load_held{}; // A guest without the map is downloading it: the clock they are loading against starts over // each time they say so, for as long as map_fetch_limit_us from the first time. inline void guest_fetching(Peer &peer, std::uint64_t now) { // (Only for a guest who is past the password.) if (!peer.handshaken && !peer.map_authorized) return; if (!peer.fetching_since) peer.fetching_since = now; if (map_fetch_policy::host_expired(peer.fetching_since, now)) return; if (!peer.handshaken) peer.connected_at = now; else if (peer.travel_since) peer.travel_since = now; } // The package of the map a host is on, for its map offers and world states. inline std::string host_map_package(std::string_view destination) { const auto lookup = map_package_lookup.load(std::memory_order_relaxed); if (!lookup || destination.empty()) return {}; try { auto package = lookup(world_destination_asset(destination)); return valid_map_package(package) ? package : std::string(); } catch (...) { return {}; } } inline std::atomic server_poll_answers_flag{}; // The server's maps for its next-map vote (session.h: next_maps), for the thread that fetches their pictures. inline std::mutex next_maps_mutex; inline std::vector next_maps_shared; inline std::atomic next_maps_serial_value{}; inline void set_next_maps(std::vector maps) { std::lock_guard lock(next_maps_mutex); if (maps.empty() && next_maps_shared.empty()) return; next_maps_shared = std::move(maps); next_maps_serial_value.fetch_add(1, std::memory_order_relaxed); } // "/p " in a lobby: one line for the local player's party only, relayed by the host. std::string send_party_chat(Session &s, std::string_view typed); // The "/" commands this session offers (the chat overlay lists them as the player types "/"). std::vector chat_commands(const Session &s); // One encoded throwdown message from this player to everyone else (through the host). void send_throwdown(Session &s, std::vector message); // Tell the session this player is typing (`on`) or has stopped. Relayed like chat. void send_typing(Session &s, bool on); // Whether `id`'s placed objects are solid for this player. The saved choice for them when // there is one; else a player's are ghosts, and a dedicated server's own (its parks, which // everyone is there to skate) are solid. Read when they are first seen and handed to the // objects (network_object_runtime.h). bool objects_solid(Session &s, std::uint64_t id); // session_send.cpp Packet packet(Session &s, PacketKind kind, std::uint64_t now); void reset_direct(Session &s, Peer &p, std::uint64_t now); void disconnect(Session &s, std::uint64_t id, const std::string &reason); bool send_packet(Session &s, std::uint64_t id, const Packet &p, bool reliable, bool fresh, std::span raw = {}, std::span wire = {}); void send_required(Session &s, std::uint64_t id, const std::vector &bytes); void send_world_state(Session &s, std::uint64_t now); void begin_host_world(Session &s, std::string_view destination, std::uint64_t now); void broadcast(Session &s, const Packet &packet, bool reliable, bool fresh, std::uint64_t now, std::uint64_t except = 0); void sync_objects(Session &s, const NativeFrame &local, std::uint64_t now); void refresh_host_choices(Session &s, std::uint64_t now); void send_roster(Session &s, std::uint64_t now); // Host: sends its physics tuning when it changes. Guest: skates with the session's tuning // while the host enforces it, and with its own otherwise. void update_physics_tuning(Session &s, const NativeFrame &local, std::uint64_t now); // How the local mods change trick scoring (Engine/Vfs/mod_scoring.h). Guest: reported to the host // or dedicated server once known and whenever it changes. Host: judged like a guest's. void update_scoring(Session &s, std::uint64_t now); // Host: flags or clears a guest from its report and the host's choice. void judge_scoring(Session &s, Peer &peer); // The chat line for the local player's own mods changing scoring or physics. std::string own_scoring_notice(); void send_local(Session &s, const NativeFrame &local, std::uint64_t now, std::uint64_t captured_at = 0, bool pose_captured = true); // session_receive.cpp void apply_roster(Session &s, const Packet &p, std::uint64_t now); // Parties (session_party.cpp). std::uint32_t party_of(const Session &s, std::uint64_t id); // 0 = none; the local player too // Another player in the local player's party. bool party_member(const Session &s, std::uint64_t id); std::uint64_t party_leader(const Session &s); // the local party's leader, 0 without one // The local player's roster entry changed (roster from the host, or the host's own). void set_local_party(Session &s, const Member &local); void receive_party(Session &s, const Packet &p, std::uint64_t now); void expire_party_invites(Session &s, std::uint64_t now); // Sends a request to whoever owns the parties (a lobby's host answers its own at once); // returns why it can't, or empty. std::string send_party_request(Session &s, PartyAction action, std::uint64_t player); // A lobby's host: answers `from`'s party request, as a dedicated server does. void host_party_request(Session &s, std::uint64_t from, PartyAction action, std::uint64_t player, std::uint64_t now); // A lobby's host: `from`'s "/p" line, relayed to the rest of their party only. void host_party_chat(Session &s, std::uint64_t from, std::string_view text, std::uint64_t now); // A lobby's host, every network tick: players who left are out of their parties, lapsed // invites are withdrawn, and a change sends a new roster. void tick_host_parties(Session &s, std::uint64_t now); // A lobby's host: each roster member's party as the book has it. void fill_roster_parties(Session &s, std::vector &members); bool accept_data(Peer &peer, const Packet &p, std::uint64_t now); // The same, moving a pose into the playback buffer (the packet's pose is left empty). bool accept_data(Peer &peer, Packet &&p, std::uint64_t now); void networking(Session &s, const NativeFrame &local, std::uint64_t now); // session_commands.cpp void apply_distances(Session &s, const MultiplayerDistances &distances); void apply_object_placement(Session &s, ObjectPlacement policy); // The session's limit, and this game's own share of it: none while hosting or as a server's admin. void apply_object_limit(Session &s, unsigned limit); // Whether a dedicated server lets players resize what they place, and whether this game may (the // host and a role that may always build are exempt): the placements follow it. void apply_object_scaling(Session &s, bool allowed); // Stores what guests may use and applies it to the local player (the host and a dedicated // server's admins are exempt). void apply_guest_tools(Session &s, bool noclip, bool no_bail, bool boosts, bool ollies = true); // Shows the chosen nametags: ReSkate's (and none of the game's nametags or compass // arrows), the game's own, or none. void apply_nametags(const Session &s); } // namespace dingosdk::multiplayer::session_detail