add renet_steam support (#92)
This commit is contained in:
13
Cargo.lock
generated
13
Cargo.lock
generated
@@ -1519,6 +1519,7 @@ dependencies = [
|
||||
"bevy_time",
|
||||
"renet",
|
||||
"renet_netcode",
|
||||
"renet_steam",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5564,6 +5565,18 @@ dependencies = [
|
||||
"renetcode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "renet_steam"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f6018afe469d3d2d49fab8fd1cecc46c588ac61498cad879d5781c44b277421"
|
||||
dependencies = [
|
||||
"bevy_ecs",
|
||||
"log",
|
||||
"renet",
|
||||
"steamworks",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "renetcode"
|
||||
version = "1.0.0"
|
||||
|
||||
@@ -61,7 +61,8 @@ bevy_pkv = { version = "0.14", default-features = false, features = [
|
||||
"redb",
|
||||
] }
|
||||
bevy_replicon = "0.37.1"
|
||||
bevy_replicon_renet = "0.13.0"
|
||||
# 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",
|
||||
@@ -76,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."*"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
GameState,
|
||||
config::NetworkingConfig,
|
||||
config::NetConfig,
|
||||
protocol::{
|
||||
ClientEnteredPlaying, TbMapEntityId, TbMapEntityMapping, messages::DespawnTbMapEntity,
|
||||
},
|
||||
@@ -17,14 +17,10 @@ use bevy_replicon::{
|
||||
};
|
||||
use bevy_replicon_renet::{
|
||||
RenetChannelsExt,
|
||||
netcode::{ClientAuthentication, NetcodeClientTransport, NetcodeError},
|
||||
renet::{ConnectionConfig, RenetClient},
|
||||
};
|
||||
use bevy_steamworks::Client;
|
||||
use bevy_trenchbroom::geometry::Brushes;
|
||||
use std::{
|
||||
net::{Ipv4Addr, UdpSocket},
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
pub mod audio;
|
||||
pub mod backpack;
|
||||
@@ -55,7 +51,7 @@ pub fn plugin(app: &mut App) {
|
||||
|
||||
app.add_systems(
|
||||
OnEnter(GameState::Connecting),
|
||||
connect_to_server.run_if(|config: Res<NetworkingConfig>| config.server.is_some()),
|
||||
connect_to_server.run_if(|config: Res<NetConfig>| config.is_client()),
|
||||
);
|
||||
app.add_systems(Update, despawn_absent_map_entities);
|
||||
app.add_systems(
|
||||
@@ -89,8 +85,9 @@ fn on_disconnect() {
|
||||
|
||||
fn connect_to_server(
|
||||
mut commands: Commands,
|
||||
config: Res<NetworkingConfig>,
|
||||
config: Res<NetConfig>,
|
||||
channels: Res<RepliconChannels>,
|
||||
steam_client: Option<Res<Client>>,
|
||||
) -> Result {
|
||||
let server_channels_config = channels.server_configs();
|
||||
let client_channels_config = channels.client_configs();
|
||||
@@ -102,30 +99,45 @@ fn connect_to_server(
|
||||
});
|
||||
|
||||
commands.insert_resource(client);
|
||||
commands.insert_resource(client_transport(&config)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
if let NetConfig::SteamClient(host_steam_id) = &*config {
|
||||
let Some(steam_client) = steam_client else {
|
||||
return Err("Steam client not found".into());
|
||||
};
|
||||
|
||||
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:?}");
|
||||
|
||||
fn client_transport(config: &NetworkingConfig) -> Result<NetcodeClientTransport, NetcodeError> {
|
||||
let current_time = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap();
|
||||
let client_id = current_time.as_millis() as u64;
|
||||
let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0))?;
|
||||
let server_addr = config
|
||||
.server
|
||||
.flatten()
|
||||
.unwrap_or_else(|| "127.0.0.1:31111".parse().unwrap());
|
||||
let authentication = ClientAuthentication::Unsecure {
|
||||
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,
|
||||
server_addr: host_addr.clone(),
|
||||
user_data: None,
|
||||
};
|
||||
let transport = bevy_replicon_renet::netcode::NetcodeClientTransport::new(
|
||||
current_time,
|
||||
authentication,
|
||||
socket,
|
||||
)?;
|
||||
|
||||
info!("attempting connection to {server_addr}");
|
||||
NetcodeClientTransport::new(current_time, authentication, socket)
|
||||
commands.insert_resource(transport);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
|
||||
@@ -4,10 +4,11 @@ use bevy_pkv::prelude::*;
|
||||
use crate::{client::audio::SoundSettings, utils::Debounce};
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
#[cfg(not(feature = "dbg"))]
|
||||
app.insert_resource(PkvStore::new("Rustunit", "HEDZ"));
|
||||
|
||||
app.add_systems(Update, persist_settings);
|
||||
app.add_systems(Startup, load_settings);
|
||||
app.add_systems(Update, persist_settings.run_if(resource_exists::<PkvStore>));
|
||||
app.add_systems(Startup, load_settings.run_if(resource_exists::<PkvStore>));
|
||||
}
|
||||
|
||||
fn persist_settings(
|
||||
|
||||
@@ -61,6 +61,14 @@ fn test_steam_system(steam_client: Res<Client>) {
|
||||
},
|
||||
);
|
||||
|
||||
let id = steam_client.user().steam_id();
|
||||
|
||||
info!("Steam ID: {:?}", id);
|
||||
|
||||
steam_client
|
||||
.friends()
|
||||
.set_rich_presence("connect", Some(id.raw().to_string().as_str()));
|
||||
|
||||
for friend in steam_client.friends().get_friends(FriendFlags::IMMEDIATE) {
|
||||
info!(
|
||||
"Steam Friend: {:?} - {}({:?})",
|
||||
|
||||
@@ -1,25 +1,92 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use clap::Parser;
|
||||
use std::net::SocketAddr;
|
||||
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 {
|
||||
/// The IP/port to connect to.
|
||||
/// If `None`, host a local server.
|
||||
/// If Some(None), connect to the default server (`127.0.0.1:31111`)
|
||||
/// Otherwise, connect to the given server.
|
||||
/// Does nothing on the server.
|
||||
struct NetworkingConfig {
|
||||
/// Steam id of the host to connect to
|
||||
#[arg(long)]
|
||||
pub server: Option<Option<SocketAddr>>,
|
||||
/// 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>>,
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -47,6 +47,7 @@ use bevy_common_assets::ron::RonAssetPlugin;
|
||||
use bevy_replicon::{RepliconPlugins, prelude::ClientId};
|
||||
use bevy_replicon_renet::RepliconRenetPlugins;
|
||||
use bevy_sprite3d::Sprite3dPlugin;
|
||||
use bevy_steamworks::SteamworksEvent;
|
||||
use bevy_trenchbroom::{
|
||||
TrenchBroomPlugins, config::TrenchBroomConfig, prelude::TrenchBroomPhysicsPlugin,
|
||||
};
|
||||
@@ -148,21 +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.server.is_none() && !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.server.is_none() && 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.server.is_some()),
|
||||
start_client.run_if(|config: Res<NetConfig>| config.is_client()),
|
||||
);
|
||||
} else {
|
||||
app.add_systems(OnEnter(GameState::Waiting), start_dedicated_server);
|
||||
}
|
||||
|
||||
app.add_systems(Update, log_steam_events);
|
||||
}
|
||||
|
||||
#[derive(Resource, Reflect, Debug)]
|
||||
@@ -192,6 +193,17 @@ pub enum GameState {
|
||||
Playing,
|
||||
}
|
||||
|
||||
fn log_steam_events(events: Option<MessageReader<SteamworksEvent>>) {
|
||||
let Some(mut events) = events else {
|
||||
return;
|
||||
};
|
||||
|
||||
for event in events.read() {
|
||||
let SteamworksEvent::CallbackResult(result) = event;
|
||||
info!("steam: {:?}", result);
|
||||
}
|
||||
}
|
||||
|
||||
fn start_solo_client(
|
||||
commands: Commands,
|
||||
mut next: ResMut<NextState<GameState>>,
|
||||
|
||||
@@ -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},
|
||||
@@ -16,12 +18,8 @@ use bevy_replicon::{
|
||||
};
|
||||
use bevy_replicon_renet::{
|
||||
RenetChannelsExt,
|
||||
netcode::{NetcodeServerTransport, ServerAuthentication},
|
||||
renet::{ConnectionConfig, RenetServer},
|
||||
};
|
||||
use std::{
|
||||
net::{Ipv4Addr, UdpSocket},
|
||||
time::SystemTime,
|
||||
steam::SteamServerTransport,
|
||||
};
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
@@ -59,6 +57,8 @@ fn open_renet_server(
|
||||
mut commands: Commands,
|
||||
channels: Res<RepliconChannels>,
|
||||
mut next: ResMut<NextState<GameState>>,
|
||||
steam_client: Option<Res<bevy_steamworks::Client>>,
|
||||
config: Res<NetConfig>,
|
||||
) -> Result<(), BevyError> {
|
||||
info!("opening server");
|
||||
|
||||
@@ -71,22 +71,48 @@ fn open_renet_server(
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
if let NetConfig::SteamHost = *config {
|
||||
let Some(steam_client) = steam_client else {
|
||||
return Err("Steam client not found".into());
|
||||
};
|
||||
|
||||
let steam_config = bevy_replicon_renet::steam::SteamServerConfig {
|
||||
access_permission: bevy_replicon_renet::steam::AccessPermission::FriendsOnly,
|
||||
max_clients: 16,
|
||||
};
|
||||
|
||||
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 port = 31111;
|
||||
let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, port))?;
|
||||
let socket = std::net::UdpSocket::bind((std::net::Ipv4Addr::UNSPECIFIED, port))?;
|
||||
|
||||
let server_config = bevy_replicon_renet::netcode::ServerConfig {
|
||||
current_time,
|
||||
max_clients: 1,
|
||||
max_clients: 8,
|
||||
protocol_id: 0,
|
||||
authentication: ServerAuthentication::Unsecure,
|
||||
authentication: bevy_replicon_renet::netcode::ServerAuthentication::Unsecure,
|
||||
public_addresses: Default::default(),
|
||||
};
|
||||
let transport = NetcodeServerTransport::new(server_config, socket)?;
|
||||
let transport =
|
||||
bevy_replicon_renet::netcode::NetcodeServerTransport::new(server_config, socket)?;
|
||||
|
||||
commands.insert_resource(server);
|
||||
commands.insert_resource(transport);
|
||||
|
||||
info!("hosting a server on port {port}");
|
||||
info!("hosting server: netcode on port {port}");
|
||||
} else {
|
||||
return Err("Invalid configuration, choose either steam or netcode".into());
|
||||
}
|
||||
|
||||
next.set(GameState::Playing);
|
||||
|
||||
|
||||
4
justfile
4
justfile
@@ -13,7 +13,7 @@ run *args:
|
||||
RUST_BACKTRACE=1 cargo r {{ client_args }} -- {{ args }}
|
||||
|
||||
server:
|
||||
RUST_BACKTRACE=1 cargo r {{ server_args }}
|
||||
RUST_BACKTRACE=1 cargo r {{ server_args }} -- --netcode-host
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user