aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock16
-rw-r--r--Cargo.toml3
-rwxr-xr-xlaunch_2p.sh4
-rw-r--r--src/main.rs295
4 files changed, 207 insertions, 111 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0d8296e..10c2ed8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -137,6 +137,9 @@ name = "arrayvec"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
+dependencies = [
+ "serde",
+]
[[package]]
name = "ash"
@@ -609,6 +612,7 @@ dependencies = [
"log",
"nalgebra",
"rapier2d",
+ "serde",
]
[[package]]
@@ -929,6 +933,9 @@ name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+dependencies = [
+ "serde",
+]
[[package]]
name = "bitfield-rle"
@@ -1916,6 +1923,7 @@ checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
dependencies = [
"autocfg",
"hashbrown 0.11.2",
+ "serde",
]
[[package]]
@@ -1969,6 +1977,7 @@ dependencies = [
"bevy",
"bevy_ggrs",
"bevy_rapier2d",
+ "bincode",
"bytemuck",
"ggrs",
"serde",
@@ -2262,6 +2271,7 @@ dependencies = [
"num-complex",
"num-rational",
"num-traits",
+ "serde",
"simba",
"typenum",
]
@@ -2427,6 +2437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
dependencies = [
"num-traits",
+ "serde",
]
[[package]]
@@ -2648,10 +2659,12 @@ dependencies = [
"bitflags",
"downcast-rs",
"either",
+ "indexmap",
"nalgebra",
"num-derive",
"num-traits",
"rustc-hash",
+ "serde",
"simba",
"slab",
"smallvec",
@@ -2837,11 +2850,13 @@ dependencies = [
"bitflags",
"crossbeam",
"downcast-rs",
+ "indexmap",
"nalgebra",
"num-derive",
"num-traits",
"parry2d",
"rustc-hash",
+ "serde",
"simba",
]
@@ -3046,6 +3061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13a2609e876d4f77f6ab7ff5254fc39b4f1927ba8e6db3d18be7c32534d3725e"
dependencies = [
"approx",
+ "libm",
"num-complex",
"num-traits",
"paste",
diff --git a/Cargo.toml b/Cargo.toml
index 609e9af..b95538d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
[dependencies]
bevy = { version = "0.8", features = ["dynamic"] }
-bevy_rapier2d = "0.16.0"
+bevy_rapier2d = { version = "0.16.0", features = ["enhanced-determinism", "serde-serialize"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
@@ -15,3 +15,4 @@ bevy_ggrs = { git = "https://github.com/paxsethorld/bevy_ggrs", rev = "bc78d31"
ggrs = { git = "https://github.com/gschup/ggrs", features = ["sync-send"] }
bytemuck = "1.11.0"
structopt = "0.3.26"
+bincode = "1.3.3"
diff --git a/launch_2p.sh b/launch_2p.sh
index 14fd8a7..6759993 100755
--- a/launch_2p.sh
+++ b/launch_2p.sh
@@ -1,4 +1,4 @@
#! /usr/bin/bash
-cargo run -- --local-port=40000 --players localhost 127.0.0.1:40001 &
-cargo run -- --local-port=40001 --players localhost 127.0.0.1:40001 &
+alacritty --hold -e cargo run -- --local-port=40000 --players localhost 127.0.0.1:40001 & (sleep 1 &&
+alacritty --hold -e cargo run -- --local-port=40001 --players 127.0.0.1:40000 localhost) &
diff --git a/src/main.rs b/src/main.rs
index 1074319..7131c68 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8,12 +8,16 @@ use bevy::{prelude::*, render::camera::ScalingMode, window::WindowResized};
use bevy_rapier2d::{pipeline::CollisionEvent::*, prelude::*};
-use bevy_ggrs::{GGRSPlugin, SessionType};
-use ggrs::{Config, PlayerHandle, PlayerType, SessionBuilder, UdpNonBlockingSocket};
+use bevy_ggrs::{GGRSPlugin, Rollback, RollbackIdProvider, SessionType};
+use ggrs::{
+ Config, InputStatus, P2PSession, PlayerHandle, PlayerType, SessionBuilder, SpectatorSession,
+ SyncTestSession, UdpNonBlockingSocket,
+};
use bytemuck::{Pod, Zeroable};
use std::net::SocketAddr;
+use bincode::{deserialize, serialize};
use structopt::StructOpt;
#[derive(Debug)]
@@ -24,39 +28,9 @@ impl Config for GGRSConfig {
type Address = SocketAddr;
}
-#[repr(C)]
-#[derive(Copy, Clone, PartialEq, Pod, Zeroable)]
-pub struct BoxInput {
- pub inp: u8,
-}
-
-const FPS: usize = 60;
+const FPS: usize = 10;
const ROLLBACK_DEFAULT: &str = "rollback_default";
-const INPUT_UP: u8 = 1 << 0;
-const INPUT_DOWN: u8 = 1 << 1;
-const INPUT_LEFT: u8 = 1 << 2;
-const INPUT_RIGHT: u8 = 1 << 3;
-
-pub fn input(_handle: In<PlayerHandle>, keyboard_input: Res<Input<KeyCode>>) -> BoxInput {
- let mut input: u8 = 0;
-
- if keyboard_input.pressed(KeyCode::W) {
- input |= INPUT_UP;
- }
- if keyboard_input.pressed(KeyCode::A) {
- input |= INPUT_LEFT;
- }
- if keyboard_input.pressed(KeyCode::S) {
- input |= INPUT_DOWN;
- }
- if keyboard_input.pressed(KeyCode::D) {
- input |= INPUT_RIGHT;
- }
-
- BoxInput { inp: input }
-}
-
// structopt will read command line parameters for u
#[derive(StructOpt)]
struct Opt {
@@ -105,12 +79,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
GGRSPlugin::<GGRSConfig>::new()
.with_update_frequency(FPS)
.with_input_system(input)
- .register_rollback_type::<Transform>()
- .register_rollback_type::<Velocity>()
+ .register_rollback_type::<SerPhysics>()
+ .register_rollback_type::<Player>()
+ .register_rollback_type::<Bullet>()
.with_rollback_schedule(
Schedule::default().with_stage(
ROLLBACK_DEFAULT,
- SystemStage::parallel()
+ SystemStage::single_threaded()
+ .with_system(physics_deser)
+ .with_system(movement)
+ .with_system(shoot)
+ .with_system(hits)
.with_system_set(RapierPhysicsPlugin::<()>::get_systems(
PhysicsStages::SyncBackend,
))
@@ -122,7 +101,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
))
.with_system_set(RapierPhysicsPlugin::<()>::get_systems(
PhysicsStages::DetectDespawn,
- )),
+ ))
+ .with_system(physics_ser),
),
)
.build(&mut app);
@@ -136,24 +116,50 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.insert_resource(sess)
.insert_resource(SessionType::P2PSession)
.add_plugins(DefaultPlugins)
- .add_startup_system(spawn_camera)
+ .add_startup_system(physics_init)
.add_startup_system(setup)
- .add_plugin(LogDiagnosticsPlugin::default())
- .add_plugin(FrameTimeDiagnosticsPlugin::default())
+ .add_startup_system(spawn_camera)
+ //.add_plugin(LogDiagnosticsPlugin::default())
+ //.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(
RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0).with_default_system_setup(false),
)
- .add_plugin(RapierDebugRenderPlugin::default())
- .add_system_to_stage(CoreStage::Update, movement)
- .add_system_to_stage(CoreStage::Update, shoot)
+ //.add_plugin(RapierDebugRenderPlugin::default())
.add_system_to_stage(CoreStage::PostUpdate, camera_follow)
- .add_system_to_stage(CoreStage::PostUpdate, hits)
.add_system(window_resized_event)
.run();
Ok(())
}
+#[derive(Default, Reflect, Component)]
+struct SerPhysics {
+ pub ser: Vec<u8>,
+}
+
+fn physics_init(
+ mut commands: Commands,
+ context: Res<RapierContext>,
+ mut rip: ResMut<RollbackIdProvider>,
+) {
+ commands
+ .spawn()
+ .insert(SerPhysics {
+ ser: bincode::serialize(context.into_inner()).unwrap(),
+ })
+ .insert(Rollback::new(rip.next_id()));
+}
+
+fn physics_ser(mut ser_query: Query<&mut SerPhysics>, context: Res<RapierContext>) {
+ ser_query.single_mut().ser = bincode::serialize(context.into_inner()).unwrap();
+}
+
+fn physics_deser(ser_query: Query<&SerPhysics>, mut commands: Commands) {
+ commands.remove_resource::<RapierContext>();
+ commands
+ .insert_resource(bincode::deserialize::<RapierContext>(&ser_query.single().ser).unwrap());
+}
+
fn window_resized_event(
mut events: EventReader<WindowResized>,
mut window: ResMut<WindowDescriptor>,
@@ -167,11 +173,18 @@ fn window_resized_event(
fn camera_follow(
player_query: Query<(&Player, &Transform)>,
mut camera_query: Query<&mut Transform, (Without<Player>, With<Camera>)>,
+ p2p_session: Option<Res<P2PSession<GGRSConfig>>>,
) {
- let (_, transform) = player_query.single();
- let mut camera_transform = camera_query.single_mut();
- camera_transform.translation.x = transform.translation.x;
- camera_transform.translation.y = transform.translation.y;
+ let handles = p2p_session.unwrap().local_player_handles();
+ if handles.len() > 0 {
+ let (_, transform) = player_query
+ .iter()
+ .find(|(p, _)| p.handle == handles[0])
+ .unwrap();
+ let mut camera_transform = camera_query.single_mut();
+ camera_transform.translation.x = transform.translation.x;
+ camera_transform.translation.y = transform.translation.y;
+ }
}
fn spawn_camera(mut commands: Commands) {
@@ -205,30 +218,86 @@ fn hits(
}
}
-#[derive(Component)]
+#[derive(Component, Default, Reflect)]
pub struct Bullet;
-#[derive(Component)]
+#[derive(Component, Default, Reflect)]
pub struct Player {
- speed: f32,
- radius: f32,
+ pub handle: usize,
+ pub speed: f32,
+ pub radius: f32,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, PartialEq, Pod, Zeroable)]
+pub struct BoxInput {
+ pub inp: u8,
+ pub sx: u8,
+ pub sy: u8,
+}
+
+const INPUT_UP: u8 = 1 << 0;
+const INPUT_DOWN: u8 = 1 << 1;
+const INPUT_LEFT: u8 = 1 << 2;
+const INPUT_RIGHT: u8 = 1 << 3;
+
+pub fn input(_handle: In<PlayerHandle>, keyboard_input: Res<Input<KeyCode>>) -> BoxInput {
+ let mut input: u8 = 0;
+
+ if keyboard_input.pressed(KeyCode::W) {
+ input |= INPUT_UP;
+ }
+ if keyboard_input.pressed(KeyCode::A) {
+ input |= INPUT_LEFT;
+ }
+ if keyboard_input.pressed(KeyCode::S) {
+ input |= INPUT_DOWN;
+ }
+ if keyboard_input.pressed(KeyCode::D) {
+ input |= INPUT_RIGHT;
+ }
+
+ let mut x: u8 = 127;
+ let mut y: u8 = 127;
+ if keyboard_input.pressed(KeyCode::Up) {
+ y = 255;
+ }
+ if keyboard_input.pressed(KeyCode::Down) {
+ y = 0;
+ }
+ if keyboard_input.pressed(KeyCode::Left) {
+ x = 0;
+ }
+ if keyboard_input.pressed(KeyCode::Right) {
+ x = 255;
+ }
+
+ BoxInput {
+ inp: input,
+ sx: x,
+ sy: y,
+ }
}
-fn movement(mut player_query: Query<(&mut Player, &mut Velocity)>, keyboard: Res<Input<KeyCode>>) {
+fn movement(
+ mut player_query: Query<(&mut Player, &mut Velocity)>,
+ inputs: Res<Vec<(BoxInput, InputStatus)>>,
+) {
for (player, mut rb_vels) in player_query.iter_mut() {
+ let input = inputs[player.handle as usize].0.inp;
let mut acc = Vec2::new(0.0, 0.0);
- if keyboard.pressed(KeyCode::W) {
- acc.y += 1.0;
- }
- if keyboard.pressed(KeyCode::S) {
+ if input & INPUT_UP != 0 && input & INPUT_DOWN == 0 {
acc.y -= 1.0;
}
- if keyboard.pressed(KeyCode::A) {
+ if input & INPUT_UP == 0 && input & INPUT_DOWN != 0 {
acc.x -= 1.0;
}
- if keyboard.pressed(KeyCode::D) {
+ if input & INPUT_LEFT != 0 && input & INPUT_RIGHT == 0 {
acc.x += 1.0;
}
+ if input & INPUT_LEFT == 0 && input & INPUT_RIGHT != 0 {
+ acc.x -= 1.0;
+ }
if acc.length_squared() > 0.0 {
acc /= acc.length();
}
@@ -238,23 +307,15 @@ fn movement(mut player_query: Query<(&mut Player, &mut Velocity)>, keyboard: Res
fn shoot(
player_query: Query<(&Player, &Transform, &Velocity)>,
- keyboard: Res<Input<KeyCode>>,
+ inputs: Res<Vec<(BoxInput, InputStatus)>>,
mut commands: Commands,
+ mut rip: ResMut<RollbackIdProvider>,
) {
for (player, player_transform, _rb_vels) in player_query.iter() {
- let mut acc = Vec2::new(0.0, 0.0);
- if keyboard.pressed(KeyCode::Up) {
- acc.y += 1.0;
- }
- if keyboard.pressed(KeyCode::Down) {
- acc.y -= 1.0;
- }
- if keyboard.pressed(KeyCode::Left) {
- acc.x -= 1.0;
- }
- if keyboard.pressed(KeyCode::Right) {
- acc.x += 1.0;
- }
+ let input = inputs[player.handle as usize].0;
+ let sx: f32 = (input.sx as f32) - 127.0 / 256.0;
+ let sy: f32 = (input.sy as f32) - 127.0 / 256.0;
+ let mut acc = Vec2::new(sx, sy);
if acc.length_squared() > 0.0 {
acc /= acc.length();
let head = Vec3::new(acc.x, acc.y, 0.0) * (2.0 + player.radius + 3.0);
@@ -285,7 +346,8 @@ fn shoot(
.insert(Ccd::enabled())
.insert(Velocity::linear(acc * 1000.0))
.insert(CollisionGroups::new(0b010, 0b101))
- .insert(ActiveEvents::COLLISION_EVENTS);
+ .insert(ActiveEvents::COLLISION_EVENTS)
+ .insert(Rollback::new(rip.next_id()));
}
}
}
@@ -368,7 +430,14 @@ fn setup_map(mut commands: Commands) {
}
}
-fn setup(mut commands: Commands, mut rapier_config: ResMut<RapierConfiguration>) {
+fn setup(
+ mut commands: Commands,
+ mut rapier_config: ResMut<RapierConfiguration>,
+ mut rip: ResMut<RollbackIdProvider>,
+ p2p_session: Option<Res<P2PSession<GGRSConfig>>>,
+ synctest_session: Option<Res<SyncTestSession<GGRSConfig>>>,
+ spectator_session: Option<Res<SpectatorSession<GGRSConfig>>>,
+) {
rapier_config.gravity = Vec2::ZERO;
let color = commands
@@ -383,40 +452,50 @@ fn setup(mut commands: Commands, mut rapier_config: ResMut<RapierConfiguration>)
})
.id();
- commands
- .spawn()
- .insert_bundle(SpriteBundle {
- transform: Transform {
- translation: Vec3::new(0.0, 0.0, 0.0),
- scale: Vec3::splat(10.0),
- ..default()
- },
- sprite: Sprite {
- color: Color::WHITE,
+ let num_players = p2p_session
+ .map(|s| s.num_players())
+ .or_else(|| synctest_session.map(|s| s.num_players()))
+ .or_else(|| spectator_session.map(|s| s.num_players()))
+ .expect("No GGRS session found");
+
+ for handle in 0..num_players {
+ commands
+ .spawn()
+ .insert_bundle(SpriteBundle {
+ transform: Transform {
+ translation: Vec3::new(handle as f32, 0.0, 0.0),
+ scale: Vec3::splat(10.0),
+ ..default()
+ },
+ sprite: Sprite {
+ color: Color::WHITE,
+ ..default()
+ },
..default()
- },
- ..default()
- })
- .push_children(&[color])
- .insert(Player {
- speed: 150.0,
- radius: 10.0,
- })
- .insert(RigidBody::Dynamic)
- .insert(Restitution::coefficient(0.0))
- .insert(Collider::ball(1.0))
- .insert(LockedAxes::ROTATION_LOCKED)
- .insert(Damping {
- linear_damping: 30.0,
- angular_damping: 1.0,
- })
- .insert(Friction {
- coefficient: 0.0,
- combine_rule: CoefficientCombineRule::Min,
- })
- .insert(Ccd::enabled())
- .insert(Velocity::zero())
- .insert(CollisionGroups::new(0b001, 0b111));
+ })
+ .push_children(&[color])
+ .insert(Player {
+ handle,
+ speed: 150.0,
+ radius: 10.0,
+ })
+ .insert(RigidBody::Dynamic)
+ .insert(Restitution::coefficient(0.0))
+ .insert(Collider::ball(1.0))
+ .insert(LockedAxes::ROTATION_LOCKED)
+ .insert(Damping {
+ linear_damping: 30.0,
+ angular_damping: 1.0,
+ })
+ .insert(Friction {
+ coefficient: 0.0,
+ combine_rule: CoefficientCombineRule::Min,
+ })
+ .insert(Ccd::enabled())
+ .insert(Velocity::zero())
+ .insert(CollisionGroups::new(0b001, 0b111))
+ .insert(Rollback::new(rip.next_id()));
+ }
setup_map(commands);
}