Skip to content

Commit

Permalink
feat: remove MAX_PLAYER limit for networked games (#407)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
dignifiedquire committed Jul 3, 2024
1 parent 0d82737 commit 6a55dc2
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 216 deletions.
65 changes: 32 additions & 33 deletions framework_crates/bones_framework/src/networking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,6 @@ pub const NETWORK_MAX_PREDICTION_WINDOW_DEFAULT: usize = 7;
/// Amount of frames GGRS will delay local input.
pub const NETWORK_LOCAL_INPUT_DELAY_DEFAULT: usize = 2;

#[doc(inline)]
pub use bones_matchmaker_proto::MAX_PLAYERS;

/// Possible errors returned by network loop.
pub enum NetworkError {
/// The session was disconnected.
Expand Down Expand Up @@ -157,15 +154,13 @@ pub trait NetworkSocket: Sync + Send {
fn send_reliable(&self, target: SocketTarget, message: &[u8]);
/// Receive reliable messages from other players. The `usize` is the index of the player that
/// sent the message.
fn recv_reliable(&self) -> Vec<(usize, Vec<u8>)>;
fn recv_reliable(&self) -> Vec<(u32, Vec<u8>)>;
/// Close the connection.
fn close(&self);
/// Get the player index of the local player.
fn player_idx(&self) -> usize;
/// Return, for every player index, whether the player is a local player.
fn player_is_local(&self) -> [bool; MAX_PLAYERS];
fn player_idx(&self) -> u32;
/// Get the player count for this network match.
fn player_count(&self) -> usize;
fn player_count(&self) -> u32;

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

/// Array containing a flag indicating, for each player, whether they are a local player.
pub player_is_local: [bool; MAX_PLAYERS],
/// Local player idx.
pub player_idx: u32,

/// Index of local player, computed from player_is_local
pub local_player_idx: usize,
pub local_player_idx: u32,

/// The frame time accumulator, used to produce a fixed refresh rate.
pub accumulator: f64,
Expand Down Expand Up @@ -254,10 +249,10 @@ pub struct GgrsSessionRunner<'a, InputTypes: NetworkInputConfig<'a>> {
pub struct GgrsSessionRunnerInfo {
/// The socket that will be converted into GGRS socket implementation.
pub socket: Socket,
/// The list of local players.
pub player_is_local: [bool; MAX_PLAYERS],
/// The local player idx
pub player_idx: u32,
/// the player count.
pub player_count: usize,
pub player_count: u32,

/// Max prediction window (max number of frames client may predict ahead of last confirmed frame)
/// `None` will use Bone's default.
Expand All @@ -278,11 +273,11 @@ impl GgrsSessionRunnerInfo {
max_prediction_window: Option<usize>,
local_input_delay: Option<usize>,
) -> Self {
let player_is_local = socket.player_is_local();
let player_idx = socket.player_idx();
let player_count = socket.player_count();
Self {
socket,
player_is_local,
player_idx,
player_count,
max_prediction_window,
local_input_delay,
Expand Down Expand Up @@ -323,31 +318,32 @@ where
.unwrap();

let mut builder = ggrs::SessionBuilder::new()
.with_num_players(info.player_count)
.with_num_players(info.player_count as usize)
.with_input_delay(local_input_delay)
.with_fps(network_fps)
.unwrap()
.with_max_prediction_window(max_prediction)
.unwrap();

let mut local_player_idx: Option<usize> = None;
let local_player_idx = info.player_idx;
for i in 0..info.player_count {
if info.player_is_local[i] {
builder = builder.add_player(ggrs::PlayerType::Local, i).unwrap();
local_player_idx = Some(i);
if i == info.player_idx {
builder = builder
.add_player(ggrs::PlayerType::Local, i as usize)
.unwrap();
} else {
builder = builder.add_player(ggrs::PlayerType::Remote(i), i).unwrap();
builder = builder
.add_player(ggrs::PlayerType::Remote(i as usize), i as usize)
.unwrap();
}
}
let local_player_idx =
local_player_idx.expect("Networking player_is_local array has no local players.");

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

Self {
last_player_input: InputTypes::Dense::default(),
session,
player_is_local: info.player_is_local,
player_idx: info.player_idx,
local_player_idx,
accumulator: default(),
last_run: None,
Expand Down Expand Up @@ -395,11 +391,11 @@ where
self.input_collector.update_just_pressed();

// save local players dense input for use with ggrs
match player_inputs.get_control_source(self.local_player_idx) {
match player_inputs.get_control_source(self.local_player_idx as usize) {
Some(control_source) => {
let control = self
.input_collector
.get_control(self.local_player_idx, control_source);
.get_control(self.local_player_idx as usize, control_source);

self.last_player_input = control.get_dense_input();
},
Expand Down Expand Up @@ -482,13 +478,16 @@ where

if !self.local_input_disabled {
self.session
.add_local_input(self.local_player_idx, self.last_player_input)
.add_local_input(self.local_player_idx as usize, self.last_player_input)
.unwrap();
} else {
// If local input is disabled, we still submit a default value representing no-inputs.
// This way if input is disabled current inputs will not be held down indefinitely.
self.session
.add_local_input(self.local_player_idx, InputTypes::Dense::default())
.add_local_input(
self.local_player_idx as usize,
InputTypes::Dense::default(),
)
.unwrap();
}

Expand Down Expand Up @@ -566,7 +565,7 @@ where
{
trace!(
"Net player({player_idx}) local: {}, status: {status:?}, input: {:?}",
self.local_player_idx == player_idx,
self.local_player_idx as usize == player_idx,
input
);
player_inputs.network_update(
Expand Down Expand Up @@ -630,8 +629,8 @@ where

let runner_info = GgrsSessionRunnerInfo {
socket: self.socket.clone(),
player_is_local: self.player_is_local,
player_count: self.session.num_players(),
player_idx: self.player_idx,
player_count: self.session.num_players().try_into().unwrap(),
max_prediction_window: Some(self.session.max_prediction()),
local_input_delay: Some(self.local_input_delay),
};
Expand Down
39 changes: 19 additions & 20 deletions framework_crates/bones_framework/src/networking/lan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ static PINGER: Lazy<Pinger> = Lazy::new(|| {
});

/// Host a server.
pub fn start_server(server: ServerInfo, player_count: usize) {
///
/// The number of players is limited to `u32::MAX`.
pub fn start_server(server: ServerInfo, player_count: u32) {
MDNS.register(server.service)
.expect("Could not register MDNS service.");
LAN_MATCHMAKER
Expand Down Expand Up @@ -295,7 +297,7 @@ async fn lan_matchmaker(

async fn lan_start_server(
matchmaker_channel: &BiChannelServer<LanMatchmakerRequest, LanMatchmakerResponse>,
mut player_count: usize,
mut player_count: u32,
) -> anyhow::Result<()> {
info!("Starting LAN server");
matchmaker_channel
Expand Down Expand Up @@ -366,14 +368,14 @@ async fn lan_start_server(
info!(%current_players, %target_players);

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

let endpoint = get_network_endpoint().await;

// Tell all clients we're ready
for (i, conn) in connections.iter().enumerate() {
let mut peers = std::array::from_fn(|_| None);
let mut peers = Vec::new();
connections
.iter()
.enumerate()
Expand All @@ -390,27 +392,24 @@ async fn lan_start_server(
);
}

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

let mut uni = conn.open_uni().await?;
uni.write_all(&postcard::to_vec::<_, 20>(&MatchmakerNetMsg::MatchReady {
player_idx: i + 1,
player_idx: (i + 1).try_into()?,
peers,
player_count,
})?)
.await?;
uni.finish().await?;
}

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

// Send the connections to the game so that it can start the network match.
matchmaker_channel
Expand Down Expand Up @@ -479,10 +478,10 @@ async fn lan_join_server(
enum MatchmakerNetMsg {
MatchReady {
/// The peers they have for the match, with the index in the array being the player index of the peer.
peers: [Option<NodeAddr>; MAX_PLAYERS],
peers: Vec<(u32, NodeAddr)>,
/// The player index of the player getting the message.
player_idx: usize,
player_count: usize,
player_idx: u32,
player_count: u32,
},
}

Expand All @@ -496,7 +495,7 @@ pub enum LanMatchmakerRequest {
/// Start matchmaker server
StartServer {
/// match player count
player_count: usize,
player_count: u32,
},
/// Join server
JoinServer {
Expand All @@ -520,9 +519,9 @@ pub enum LanMatchmakerResponse {
/// Lan socket to game
socket: Socket,
/// Local player index
player_idx: usize,
player_idx: u32,
/// Game player count
player_count: usize,
player_count: u32,
},
}

Expand Down
14 changes: 7 additions & 7 deletions framework_crates/bones_framework/src/networking/online.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub struct OnlineMatchmaker(BiChannelClient<OnlineMatchmakerRequest, OnlineMatch
/// Online matchmaker request
#[derive(Debug)]
pub enum OnlineMatchmakerRequest {
SearchForGame { id: NodeId, player_count: usize },
SearchForGame { id: NodeId, player_count: u32 },
StopSearch,
}

Expand Down Expand Up @@ -80,7 +80,7 @@ async fn online_matchmaker(
async fn search_for_game(
matchmaker_channel: &BiChannelServer<OnlineMatchmakerRequest, OnlineMatchmakerResponse>,
id: NodeId,
player_count: usize,
player_count: u32,
) -> anyhow::Result<()> {
info!("Connecting to online matchmaker");
let ep = get_network_endpoint().await;
Expand All @@ -95,7 +95,7 @@ async fn search_for_game(
let (mut send, mut recv) = conn.open_bi().await?;

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

let peer_connections = establish_peer_connections(
player_idx as usize,
client_count as usize,
player_idx,
client_count,
player_ids,
None,
)
.await?;

let socket = Socket::new(player_idx as usize, peer_connections);
let socket = Socket::new(player_idx, peer_connections);

matchmaker_channel.try_send(OnlineMatchmakerResponse::GameStarting {
socket,
Expand All @@ -176,7 +176,7 @@ async fn search_for_game(
}

/// Search for game with `matchmaking_server` and `player_count`
pub fn start_search_for_game(matchmaking_server: NodeId, player_count: usize) {
pub fn start_search_for_game(matchmaking_server: NodeId, player_count: u32) {
// TODO remove
info!("Starting search for online game with player count {player_count}");
ONLINE_MATCHMAKER
Expand Down
Loading

0 comments on commit 6a55dc2

Please sign in to comment.