Client/Server Feature Split (#63)

This commit is contained in:
PROMETHIA-27
2025-09-26 21:59:08 -04:00
committed by GitHub
parent 7f6c00b5d6
commit 2f5d154d26
30 changed files with 674 additions and 271 deletions

View File

@@ -4,26 +4,22 @@ version = "0.1.0"
edition = "2024"
[features]
default = ["lightyear/server"]
default = ["shared/server"]
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui", "shared/dbg"]
[dependencies]
avian3d = { workspace = true }
bevy = { workspace = true }
bevy = { workspace = true, default-features = false }
bevy-inspector-egui = { workspace = true, optional = true }
bevy-steamworks = { workspace = true }
bevy-ui-gradients = { workspace = true }
bevy_asset_loader = { workspace = true }
bevy_ballistic = { workspace = true }
bevy_common_assets = { workspace = true }
bevy_debug_log = { workspace = true }
bevy_sprite3d = { workspace = true }
bevy_trenchbroom = { workspace = true }
clap = { version = "=4.5.47", features = ["derive"] }
happy_feet = { workspace = true }
lightyear = { workspace = true }
lightyear_avian3d = { workspace = true }
nil = { workspace = true }
rand = { workspace = true }
ron = { workspace = true }
serde = { workspace = true }
shared = { workspace = true }

View File

@@ -0,0 +1,20 @@
use bevy::prelude::*;
use clap::Parser;
pub fn plugin(app: &mut App) {
let config = ServerConfig::parse();
app.insert_resource(config);
}
/// The server for HEDZ Reloaded
#[derive(Resource, Parser, Debug)]
#[command(version, about, long_about = None)]
pub struct ServerConfig {
#[arg(long, default_value_t = f32::INFINITY)]
/// How long to wait for a client to connect before closing, in seconds
pub timeout: f32,
#[arg(long, default_value_t = false)]
/// Whether to close when a client disconnects
pub close_on_client_disconnect: bool,
}

View File

@@ -1,6 +1,12 @@
use crate::utils::{auto_rotate, explosions};
use avian3d::prelude::*;
use bevy::{audio::Volume, core_pipeline::tonemapping::Tonemapping, prelude::*};
use bevy::{
app::plugin_group,
audio::Volume,
core_pipeline::tonemapping::Tonemapping,
log::{BoxedLayer, tracing_subscriber::Layer},
prelude::*,
};
use bevy_common_assets::ron::RonAssetPlugin;
use bevy_sprite3d::Sprite3dPlugin;
use bevy_trenchbroom::prelude::*;
@@ -11,8 +17,61 @@ use shared::*;
use std::time::Duration;
use utils::{billboards, sprite_3d_animation, squish_animation, trail};
mod config;
mod server;
plugin_group! {
pub struct DefaultPlugins {
bevy::app:::PanicHandlerPlugin,
bevy::log:::LogPlugin,
bevy::app:::TaskPoolPlugin,
bevy::diagnostic:::FrameCountPlugin,
bevy::time:::TimePlugin,
bevy::transform:::TransformPlugin,
bevy::diagnostic:::DiagnosticsPlugin,
bevy::input:::InputPlugin,
bevy::app:::ScheduleRunnerPlugin,
bevy::window:::WindowPlugin,
bevy::a11y:::AccessibilityPlugin,
bevy::app:::TerminalCtrlCHandlerPlugin,
bevy::asset:::AssetPlugin,
bevy::scene:::ScenePlugin,
bevy::render:::RenderPlugin,
bevy::render::texture:::ImagePlugin,
bevy::render::pipelined_rendering:::PipelinedRenderingPlugin,
bevy::core_pipeline:::CorePipelinePlugin,
bevy::sprite:::SpritePlugin,
bevy::text:::TextPlugin,
bevy::ui:::UiPlugin,
bevy::pbr:::PbrPlugin,
bevy::gltf:::GltfPlugin,
bevy::audio:::AudioPlugin,
bevy::gilrs:::GilrsPlugin,
bevy::animation:::AnimationPlugin,
bevy::gizmos:::GizmoPlugin,
bevy::state::app:::StatesPlugin,
#[plugin_group]
bevy::picking:::DefaultPickingPlugins,
}
}
pub fn log_to_file_layer(_app: &mut App) -> Option<BoxedLayer> {
let file = std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open("server.log")
.ok()?;
Some(
bevy::log::tracing_subscriber::fmt::layer()
.with_writer(std::sync::Mutex::new(file))
.with_ansi(false)
.with_file(true)
.with_line_number(true)
.boxed(),
)
}
fn main() {
let mut app = App::new();
@@ -26,28 +85,13 @@ fn main() {
cam_follow: true,
});
app.add_plugins(
DefaultPlugins
.set(WindowPlugin {
primary_window: Some(Window {
title: "HEDZ Reloaded".into(),
// resolution: (1024., 768.).into(),
..default()
}),
..default()
})
.set(bevy::log::LogPlugin {
filter: "info".into(),
level: bevy::log::Level::INFO,
// provide custom log layer to receive logging events
custom_layer: bevy_debug_log::log_capture_layer,
}),
);
app.add_plugins(DefaultPlugins.set(bevy::log::LogPlugin {
filter: "info,lightyear_replication=off".into(),
level: bevy::log::Level::INFO,
// provide custom log layer to receive logging events
custom_layer: log_to_file_layer,
}));
app.add_plugins(
bevy_debug_log::LogViewerPlugin::default()
.auto_open_threshold(bevy::log::tracing::level_filters::LevelFilter::OFF),
);
app.add_plugins(ServerPlugins {
tick_duration: Duration::from_secs_f32(1.0 / 60.0),
});
@@ -78,6 +122,7 @@ fn main() {
app.add_plugins(animation::plugin);
app.add_plugins(character::plugin);
app.add_plugins(cash::plugin);
app.add_plugins(config::plugin);
app.add_plugins(player::plugin);
app.add_plugins(gates::plugin);
app.add_plugins(platforms::plugin);

View File

@@ -1,16 +1,25 @@
use crate::config::ServerConfig;
use bevy::prelude::*;
use lightyear::prelude::{
server::{NetcodeConfig, NetcodeServer, ServerUdpIo},
server::{NetcodeConfig, NetcodeServer, ServerUdpIo, Started},
*,
};
use shared::{heads_database::HeadsDatabase, tb_entities::SpawnPoint, utils::commands::IsServer};
use shared::{GameState, global_observer, heads_database::HeadsDatabase, tb_entities::SpawnPoint};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
pub fn plugin(app: &mut App) {
app.init_resource::<IsServer>();
app.add_systems(Startup, (start_server, setup_timeout_timer));
app.add_systems(
Update,
(
notify_started.run_if(in_state(GameState::Playing)),
run_timeout,
),
);
app.add_systems(Startup, start_server);
app.add_observer(handle_new_client);
global_observer!(app, handle_new_client);
global_observer!(app, close_on_disconnect);
global_observer!(app, cancel_timeout);
}
fn handle_new_client(
@@ -34,17 +43,52 @@ fn handle_new_client(
Ok(())
}
fn close_on_disconnect(
_trigger: Trigger<OnRemove, Connected>,
config: Res<ServerConfig>,
mut writer: EventWriter<AppExit>,
) {
if config.close_on_client_disconnect {
writer.write(AppExit::Success);
}
}
fn start_server(mut commands: Commands) -> Result {
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 25565);
commands
.spawn((
Name::from("Server"),
LocalAddr(server_addr),
ServerUdpIo::default(),
NetcodeServer::new(NetcodeConfig::default()),
))
.trigger(server::Start);
let mut commands = commands.spawn((
Name::from("Server"),
LocalAddr(server_addr),
ServerUdpIo::default(),
NetcodeServer::new(NetcodeConfig::default()),
));
commands.trigger(server::Start);
Ok(())
}
fn notify_started(started: Query<&Started>, mut notified: Local<bool>) {
if !*notified && !started.is_empty() {
println!("hedz.server_started");
*notified = true;
}
}
#[derive(Resource)]
struct TimeoutTimer(f32);
fn setup_timeout_timer(mut commands: Commands, config: Res<ServerConfig>) {
commands.insert_resource(TimeoutTimer(config.timeout));
}
fn run_timeout(mut timer: ResMut<TimeoutTimer>, mut writer: EventWriter<AppExit>, time: Res<Time>) {
timer.0 -= time.delta_secs();
if timer.0 <= 0.0 {
writer.write(AppExit::Success);
}
}
fn cancel_timeout(_trigger: Trigger<OnAdd, Connected>, mut timer: ResMut<TimeoutTimer>) {
timer.0 = f32::INFINITY;
}