use aeronet_io::Session;
use aeronet_io::server::Close;
use aeronet_steam::SessionConfig;
use aeronet_steam::server::{
ListenTarget, SessionRequest, SessionResponse, SteamNetServer, SteamNetServerClient,
};
use alloc::format;
use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*;
use bevy_ecs::relationship::RelationshipTarget;
use lightyear_aeronet::server::ServerAeronetPlugin;
use lightyear_aeronet::{AeronetLink, AeronetLinkOf, AeronetPlugin};
use lightyear_connection::client::{Connected, Disconnected};
use lightyear_connection::client_of::{ClientOf, SkipNetcode};
use lightyear_connection::server::{Start, Started, Stop};
use lightyear_core::id::{PeerId, RemoteId};
use lightyear_link::prelude::LinkOf;
use lightyear_link::server::Server;
use lightyear_link::{Link, LinkStart, Linked, Linking};
use tracing::{info, trace};
pub struct SteamServerPlugin;
impl Plugin for SteamServerPlugin {
fn build(&self, app: &mut App) {
if !app.is_plugin_added::<AeronetPlugin>() {
app.add_plugins(AeronetPlugin);
}
if !app.is_plugin_added::<ServerAeronetPlugin>() {
app.add_plugins(ServerAeronetPlugin);
}
app.add_plugins(aeronet_steam::server::SteamNetServerPlugin);
app.add_observer(Self::link);
app.add_observer(Self::on_linked);
app.add_observer(Self::start);
app.add_observer(Self::on_session_request);
app.add_observer(Self::on_connection);
app.add_observer(Self::on_disconnected);
app.add_observer(Self::stop);
}
}
#[derive(Debug, Component)]
#[require(Server)]
pub struct SteamServerIo {
pub target: ListenTarget,
pub config: SessionConfig,
}
#[derive(Component)]
#[require(SkipNetcode)]
pub struct SteamClientOf;
impl SteamServerPlugin {
fn link(
trigger: On<LinkStart>,
query: Query<(Entity, &SteamServerIo), (Without<Linking>, Without<Linked>)>,
mut commands: Commands,
) -> Result {
if let Ok((entity, io)) = query.get(trigger.entity) {
let config = io.config.clone();
let target = io.target;
commands.queue(move |world: &mut World| {
info!("Server Steam starting at {:?}", target);
let child = world.spawn((AeronetLinkOf(entity), Name::from("SteamServer")));
SteamNetServer::open(config, target).apply(child);
});
}
Ok(())
}
fn on_linked(
trigger: On<Add, Linked>,
query: Query<(), With<SteamServerIo>>,
mut commands: Commands,
) {
if query.get(trigger.entity).is_ok() {
commands.entity(trigger.entity).insert(Started);
}
}
fn start(
trigger: On<Start>,
query: Query<(), (Without<Linking>, Without<Linked>, With<SteamServerIo>)>,
mut commands: Commands,
) {
if query.get(trigger.entity).is_ok() {
trace!("SteamServer Start triggered, triggering LinkStart");
commands.trigger(LinkStart {
entity: trigger.entity,
});
}
}
fn on_session_request(mut request: On<SessionRequest>) {
trace!("Accepted steam link-of request: {:?}", request.event());
request.respond(SessionResponse::Accepted);
}
fn on_connection(
trigger: On<Add, Session>,
query: Query<&AeronetLinkOf>,
child_query: Query<(&ChildOf, &SteamNetServerClient)>,
mut commands: Commands,
) {
if let Ok((child_of, steam_conn)) = child_query.get(trigger.entity)
&& let Ok(server_link) = query.get(child_of.parent())
{
trace!(
"New Steam connection established with client that has SteamId: {:?}",
steam_conn.steam_id()
);
let link_entity = commands
.spawn((
LinkOf {
server: server_link.0,
},
Link::new(None),
ClientOf,
Connected,
RemoteId(PeerId::Steam(steam_conn.steam_id().raw())),
SteamClientOf,
))
.id();
commands
.entity(trigger.entity)
.insert((AeronetLinkOf(link_entity), Name::from("SteamClientOf")));
}
}
fn on_disconnected(
trigger: On<aeronet_io::connection::Disconnected>,
query: Query<&AeronetLinkOf, With<SteamNetServerClient>>,
mut commands: Commands,
) {
if let Ok(aeronet_link_of) = query.get(trigger.entity)
&& let Ok(mut link_of_entity) = commands.get_entity(aeronet_link_of.0)
{
trace!(
"Aeronet SteamClientOf entity {:?} disconnected: {:?}. Disconnecting and despawning LinkOf entity {:?}.",
trigger.entity,
trigger,
link_of_entity.id()
);
link_of_entity
.remove::<AeronetLink>()
.insert(Disconnected {
reason: Some(format!("Aeronet link disconnected: {trigger:?}")),
})
.try_despawn();
}
}
fn stop(
trigger: On<Stop>,
query: Query<&AeronetLink, With<SteamServerIo>>,
mut commands: Commands,
) {
if let Ok(aeronet_link) = query.get(trigger.entity) {
trace!("SteamServer Stop triggered, closing.");
commands.trigger(Close::new(*aeronet_link.collection(), "User requested"));
}
}
}