Compare commits

...

3 commits

Author SHA1 Message Date
1461dd11ec
Update log dependency
All checks were successful
CI / Formatting (push) Successful in 1m28s
2025-05-25 22:42:35 -04:00
73ea0d6fd8
Move game logic plugin configuration to game-specific plugin 2025-05-25 22:29:20 -04:00
d6c4741582
Add README 2025-05-25 21:09:29 -04:00
13 changed files with 154 additions and 74 deletions

View file

@ -18,9 +18,11 @@
"rust-analyzer.cargo.targetDir": true,
"cSpell.words": [
"Backquote",
"Cheatbook",
"codegen",
"despawn",
"Despawns",
"Iyes",
"lerp",
"PRNG",
"recip",

View file

@ -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",

15
README.md Normal file
View file

@ -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.

View file

@ -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());

View file

@ -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;
}

View file

@ -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)]

91
src/game/plugin.rs Normal file
View file

@ -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<SocketAddr>, seed: Option<Seed>) -> Option<Self> {
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<SocketAddr> {
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::<AppState>()
.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());
}
};
}
}

View file

@ -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 {

View file

@ -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},

9
src/game/state.rs Normal file
View file

@ -0,0 +1,9 @@
use bevy::prelude::*;
#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)]
#[states(scoped_entities)]
pub enum AppState {
#[default]
Loading,
InGame,
}

View file

@ -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,18 +36,9 @@ struct Source {
connect: Option<SocketAddr>,
}
#[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((
app.insert_resource(Gravity(Vector::ZERO)).add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Window {
title: "Distributed physics test".into(),
@ -62,39 +48,12 @@ impl Plugin for AppSettings {
.into(),
..default()
}),
PhysicsPlugins::default().with_length_unit(50.0),
#[cfg(feature = "dev")]
dev::dev_tools,
net::NetIOPlugin::new(self.port, self.source.connect),
))
.init_state::<AppState>()
.add_systems(Startup, setup_ui)
.add_systems(
OnEnter(AppState::InGame),
(
setup_from_seed,
(setup_player, setup_balls, setup_walls).after(setup_from_seed),
GamePlugin::new(
DataSource::try_from_options(self.source.connect, self.source.seed).unwrap(),
self.port,
),
)
.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());
}
));
}
}

View file

@ -1,6 +1,5 @@
mod plugin;
pub use plugin::NetIOPlugin;
mod socket;
mod thread;
mod types;
pub use plugin::NetIOPlugin;

View file

@ -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,