MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
space::MultiplayerSession Class Reference

Owns a host or client UDP session and exchanges replicated game state. More...

#include <examples/asteroids-net/multiplayer.hpp>

Public Member Functions

bool active () const
 Returns whether a host or client session is active.
bool connected () const
 Returns whether the local player has an established peer connection.
NetworkExchange exchange (const NetworkState &local_state, float delta_time)
 Sends local state and receives remote updates.
void host (const std::string &port, const std::string &player_name)
 Starts hosting a session.
bool is_host () const
 Returns whether this session is the host.
void join (const std::string &address, const std::string &port, const std::string &player_name, const std::string &join_code)
 Joins a hosted session.
const std::string & join_code () const
 Returns the session join code.
std::uint8_t local_player_id () const
 Returns the assigned local player slot.
 MultiplayerSession ()
 Initializes the networking subsystem.
 MultiplayerSession (const MultiplayerSession &)=delete
MultiplayerSessionoperator= (const MultiplayerSession &)=delete
const std::array< bool, NETWORK_PLAYER_COUNT > & player_connected () const
 Returns connection flags indexed by player slot.
std::size_t player_count () const
 Returns the number of connected players.
const std::array< std::string, NETWORK_PLAYER_COUNT > & player_names () const
 Returns display names indexed by player slot.
const std::string & port_mapping_status () const
 Returns the automatic router port-mapping status.
void stop ()
 Stops the active host or client session.
 ~MultiplayerSession ()
 Stops the session and releases networking resources.

Detailed Description

Owns a host or client UDP session and exchanges replicated game state.

Definition at line 92 of file multiplayer.hpp.

Constructor & Destructor Documentation

◆ MultiplayerSession() [1/2]

space::MultiplayerSession::MultiplayerSession ( )
default

Initializes the networking subsystem.

◆ ~MultiplayerSession()

space::MultiplayerSession::~MultiplayerSession ( )

Stops the session and releases networking resources.

Definition at line 13 of file multiplayer.cpp.

13{ stop(); }
void stop()
Stops the active host or client session.

◆ MultiplayerSession() [2/2]

space::MultiplayerSession::MultiplayerSession ( const MultiplayerSession & )
delete

Member Function Documentation

◆ active()

bool space::MultiplayerSession::active ( ) const
nodiscard

Returns whether a host or client session is active.

Definition at line 197 of file multiplayer.cpp.

197{ return session_active; }

◆ connected()

bool space::MultiplayerSession::connected ( ) const
nodiscard

Returns whether the local player has an established peer connection.

Definition at line 198 of file multiplayer.cpp.

198{ return player_count() > 1; }
std::size_t player_count() const
Returns the number of connected players.

◆ exchange()

NetworkExchange space::MultiplayerSession::exchange ( const NetworkState & local_state,
float delta_time )

Sends local state and receives remote updates.

Parameters
local_stateCurrent local gameplay state.
delta_timeFrame duration in seconds.
Returns
Remote states received during this tick.

Definition at line 93 of file multiplayer.cpp.

93 {
94 NetworkExchange result{};
95 if (!session_active) {
96 return result;
97 }
98
99 if (assigned_player_id < NETWORK_PLAYER_COUNT) {
100 peers[assigned_player_id].state = local_state;
101 }
102 if (host_role) {
103 for (std::uint8_t player = 1; player < NETWORK_PLAYER_COUNT; ++player) {
104 if (!peers[player].connected)
105 continue;
106 peers[player].idle_seconds += delta_time;
107 if (peers[player].idle_seconds > 5.0f) {
108 peers[player] = {};
109 connected_players[player] = false;
110 names[player].clear();
111 }
112 }
113 } else if (assigned_player_id < NETWORK_PLAYER_COUNT && connected_players[0]) {
114 peers[0].idle_seconds += delta_time;
115 if (peers[0].idle_seconds > 5.0f) {
116 for (std::uint8_t player = 0; player < NETWORK_PLAYER_COUNT; ++player) {
117 if (player != assigned_player_id) {
118 connected_players[player] = false;
119 }
120 }
121 }
122 }
123
124 while (1) {
125 Packet packet{};
126 mxnetwork::SocketAddress sender{};
127 const ssize_t received = host_role ? socket.recvfrom(&packet, sizeof(packet), sender) : socket.recvfrom(&packet, sizeof(packet));
128 if (received < 0) {
129 break;
130 }
131 if (received != static_cast<ssize_t>(sizeof(packet)) || packet.magic != Packet{}.magic ||
132 packet.version != Packet{}.version || packet.size != sizeof(Packet)) {
133 continue;
134 }
135
136 if (host_role) {
137 const std::string supplied_code(packet.join_code.data(), strnlen(packet.join_code.data(), packet.join_code.size()));
138 if (supplied_code != session_join_code) {
139 continue;
140 }
141 const std::optional<std::uint8_t> player = find_or_assign_peer(sender);
142 if (!player.has_value()) {
143 continue;
144 }
145 Peer &peer = peers[*player];
146 if (packet.sequence <= peer.receive_sequence && peer.receive_sequence != 0) {
147 continue;
148 }
149 peer.receive_sequence = packet.sequence;
150 peer.idle_seconds = 0.0f;
151 peer.state = packet.state;
152 peer.name = packet_name(packet.player_name);
153 names[*player] = peer.name;
154 connected_players[*player] = true;
155 result.players.push_back({*player, packet.state});
156 } else {
157 peers[0].idle_seconds = 0.0f;
158 for (std::uint8_t player = 0; player < NETWORK_PLAYER_COUNT; ++player) {
159 connected_players[player] = (packet.connected_mask & (1U << player)) != 0U;
160 }
161 if (packet.assigned_player_id < NETWORK_PLAYER_COUNT) {
162 assigned_player_id = packet.assigned_player_id;
163 names[assigned_player_id] = local_player_name;
164 connected_players[assigned_player_id] = true;
165 }
166 if (packet.player_id < NETWORK_PLAYER_COUNT) {
167 names[packet.player_id] = packet_name(packet.player_name);
168 connected_players[packet.player_id] = true;
169 result.players.push_back({packet.player_id, packet.state});
170 }
171 }
172 }
173
174 send_accumulator += delta_time;
175 if (send_accumulator >= (1.0f / 30.0f)) {
176 send_accumulator = 0.0f;
177 if (host_role) {
178 for (std::uint8_t destination = 1; destination < NETWORK_PLAYER_COUNT; ++destination) {
179 if (!peers[destination].connected) {
180 continue;
181 }
182 for (std::uint8_t source = 0; source < NETWORK_PLAYER_COUNT; ++source) {
183 if (peers[source].connected) {
184 send_host_relay(source, destination);
185 }
186 }
187 }
188 } else {
189 send_client_state(local_state);
190 }
191 } else if (!host_role && assigned_player_id == 0xFFU) {
192 send_client_state(local_state);
193 }
194 return result;
195 }
bool connected() const
Returns whether the local player has an established peer connection.
std::string packet_name(const std::array< char, 32 > &name)
constexpr std::size_t NETWORK_PLAYER_COUNT
Maximum number of players in a session.

◆ host()

void space::MultiplayerSession::host ( const std::string & port,
const std::string & player_name )

Starts hosting a session.

Parameters
portLocal UDP port.
player_nameHost display name.

Definition at line 37 of file multiplayer.cpp.

37 {
38 stop();
39 socket = mxnetwork::Socket(mxnetwork::SocketType::TYPE_INET_DGRAM);
40 socket.bind(port);
41 if (!socket.setblocking(false)) {
42 stop();
43 throw mxnetwork::Exception("Could not make the host UDP socket nonblocking.");
44 }
45 port_mapping = std::make_unique<PortMapping>();
46 port_mapping->open(port);
47 session_active = true;
48 host_role = true;
49 assigned_player_id = 0;
50 local_player_name = player_name;
51 names[0] = player_name;
52 connected_players[0] = true;
53 peers[0].connected = true;
54 peers[0].name = player_name;
55 session_join_code = random_join_code();
56 }
@ TYPE_INET_DGRAM
IPv4 datagram socket.
Definition socket.hpp:47

◆ is_host()

bool space::MultiplayerSession::is_host ( ) const
nodiscard

Returns whether this session is the host.

Definition at line 199 of file multiplayer.cpp.

199{ return host_role; }

◆ join()

void space::MultiplayerSession::join ( const std::string & address,
const std::string & port,
const std::string & player_name,
const std::string & join_code )

Joins a hosted session.

Parameters
addressHost address.
portHost UDP port.
player_nameLocal display name.
join_codeHost-provided session code.

Definition at line 58 of file multiplayer.cpp.

58 {
59 stop();
60 socket = mxnetwork::Socket(mxnetwork::SocketType::TYPE_INET_DGRAM);
61 if (!socket.connect(address, port)) {
62 stop();
63 throw mxnetwork::Exception("Could not connect the UDP socket to the host.");
64 }
65 if (!socket.setblocking(false)) {
66 stop();
67 throw mxnetwork::Exception("Could not make the client UDP socket nonblocking.");
68 }
69 session_active = true;
70 host_role = false;
71 local_player_name = player_name;
72 session_join_code = join_code;
73 }
const std::string & join_code() const
Returns the session join code.

◆ join_code()

const std::string & space::MultiplayerSession::join_code ( ) const
nodiscard

Returns the session join code.

Definition at line 208 of file multiplayer.cpp.

208{ return session_join_code; }

◆ local_player_id()

std::uint8_t space::MultiplayerSession::local_player_id ( ) const
nodiscard

Returns the assigned local player slot.

Definition at line 200 of file multiplayer.cpp.

200{ return assigned_player_id; }

◆ operator=()

MultiplayerSession & space::MultiplayerSession::operator= ( const MultiplayerSession & )
delete

◆ player_connected()

const std::array< bool, NETWORK_PLAYER_COUNT > & space::MultiplayerSession::player_connected ( ) const
nodiscard

Returns connection flags indexed by player slot.

Definition at line 207 of file multiplayer.cpp.

207{ return connected_players; }

◆ player_count()

std::size_t space::MultiplayerSession::player_count ( ) const
nodiscard

Returns the number of connected players.

Definition at line 202 of file multiplayer.cpp.

202 {
203 return static_cast<std::size_t>(std::count(connected_players.begin(), connected_players.end(), true));
204 }

◆ player_names()

const std::array< std::string, NETWORK_PLAYER_COUNT > & space::MultiplayerSession::player_names ( ) const
nodiscard

Returns display names indexed by player slot.

Definition at line 206 of file multiplayer.cpp.

206{ return names; }

◆ port_mapping_status()

const std::string & space::MultiplayerSession::port_mapping_status ( ) const
nodiscard

Returns the automatic router port-mapping status.

Definition at line 210 of file multiplayer.cpp.

210 {
211 static const std::string NO_MAPPING = "Automatic router mapping unavailable; forward the UDP port manually.";
212 return port_mapping ? port_mapping->status() : NO_MAPPING;
213 }

◆ stop()

void space::MultiplayerSession::stop ( )

Stops the active host or client session.

Definition at line 75 of file multiplayer.cpp.

75 {
76 port_mapping.reset();
77 if (socket.valid()) {
78 socket.close();
79 }
80 socket = mxnetwork::Socket{};
81 session_active = false;
82 host_role = false;
83 send_accumulator = 0.0f;
84 send_sequence = 0;
85 assigned_player_id = 0xFFU;
86 local_player_name.clear();
87 session_join_code.clear();
88 peers = {};
89 names = {};
90 connected_players = {};
91 }

The documentation for this class was generated from the following files: