M Cargo.lock => Cargo.lock +93 -4
@@ 1646,7 1646,21 @@ dependencies = [
"bitflags 2.9.1",
"log",
"nalgebra 0.32.6",
- "rapier2d",
+ "rapier2d 0.18.0",
+ "serde",
+]
+
+[[package]]
+name = "bevy_rapier2d"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ad67c66adfccd20a3253e2abc135aa38d57b5b35a63abe682737c763c2807f9"
+dependencies = [
+ "bevy 0.16.1",
+ "bitflags 2.9.1",
+ "log",
+ "nalgebra 0.33.2",
+ "rapier2d 0.25.1",
"serde",
]
@@ 2499,6 2513,9 @@ name = "bit-vec"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
+dependencies = [
+ "serde",
+]
[[package]]
name = "bit_field"
@@ 3698,6 3715,15 @@ dependencies = [
]
[[package]]
+name = "ena"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5"
+dependencies = [
+ "log",
+]
+
+[[package]]
name = "encase"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 5642,11 5668,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b"
dependencies = [
"approx",
+ "glam 0.29.3",
"matrixmultiply",
"nalgebra-macros",
"num-complex",
"num-rational",
"num-traits",
+ "serde",
"simba 0.9.0",
"typenum",
]
@@ 6317,6 6345,15 @@ dependencies = [
]
[[package]]
+name = "ordered-float"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2c1f9f56e534ac6a9b8a4600bdf0f530fb393b5f393e7b4d03489c3cf0c3f01"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
name = "os_info"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 6395,6 6432,31 @@ dependencies = [
]
[[package]]
+name = "parry2d"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b89f8a3309b82a3a81a6957c7916fe00834e79678451683b048e8d75109b9e4"
+dependencies = [
+ "approx",
+ "arrayvec",
+ "bitflags 2.9.1",
+ "downcast-rs 2.0.1",
+ "either",
+ "ena",
+ "hashbrown 0.15.4",
+ "log",
+ "nalgebra 0.33.2",
+ "num-derive",
+ "num-traits",
+ "ordered-float 5.0.0",
+ "serde",
+ "simba 0.9.0",
+ "slab",
+ "spade",
+ "thiserror 2.0.12",
+]
+
+[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 6873,13 6935,38 @@ dependencies = [
"nalgebra 0.32.6",
"num-derive",
"num-traits",
- "parry2d",
+ "parry2d 0.13.8",
"rustc-hash 1.1.0",
"serde",
"simba 0.8.1",
]
[[package]]
+name = "rapier2d"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ec6acdc5db3699c64e000945450c844d34b215dae3bf875b40e20b1909c0063"
+dependencies = [
+ "approx",
+ "arrayvec",
+ "bit-vec 0.8.0",
+ "bitflags 2.9.1",
+ "crossbeam",
+ "downcast-rs 2.0.1",
+ "log",
+ "nalgebra 0.33.2",
+ "num-derive",
+ "num-traits",
+ "ordered-float 5.0.0",
+ "parry2d 0.20.2",
+ "profiling",
+ "rustc-hash 2.1.1",
+ "serde",
+ "simba 0.9.0",
+ "thiserror 2.0.12",
+]
+
+[[package]]
name = "rav1e"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 7642,6 7729,7 @@ dependencies = [
"hashbrown 0.15.4",
"num-traits",
"robust",
+ "serde",
"smallvec",
]
@@ 7680,6 7768,7 @@ name = "starkingdoms"
version = "0.1.0"
dependencies = [
"bevy 0.16.1",
+ "bevy_rapier2d 0.30.0",
"bevy_replicon",
"bevy_replicon_renet2",
"clap",
@@ 7742,7 7831,7 @@ name = "starkingdoms-server"
version = "0.1.0-alpha1"
dependencies = [
"bevy 0.16.1",
- "bevy_rapier2d",
+ "bevy_rapier2d 0.25.0",
"crossbeam-channel",
"hex",
"hmac",
@@ 9193,7 9282,7 @@ dependencies = [
"ndk-sys 0.5.0+25.2.9519653",
"objc",
"once_cell",
- "ordered-float",
+ "ordered-float 4.6.0",
"parking_lot",
"profiling",
"range-alloc",
M crates/unified/Cargo.toml => crates/unified/Cargo.toml +1 -0
@@ 6,6 6,7 @@ version = "0.1.0"
[dependencies]
bevy = { version = "0.16", features = ["serialize"] }
+bevy_rapier2d = { version = "0.30", features = ["serde-serialize"] }
bevy_replicon = "0.34"
bevy_replicon_renet2 = { version = "0.10", features = ["native_transport"] }
M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +61 -3
@@ 1,12 1,13 @@
use std::net::{SocketAddr, UdpSocket};
use std::time::SystemTime;
use bevy::prelude::*;
+use bevy::window::PrimaryWindow;
+use bevy_rapier2d::prelude::RigidBody;
use bevy_replicon::prelude::RepliconChannels;
use bevy_replicon_renet2::netcode::{ClientAuthentication, NetcodeClientTransport, NativeSocket};
use bevy_replicon_renet2::renet2::{ConnectionConfig, RenetClient};
use bevy_replicon_renet2::RenetChannelsExt;
-
-
+use crate::ecs::{Ball, CursorWorldCoordinates, Ground, MainCamera, SendBallHere};
pub struct ClientPlugin {
pub server: SocketAddr
@@ 15,6 16,7 @@ impl Plugin for ClientPlugin {
fn build(&self, app: &mut App) {
let server = self.server.clone();
app
+ .insert_resource(CursorWorldCoordinates(None))
.add_systems(Startup, move |mut commands: Commands, channels: Res<RepliconChannels>| {
let client = RenetClient::new(
ConnectionConfig::from_channels(channels.server_configs(), channels.client_configs()),
@@ 36,7 38,63 @@ impl Plugin for ClientPlugin {
commands.insert_resource(transport);
info!(?client_id, "connected!");
- });
+ })
+ .add_systems(Startup, setup_graphics)
+ .add_systems(Update, add_ball_sprite)
+ .add_systems(Update, add_ground_sprite)
+ .add_systems(Update, update_cursor_position)
+ .add_systems(Update, teleport_cube_around);
+ }
+}
+
+fn setup_graphics(mut commands: Commands) {
+ commands.spawn(Camera2d::default())
+ .insert(MainCamera);
+}
+
+fn add_ball_sprite(mut commands: Commands, q: Query<Entity, Added<Ball>>, asset_server: Res<AssetServer>) {
+ for item in &q {
+ let mut sprite = Sprite::from_image(asset_server.load("textures/earth.png"));
+ sprite.custom_size = Some(Vec2::new(100.0, 100.0));
+
+ commands
+ .entity(item)
+ .insert(sprite);
+ }
+}
+fn add_ground_sprite(mut commands: Commands, q: Query<Entity, Added<Ground>>, asset_server: Res<AssetServer>) {
+ for item in &q {
+ let mut sprite = Sprite::from_image(asset_server.load("textures/hearty.png"));
+ sprite.custom_size = Some(Vec2::new(1000.0, 100.0));
+
+ commands
+ .entity(item)
+ .insert(sprite);
+ }
+}
+
+fn update_cursor_position(
+ q_windows: Query<&Window, With<PrimaryWindow>>,
+ q_camera: Query<(&Camera, &GlobalTransform), With<MainCamera>>,
+ mut coords: ResMut<CursorWorldCoordinates>
+) {
+ let (camera, camera_transform) = q_camera.single().unwrap();
+ let window = q_windows.single().unwrap();
+ if let Some(world_position) = window.cursor_position()
+ .and_then(|cursor| camera.viewport_to_world(camera_transform, cursor).ok())
+ .map(|ray| ray.origin.truncate()) {
+ coords.0 = Some(world_position);
+ } else {
+ coords.0 = None;
+ }
+}
+fn teleport_cube_around(
+ cursor_world_coordinates: Res<CursorWorldCoordinates>,
+ button_input: Res<ButtonInput<MouseButton>>,
+ mut events: EventWriter<SendBallHere>
+) {
+ if let Some(position) = cursor_world_coordinates.0 && button_input.pressed(MouseButton::Left) {
+ events.write(SendBallHere(position));
}
}=
\ No newline at end of file
M crates/unified/src/client_plugins.rs => crates/unified/src/client_plugins.rs +2 -0
@@ 1,6 1,7 @@
use std::net::SocketAddr;
use bevy::app::{PluginGroup, PluginGroupBuilder};
use bevy::DefaultPlugins;
+use bevy_rapier2d::render::RapierDebugRenderPlugin;
use bevy_replicon::RepliconPlugins;
use bevy_replicon_renet2::RepliconRenetClientPlugin;
use crate::client::ClientPlugin;
@@ 17,5 18,6 @@ impl PluginGroup for ClientPluginGroup {
)
.add(RepliconRenetClientPlugin)
.add(ClientPlugin { server: self.server })
+ .add(RapierDebugRenderPlugin::default())
}
}=
\ No newline at end of file
M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +15 -0
@@ 0,0 1,15 @@
+use bevy::math::Vec2;
+use bevy::prelude::{Component, Event, Resource};
+use serde::{Deserialize, Serialize};
+
+#[derive(Component, Serialize, Deserialize)]
+pub struct Ball;
+#[derive(Component, Serialize, Deserialize)]
+pub struct Ground;
+#[derive(Component)]
+pub struct MainCamera;
+#[derive(Resource, Default)]
+pub struct CursorWorldCoordinates(pub Option<Vec2>);
+
+#[derive(Debug, Default, Deserialize, Event, Serialize)]
+pub struct SendBallHere(pub Vec2);<
\ No newline at end of file
M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +33 -2
@@ 1,10 1,12 @@
use std::net::{SocketAddr, UdpSocket};
use std::time::{SystemTime, UNIX_EPOCH};
use bevy::prelude::*;
-use bevy_replicon::prelude::RepliconChannels;
+use bevy_rapier2d::prelude::{Collider, Restitution, RigidBody, Velocity};
+use bevy_replicon::prelude::{FromClient, Replicated, RepliconChannels};
use bevy_replicon_renet2::netcode::{NativeSocket, NetcodeServerTransport, ServerAuthentication, ServerSetupConfig};
use bevy_replicon_renet2::renet2::{ConnectionConfig, RenetServer};
use bevy_replicon_renet2::RenetChannelsExt;
+use crate::ecs::{Ball, Ground, SendBallHere};
pub struct ServerPlugin {
pub bind: SocketAddr,
@@ 38,6 40,35 @@ impl Plugin for ServerPlugin {
commands.insert_resource(transport);
info!("websocket server listening");
- });
+ })
+ .add_systems(Startup, setup_physics)
+ .add_systems(PreUpdate, receive_send_ball_here);
+ }
+}
+
+fn setup_physics(mut commands: Commands) {
+ commands.spawn(Collider::cuboid(500.0, 50.0))
+ .insert(Transform::from_xyz(0.0, -100.0, 0.0))
+ .insert(Restitution::coefficient(1.0))
+ .insert(Ground)
+ .insert(Replicated);
+
+ commands.spawn(RigidBody::Dynamic)
+ .insert(Collider::ball(50.0))
+ .insert(Restitution::coefficient(1.0))
+ .insert(Transform::from_xyz(0.0, 400.0, 0.0))
+ .insert(Velocity::default())
+ .insert(Ball)
+ .insert(Replicated);
+}
+
+fn receive_send_ball_here(mut events: EventReader<FromClient<SendBallHere>>, mut ball: Query<(&mut Transform, &mut Velocity), With<Ball>>) {
+ for FromClient { client_entity, event } in events.read() {
+ for (mut position, mut velocity) in &mut ball {
+ position.translation.x = event.0.x;
+ position.translation.y = event.0.y;
+ velocity.linvel = Vec2::ZERO;
+ velocity.angvel = 0.0;
+ }
}
}=
\ No newline at end of file
M crates/unified/src/shared_plugins.rs => crates/unified/src/shared_plugins.rs +10 -1
@@ 1,14 1,23 @@
use bevy::app::{App, PluginGroup, PluginGroupBuilder};
+use bevy::prelude::{Sprite, Transform};
+use bevy_rapier2d::prelude::*;
+use bevy_replicon::prelude::{AppRuleExt, Channel, ClientEventAppExt};
+use crate::ecs::{Ball, Ground, SendBallHere};
pub struct SharedPluginGroup;
impl PluginGroup for SharedPluginGroup {
fn build(self) -> PluginGroupBuilder {
PluginGroupBuilder::start::<Self>()
+ .add(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0))
.add(register_everything)
}
}
pub fn register_everything(app: &mut App) {
- app;
+ app
+ .add_client_event::<SendBallHere>(Channel::Ordered)
+ .replicate::<Transform>()
+ .replicate::<Ball>()
+ .replicate::<Ground>();
}=
\ No newline at end of file