Split crate into shared logic library and binary crate (#52) (#53)

This commit is contained in:
extrawurst
2025-06-29 12:45:25 +02:00
committed by GitHub
parent 5d4c7630ef
commit 7996d632f7
65 changed files with 497 additions and 82 deletions

2
.cargo/config.toml Normal file
View File

@@ -0,0 +1,2 @@
[env]
BEVY_ASSET_ROOT = { value = "", relative = true }

View File

@@ -21,7 +21,7 @@ jobs:
packages: libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev packages: libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
version: 1.0 version: 1.0
- name: Build - name: Build client
run: | run: |
cargo build --locked --target=x86_64-unknown-linux-gnu cargo build --locked --target=x86_64-unknown-linux-gnu

48
Cargo.lock generated
View File

@@ -3146,6 +3146,7 @@ dependencies = [
"rand 0.8.5", "rand 0.8.5",
"ron", "ron",
"serde", "serde",
"shared",
"steamworks", "steamworks",
"vergen-gitcl", "vergen-gitcl",
] ]
@@ -5120,6 +5121,30 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "server"
version = "0.1.0"
dependencies = [
"avian3d",
"bevy",
"bevy-inspector-egui",
"bevy-steamworks",
"bevy-ui-gradients",
"bevy_asset_loader",
"bevy_ballistic",
"bevy_common_assets",
"bevy_debug_log",
"bevy_sprite3d",
"bevy_trenchbroom",
"happy_feet",
"nil 0.14.0",
"rand 0.8.5",
"ron",
"serde",
"shared",
"steamworks",
]
[[package]] [[package]]
name = "sharded-slab" name = "sharded-slab"
version = "0.1.7" version = "0.1.7"
@@ -5129,6 +5154,29 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "shared"
version = "0.1.0"
dependencies = [
"avian3d",
"bevy",
"bevy-inspector-egui",
"bevy-steamworks",
"bevy-ui-gradients",
"bevy_asset_loader",
"bevy_ballistic",
"bevy_common_assets",
"bevy_debug_log",
"bevy_sprite3d",
"bevy_trenchbroom",
"happy_feet",
"nil 0.14.0",
"rand 0.8.5",
"ron",
"serde",
"steamworks",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"

View File

@@ -1,16 +1,9 @@
[package]
name = "hedz_reloaded"
version = "0.1.0"
edition = "2024"
build = "build.rs"
[profile.dev.package."*"] [workspace]
opt-level = 3 resolver = "3"
members = ["crates/*"]
[features] [workspace.dependencies]
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui"]
[dependencies]
avian3d = { version = "0.3", default-features = false, features = [ avian3d = { version = "0.3", default-features = false, features = [
"3d", "3d",
"f32", "f32",
@@ -21,28 +14,24 @@ avian3d = { version = "0.3", default-features = false, features = [
"parallel", "parallel",
] } ] }
bevy = { version = "0.16.0", features = ["track_location"] } bevy = { version = "0.16.0", features = ["track_location"] }
bevy_trenchbroom = { version = "0.8.1", features = ["avian"] } bevy-inspector-egui = { version = "0.31" }
nil = "0.14.0"
bevy_asset_loader = "0.23.0-rc.3"
bevy_sprite3d = "5.0.0"
rand = "=0.8.5"
bevy-inspector-egui = { version = "0.31", optional = true }
bevy-steamworks = "0.13.0" bevy-steamworks = "0.13.0"
steamworks = "0.11"
bevy_ballistic = "0.4.0"
bevy-ui-gradients = "0.4.0" bevy-ui-gradients = "0.4.0"
bevy_debug_log = "0.6.0" bevy_asset_loader = "0.23.0-rc.3"
bevy_ballistic = "0.4.0"
bevy_common_assets = { version = "0.13.0", features = ["ron"] } bevy_common_assets = { version = "0.13.0", features = ["ron"] }
serde = { version = "1.0.219", features = ["derive"] } bevy_debug_log = "0.6.0"
ron = "0.8" bevy_sprite3d = "5.0.0"
bevy_trenchbroom = { version = "0.8.1", features = ["avian"] }
happy_feet = { git = "https://github.com/rustunit/happy_feet.git", rev = "ecfecc6243862bc2bc64dcadfd0efd21c766ab5b" } happy_feet = { git = "https://github.com/rustunit/happy_feet.git", rev = "ecfecc6243862bc2bc64dcadfd0efd21c766ab5b" }
nil = "0.14.0"
[build-dependencies] rand = "=0.8.5"
vergen-gitcl = "1.0" ron = "0.8"
serde = { version = "1.0.219", features = ["derive"] }
[lints.clippy] shared = { path = "crates/shared" }
too_many_arguments = "allow" steamworks = "0.11"
type_complexity = "allow" [profile.dev.package."*"]
opt-level = 3
[patch.crates-io] [patch.crates-io]
bevy-steamworks = { git = "https://github.com/HouraiTeahouse/bevy_steamworks.git", rev = "1933e5d" } bevy-steamworks = { git = "https://github.com/HouraiTeahouse/bevy_steamworks.git", rev = "1933e5d" }

31
crates/client/Cargo.toml Normal file
View File

@@ -0,0 +1,31 @@
[package]
name = "hedz_reloaded"
version = "0.1.0"
edition = "2024"
build = "build.rs"
[features]
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui", "shared/dbg"]
[dependencies]
avian3d = { workspace = true }
bevy = { workspace = true }
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 }
happy_feet = { workspace = true }
nil = { workspace = true }
rand = { workspace = true }
ron = { workspace = true }
serde = { workspace = true }
shared = { workspace = true }
steamworks = { workspace = true }
[build-dependencies]
vergen-gitcl = "1.0"

View File

@@ -1,34 +1,4 @@
mod abilities;
mod ai;
mod aim;
mod animation;
mod backpack;
mod camera;
mod cash;
mod cash_heal;
mod character;
mod control;
mod cutscene;
mod debug; mod debug;
mod gates;
mod head;
mod head_drop;
mod heads;
mod heads_database;
mod heal_effect;
mod hitpoints;
mod keys;
mod loading_assets;
mod loading_map;
mod movables;
mod npc;
mod physics_layers;
mod platforms;
mod player;
mod sounds;
mod tb_entities;
mod utils;
mod water;
use crate::utils::{auto_rotate, explosions}; use crate::utils::{auto_rotate, explosions};
use avian3d::prelude::*; use avian3d::prelude::*;
@@ -46,27 +16,10 @@ use bevy_ui_gradients::UiGradientsPlugin;
use camera::MainCamera; use camera::MainCamera;
use heads_database::HeadDatabaseAsset; use heads_database::HeadDatabaseAsset;
use loading_assets::AudioAssets; use loading_assets::AudioAssets;
use shared::*;
use std::io::{Read, Write}; use std::io::{Read, Write};
use utils::{billboards, sprite_3d_animation, squish_animation, trail}; use utils::{billboards, sprite_3d_animation, squish_animation, trail};
#[derive(Resource, Reflect, Debug)]
#[reflect(Resource)]
struct DebugVisuals {
pub unlit: bool,
pub tonemapping: Tonemapping,
pub exposure: f32,
pub shadows: bool,
pub cam_follow: bool,
}
#[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)]
enum GameState {
#[default]
AssetLoading,
MapLoading,
Playing,
}
fn main() { fn main() {
let mut app = App::new(); let mut app = App::new();

27
crates/server/Cargo.toml Normal file
View File

@@ -0,0 +1,27 @@
[package]
name = "server"
version = "0.1.0"
edition = "2024"
[features]
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui", "shared/dbg"]
[dependencies]
avian3d = { workspace = true }
bevy = { workspace = true }
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 }
happy_feet = { workspace = true }
nil = { workspace = true }
rand = { workspace = true }
ron = { workspace = true }
serde = { workspace = true }
shared = { workspace = true }
steamworks = { workspace = true }

274
crates/server/src/main.rs Normal file
View File

@@ -0,0 +1,274 @@
use crate::utils::{auto_rotate, explosions};
use avian3d::prelude::*;
use bevy::{
audio::{PlaybackMode, Volume},
core_pipeline::tonemapping::Tonemapping,
prelude::*,
render::view::ColorGrading,
};
use bevy_common_assets::ron::RonAssetPlugin;
use bevy_sprite3d::Sprite3dPlugin;
use bevy_steamworks::{Client, FriendFlags, SteamworksEvent, SteamworksPlugin};
use bevy_trenchbroom::prelude::*;
use bevy_ui_gradients::UiGradientsPlugin;
use camera::MainCamera;
use heads_database::HeadDatabaseAsset;
use loading_assets::AudioAssets;
use shared::*;
use std::io::{Read, Write};
use utils::{billboards, sprite_3d_animation, squish_animation, trail};
fn main() {
let mut app = App::new();
app.register_type::<DebugVisuals>()
.register_type::<TransformInterpolation>();
app.insert_resource(DebugVisuals {
unlit: false,
tonemapping: Tonemapping::None,
exposure: 1.,
shadows: true,
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,
}),
);
let app_id = 1603000;
// should only be done in production builds
#[cfg(not(debug_assertions))]
if steamworks::restart_app_if_necessary(app_id.into()) {
info!("Restarting app via steam");
return;
}
match SteamworksPlugin::init_app(app_id) {
Ok(plugin) => {
app.add_plugins(plugin);
}
Err(e) => {
warn!("steam init error: {e:?}");
}
};
app.add_plugins(
bevy_debug_log::LogViewerPlugin::default()
.auto_open_threshold(bevy::log::tracing::level_filters::LevelFilter::OFF),
);
app.add_plugins(PhysicsPlugins::default());
app.add_plugins(Sprite3dPlugin);
app.add_plugins(TrenchBroomPlugins(
TrenchBroomConfig::new("hedz").icon(None),
));
app.add_plugins(UiGradientsPlugin);
app.add_plugins(RonAssetPlugin::<HeadDatabaseAsset>::new(&["headsdb.ron"]));
#[cfg(feature = "dbg")]
{
app.add_plugins(bevy_inspector_egui::bevy_egui::EguiPlugin {
enable_multipass_for_primary_context: true,
});
app.add_plugins(bevy_inspector_egui::quick::WorldInspectorPlugin::new());
app.add_plugins(PhysicsDebugPlugin::default());
// app.add_plugins(bevy::pbr::wireframe::WireframePlugin)
// .insert_resource(bevy::pbr::wireframe::WireframeConfig {
// global: true,
// default_color: bevy::color::palettes::css::WHITE.into(),
// });
}
app.add_plugins(ai::plugin);
app.add_plugins(animation::plugin);
app.add_plugins(character::plugin);
app.add_plugins(cash::plugin);
app.add_plugins(player::plugin);
app.add_plugins(gates::plugin);
app.add_plugins(platforms::plugin);
app.add_plugins(movables::plugin);
app.add_plugins(billboards::plugin);
app.add_plugins(aim::plugin);
app.add_plugins(npc::plugin);
app.add_plugins(keys::plugin);
app.add_plugins(squish_animation::plugin);
app.add_plugins(cutscene::plugin);
app.add_plugins(control::plugin);
app.add_plugins(sounds::plugin);
app.add_plugins(camera::plugin);
app.add_plugins(backpack::plugin);
app.add_plugins(loading_assets::LoadingPlugin);
app.add_plugins(loading_map::plugin);
app.add_plugins(sprite_3d_animation::plugin);
app.add_plugins(abilities::plugin);
app.add_plugins(heads::plugin);
app.add_plugins(hitpoints::plugin);
app.add_plugins(cash_heal::plugin);
app.add_plugins(utils::observers::plugin);
app.add_plugins(water::plugin);
app.add_plugins(head_drop::plugin);
app.add_plugins(trail::plugin);
app.add_plugins(auto_rotate::plugin);
app.add_plugins(heal_effect::plugin);
app.add_plugins(tb_entities::plugin);
app.add_plugins(explosions::plugin);
app.init_state::<GameState>();
app.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 400.,
..Default::default()
});
app.insert_resource(ClearColor(Color::BLACK));
//TODO: let user control this
app.insert_resource(GlobalVolume::new(Volume::Linear(0.4)));
app.add_systems(
Startup,
(
write_trenchbroom_config,
(steam_system, steam_events)
.chain()
.run_if(resource_exists::<Client>),
),
);
app.add_systems(OnEnter(GameState::Playing), music);
app.add_systems(Update, (set_materials_unlit, set_tonemapping, set_shadows));
app.run();
}
fn steam_events(mut events: EventReader<SteamworksEvent>) {
for e in events.read() {
info!("steam ev: {:?}", e);
}
}
fn steam_system(steam_client: Res<Client>) {
steam_client.matchmaking().request_lobby_list(|list| {
let Ok(list) = list else { return };
info!("lobby list: [{}]", list.len());
for (i, l) in list.iter().enumerate() {
info!("lobby [{i}]: {:?}", l);
}
});
steam_client
.matchmaking()
.create_lobby(
steamworks::LobbyType::FriendsOnly,
4,
|result| match result {
Ok(lobby_id) => {
info!("Created lobby with ID: {:?}", lobby_id);
}
Err(e) => error!("Failed to create lobby: {}", e),
},
);
for friend in steam_client.friends().get_friends(FriendFlags::IMMEDIATE) {
info!(
"Steam Friend: {:?} - {}({:?})",
friend.id(),
friend.name(),
friend.state()
);
}
steam_client
.remote_storage()
.set_cloud_enabled_for_app(true);
let f = steam_client.remote_storage().file("hedz_data.dat");
if f.exists() {
let mut buf = String::new();
if let Err(e) = f.read().read_to_string(&mut buf) {
error!("File read error: {}", e);
} else {
info!("File content: {}", buf);
}
} else {
info!("File does not exist");
if let Err(e) = f.write().write_all(String::from("hello world").as_bytes()) {
error!("steam cloud error: {}", e);
} else {
info!("steam cloud saved");
}
}
}
fn music(assets: Res<AudioAssets>, mut commands: Commands) {
commands.spawn((
Name::new("sfx-music"),
AudioPlayer::new(assets.music.clone()),
PlaybackSettings {
mode: PlaybackMode::Loop,
volume: Volume::Linear(0.6),
..default()
},
));
commands.spawn((
Name::new("sfx-ambient"),
AudioPlayer::new(assets.ambient.clone()),
PlaybackSettings {
mode: PlaybackMode::Loop,
volume: Volume::Linear(0.8),
..default()
},
));
}
fn write_trenchbroom_config(server: Res<TrenchBroomServer>, type_registry: Res<AppTypeRegistry>) {
if let Err(e) = server
.config
.write_game_config("trenchbroom/hedz", &type_registry.read())
{
warn!("Failed to write trenchbroom config: {}", e);
}
}
fn set_tonemapping(
mut cams: Query<(&mut Tonemapping, &mut ColorGrading), With<MainCamera>>,
visuals: Res<DebugVisuals>,
) {
for (mut tm, mut color) in cams.iter_mut() {
*tm = visuals.tonemapping;
color.global.exposure = visuals.exposure;
}
}
fn set_materials_unlit(
mut materials: ResMut<Assets<StandardMaterial>>,
visuals: Res<DebugVisuals>,
) {
if !materials.is_changed() {
return;
}
for (_, material) in materials.iter_mut() {
material.unlit = visuals.unlit;
}
}
fn set_shadows(mut lights: Query<&mut DirectionalLight>, visuals: Res<DebugVisuals>) {
for mut l in lights.iter_mut() {
l.shadows_enabled = visuals.shadows;
}
}

30
crates/shared/Cargo.toml Normal file
View File

@@ -0,0 +1,30 @@
[package]
name = "shared"
version = "0.1.0"
edition = "2024"
[features]
dbg = []
[dependencies]
avian3d = { workspace = true }
bevy = { workspace = true }
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 }
happy_feet = { workspace = true }
nil = { workspace = true }
rand = { workspace = true }
ron = { workspace = true }
serde = { workspace = true }
steamworks = { workspace = true }
[lints.clippy]
too_many_arguments = "allow"
type_complexity = "allow"

51
crates/shared/src/lib.rs Normal file
View File

@@ -0,0 +1,51 @@
pub mod abilities;
pub mod ai;
pub mod aim;
pub mod animation;
pub mod backpack;
pub mod camera;
pub mod cash;
pub mod cash_heal;
pub mod character;
pub mod control;
pub mod cutscene;
pub mod gates;
pub mod head;
pub mod head_drop;
pub mod heads;
pub mod heads_database;
pub mod heal_effect;
pub mod hitpoints;
pub mod keys;
pub mod loading_assets;
pub mod loading_map;
pub mod movables;
pub mod npc;
pub mod physics_layers;
pub mod platforms;
pub mod player;
pub mod sounds;
pub mod tb_entities;
pub mod utils;
pub mod water;
use bevy::{core_pipeline::tonemapping::Tonemapping, prelude::*};
use utils::{billboards, squish_animation};
#[derive(Resource, Reflect, Debug)]
#[reflect(Resource)]
pub struct DebugVisuals {
pub unlit: bool,
pub tonemapping: Tonemapping,
pub exposure: f32,
pub shadows: bool,
pub cam_follow: bool,
}
#[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)]
pub enum GameState {
#[default]
AssetLoading,
MapLoading,
Playing,
}

View File

@@ -70,11 +70,11 @@ fn on_spawn_sounds(
} }
} }
PlaySound::Head(name) => { PlaySound::Head(name) => {
let filename = format!("{}.ogg", name); let filename = format!("{name}.ogg");
assets assets
.head .head
.get(filename.as_str()) .get(filename.as_str())
.unwrap_or_else(|| panic!("invalid head '{}'", filename)) .unwrap_or_else(|| panic!("invalid head '{filename}'"))
.clone() .clone()
} }
}; };

View File

@@ -141,7 +141,7 @@ impl EnemySpawn {
world.commands().entity(entity).insert(( world.commands().entity(entity).insert((
this_transform, this_transform,
Name::from(format!("enemy [{}]", head)), Name::from(format!("enemy [{head}]")),
Visibility::default(), Visibility::default(),
RigidBody::Dynamic, RigidBody::Dynamic,
Collider::capsule(0.6, 2.), Collider::capsule(0.6, 2.),

View File

@@ -5,10 +5,20 @@ tb_setup_mac:
ln -s $(pwd)/trenchbroom/hedz/hedz.fgd "$HOME/Library/Application Support/TrenchBroom/games/hedz/hedz.fgd" | true ln -s $(pwd)/trenchbroom/hedz/hedz.fgd "$HOME/Library/Application Support/TrenchBroom/games/hedz/hedz.fgd" | true
ln -s $(pwd)/trenchbroom/hedz/GameConfig.cfg "$HOME/Library/Application Support/TrenchBroom/games/hedz/GameConfig.cfg" | true ln -s $(pwd)/trenchbroom/hedz/GameConfig.cfg "$HOME/Library/Application Support/TrenchBroom/games/hedz/GameConfig.cfg" | true
run:
RUST_BACKTRACE=1 cargo r --bin hedz_reloaded
dbg: dbg:
RUST_BACKTRACE=1 cargo r --features dbg RUST_BACKTRACE=1 cargo r --bin hedz_reloaded --features dbg
dbg-server:
RUST_BACKTRACE=1 cargo r --bin server --features dbg
sort:
cargo sort
check: check:
cargo sort --check --workspace
cargo fmt --check cargo fmt --check
cargo b cargo b
cargo test cargo test