mod earth_parts;
mod gravity;
mod part;
mod heat;
mod drill;
mod craft;
mod damping;
mod management;
pub mod planets;
pub mod player;
mod system_sets;
mod priority;
pub mod orbit;
pub mod plugins;
pub mod components;
pub mod visibility;
pub mod client_timing;
use std::net::{SocketAddr, UdpSocket};
use bevy_replicon::prelude::{ConnectedClient, Replicated, RepliconChannels};
use bevy_replicon::server::AuthorizedClient;
use bevy_replicon_renet2::RenetChannelsExt;
use bevy_replicon_renet2::netcode::{
BoxedSocket, NativeSocket, NetcodeServerTransport, ServerAuthentication, ServerSetupConfig,
WebTransportServer, WebTransportServerConfig,
};
use bevy_replicon_renet2::renet2::{ConnectionConfig, RenetServer};
use web_time::SystemTime;
use crate::server::craft::craft_plugin;
use crate::server::damping::damping_plugin;
use crate::server::drill::drill_plugin;
use crate::server::earth_parts::spawn_parts_plugin;
use crate::server::gravity::newtonian_gravity_plugin;
use crate::server::management::spawn_management_server;
use crate::server::part::part_management_plugin;
use crate::server::priority::replication_priority_plugin;
use crate::server::planets::planets_plugin;
use crate::server::player::player_management_plugin;
use crate::server::system_sets::{PlayerInputSet, WorldUpdateSet};
use crate::prelude::*;
use crate::server::client_timing::client_timing_plugin;
use crate::server::orbit::OrbitPlugin;
use crate::server::player::thrust::server_thrust_plugin;
use crate::shared::net::{encode_cert_hash, ManagementInfo, STARKINGDOMS_PROTOCOL_MAGIC};
const MAX_CLIENTS: usize = 32;
pub struct ServerPlugin {
pub management_bind: SocketAddr,
pub native_bind: SocketAddr,
pub wt_bind: SocketAddr,
}
impl Plugin for ServerPlugin {
fn build(&self, app: &mut App) {
app
.add_plugins(planets_plugin)
.add_plugins(newtonian_gravity_plugin)
.add_plugins(player_management_plugin)
.add_plugins(spawn_parts_plugin)
.add_plugins(part_management_plugin)
.add_plugins(server_thrust_plugin)
/*.add_plugins(heat_cooling_plugin)
.add_plugins(heat_radiation_plugin)
.add_plugins(heat_conduction_plugin)*/
.add_plugins(drill_plugin)
.add_plugins(craft_plugin)
.add_plugins(OrbitPlugin)
.add_plugins(damping_plugin)
.add_plugins(replication_priority_plugin)
.add_plugins(client_timing_plugin)
.configure_sets(Update, WorldUpdateSet.before(PlayerInputSet))
.add_systems(Update, handle_authorized)
.add_observer(on_client_disconnected);
let management_bind = self.management_bind;
let native_bind = self.native_bind;
let wt_bind = self.wt_bind;
app.add_systems(Startup, move |mut commands: Commands, channels: Res<RepliconChannels>| {
let connection_config = ConnectionConfig::from_channels(channels.server_configs(), channels.client_configs());
let server = RenetServer::new(connection_config);
let native_socket = NativeSocket::new(UdpSocket::bind(native_bind).expect("failed to bind server UDP socket"))
.expect("failed to create native socket");
let (wt_config, cert_hash) = WebTransportServerConfig::new_selfsigned(wt_bind, MAX_CLIENTS)
.expect("failed to create self-signed WebTransport server config");
let runtime = tokio::runtime::Runtime::new().expect("failed to create tokio runtime");
let wt_socket = WebTransportServer::new(wt_config, runtime.handle().clone())
.expect("failed to create WebTransport server socket");
let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let server_config = ServerSetupConfig {
current_time,
max_clients: MAX_CLIENTS,
protocol_id: STARKINGDOMS_PROTOCOL_MAGIC,
socket_addresses: vec![vec![native_bind], vec![wt_bind]],
authentication: ServerAuthentication::Unsecure,
};
let transport = NetcodeServerTransport::new_with_sockets(server_config, vec![
BoxedSocket::new(native_socket),
BoxedSocket::new(wt_socket),
]).expect("failed to create server transport");
spawn_management_server(management_bind, ManagementInfo {
native_port: native_bind.port(),
wt_port: wt_bind.port(),
cert_hash: encode_cert_hash(&cert_hash.hash),
});
commands.insert_resource(server);
commands.insert_resource(transport);
commands.insert_resource(TokioRuntime(runtime));
});
}
}
#[derive(Resource)]
struct TokioRuntime(tokio::runtime::Runtime);
#[derive(Component, Debug)]
pub struct ConnectedGameEntity {
pub network_entity: Entity,
}
#[derive(Component)]
pub struct ConnectedNetworkEntity {
pub game_entity: Entity,
}
fn handle_authorized(
newly_authorized_clients: Query<Entity, Added<AuthorizedClient>>,
mut commands: Commands
) {
for client in newly_authorized_clients.iter() {
let player = commands
.spawn((Replicated, ConnectedGameEntity {
network_entity: client,
}))
.id();
commands.entity(client).insert((
Replicated,
ConnectedNetworkEntity {
game_entity: player,
},
));
}
}
fn on_client_disconnected(
trigger: On<Remove, ConnectedClient>,
network_entities: Query<&ConnectedNetworkEntity>,
mut commands: Commands,
) {
let client = trigger.event_target();
let Ok(connected) = network_entities.get(client) else {
return;
};
if let Ok(mut entity) = commands.get_entity(connected.game_entity) {
entity.despawn();
}
}