MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
multiplayer.hpp
Go to the documentation of this file.
1#ifndef ASTEROIDS_NET_MULTIPLAYER_HPP
2#define ASTEROIDS_NET_MULTIPLAYER_HPP
3
4/**
5 * @file multiplayer.hpp
6 * @brief UDP multiplayer state exchange for Asteroids.
7 */
8
10
11#include <array>
12#include <cstdint>
13#include <memory>
14#include <optional>
15#include <string>
16#include <vector>
17
18namespace space {
19
20 class PortMapping;
21
22 /** @brief Maximum projectile snapshots carried in one network state. */
23 constexpr std::size_t NETWORK_PROJECTILE_COUNT = 15;
24 /** @brief Maximum asteroid snapshots carried in one network state. */
25 constexpr std::size_t NETWORK_ASTEROID_COUNT = 16;
26 /** @brief Maximum number of players in a session. */
27 constexpr std::size_t NETWORK_PLAYER_COUNT = 4;
28
29 /**
30 * @struct NetworkProjectile
31 * @brief Serialized gameplay state for one projectile.
32 */
34 std::uint32_t id = 0; ///< Projectile identifier assigned by its owner.
35 std::array<float, 3> position{}; ///< World-space position.
36 std::array<float, 3> velocity{}; ///< World-space velocity.
37 float lifetime = 0.0f; ///< Remaining lifetime in seconds.
38 std::uint8_t active = 0; ///< Nonzero when the projectile is active.
39 };
40
41 /**
42 * @struct NetworkAsteroid
43 * @brief Serialized gameplay state for one asteroid.
44 */
46 std::array<float, 3> position{}; ///< World-space position.
47 std::array<float, 3> rotation{}; ///< Euler rotation in degrees.
48 float radius = 0.0f; ///< Collision radius.
49 std::uint8_t slot = 0; ///< Source asteroid slot.
50 std::uint8_t active = 0; ///< Nonzero when the asteroid is active.
51 };
52
53 /**
54 * @struct NetworkState
55 * @brief Complete replicated gameplay state published by one player.
56 */
57 struct NetworkState {
58 std::array<float, 3> position{}; ///< Ship world-space position.
59 std::array<float, 3> rotation{}; ///< Ship Euler rotation in degrees.
60 float current_speed = 1.0f; ///< Ship forward speed.
61 std::array<NetworkProjectile, NETWORK_PROJECTILE_COUNT> projectiles{}; ///< Locally owned projectiles.
62 std::array<NetworkAsteroid, NETWORK_ASTEROID_COUNT> asteroids{}; ///< Host-authoritative asteroids.
63 std::array<std::uint32_t, NETWORK_PLAYER_COUNT> kills{}; ///< Kill counters by player slot.
64 std::array<std::uint32_t, NETWORK_PLAYER_COUNT> death_serials{}; ///< Monotonic death events by player slot.
65 std::array<std::uint32_t, NETWORK_PLAYER_COUNT> consumed_projectile_ids{}; ///< Last consumed projectile per player.
66 std::uint8_t exploding = 0; ///< Nonzero while the ship is exploding.
67 std::uint8_t match_started = 0; ///< Nonzero after the host starts the match.
68 std::uint8_t winner = 0; ///< Winning player identifier, or zero if unset.
69 };
70
71 /**
72 * @struct NetworkPlayerUpdate
73 * @brief State received for a specific player slot.
74 */
76 std::uint8_t player_id = 0; ///< Player slot owning the state.
77 NetworkState state{}; ///< Most recently received state.
78 };
79
80 /**
81 * @struct NetworkExchange
82 * @brief Collection of remote player updates produced by one exchange tick.
83 */
85 std::vector<NetworkPlayerUpdate> players{}; ///< Updates received during the tick.
86 };
87
88 /**
89 * @class MultiplayerSession
90 * @brief Owns a host or client UDP session and exchanges replicated game state.
91 */
93 public:
94 /** @brief Initializes the networking subsystem. */
96 /** @brief Stops the session and releases networking resources. */
100
101 /**
102 * @brief Starts hosting a session.
103 * @param port Local UDP port.
104 * @param player_name Host display name.
105 */
106 void host(const std::string &port, const std::string &player_name);
107 /**
108 * @brief Joins a hosted session.
109 * @param address Host address.
110 * @param port Host UDP port.
111 * @param player_name Local display name.
112 * @param join_code Host-provided session code.
113 */
114 void join(const std::string &address, const std::string &port, const std::string &player_name, const std::string &join_code);
115 /** @brief Stops the active host or client session. */
116 void stop();
117 /**
118 * @brief Sends local state and receives remote updates.
119 * @param local_state Current local gameplay state.
120 * @param delta_time Frame duration in seconds.
121 * @return Remote states received during this tick.
122 */
123 NetworkExchange exchange(const NetworkState &local_state, float delta_time);
124
125 /** @brief Returns whether a host or client session is active. */
126 [[nodiscard]] bool active() const;
127 /** @brief Returns whether the local player has an established peer connection. */
128 [[nodiscard]] bool connected() const;
129 /** @brief Returns whether this session is the host. */
130 [[nodiscard]] bool is_host() const;
131 /** @brief Returns the assigned local player slot. */
132 [[nodiscard]] std::uint8_t local_player_id() const;
133 /** @brief Returns the number of connected players. */
134 [[nodiscard]] std::size_t player_count() const;
135 /** @brief Returns display names indexed by player slot. */
136 [[nodiscard]] const std::array<std::string, NETWORK_PLAYER_COUNT> &player_names() const;
137 /** @brief Returns connection flags indexed by player slot. */
138 [[nodiscard]] const std::array<bool, NETWORK_PLAYER_COUNT> &player_connected() const;
139 /** @brief Returns the session join code. */
140 [[nodiscard]] const std::string &join_code() const;
141 /** @brief Returns the automatic router port-mapping status. */
142 [[nodiscard]] const std::string &port_mapping_status() const;
143
144 private:
145 struct Packet {
146 std::uint32_t magic = 0x4D584E54U;
147 std::uint16_t version = 2;
148 std::uint16_t size = sizeof(Packet);
149 std::uint32_t sequence = 0;
150 std::uint8_t player_id = 0xFFU;
151 std::uint8_t assigned_player_id = 0xFFU;
152 std::uint8_t connected_mask = 0;
153 NetworkState state{};
154 std::array<char, 32> player_name{};
155 std::array<char, 9> join_code{};
156 };
157 static_assert(sizeof(Packet) <= 1200, "Multiplayer UDP packet must stay below the conservative MTU payload budget");
158
159 mxnetwork::MXNetworkInit network_init{};
160 mxnetwork::Socket socket{};
161 bool session_active = false;
162 bool host_role = false;
163 struct Peer {
164 mxnetwork::SocketAddress address{};
165 NetworkState state{};
166 std::string name{};
167 std::uint32_t receive_sequence = 0;
168 float idle_seconds = 0.0f;
169 bool connected = false;
170 };
171 std::array<Peer, NETWORK_PLAYER_COUNT> peers{};
172 std::array<std::string, NETWORK_PLAYER_COUNT> names{};
173 std::array<bool, NETWORK_PLAYER_COUNT> connected_players{};
174 std::uint8_t assigned_player_id = 0xFFU;
175 float send_accumulator = 0.0f;
176 std::uint32_t send_sequence = 0;
177 std::string local_player_name{};
178 std::string session_join_code{};
179 std::unique_ptr<PortMapping> port_mapping{};
180
181 void send_client_state(const NetworkState &state);
182 void send_host_relay(std::uint8_t source_player, std::uint8_t destination_player);
183 std::optional<std::uint8_t> find_or_assign_peer(const mxnetwork::SocketAddress &address);
184 };
185
186} // namespace space
187
188#endif
MultiplayerSession()
Initializes the networking subsystem.
void host(const std::string &port, const std::string &player_name)
Starts hosting a session.
MultiplayerSession & operator=(const MultiplayerSession &)=delete
MultiplayerSession(const MultiplayerSession &)=delete
~MultiplayerSession()
Stops the session and releases networking resources.
void join(const std::string &address, const std::string &port, const std::string &player_name, const std::string &join_code)
Joins a hosted session.
bool is_host() const
Returns whether this session is the host.
NetworkExchange exchange(const NetworkState &local_state, float delta_time)
Sends local state and receives remote updates.
bool connected() const
Returns whether the local player has an established peer connection.
const std::array< bool, NETWORK_PLAYER_COUNT > & player_connected() const
Returns connection flags indexed by player slot.
void stop()
Stops the active host or client session.
bool active() const
Returns whether a host or client session is active.
std::uint8_t local_player_id() const
Returns the assigned local player slot.
std::size_t player_count() const
Returns the number of connected players.
const std::string & port_mapping_status() const
Returns the automatic router port-mapping status.
const std::array< std::string, NETWORK_PLAYER_COUNT > & player_names() const
Returns display names indexed by player slot.
const std::string & join_code() const
Returns the session join code.
Owns an automatically created UPnP or NAT-PMP port mapping.
constexpr std::size_t NETWORK_ASTEROID_COUNT
Maximum asteroid snapshots carried in one network state.
constexpr std::size_t NETWORK_PLAYER_COUNT
Maximum number of players in a session.
constexpr std::size_t NETWORK_PROJECTILE_COUNT
Maximum projectile snapshots carried in one network state.
Serialized gameplay state for one asteroid.
std::uint8_t slot
Source asteroid slot.
std::array< float, 3 > position
World-space position.
std::array< float, 3 > rotation
Euler rotation in degrees.
float radius
Collision radius.
std::uint8_t active
Nonzero when the asteroid is active.
Collection of remote player updates produced by one exchange tick.
std::vector< NetworkPlayerUpdate > players
Updates received during the tick.
State received for a specific player slot.
std::uint8_t player_id
Player slot owning the state.
NetworkState state
Most recently received state.
Serialized gameplay state for one projectile.
std::uint8_t active
Nonzero when the projectile is active.
float lifetime
Remaining lifetime in seconds.
std::array< float, 3 > velocity
World-space velocity.
std::array< float, 3 > position
World-space position.
Complete replicated gameplay state published by one player.
std::uint8_t exploding
Nonzero while the ship is exploding.
std::array< std::uint32_t, NETWORK_PLAYER_COUNT > consumed_projectile_ids
Last consumed projectile per player.
std::array< std::uint32_t, NETWORK_PLAYER_COUNT > kills
Kill counters by player slot.
std::array< float, 3 > rotation
Ship Euler rotation in degrees.
std::array< NetworkAsteroid, NETWORK_ASTEROID_COUNT > asteroids
Host-authoritative asteroids.
std::array< std::uint32_t, NETWORK_PLAYER_COUNT > death_serials
Monotonic death events by player slot.
std::array< NetworkProjectile, NETWORK_PROJECTILE_COUNT > projectiles
Locally owned projectiles.
std::array< float, 3 > position
Ship world-space position.
float current_speed
Ship forward speed.
std::uint8_t winner
Winning player identifier, or zero if unset.
std::uint8_t match_started
Nonzero after the host starts the match.