allow to choosing renet_netcode vs steam

This commit is contained in:
2025-12-20 11:33:23 -05:00
parent 930753170f
commit 12c3cdc87b
7 changed files with 318 additions and 61 deletions

146
Cargo.lock generated
View File

@@ -85,6 +85,16 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aead"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"crypto-common",
"generic-array",
]
[[package]]
name = "ahash"
version = "0.8.12"
@@ -1508,6 +1518,7 @@ dependencies = [
"bevy_ecs",
"bevy_time",
"renet",
"renet_netcode",
"renet_steam",
]
@@ -2155,6 +2166,41 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chacha20"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "chacha20poly1305"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
dependencies = [
"aead",
"chacha20",
"cipher",
"poly1305",
"zeroize",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
"zeroize",
]
[[package]]
name = "clang-sys"
version = "1.8.1"
@@ -2443,6 +2489,15 @@ dependencies = [
"windows 0.54.0",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -2507,6 +2562,17 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"rand_core 0.6.4",
"typenum",
]
[[package]]
name = "csv"
version = "1.4.0"
@@ -3197,6 +3263,16 @@ dependencies = [
"thread_local",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "gethostname"
version = "1.1.0"
@@ -3815,6 +3891,15 @@ dependencies = [
"libc",
]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"generic-array",
]
[[package]]
name = "inventory"
version = "0.3.21"
@@ -4810,6 +4895,12 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "opaque-debug"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "opener"
version = "0.8.3"
@@ -5054,6 +5145,17 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "poly1305"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
dependencies = [
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "portable-atomic"
version = "1.11.1"
@@ -5451,6 +5553,18 @@ dependencies = [
"octets",
]
[[package]]
name = "renet_netcode"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d73ffa79c2081fe93286acac186a20d49657b93b8cfa4e0e8b79b1f3ee81241"
dependencies = [
"bevy_ecs",
"log",
"renet",
"renetcode",
]
[[package]]
name = "renet_steam"
version = "2.1.0"
@@ -5463,6 +5577,16 @@ dependencies = [
"steamworks",
]
[[package]]
name = "renetcode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "118d456f815f7fd5bd12713a9e69a0b0f8b45806bd515e05bb60146f1867310d"
dependencies = [
"chacha20poly1305",
"log",
]
[[package]]
name = "rmp"
version = "0.8.14"
@@ -5941,6 +6065,12 @@ dependencies = [
"syn",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "svg_fmt"
version = "0.4.5"
@@ -6400,6 +6530,16 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "universal-hash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
"crypto-common",
"subtle",
]
[[package]]
name = "url"
version = "2.5.7"
@@ -7470,6 +7610,12 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
[[package]]
name = "zerotrie"
version = "0.2.3"

View File

@@ -61,10 +61,8 @@ bevy_pkv = { version = "0.14", default-features = false, features = [
"redb",
] }
bevy_replicon = "0.37.1"
bevy_replicon_renet = { version = "0.13.0", default-features = false, features = [
"server",
"renet_steam",
] }
# TODO: i dont think we need this in dedicated server mode
bevy_replicon_renet = { version = "0.13.0", features = ["renet_steam"] }
bevy_sprite3d = "7.0.0"
bevy_trenchbroom = { version = "0.10", default-features = false, features = [
"physics-integration",
@@ -79,6 +77,7 @@ rand = "=0.8.5"
ron = "0.8"
serde = { version = "1.0.219", features = ["derive"] }
shared = { path = "crates/shared" }
# TODO: i dont think we need this in dedicated server mode
steamworks = "0.12"
[profile.dev.package."*"]

View File

@@ -1,6 +1,6 @@
use crate::{
GameState,
config::NetworkingConfig,
config::NetConfig,
protocol::{
ClientEnteredPlaying, TbMapEntityId, TbMapEntityMapping, messages::DespawnTbMapEntity,
},
@@ -18,11 +18,9 @@ use bevy_replicon::{
use bevy_replicon_renet::{
RenetChannelsExt,
renet::{ConnectionConfig, RenetClient},
steam::SteamClientTransport,
};
use bevy_steamworks::Client;
use bevy_trenchbroom::geometry::Brushes;
use steamworks::SteamId;
pub mod audio;
pub mod backpack;
@@ -53,7 +51,7 @@ pub fn plugin(app: &mut App) {
app.add_systems(
OnEnter(GameState::Connecting),
connect_to_server.run_if(|config: Res<NetworkingConfig>| config.connect_to_host()),
connect_to_server.run_if(|config: Res<NetConfig>| config.is_client()),
);
app.add_systems(Update, despawn_absent_map_entities);
app.add_systems(
@@ -87,7 +85,7 @@ fn on_disconnect() {
fn connect_to_server(
mut commands: Commands,
config: Res<NetworkingConfig>,
config: Res<NetConfig>,
channels: Res<RepliconChannels>,
steam_client: Res<Client>,
) -> Result {
@@ -100,14 +98,40 @@ fn connect_to_server(
..Default::default()
});
let steam_id: u64 = config.steam_id.clone().unwrap().parse().unwrap();
let steam_id = SteamId::from_raw(steam_id);
info!("attempting connection to {steam_id:?}");
let transport = SteamClientTransport::new((**steam_client).clone(), &steam_id)?;
commands.insert_resource(client);
commands.insert_resource(transport);
if let NetConfig::SteamClient(host_steam_id) = &*config {
info!("connecting to steam host: {host_steam_id:?}");
let transport = bevy_replicon_renet::steam::SteamClientTransport::new(
(**steam_client).clone(),
host_steam_id,
)?;
commands.insert_resource(transport);
} else if let NetConfig::NetcodeClient(host_addr) = &*config {
use std::time::SystemTime;
info!("connecting to netcode host: {host_addr:?}");
let current_time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
let client_id = current_time.as_millis() as u64;
let socket = std::net::UdpSocket::bind((std::net::Ipv4Addr::UNSPECIFIED, 0))?;
let authentication = bevy_replicon_renet::netcode::ClientAuthentication::Unsecure {
client_id,
protocol_id: 0,
server_addr: host_addr.clone(),
user_data: None,
};
let transport = bevy_replicon_renet::netcode::NetcodeClientTransport::new(
current_time,
authentication,
socket,
)?;
commands.insert_resource(transport);
}
Ok(())
}

View File

@@ -1,26 +1,92 @@
use std::net::SocketAddr;
use bevy::prelude::*;
use clap::Parser;
use steamworks::SteamId;
pub fn plugin(app: &mut App) {
let config = NetworkingConfig::parse();
let config: NetConfig = config.into();
info!("net config: {:?}", config);
app.insert_resource(config);
}
#[derive(Resource, Parser, Debug)]
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
pub struct NetworkingConfig {
struct NetworkingConfig {
/// Steam id of the host to connect to
#[arg(long)]
pub steam_id: Option<String>,
/// Whether or not to open a port when opening the client, for other clients
/// to connect. Does nothing if `server` is set.
pub steam_host_id: Option<String>,
/// Act as steam host
#[arg(long)]
pub host: bool,
pub steam_host: bool,
/// Act as host using netcode, so we have to define our port
#[arg(long)]
pub netcode_host: Option<Option<u16>>,
/// Host address we connect to as a client
#[arg(long)]
pub netcode_client: Option<Option<String>>,
}
impl NetworkingConfig {
pub fn connect_to_host(&self) -> bool {
self.steam_id.is_some()
#[derive(Resource, Debug)]
pub enum NetConfig {
Singleplayer,
SteamHost,
NetcodeHost { port: u16 },
SteamClient(SteamId),
NetcodeClient(SocketAddr),
}
impl NetConfig {
pub fn is_client(&self) -> bool {
matches!(
self,
NetConfig::SteamClient(_) | NetConfig::NetcodeClient(_)
)
}
pub fn is_host(&self) -> bool {
matches!(self, NetConfig::SteamHost | NetConfig::NetcodeHost { .. })
}
pub fn is_singleplayer(&self) -> bool {
!self.is_client() && !self.is_host()
}
}
impl From<NetworkingConfig> for NetConfig {
fn from(config: NetworkingConfig) -> Self {
match (
config.steam_host,
config.steam_host_id,
config.netcode_host,
config.netcode_client,
) {
(false, None, None, None) => Self::Singleplayer,
(true, None, None, None) => Self::SteamHost,
(false, Some(id), None, None) => Self::SteamClient(parse_steam_id(id)),
(false, None, Some(port), None) => Self::NetcodeHost {
port: port.unwrap_or(31111),
},
(false, None, None, Some(addr)) => Self::NetcodeClient(parse_addr(addr)),
_ => panic!("Invalid configuration"),
}
}
}
fn parse_addr(addr: Option<String>) -> SocketAddr {
addr.map(|addr| addr.parse().ok())
.flatten()
.unwrap_or_else(|| "127.0.0.1:31111".parse().unwrap())
}
fn parse_steam_id(id: String) -> SteamId {
let id: u64 = id.parse().unwrap();
SteamId::from_raw(id)
}

View File

@@ -34,7 +34,7 @@ pub mod utils;
pub mod water;
use crate::{
config::NetworkingConfig,
config::NetConfig,
heads_database::{HeadDatabaseAsset, HeadsDatabase},
protocol::{PlayerIdCounter, messages::AssignClientPlayer},
tb_entities::SpawnPoint,
@@ -52,7 +52,6 @@ use bevy_trenchbroom::{
TrenchBroomPlugins, config::TrenchBroomConfig, prelude::TrenchBroomPhysicsPlugin,
};
use bevy_trenchbroom_avian::AvianPhysicsBackend;
use steamworks::{CallbackResult, P2PSessionRequest};
use utils::{billboards, squish_animation};
pub const HEDZ_GREEN: Srgba = Srgba::rgb(0.0, 1.0, 0.0);
@@ -150,23 +149,21 @@ pub fn plugin(app: &mut App) {
if cfg!(feature = "client") {
app.add_systems(
OnEnter(GameState::Waiting),
start_solo_client
.run_if(|config: Res<NetworkingConfig>| !config.connect_to_host() && !config.host),
start_solo_client.run_if(|config: Res<NetConfig>| config.is_singleplayer()),
);
app.add_systems(
OnEnter(GameState::Waiting),
start_listen_server
.run_if(|config: Res<NetworkingConfig>| !config.connect_to_host() && config.host),
start_listen_server.run_if(|config: Res<NetConfig>| config.is_host()),
);
app.add_systems(
OnEnter(GameState::Waiting),
start_client.run_if(|config: Res<NetworkingConfig>| config.connect_to_host()),
start_client.run_if(|config: Res<NetConfig>| config.is_client()),
);
} else {
app.add_systems(OnEnter(GameState::Waiting), start_dedicated_server);
}
app.add_systems(Update, accept_p2p_sessions);
app.add_systems(Update, log_steam_events);
}
#[derive(Resource, Reflect, Debug)]
@@ -196,20 +193,14 @@ pub enum GameState {
Playing,
}
fn accept_p2p_sessions(
mut events: MessageReader<SteamworksEvent>,
steam_client: Res<bevy_steamworks::Client>,
) {
fn log_steam_events(events: Option<MessageReader<SteamworksEvent>>) {
let Some(mut events) = events else {
return;
};
for event in events.read() {
if let SteamworksEvent::CallbackResult(CallbackResult::P2PSessionRequest(
P2PSessionRequest { remote },
)) = event
{
info!("Accepting P2P session from: {:?}", remote);
steam_client.networking().accept_p2p_session(*remote);
} else {
info!("steamworks event: {:?}", event);
}
let SteamworksEvent::CallbackResult(result) = event;
info!("steam: {:?}", result);
}
}

View File

@@ -1,5 +1,7 @@
use crate::{
GameState, global_observer,
GameState,
config::NetConfig,
global_observer,
heads_database::HeadsDatabase,
player::ClientPlayerId,
protocol::{ClientEnteredPlaying, PlayerIdCounter, SetGameTick, messages::AssignClientPlayer},
@@ -55,7 +57,8 @@ fn open_renet_server(
mut commands: Commands,
channels: Res<RepliconChannels>,
mut next: ResMut<NextState<GameState>>,
steam_client: Res<bevy_steamworks::Client>,
steam_client: Option<Res<bevy_steamworks::Client>>,
config: Res<NetConfig>,
) -> Result<(), BevyError> {
info!("opening server");
@@ -68,18 +71,46 @@ fn open_renet_server(
..Default::default()
});
let steam_config = bevy_replicon_renet::steam::SteamServerConfig {
access_permission: bevy_replicon_renet::steam::AccessPermission::FriendsOnly,
max_clients: 16,
};
if let NetConfig::SteamHost = *config {
let Some(steam_client) = steam_client else {
return Err("Steam client not found".into());
};
let client = (**steam_client).clone();
let transport = SteamServerTransport::new(client, steam_config)?;
let steam_config = bevy_replicon_renet::steam::SteamServerConfig {
access_permission: bevy_replicon_renet::steam::AccessPermission::FriendsOnly,
max_clients: 16,
};
commands.queue(|w: &mut World| {
w.insert_resource(server);
w.insert_non_send_resource(transport);
});
let client = (**steam_client).clone();
let transport = SteamServerTransport::new(client, steam_config)?;
commands.queue(|w: &mut World| {
w.insert_resource(server);
w.insert_non_send_resource(transport);
});
info!("hosting server: steam");
} else if let NetConfig::NetcodeHost { port } = *config {
use std::time::SystemTime;
let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
let socket = std::net::UdpSocket::bind((std::net::Ipv4Addr::UNSPECIFIED, port))?;
let server_config = bevy_replicon_renet::netcode::ServerConfig {
current_time,
max_clients: 1,
protocol_id: 0,
authentication: bevy_replicon_renet::netcode::ServerAuthentication::Unsecure,
public_addresses: Default::default(),
};
let transport =
bevy_replicon_renet::netcode::NetcodeServerTransport::new(server_config, socket)?;
commands.insert_resource(server);
commands.insert_resource(transport);
info!("hosting server: netcode on port {port}");
}
next.set(GameState::Playing);

View File

@@ -12,8 +12,8 @@ server_args := "--bin hedz_reloaded_server --no-default-features"
run *args:
RUST_BACKTRACE=1 cargo r {{ client_args }} -- {{ args }}
server:
RUST_BACKTRACE=1 cargo r {{ server_args }}
server *args:
RUST_BACKTRACE=1 cargo r {{ server_args }} -- {{ args }}
dbg *args:
RUST_BACKTRACE=1 cargo r {{ client_args }} --features dbg -- {{ args }}
@@ -22,7 +22,7 @@ dbg-server:
RUST_BACKTRACE=1 cargo r {{ server_args }} --features dbg
sort:
cargo sort --check --workspace
cargo sort --workspace
check:
cargo sort --check --workspace