From d6c4741582a0ee746d6781e8198bd1e12b7cc04e Mon Sep 17 00:00:00 2001 From: Michael Bradley Date: Sun, 25 May 2025 21:09:29 -0400 Subject: [PATCH 1/3] Add README --- .vscode/settings.json | 2 ++ README.md | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 README.md diff --git a/.vscode/settings.json b/.vscode/settings.json index d614d6a..7d9ea05 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,9 +18,11 @@ "rust-analyzer.cargo.targetDir": true, "cSpell.words": [ "Backquote", + "Cheatbook", "codegen", "despawn", "Despawns", + "Iyes", "lerp", "PRNG", "recip", diff --git a/README.md b/README.md new file mode 100644 index 0000000..8486b3c --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Distributed Physics Test + +Messing around with learning Rust, Bevy, and trying to remember my networking course from university. +My hope is that I can make some sort of basic decentralized P2P physics system, but we'll see if I get that far. + +## Thanks + +This is my first Bevy project, so some things are inspired by or taken from the [bevy_new_2d](https://github.com/TheBevyFlock/bevy_new_2d) example repo from "The Bevy Flock". + +I've also found the [Unofficial Bevy Cheatbook](https://bevy-cheatbook.github.io/introduction.html) by Ida Iyes to be a great resource, and have used some of the snippets it provides. + +## License + +I've licensed this as AGPL3 as I like the idea of the GPL and this is networking software. +Both of the sources in my thanks section are licensed under 0-attribution models that let my freely use and relicense any code I take from them so there's no legal issue there, although I would like to reiterate my thanks in both cases. From 73ea0d6fd8c68c2816fce62afa51cbbb6e65a7ac Mon Sep 17 00:00:00 2001 From: Michael Bradley Date: Sun, 25 May 2025 22:29:20 -0400 Subject: [PATCH 2/3] Move game logic plugin configuration to game-specific plugin --- src/dev.rs | 2 +- src/game/mod.rs | 14 +++++-- src/game/objects.rs | 2 +- src/game/plugin.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++ src/game/seed.rs | 2 +- src/game/setup.rs | 2 +- src/game/state.rs | 9 +++++ src/lib.rs | 79 ++++++++++----------------------------- src/net/mod.rs | 3 +- src/net/plugin.rs | 2 +- 10 files changed, 136 insertions(+), 70 deletions(-) create mode 100644 src/game/plugin.rs create mode 100644 src/game/state.rs diff --git a/src/dev.rs b/src/dev.rs index 62f82d5..97a7d77 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -7,7 +7,7 @@ use bevy::{ prelude::*, }; -use crate::AppState; +use crate::game::state::AppState; pub(super) fn dev_tools(app: &mut App) { app.add_plugins(FpsOverlayPlugin::default()); diff --git a/src/game/mod.rs b/src/game/mod.rs index cd239ce..30eff06 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,5 +1,13 @@ mod objects; +mod plugin; mod rng; -pub mod runtime; -pub mod seed; -pub mod setup; +mod runtime; +mod seed; +mod setup; +pub mod state; + +#[allow(unused_imports)] +pub mod prelude { + pub use super::plugin::{DataSource, GamePlugin}; + pub use super::seed::Seed; +} diff --git a/src/game/objects.rs b/src/game/objects.rs index 1ede2d5..95de98f 100644 --- a/src/game/objects.rs +++ b/src/game/objects.rs @@ -1,7 +1,7 @@ use avian2d::prelude::*; use bevy::prelude::*; -use crate::AppState; +use super::state::AppState; /// Basic implementation of a physics object #[derive(Component, Default)] diff --git a/src/game/plugin.rs b/src/game/plugin.rs new file mode 100644 index 0000000..ec35bef --- /dev/null +++ b/src/game/plugin.rs @@ -0,0 +1,91 @@ +use std::net::SocketAddr; + +use avian2d::PhysicsPlugins; +use bevy::{input::common_conditions::input_pressed, prelude::*}; + +use crate::net::NetIOPlugin; + +use super::{ + runtime::{move_camera, move_player, quit, zoom_camera}, + seed::Seed, + setup::{check_for_seed, setup_balls, setup_from_seed, setup_player, setup_ui, setup_walls}, + state::AppState, +}; + +#[derive(Debug, Clone, Copy)] +pub enum DataSource { + Address(SocketAddr), + Seed(Seed), + None, +} + +impl DataSource { + pub fn try_from_options(address: Option, seed: Option) -> Option { + match (address, seed) { + (None, None) => Some(DataSource::None), + (None, Some(seed)) => Some(DataSource::Seed(seed)), + (Some(address), None) => Some(DataSource::Address(address)), + (Some(_), Some(_)) => None, + } + } + + fn try_to_address(&self) -> Option { + match self { + DataSource::Address(address) => Some(*address), + _ => None, + } + } +} + +pub struct GamePlugin { + source: DataSource, + port: u16, +} + +impl GamePlugin { + pub fn new(source: DataSource, port: u16) -> Self { + Self { source, port } + } +} + +impl Plugin for GamePlugin { + fn build(&self, app: &mut App) { + app.add_plugins(( + NetIOPlugin::new(self.port, self.source.try_to_address()), + PhysicsPlugins::default().with_length_unit(50.0), + )) + .init_state::() + .add_systems(Startup, setup_ui) + .add_systems( + OnEnter(AppState::InGame), + ( + setup_from_seed, + (setup_player, setup_balls, setup_walls).after(setup_from_seed), + ), + ) + .add_systems( + FixedUpdate, + check_for_seed.run_if(in_state(AppState::Loading)), + ) + .add_systems( + Update, + ( + ((move_player, move_camera).chain(), zoom_camera) + .run_if(in_state(AppState::InGame)), + quit.run_if(input_pressed(KeyCode::KeyQ)), + ), + ); + + match self.source { + DataSource::Address(peer) => { + info!("Will retrieve seed from peer => {peer}"); + } + DataSource::Seed(seed) => { + app.insert_resource(seed); + } + DataSource::None => { + app.insert_resource(Seed::random()); + } + }; + } +} diff --git a/src/game/seed.rs b/src/game/seed.rs index 2b1ff6b..12d6a07 100644 --- a/src/game/seed.rs +++ b/src/game/seed.rs @@ -7,7 +7,7 @@ use bevy::prelude::*; use rand::random; /// Value with which to initialize the PRNG -#[derive(Resource, Clone, Copy)] +#[derive(Resource, Debug, Clone, Copy)] pub struct Seed(u64); impl Seed { diff --git a/src/game/setup.rs b/src/game/setup.rs index 14c2d44..252e0d2 100644 --- a/src/game/setup.rs +++ b/src/game/setup.rs @@ -11,7 +11,7 @@ use bevy::{ use rand::{Rng as _, SeedableRng}; use wyrand::WyRand; -use crate::AppState; +use super::state::AppState; use super::{ objects::{Ball, Player, Radius, Wall}, diff --git a/src/game/state.rs b/src/game/state.rs new file mode 100644 index 0000000..fe219de --- /dev/null +++ b/src/game/state.rs @@ -0,0 +1,9 @@ +use bevy::prelude::*; + +#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)] +#[states(scoped_entities)] +pub enum AppState { + #[default] + Loading, + InGame, +} diff --git a/src/lib.rs b/src/lib.rs index b6783f6..3fad59d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,21 +1,16 @@ use std::net::SocketAddr; use avian2d::{math::Vector, prelude::*}; -use bevy::{input::common_conditions::input_pressed, prelude::*}; +use bevy::prelude::*; use clap::{Args, Parser}; #[cfg(feature = "dev")] mod dev; - mod game; -use game::{ - runtime::{move_camera, move_player, quit, zoom_camera}, - seed::Seed, - setup::{check_for_seed, setup_balls, setup_from_seed, setup_player, setup_ui, setup_walls}, -}; - mod net; +use game::prelude::*; + /// The initial configuration passed to the game's setup functions. /// Also functions as a Bevy plugin to pass the configuration into the app. #[derive(Parser)] @@ -41,60 +36,24 @@ struct Source { connect: Option, } -#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)] -#[states(scoped_entities)] -enum AppState { - #[default] - Loading, - InGame, -} - impl Plugin for AppSettings { fn build(&self, app: &mut App) { - app.insert_resource(Gravity(Vector::ZERO)) - .add_plugins(( - DefaultPlugins.set(WindowPlugin { - primary_window: Window { - title: "Distributed physics test".into(), - fit_canvas_to_parent: true, - ..default() - } - .into(), + app.insert_resource(Gravity(Vector::ZERO)).add_plugins(( + DefaultPlugins.set(WindowPlugin { + primary_window: Window { + title: "Distributed physics test".into(), + fit_canvas_to_parent: true, ..default() - }), - PhysicsPlugins::default().with_length_unit(50.0), - #[cfg(feature = "dev")] - dev::dev_tools, - net::NetIOPlugin::new(self.port, self.source.connect), - )) - .init_state::() - .add_systems(Startup, setup_ui) - .add_systems( - OnEnter(AppState::InGame), - ( - setup_from_seed, - (setup_player, setup_balls, setup_walls).after(setup_from_seed), - ), - ) - .add_systems( - FixedUpdate, - check_for_seed.run_if(in_state(AppState::Loading)), - ) - .add_systems( - Update, - ( - ((move_player, move_camera).chain(), zoom_camera) - .run_if(in_state(AppState::InGame)), - quit.run_if(input_pressed(KeyCode::KeyQ)), - ), - ); - - if let Some(ref seed) = self.source.seed { - app.insert_resource(*seed); - } else if let Some(ref peer) = self.source.connect { - info!("Will retrieve seed from peer => {peer}"); - } else { - app.insert_resource(Seed::random()); - } + } + .into(), + ..default() + }), + #[cfg(feature = "dev")] + dev::dev_tools, + GamePlugin::new( + DataSource::try_from_options(self.source.connect, self.source.seed).unwrap(), + self.port, + ), + )); } } diff --git a/src/net/mod.rs b/src/net/mod.rs index 286d87d..4529f49 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,6 +1,5 @@ mod plugin; +pub use plugin::NetIOPlugin; mod socket; mod thread; mod types; - -pub use plugin::NetIOPlugin; diff --git a/src/net/plugin.rs b/src/net/plugin.rs index 57aa8fa..183207e 100644 --- a/src/net/plugin.rs +++ b/src/net/plugin.rs @@ -2,7 +2,7 @@ use std::net::SocketAddr; use bevy::prelude::*; -use crate::game::seed::Seed; +use crate::game::prelude::Seed; use super::{ socket::bind_socket, From 1461dd11ec2e17a84ddd0772d91c9c00ead7eeae Mon Sep 17 00:00:00 2001 From: Michael Bradley Date: Sun, 25 May 2025 22:42:35 -0400 Subject: [PATCH 3/3] Update `log` dependency --- Cargo.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a0f83e5..5012675 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,10 +37,7 @@ bevy = { version = "0.16.0", default-features = false, features = [ bevy_rand = { version = "0.11.0", features = ["wyrand", "std"] } clap = { version = "4.5.32", features = ["derive"] } crossbeam-channel = "0.5.15" -log = { version = "*", features = [ - "max_level_debug", - "release_max_level_warn", -] } +log = { version = "0.4.27", features = ["release_max_level_warn"] } rand = { version = "0.9.1", default-features = false, features = [ "std", "thread_rng",