Files
HEDZReloaded/crates/hedz_reloaded/src/server.rs
extrawurst e9f53c11e9 allow local netcode multiplayer
* fix crashing when steam client not available
* allow more than 1 connected player
* disable settings persistence when in dbg
2025-12-20 12:08:52 -05:00

151 lines
4.3 KiB
Rust

use crate::{
GameState,
config::NetConfig,
global_observer,
heads_database::HeadsDatabase,
player::ClientPlayerId,
protocol::{ClientEnteredPlaying, PlayerIdCounter, SetGameTick, messages::AssignClientPlayer},
tb_entities::SpawnPoint,
tick::GameTick,
};
use bevy::prelude::*;
use bevy_replicon::{
prelude::{
ClientId, ConnectedClient, FromClient, RepliconChannels, SendMode, ServerTriggerExt,
ToClients,
},
server::AuthorizedClient,
};
use bevy_replicon_renet::{
RenetChannelsExt,
renet::{ConnectionConfig, RenetServer},
steam::SteamServerTransport,
};
pub fn plugin(app: &mut App) {
app.add_systems(OnEnter(GameState::Hosting), open_renet_server);
// Replicon
global_observer!(app, on_connected);
global_observer!(app, on_disconnected);
// Server logic
global_observer!(app, on_client_playing);
}
fn on_client_playing(
trigger: On<FromClient<ClientEnteredPlaying>>,
commands: Commands,
clients: Query<&ClientPlayerId>,
query: Query<&Transform, With<SpawnPoint>>,
heads_db: Res<HeadsDatabase>,
) -> Result {
info!("client has entered playing gamestate");
let id = clients.get(trigger.client_id.entity().unwrap()).unwrap();
crate::player::spawn(commands, trigger.client_id, id.0, query, heads_db)
.ok_or("failed to spawn player")?;
Ok(())
}
//
// Renet
//
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");
let server_channels_config = channels.server_configs();
let client_channels_config = channels.client_configs();
let server = RenetServer::new(ConnectionConfig {
server_channels_config,
client_channels_config,
..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 socket = std::net::UdpSocket::bind((std::net::Ipv4Addr::UNSPECIFIED, port))?;
let server_config = bevy_replicon_renet::netcode::ServerConfig {
current_time,
max_clients: 8,
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);
Ok(())
}
//
// server logic
//
fn on_connected(
trigger: On<Add, AuthorizedClient>,
game_tick: Res<GameTick>,
mut commands: Commands,
mut assign_id: MessageWriter<ToClients<AssignClientPlayer>>,
mut ids: ResMut<PlayerIdCounter>,
) {
let client = trigger.event_target();
info!("{client} connected to server!");
let id = ids.alloc();
commands.entity(client).insert(ClientPlayerId(id));
assign_id.write(ToClients {
mode: SendMode::Direct(ClientId::Client(trigger.entity)),
message: AssignClientPlayer(id),
});
commands.server_trigger(ToClients {
mode: SendMode::Direct(ClientId::Client(trigger.entity)),
message: SetGameTick(game_tick.0),
});
}
fn on_disconnected(on: On<Remove, ConnectedClient>) {
info!("client {} disconnected", on.entity);
}