Compare commits
3 commits
9ac45e9249
...
1461dd11ec
Author | SHA1 | Date | |
---|---|---|---|
1461dd11ec | |||
73ea0d6fd8 | |||
d6c4741582 |
13 changed files with 154 additions and 74 deletions
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -18,9 +18,11 @@
|
||||||
"rust-analyzer.cargo.targetDir": true,
|
"rust-analyzer.cargo.targetDir": true,
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"Backquote",
|
"Backquote",
|
||||||
|
"Cheatbook",
|
||||||
"codegen",
|
"codegen",
|
||||||
"despawn",
|
"despawn",
|
||||||
"Despawns",
|
"Despawns",
|
||||||
|
"Iyes",
|
||||||
"lerp",
|
"lerp",
|
||||||
"PRNG",
|
"PRNG",
|
||||||
"recip",
|
"recip",
|
||||||
|
|
|
@ -37,10 +37,7 @@ bevy = { version = "0.16.0", default-features = false, features = [
|
||||||
bevy_rand = { version = "0.11.0", features = ["wyrand", "std"] }
|
bevy_rand = { version = "0.11.0", features = ["wyrand", "std"] }
|
||||||
clap = { version = "4.5.32", features = ["derive"] }
|
clap = { version = "4.5.32", features = ["derive"] }
|
||||||
crossbeam-channel = "0.5.15"
|
crossbeam-channel = "0.5.15"
|
||||||
log = { version = "*", features = [
|
log = { version = "0.4.27", features = ["release_max_level_warn"] }
|
||||||
"max_level_debug",
|
|
||||||
"release_max_level_warn",
|
|
||||||
] }
|
|
||||||
rand = { version = "0.9.1", default-features = false, features = [
|
rand = { version = "0.9.1", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"thread_rng",
|
"thread_rng",
|
||||||
|
|
15
README.md
Normal file
15
README.md
Normal 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.
|
|
@ -7,7 +7,7 @@ use bevy::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::AppState;
|
use crate::game::state::AppState;
|
||||||
|
|
||||||
pub(super) fn dev_tools(app: &mut App) {
|
pub(super) fn dev_tools(app: &mut App) {
|
||||||
app.add_plugins(FpsOverlayPlugin::default());
|
app.add_plugins(FpsOverlayPlugin::default());
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
mod objects;
|
mod objects;
|
||||||
|
mod plugin;
|
||||||
mod rng;
|
mod rng;
|
||||||
pub mod runtime;
|
mod runtime;
|
||||||
pub mod seed;
|
mod seed;
|
||||||
pub mod setup;
|
mod setup;
|
||||||
|
pub mod state;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub mod prelude {
|
||||||
|
pub use super::plugin::{DataSource, GamePlugin};
|
||||||
|
pub use super::seed::Seed;
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use avian2d::prelude::*;
|
use avian2d::prelude::*;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::AppState;
|
use super::state::AppState;
|
||||||
|
|
||||||
/// Basic implementation of a physics object
|
/// Basic implementation of a physics object
|
||||||
#[derive(Component, Default)]
|
#[derive(Component, Default)]
|
||||||
|
|
91
src/game/plugin.rs
Normal file
91
src/game/plugin.rs
Normal 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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ use bevy::prelude::*;
|
||||||
use rand::random;
|
use rand::random;
|
||||||
|
|
||||||
/// Value with which to initialize the PRNG
|
/// Value with which to initialize the PRNG
|
||||||
#[derive(Resource, Clone, Copy)]
|
#[derive(Resource, Debug, Clone, Copy)]
|
||||||
pub struct Seed(u64);
|
pub struct Seed(u64);
|
||||||
|
|
||||||
impl Seed {
|
impl Seed {
|
||||||
|
|
|
@ -11,7 +11,7 @@ use bevy::{
|
||||||
use rand::{Rng as _, SeedableRng};
|
use rand::{Rng as _, SeedableRng};
|
||||||
use wyrand::WyRand;
|
use wyrand::WyRand;
|
||||||
|
|
||||||
use crate::AppState;
|
use super::state::AppState;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
objects::{Ball, Player, Radius, Wall},
|
objects::{Ball, Player, Radius, Wall},
|
||||||
|
|
9
src/game/state.rs
Normal file
9
src/game/state.rs
Normal 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,
|
||||||
|
}
|
57
src/lib.rs
57
src/lib.rs
|
@ -1,21 +1,16 @@
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use avian2d::{math::Vector, prelude::*};
|
use avian2d::{math::Vector, prelude::*};
|
||||||
use bevy::{input::common_conditions::input_pressed, prelude::*};
|
use bevy::prelude::*;
|
||||||
use clap::{Args, Parser};
|
use clap::{Args, Parser};
|
||||||
|
|
||||||
#[cfg(feature = "dev")]
|
#[cfg(feature = "dev")]
|
||||||
mod dev;
|
mod dev;
|
||||||
|
|
||||||
mod game;
|
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;
|
mod net;
|
||||||
|
|
||||||
|
use game::prelude::*;
|
||||||
|
|
||||||
/// The initial configuration passed to the game's setup functions.
|
/// The initial configuration passed to the game's setup functions.
|
||||||
/// Also functions as a Bevy plugin to pass the configuration into the app.
|
/// Also functions as a Bevy plugin to pass the configuration into the app.
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
@ -41,18 +36,9 @@ struct Source {
|
||||||
connect: Option<SocketAddr>,
|
connect: Option<SocketAddr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
#[states(scoped_entities)]
|
|
||||||
enum AppState {
|
|
||||||
#[default]
|
|
||||||
Loading,
|
|
||||||
InGame,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Plugin for AppSettings {
|
impl Plugin for AppSettings {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.insert_resource(Gravity(Vector::ZERO))
|
app.insert_resource(Gravity(Vector::ZERO)).add_plugins((
|
||||||
.add_plugins((
|
|
||||||
DefaultPlugins.set(WindowPlugin {
|
DefaultPlugins.set(WindowPlugin {
|
||||||
primary_window: Window {
|
primary_window: Window {
|
||||||
title: "Distributed physics test".into(),
|
title: "Distributed physics test".into(),
|
||||||
|
@ -62,39 +48,12 @@ impl Plugin for AppSettings {
|
||||||
.into(),
|
.into(),
|
||||||
..default()
|
..default()
|
||||||
}),
|
}),
|
||||||
PhysicsPlugins::default().with_length_unit(50.0),
|
|
||||||
#[cfg(feature = "dev")]
|
#[cfg(feature = "dev")]
|
||||||
dev::dev_tools,
|
dev::dev_tools,
|
||||||
net::NetIOPlugin::new(self.port, self.source.connect),
|
GamePlugin::new(
|
||||||
))
|
DataSource::try_from_options(self.source.connect, self.source.seed).unwrap(),
|
||||||
.init_state::<AppState>()
|
self.port,
|
||||||
.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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
mod plugin;
|
mod plugin;
|
||||||
|
pub use plugin::NetIOPlugin;
|
||||||
mod socket;
|
mod socket;
|
||||||
mod thread;
|
mod thread;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
pub use plugin::NetIOPlugin;
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::net::SocketAddr;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::game::seed::Seed;
|
use crate::game::prelude::Seed;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
socket::bind_socket,
|
socket::bind_socket,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue