Skip to content

Commit 82d4e5b

Browse files
committed
Move server network code to separate file
1 parent 287ed6d commit 82d4e5b

File tree

3 files changed

+166
-145
lines changed

3 files changed

+166
-145
lines changed

minigolf_server/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ fn main() {
33
panic!("this example is not available on WASM");
44
}
55

6+
#[cfg(not(target_family = "wasm"))]
7+
mod network;
68
#[cfg(not(target_family = "wasm"))]
79
mod server;
810

minigolf_server/src/network.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
use {
2+
crate::server::Args,
3+
aeronet::io::{
4+
Session,
5+
connection::{DisconnectReason, Disconnected, LocalAddr},
6+
server::Server,
7+
},
8+
aeronet_replicon::server::{AeronetRepliconServer, AeronetRepliconServerPlugin},
9+
aeronet_websocket::server::{WebSocketServer, WebSocketServerPlugin},
10+
aeronet_webtransport::{
11+
cert,
12+
server::{SessionRequest, SessionResponse, WebTransportServer, WebTransportServerPlugin},
13+
wtransport,
14+
},
15+
bevy::prelude::*,
16+
bevy_replicon::prelude::*,
17+
core::time::Duration,
18+
};
19+
20+
/// Sets up minigolf server networking.
21+
#[derive(Debug)]
22+
pub(crate) struct ServerNetworkPlugin;
23+
24+
impl Plugin for ServerNetworkPlugin {
25+
fn build(&self, app: &mut App) {
26+
app.add_plugins((WebTransportServerPlugin, WebSocketServerPlugin))
27+
.add_plugins((
28+
RepliconPlugins.set(ServerPlugin {
29+
// 1 frame lasts `1.0 / TICK_RATE` anyway
30+
tick_policy: TickPolicy::Manual,
31+
..Default::default()
32+
}),
33+
AeronetRepliconServerPlugin,
34+
))
35+
.add_observer(on_opened)
36+
.add_observer(on_session_request)
37+
.add_observer(on_connected)
38+
.add_observer(on_disconnected)
39+
.add_systems(Startup, (open_web_transport_server, open_web_socket_server));
40+
}
41+
}
42+
43+
//
44+
// WebTransport
45+
//
46+
47+
fn open_web_transport_server(mut commands: Commands, args: Res<Args>) {
48+
let identity = wtransport::Identity::self_signed(["localhost", "127.0.0.1", "::1"])
49+
.expect("all given SANs should be valid DNS names");
50+
let cert = &identity.certificate_chain().as_slice()[0];
51+
let spki_fingerprint = cert::spki_fingerprint_b64(cert).expect("should be a valid certificate");
52+
let cert_hash = cert::hash_to_b64(cert.hash());
53+
info!("************************");
54+
info!("SPKI FINGERPRINT");
55+
info!(" {spki_fingerprint}");
56+
info!("CERTIFICATE HASH");
57+
info!(" {cert_hash}");
58+
info!("************************");
59+
60+
let config = web_transport_config(identity, &args);
61+
let server = commands
62+
.spawn((Name::new("WebTransport Server"), AeronetRepliconServer))
63+
.queue(WebTransportServer::open(config))
64+
.id();
65+
info!("Opening WebTransport server {server}");
66+
}
67+
68+
type WebTransportServerConfig = aeronet_webtransport::server::ServerConfig;
69+
70+
fn web_transport_config(identity: wtransport::Identity, args: &Args) -> WebTransportServerConfig {
71+
WebTransportServerConfig::builder()
72+
.with_bind_default(args.wt_port)
73+
.with_identity(identity)
74+
.keep_alive_interval(Some(Duration::from_secs(1)))
75+
.max_idle_timeout(Some(Duration::from_secs(5)))
76+
.expect("should be a valid idle timeout")
77+
.build()
78+
}
79+
80+
//
81+
// WebSocket
82+
//
83+
84+
type WebSocketServerConfig = aeronet_websocket::server::ServerConfig;
85+
86+
fn open_web_socket_server(mut commands: Commands, args: Res<Args>) {
87+
let config = web_socket_config(&args);
88+
let server = commands
89+
.spawn((Name::new("WebSocket Server"), AeronetRepliconServer))
90+
.queue(WebSocketServer::open(config))
91+
.id();
92+
info!("Opening WebSocket server {server}");
93+
}
94+
95+
fn web_socket_config(args: &Args) -> WebSocketServerConfig {
96+
WebSocketServerConfig::builder()
97+
.with_bind_default(args.ws_port)
98+
.with_no_encryption()
99+
}
100+
101+
fn on_opened(trigger: Trigger<OnAdd, Server>, servers: Query<&LocalAddr>) {
102+
let server = trigger.entity();
103+
let local_addr = servers
104+
.get(server)
105+
.expect("opened server should have a binding socket `LocalAddr`");
106+
info!("{server} opened on {}", **local_addr);
107+
}
108+
109+
fn on_session_request(mut request: Trigger<SessionRequest>, clients: Query<&Parent>) {
110+
let client = request.entity();
111+
let Ok(server) = clients.get(client).map(Parent::get) else {
112+
return;
113+
};
114+
115+
info!("{client} connecting to {server} with headers:");
116+
for (header_key, header_value) in &request.headers {
117+
info!(" {header_key}: {header_value}");
118+
}
119+
120+
request.respond(SessionResponse::Accepted);
121+
}
122+
123+
fn on_connected(trigger: Trigger<OnAdd, Session>, clients: Query<&Parent>) {
124+
let client = trigger.entity();
125+
let Ok(server) = clients.get(client).map(Parent::get) else {
126+
return;
127+
};
128+
info!("{client} connected to {server}");
129+
}
130+
131+
fn on_disconnected(trigger: Trigger<Disconnected>, clients: Query<&Parent>) {
132+
let client = trigger.entity();
133+
let Ok(server) = clients.get(client).map(Parent::get) else {
134+
return;
135+
};
136+
137+
match &trigger.reason {
138+
DisconnectReason::User(reason) => {
139+
info!("{client} disconnected from {server} by user: {reason}");
140+
}
141+
DisconnectReason::Peer(reason) => {
142+
info!("{client} disconnected from {server} by peer: {reason}");
143+
}
144+
DisconnectReason::Error(err) => {
145+
warn!("{client} disconnected from {server} due to error: {err:?}");
146+
}
147+
}
148+
}

minigolf_server/src/server.rs

Lines changed: 16 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,6 @@
11
use {
2-
aeronet::io::{
3-
Session,
4-
connection::{DisconnectReason, Disconnected, LocalAddr},
5-
server::Server,
6-
},
7-
aeronet_replicon::server::{AeronetRepliconServer, AeronetRepliconServerPlugin},
8-
aeronet_websocket::server::{WebSocketServer, WebSocketServerPlugin},
9-
aeronet_webtransport::{
10-
cert,
11-
server::{SessionRequest, SessionResponse, WebTransportServer, WebTransportServerPlugin},
12-
wtransport,
13-
},
2+
crate::network::ServerNetworkPlugin,
3+
aeronet::io::{Session, connection::Disconnected},
144
avian3d::prelude::*,
155
bevy::{
166
app::ScheduleRunnerPlugin,
@@ -19,7 +9,7 @@ use {
199
render::{RenderPlugin, settings::WgpuSettings},
2010
winit::WinitPlugin,
2111
},
22-
bevy_replicon::prelude::*,
12+
bevy_replicon::{prelude::*, server::increment_tick},
2313
core::time::Duration,
2414
minigolf::{LevelMesh, MinigolfPlugin, Player, PlayerInput, TICK_RATE},
2515
};
@@ -37,13 +27,13 @@ enum GameLayer {
3727

3828
/// `move_box` demo server
3929
#[derive(Debug, Resource, clap::Parser)]
40-
struct Args {
30+
pub(crate) struct Args {
4131
/// Port to listen for WebTransport connections on
4232
#[arg(long, default_value_t = WEB_TRANSPORT_PORT)]
43-
wt_port: u16,
33+
pub(crate) wt_port: u16,
4434
/// Port to listen for WebSocket connections on
4535
#[arg(long, default_value_t = WEB_SOCKET_PORT)]
46-
ws_port: u16,
36+
pub(crate) ws_port: u16,
4737
}
4838

4939
impl FromWorld for Args {
@@ -52,6 +42,11 @@ impl FromWorld for Args {
5242
}
5343
}
5444

45+
#[derive(Component, Reflect)]
46+
struct PlayerSession {
47+
player: Entity,
48+
}
49+
5550
pub fn main() -> AppExit {
5651
App::new()
5752
.init_resource::<Args>()
@@ -68,38 +63,20 @@ pub fn main() -> AppExit {
6863
.disable::<WinitPlugin>(),
6964
)
7065
.add_plugins((
71-
// transport
72-
WebTransportServerPlugin,
73-
WebSocketServerPlugin,
74-
// replication
75-
RepliconPlugins.set(ServerPlugin {
76-
// 1 frame lasts `1.0 / TICK_RATE` anyway
77-
tick_policy: TickPolicy::Manual,
78-
..Default::default()
79-
}),
80-
AeronetRepliconServerPlugin,
81-
// game
66+
ServerNetworkPlugin,
8267
MinigolfPlugin,
8368
PhysicsPlugins::default(),
8469
))
8570
.add_plugins(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(
8671
1.0 / f64::from(TICK_RATE),
8772
)))
88-
.add_systems(
89-
Startup,
90-
(open_web_transport_server, open_web_socket_server, setup),
91-
)
92-
.add_observer(on_opened)
93-
.add_observer(on_session_request)
73+
.add_systems(Startup, setup)
9474
.add_observer(on_connected)
9575
.add_observer(on_disconnected)
9676
.insert_resource(Time::<Fixed>::from_hz(128.0))
97-
.add_systems(
98-
FixedUpdate,
99-
recv_input.chain().run_if(server_or_singleplayer),
100-
)
77+
.add_systems(FixedUpdate, recv_input.run_if(server_or_singleplayer))
10178
.add_systems(FixedUpdate, reset)
102-
.add_systems(FixedPreUpdate, bevy_replicon::server::increment_tick)
79+
.add_systems(FixedPreUpdate, increment_tick)
10380
.run()
10481
}
10582

@@ -181,96 +158,12 @@ fn recv_input(
181158
}
182159
}
183160

184-
//
185-
// WebTransport
186-
//
187-
188-
fn open_web_transport_server(mut commands: Commands, args: Res<Args>) {
189-
let identity = wtransport::Identity::self_signed(["localhost", "127.0.0.1", "::1"])
190-
.expect("all given SANs should be valid DNS names");
191-
let cert = &identity.certificate_chain().as_slice()[0];
192-
let spki_fingerprint = cert::spki_fingerprint_b64(cert).expect("should be a valid certificate");
193-
let cert_hash = cert::hash_to_b64(cert.hash());
194-
info!("************************");
195-
info!("SPKI FINGERPRINT");
196-
info!(" {spki_fingerprint}");
197-
info!("CERTIFICATE HASH");
198-
info!(" {cert_hash}");
199-
info!("************************");
200-
201-
let config = web_transport_config(identity, &args);
202-
let server = commands
203-
.spawn((Name::new("WebTransport Server"), AeronetRepliconServer))
204-
.queue(WebTransportServer::open(config))
205-
.id();
206-
info!("Opening WebTransport server {server}");
207-
}
208-
209-
type WebTransportServerConfig = aeronet_webtransport::server::ServerConfig;
210-
211-
fn web_transport_config(identity: wtransport::Identity, args: &Args) -> WebTransportServerConfig {
212-
WebTransportServerConfig::builder()
213-
.with_bind_default(args.wt_port)
214-
.with_identity(identity)
215-
.keep_alive_interval(Some(Duration::from_secs(1)))
216-
.max_idle_timeout(Some(Duration::from_secs(5)))
217-
.expect("should be a valid idle timeout")
218-
.build()
219-
}
220-
221-
fn on_session_request(mut request: Trigger<SessionRequest>, clients: Query<&Parent>) {
222-
let client = request.entity();
223-
let Ok(server) = clients.get(client).map(Parent::get) else {
224-
return;
225-
};
226-
227-
info!("{client} connecting to {server} with headers:");
228-
for (header_key, header_value) in &request.headers {
229-
info!(" {header_key}: {header_value}");
230-
}
231-
232-
request.respond(SessionResponse::Accepted);
233-
}
234-
235-
//
236-
// WebSocket
237-
//
238-
239-
type WebSocketServerConfig = aeronet_websocket::server::ServerConfig;
240-
241-
fn open_web_socket_server(mut commands: Commands, args: Res<Args>) {
242-
let config = web_socket_config(&args);
243-
let server = commands
244-
.spawn((Name::new("WebSocket Server"), AeronetRepliconServer))
245-
.queue(WebSocketServer::open(config))
246-
.id();
247-
info!("Opening WebSocket server {server}");
248-
}
249-
250-
fn web_socket_config(args: &Args) -> WebSocketServerConfig {
251-
WebSocketServerConfig::builder()
252-
.with_bind_default(args.ws_port)
253-
.with_no_encryption()
254-
}
255-
256161
//
257162
// server logic
258163
//
259164

260-
fn on_opened(trigger: Trigger<OnAdd, Server>, servers: Query<&LocalAddr>) {
261-
let server = trigger.entity();
262-
let local_addr = servers
263-
.get(server)
264-
.expect("opened server should have a binding socket `LocalAddr`");
265-
info!("{server} opened on {}", **local_addr);
266-
}
267-
268-
fn on_connected(trigger: Trigger<OnAdd, Session>, clients: Query<&Parent>, mut commands: Commands) {
165+
fn on_connected(trigger: Trigger<OnAdd, Session>, mut commands: Commands) {
269166
let client = trigger.entity();
270-
let Ok(server) = clients.get(client).map(Parent::get) else {
271-
return;
272-
};
273-
info!("{client} connected to {server}");
274167

275168
let player = commands
276169
.spawn((
@@ -292,34 +185,12 @@ fn on_connected(trigger: Trigger<OnAdd, Session>, clients: Query<&Parent>, mut c
292185
commands.entity(client).insert(PlayerSession { player });
293186
}
294187

295-
#[derive(Component, Reflect)]
296-
struct PlayerSession {
297-
player: Entity,
298-
}
299-
300188
fn on_disconnected(
301189
trigger: Trigger<Disconnected>,
302-
clients: Query<&Parent>,
303190
sessions: Query<&PlayerSession>,
304191
mut commands: Commands,
305192
) {
306193
let client = trigger.entity();
307-
let Ok(server) = clients.get(client).map(Parent::get) else {
308-
return;
309-
};
310-
311-
match &trigger.reason {
312-
DisconnectReason::User(reason) => {
313-
info!("{client} disconnected from {server} by user: {reason}");
314-
}
315-
DisconnectReason::Peer(reason) => {
316-
info!("{client} disconnected from {server} by peer: {reason}");
317-
}
318-
DisconnectReason::Error(err) => {
319-
warn!("{client} disconnected from {server} due to error: {err:?}");
320-
}
321-
}
322-
323194
let Ok(session) = sessions.get(client) else {
324195
return;
325196
};

0 commit comments

Comments
 (0)