Skip to content

Commit 6a55dc2

Browse files
feat: remove MAX_PLAYER limit for networked games (#407)
Naive pass at removing the max player limit. I haven't thought all parts through, but putting this here for a first round of feedback. Closes #298
1 parent 0d82737 commit 6a55dc2

File tree

7 files changed

+211
-216
lines changed

7 files changed

+211
-216
lines changed

framework_crates/bones_framework/src/networking.rs

+32-33
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,6 @@ pub const NETWORK_MAX_PREDICTION_WINDOW_DEFAULT: usize = 7;
7575
/// Amount of frames GGRS will delay local input.
7676
pub const NETWORK_LOCAL_INPUT_DELAY_DEFAULT: usize = 2;
7777

78-
#[doc(inline)]
79-
pub use bones_matchmaker_proto::MAX_PLAYERS;
80-
8178
/// Possible errors returned by network loop.
8279
pub enum NetworkError {
8380
/// The session was disconnected.
@@ -157,15 +154,13 @@ pub trait NetworkSocket: Sync + Send {
157154
fn send_reliable(&self, target: SocketTarget, message: &[u8]);
158155
/// Receive reliable messages from other players. The `usize` is the index of the player that
159156
/// sent the message.
160-
fn recv_reliable(&self) -> Vec<(usize, Vec<u8>)>;
157+
fn recv_reliable(&self) -> Vec<(u32, Vec<u8>)>;
161158
/// Close the connection.
162159
fn close(&self);
163160
/// Get the player index of the local player.
164-
fn player_idx(&self) -> usize;
165-
/// Return, for every player index, whether the player is a local player.
166-
fn player_is_local(&self) -> [bool; MAX_PLAYERS];
161+
fn player_idx(&self) -> u32;
167162
/// Get the player count for this network match.
168-
fn player_count(&self) -> usize;
163+
fn player_count(&self) -> u32;
169164

170165
/// Increment match id so messages from previous match that are still in flight
171166
/// will be filtered out. Used when starting new session with existing socket.
@@ -175,7 +170,7 @@ pub trait NetworkSocket: Sync + Send {
175170
/// The destination for a reliable network message.
176171
pub enum SocketTarget {
177172
/// Send to a specific player.
178-
Player(usize),
173+
Player(u32),
179174
/// Broadcast to all players.
180175
All,
181176
}
@@ -215,11 +210,11 @@ pub struct GgrsSessionRunner<'a, InputTypes: NetworkInputConfig<'a>> {
215210
/// The GGRS peer-to-peer session.
216211
pub session: P2PSession<GgrsConfig<InputTypes::Dense>>,
217212

218-
/// Array containing a flag indicating, for each player, whether they are a local player.
219-
pub player_is_local: [bool; MAX_PLAYERS],
213+
/// Local player idx.
214+
pub player_idx: u32,
220215

221216
/// Index of local player, computed from player_is_local
222-
pub local_player_idx: usize,
217+
pub local_player_idx: u32,
223218

224219
/// The frame time accumulator, used to produce a fixed refresh rate.
225220
pub accumulator: f64,
@@ -254,10 +249,10 @@ pub struct GgrsSessionRunner<'a, InputTypes: NetworkInputConfig<'a>> {
254249
pub struct GgrsSessionRunnerInfo {
255250
/// The socket that will be converted into GGRS socket implementation.
256251
pub socket: Socket,
257-
/// The list of local players.
258-
pub player_is_local: [bool; MAX_PLAYERS],
252+
/// The local player idx
253+
pub player_idx: u32,
259254
/// the player count.
260-
pub player_count: usize,
255+
pub player_count: u32,
261256

262257
/// Max prediction window (max number of frames client may predict ahead of last confirmed frame)
263258
/// `None` will use Bone's default.
@@ -278,11 +273,11 @@ impl GgrsSessionRunnerInfo {
278273
max_prediction_window: Option<usize>,
279274
local_input_delay: Option<usize>,
280275
) -> Self {
281-
let player_is_local = socket.player_is_local();
276+
let player_idx = socket.player_idx();
282277
let player_count = socket.player_count();
283278
Self {
284279
socket,
285-
player_is_local,
280+
player_idx,
286281
player_count,
287282
max_prediction_window,
288283
local_input_delay,
@@ -323,31 +318,32 @@ where
323318
.unwrap();
324319

325320
let mut builder = ggrs::SessionBuilder::new()
326-
.with_num_players(info.player_count)
321+
.with_num_players(info.player_count as usize)
327322
.with_input_delay(local_input_delay)
328323
.with_fps(network_fps)
329324
.unwrap()
330325
.with_max_prediction_window(max_prediction)
331326
.unwrap();
332327

333-
let mut local_player_idx: Option<usize> = None;
328+
let local_player_idx = info.player_idx;
334329
for i in 0..info.player_count {
335-
if info.player_is_local[i] {
336-
builder = builder.add_player(ggrs::PlayerType::Local, i).unwrap();
337-
local_player_idx = Some(i);
330+
if i == info.player_idx {
331+
builder = builder
332+
.add_player(ggrs::PlayerType::Local, i as usize)
333+
.unwrap();
338334
} else {
339-
builder = builder.add_player(ggrs::PlayerType::Remote(i), i).unwrap();
335+
builder = builder
336+
.add_player(ggrs::PlayerType::Remote(i as usize), i as usize)
337+
.unwrap();
340338
}
341339
}
342-
let local_player_idx =
343-
local_player_idx.expect("Networking player_is_local array has no local players.");
344340

345341
let session = builder.start_p2p_session(info.socket.clone()).unwrap();
346342

347343
Self {
348344
last_player_input: InputTypes::Dense::default(),
349345
session,
350-
player_is_local: info.player_is_local,
346+
player_idx: info.player_idx,
351347
local_player_idx,
352348
accumulator: default(),
353349
last_run: None,
@@ -395,11 +391,11 @@ where
395391
self.input_collector.update_just_pressed();
396392

397393
// save local players dense input for use with ggrs
398-
match player_inputs.get_control_source(self.local_player_idx) {
394+
match player_inputs.get_control_source(self.local_player_idx as usize) {
399395
Some(control_source) => {
400396
let control = self
401397
.input_collector
402-
.get_control(self.local_player_idx, control_source);
398+
.get_control(self.local_player_idx as usize, control_source);
403399

404400
self.last_player_input = control.get_dense_input();
405401
},
@@ -482,13 +478,16 @@ where
482478

483479
if !self.local_input_disabled {
484480
self.session
485-
.add_local_input(self.local_player_idx, self.last_player_input)
481+
.add_local_input(self.local_player_idx as usize, self.last_player_input)
486482
.unwrap();
487483
} else {
488484
// If local input is disabled, we still submit a default value representing no-inputs.
489485
// This way if input is disabled current inputs will not be held down indefinitely.
490486
self.session
491-
.add_local_input(self.local_player_idx, InputTypes::Dense::default())
487+
.add_local_input(
488+
self.local_player_idx as usize,
489+
InputTypes::Dense::default(),
490+
)
492491
.unwrap();
493492
}
494493

@@ -566,7 +565,7 @@ where
566565
{
567566
trace!(
568567
"Net player({player_idx}) local: {}, status: {status:?}, input: {:?}",
569-
self.local_player_idx == player_idx,
568+
self.local_player_idx as usize == player_idx,
570569
input
571570
);
572571
player_inputs.network_update(
@@ -630,8 +629,8 @@ where
630629

631630
let runner_info = GgrsSessionRunnerInfo {
632631
socket: self.socket.clone(),
633-
player_is_local: self.player_is_local,
634-
player_count: self.session.num_players(),
632+
player_idx: self.player_idx,
633+
player_count: self.session.num_players().try_into().unwrap(),
635634
max_prediction_window: Some(self.session.max_prediction()),
636635
local_input_delay: Some(self.local_input_delay),
637636
};

framework_crates/bones_framework/src/networking/lan.rs

+19-20
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ static PINGER: Lazy<Pinger> = Lazy::new(|| {
7171
});
7272

7373
/// Host a server.
74-
pub fn start_server(server: ServerInfo, player_count: usize) {
74+
///
75+
/// The number of players is limited to `u32::MAX`.
76+
pub fn start_server(server: ServerInfo, player_count: u32) {
7577
MDNS.register(server.service)
7678
.expect("Could not register MDNS service.");
7779
LAN_MATCHMAKER
@@ -295,7 +297,7 @@ async fn lan_matchmaker(
295297

296298
async fn lan_start_server(
297299
matchmaker_channel: &BiChannelServer<LanMatchmakerRequest, LanMatchmakerResponse>,
298-
mut player_count: usize,
300+
mut player_count: u32,
299301
) -> anyhow::Result<()> {
300302
info!("Starting LAN server");
301303
matchmaker_channel
@@ -366,14 +368,14 @@ async fn lan_start_server(
366368
info!(%current_players, %target_players);
367369

368370
// If we're ready to start a match
369-
if connections.len() == player_count - 1 {
371+
if connections.len() == (player_count - 1) as usize {
370372
info!("All players joined.");
371373

372374
let endpoint = get_network_endpoint().await;
373375

374376
// Tell all clients we're ready
375377
for (i, conn) in connections.iter().enumerate() {
376-
let mut peers = std::array::from_fn(|_| None);
378+
let mut peers = Vec::new();
377379
connections
378380
.iter()
379381
.enumerate()
@@ -390,27 +392,24 @@ async fn lan_start_server(
390392
);
391393
}
392394

393-
peers[i + 1] = Some(addr);
395+
peers.push((u32::try_from(i + 1).unwrap(), addr));
394396
});
395397

396398
let mut uni = conn.open_uni().await?;
397399
uni.write_all(&postcard::to_vec::<_, 20>(&MatchmakerNetMsg::MatchReady {
398-
player_idx: i + 1,
400+
player_idx: (i + 1).try_into()?,
399401
peers,
400402
player_count,
401403
})?)
402404
.await?;
403405
uni.finish().await?;
404406
}
405407

406-
// Collect the list of client connections
407-
let connections = std::array::from_fn(|i| {
408-
if i == 0 {
409-
None
410-
} else {
411-
connections.get(i - 1).cloned()
412-
}
413-
});
408+
let connections = connections
409+
.into_iter()
410+
.enumerate()
411+
.map(|(i, c)| (u32::try_from(i + 1).unwrap(), c))
412+
.collect();
414413

415414
// Send the connections to the game so that it can start the network match.
416415
matchmaker_channel
@@ -479,10 +478,10 @@ async fn lan_join_server(
479478
enum MatchmakerNetMsg {
480479
MatchReady {
481480
/// The peers they have for the match, with the index in the array being the player index of the peer.
482-
peers: [Option<NodeAddr>; MAX_PLAYERS],
481+
peers: Vec<(u32, NodeAddr)>,
483482
/// The player index of the player getting the message.
484-
player_idx: usize,
485-
player_count: usize,
483+
player_idx: u32,
484+
player_count: u32,
486485
},
487486
}
488487

@@ -496,7 +495,7 @@ pub enum LanMatchmakerRequest {
496495
/// Start matchmaker server
497496
StartServer {
498497
/// match player count
499-
player_count: usize,
498+
player_count: u32,
500499
},
501500
/// Join server
502501
JoinServer {
@@ -520,9 +519,9 @@ pub enum LanMatchmakerResponse {
520519
/// Lan socket to game
521520
socket: Socket,
522521
/// Local player index
523-
player_idx: usize,
522+
player_idx: u32,
524523
/// Game player count
525-
player_count: usize,
524+
player_count: u32,
526525
},
527526
}
528527

framework_crates/bones_framework/src/networking/online.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub struct OnlineMatchmaker(BiChannelClient<OnlineMatchmakerRequest, OnlineMatch
4444
/// Online matchmaker request
4545
#[derive(Debug)]
4646
pub enum OnlineMatchmakerRequest {
47-
SearchForGame { id: NodeId, player_count: usize },
47+
SearchForGame { id: NodeId, player_count: u32 },
4848
StopSearch,
4949
}
5050

@@ -80,7 +80,7 @@ async fn online_matchmaker(
8080
async fn search_for_game(
8181
matchmaker_channel: &BiChannelServer<OnlineMatchmakerRequest, OnlineMatchmakerResponse>,
8282
id: NodeId,
83-
player_count: usize,
83+
player_count: u32,
8484
) -> anyhow::Result<()> {
8585
info!("Connecting to online matchmaker");
8686
let ep = get_network_endpoint().await;
@@ -95,7 +95,7 @@ async fn search_for_game(
9595
let (mut send, mut recv) = conn.open_bi().await?;
9696

9797
let message = MatchmakerRequest::RequestMatch(MatchInfo {
98-
client_count: player_count.try_into().unwrap(),
98+
client_count: player_count,
9999
match_data: b"jumpy_default_game".to_vec(),
100100
});
101101
info!(request=?message, "Sending match request");
@@ -150,14 +150,14 @@ async fn search_for_game(
150150
info!(%random_seed, %player_idx, player_count=%client_count, "Online match complete");
151151

152152
let peer_connections = establish_peer_connections(
153-
player_idx as usize,
154-
client_count as usize,
153+
player_idx,
154+
client_count,
155155
player_ids,
156156
None,
157157
)
158158
.await?;
159159

160-
let socket = Socket::new(player_idx as usize, peer_connections);
160+
let socket = Socket::new(player_idx, peer_connections);
161161

162162
matchmaker_channel.try_send(OnlineMatchmakerResponse::GameStarting {
163163
socket,
@@ -176,7 +176,7 @@ async fn search_for_game(
176176
}
177177

178178
/// Search for game with `matchmaking_server` and `player_count`
179-
pub fn start_search_for_game(matchmaking_server: NodeId, player_count: usize) {
179+
pub fn start_search_for_game(matchmaking_server: NodeId, player_count: u32) {
180180
// TODO remove
181181
info!("Starting search for online game with player count {player_count}");
182182
ONLINE_MATCHMAKER

0 commit comments

Comments
 (0)