mod earth_parts;
mod gravity;
mod part;
pub mod planets;
pub mod player;
mod world_config;
mod system_sets;
use crate::server::earth_parts::spawn_parts_plugin;
use crate::server::gravity::newtonian_gravity_plugin;
use crate::server::part::part_management_plugin;
use crate::server::planets::planets_plugin;
use crate::server::player::player_management_plugin;
use crate::server::world_config::world_config_plugin;
use aeronet::io::Session;
use aeronet::io::connection::{Disconnected, LocalAddr};
use aeronet::io::server::Server;
use aeronet_replicon::server::AeronetRepliconServer;
use aeronet_websocket::server::WebSocketServer;
use bevy::prelude::*;
use bevy_replicon::prelude::Replicated;
use std::net::SocketAddr;
use bevy_rapier2d::prelude::PhysicsSet;
use crate::server::system_sets::{PlayerInputSet, WorldUpdateSet};
pub struct ServerPlugin {
pub bind: SocketAddr,
pub max_clients: usize,
}
impl Plugin for ServerPlugin {
fn build(&self, app: &mut App) {
let config = self.websocket_config();
app.add_systems(FixedPreUpdate, bevy_replicon::server::increment_tick) // !!important!! do not remove or move
.add_systems(Startup, move |mut commands: Commands| {
let server = commands
.spawn(Name::new("ws-server"))
.insert(AeronetRepliconServer)
.insert(Transform::from_xyz(0.0, 0.0, 0.0))
.queue(WebSocketServer::open(config.clone()))
.id();
info!(entity_id=?server, "opening websocket server");
})
.add_observer(on_opened)
.add_observer(on_connected)
.add_observer(on_disconnected)
.add_plugins(planets_plugin)
.add_plugins(world_config_plugin)
.add_plugins(newtonian_gravity_plugin)
.add_plugins(player_management_plugin)
.add_plugins(spawn_parts_plugin)
.add_plugins(part_management_plugin)
.configure_sets(Update, WorldUpdateSet.before(PlayerInputSet))
.configure_sets(Update, PlayerInputSet.before(PhysicsSet::SyncBackend));
}
}
impl ServerPlugin {
fn websocket_config(&self) -> aeronet_websocket::server::ServerConfig {
aeronet_websocket::server::ServerConfig::builder()
.with_bind_address(self.bind)
.with_no_encryption()
}
}
#[derive(Component)]
pub struct ConnectedGameEntity {
pub network_entity: Entity,
}
#[derive(Component)]
pub struct ConnectedNetworkEntity {
pub game_entity: Entity,
}
fn on_opened(trigger: Trigger<OnAdd, Server>, servers: Query<&LocalAddr>) {
let server = trigger.target();
let local_addr = servers.get(server).unwrap();
info!(server_entity=?server, "websocket server opened on {:?}", *local_addr);
}
fn on_connected(
trigger: Trigger<OnAdd, Session>,
clients: Query<&ChildOf>,
mut commands: Commands,
) {
let client = trigger.target();
let Ok(&ChildOf(server)) = clients.get(client) else {
return;
};
info!(?client, ?server, "client connected");
// spawn the player
let player = commands
.spawn(ConnectedGameEntity {
network_entity: client,
})
.id();
commands.entity(client).insert((
Replicated,
ConnectedNetworkEntity {
game_entity: player,
},
));
}
fn on_disconnected(
trigger: Trigger<Disconnected>,
clients: Query<&ChildOf>,
player_entity: Query<&ConnectedNetworkEntity>,
mut commands: Commands,
) {
let client = trigger.target();
let Ok(&ChildOf(server)) = clients.get(client) else {
return;
};
match &*trigger {
Disconnected::ByUser(reason) => {
info!(?client, ?server, ?reason, "client disconnected by user");
}
Disconnected::ByPeer(reason) => {
info!(?client, ?server, ?reason, "client disconnected by peer");
}
Disconnected::ByError(err) => {
warn!(?client, ?server, "client disconnected with error: {err:?}");
}
}
let Ok(other_entity) = player_entity.get(client) else {
return;
};
let Ok(mut commands) = commands.get_entity(other_entity.game_entity) else {
return;
};
commands.despawn();
}