From d2dc2f8a62538214e085896a81874e1f98a1f9ec Mon Sep 17 00:00:00 2001 From: core Date: Fri, 11 Jul 2025 21:35:00 -0400 Subject: [PATCH] ??? ? ? ? ????????????????????? --- .../assets/config/parts/hearty.part.toml | 8 +- .../assets/config/parts/housing.part.toml | 10 +- crates/unified/src/client/key_input.rs | 13 +- crates/unified/src/client/parts.rs | 6 +- crates/unified/src/ecs.rs | 2 + crates/unified/src/server/player.rs | 114 +++++++++++++++++- 6 files changed, 132 insertions(+), 21 deletions(-) diff --git a/crates/unified/assets/config/parts/hearty.part.toml b/crates/unified/assets/config/parts/hearty.part.toml index c2219bd9a54d3df105819b4ff5c6a98e0dfb081c..96cb585d661679832fafea90eddbb33380d345c9 100644 --- a/crates/unified/assets/config/parts/hearty.part.toml +++ b/crates/unified/assets/config/parts/hearty.part.toml @@ -14,21 +14,21 @@ exhaust_speed = 250 [[joints]] id = "Top" -target = { translation = [ 0.0, 50.0, 0.0 ], rotation = 0.0 } +target = { translation = [ 0.0, 55.0, 0.0 ], rotation = 0.0 } snap = { translation = [ 0.0, 25.0, 0.0 ], rotation = 0.0 } [[joints]] id = "Right" -target = { translation = [ 50.0, 0.0, 0.0 ], rotation = -90.0 } +target = { translation = [ 55.0, 0.0, 0.0 ], rotation = -90.0 } snap = { translation = [ 25.0, 0.0, 0.0 ], rotation = 0.0 } [[joints]] id = "Bottom" -target = { translation = [ 0.0, -50.0, 0.0 ], rotation = -180.0 } +target = { translation = [ 0.0, -55.0, 0.0 ], rotation = -180.0 } snap = { translation = [ 0.0, -25.0, 0.0 ], rotation = 0.0 } [[joints]] id = "Left" -target = { translation = [ -50.0, 0.0, 0.0 ], rotation = -270.0 } +target = { translation = [ -55.0, 0.0, 0.0 ], rotation = -270.0 } snap = { translation = [ -25.0, 0.0, 0.0 ], rotation = 0.0 } \ No newline at end of file diff --git a/crates/unified/assets/config/parts/housing.part.toml b/crates/unified/assets/config/parts/housing.part.toml index b28c4f604952ad55604528625f97126c1d73dcd6..498150f6453ee620a2df2110a5c7d99f218e6b68 100644 --- a/crates/unified/assets/config/parts/housing.part.toml +++ b/crates/unified/assets/config/parts/housing.part.toml @@ -10,21 +10,21 @@ mass = 50 [[joints]] id = "Top" -target = { translation = [ 0.0, 50.0, 0.0 ], rotation = 0.0 } +target = { translation = [ 0.0, 55.0, 0.0 ], rotation = 0.0 } snap = { translation = [ 0.0, 25.0, 0.0 ], rotation = 0.0 } [[joints]] id = "Right" -target = { translation = [ 50.0, 0.0, 0.0 ], rotation = -90.0 } +target = { translation = [ 55.0, 0.0, 0.0 ], rotation = -90.0 } snap = { translation = [ 25.0, 0.0, 0.0 ], rotation = 0.0 } [[joints]] id = "Bottom" -target = { translation = [ 0.0, -50.0, 0.0 ], rotation = -180.0 } +target = { translation = [ 0.0, -55.0, 0.0 ], rotation = -180.0 } snap = { translation = [ 0.0, -25.0, 0.0 ], rotation = 0.0 } [[joints]] id = "Left" -target = { translation = [ -50.0, 0.0, 0.0 ], rotation = -270.0 } -snap = { translation = [ -25.0, 0.0, 0.0 ], rotation = 0.0 } +target = { translation = [ -55.0, 0.0, 0.0 ], rotation = -270.0 } +snap = { translation = [ -25.0, 0.0, 0.0 ], rotation = 0.0 } \ No newline at end of file diff --git a/crates/unified/src/client/key_input.rs b/crates/unified/src/client/key_input.rs index 1f42ac5d30763757bc743a583ab479b332c7c3a4..c9e570e0f85d4d017ad1da0d314f42b33d422b77 100644 --- a/crates/unified/src/client/key_input.rs +++ b/crates/unified/src/client/key_input.rs @@ -1,6 +1,6 @@ -use crate::attachment::{JointOf, SnapOf}; +use crate::attachment::{JointOf, Peer, SnapOf}; use crate::ecs::{Part, ThrustEvent}; -use bevy::color::palettes::css::{FUCHSIA, GREEN}; +use bevy::color::palettes::css::{FUCHSIA, GREEN, WHITE}; use bevy::dev_tools::picking_debug::DebugPickingMode; use bevy::math::Vec3Swizzles; use bevy::prelude::{Gizmos, GlobalTransform, Query, ResMut, Resource, Transform, With}; @@ -73,7 +73,7 @@ fn directional_keys(keys: Res>, mut thrust_event: EventWrit } fn draw_attachment_debug( - joints: Query<(&Transform, &JointOf)>, + joints: Query<(&Transform, &JointOf, Option<&Peer>)>, snaps: Query<(&Transform, &SnapOf)>, parts: Query<&GlobalTransform, With>, mut gizmos: Gizmos, @@ -82,12 +82,17 @@ fn draw_attachment_debug( if !state.0 { return; } - for (offset, parent) in joints.iter() { + for (offset, parent, peer) in joints.iter() { let Ok(parent_pos) = parts.get(parent.0) else { continue; }; let joint_target = parent_pos.transform_point(offset.translation); gizmos.cross_2d(joint_target.xy(), 4.0, FUCHSIA); + + if let Some(peer_id) = peer && let Ok(peer) = joints.get(peer_id.0) { + let Ok(peer_parent_pos) = parts.get(peer.1.0) else { continue }; + gizmos.arrow_2d(parent_pos.translation().xy(), peer_parent_pos.translation().xy(), WHITE); + } } for (offset, parent) in snaps.iter() { let Ok(parent_pos) = parts.get(parent.0) else { diff --git a/crates/unified/src/client/parts.rs b/crates/unified/src/client/parts.rs index d45038098a1462bfa902eae19cc038d6b051ff79..62349f41fad722324f6c5897637065bb0a80fa99 100644 --- a/crates/unified/src/client/parts.rs +++ b/crates/unified/src/client/parts.rs @@ -10,7 +10,7 @@ use bevy_rapier2d::prelude::AdditionalMassProperties; pub fn parts_plugin(app: &mut App) { app.insert_resource(DragResource(None)); - app.insert_resource(SnapResource(None)); + app.insert_resource(SnapResource(None,None)); app.add_systems( Update, ( @@ -77,7 +77,7 @@ fn handle_updated_parts( #[derive(Resource)] struct DragResource(Option); #[derive(Resource)] -struct SnapResource(Option); +struct SnapResource(Option, Option); #[derive(Component)] struct DragGhost; @@ -130,6 +130,7 @@ fn on_part_release( drag_to: c, set_rotation: rotation, snap_target: snap.0, + peer_snap: snap.1 }); } } @@ -290,4 +291,5 @@ fn update_drag_ghosts( ghost.translation += dx; rsnap.0 = snap; + rsnap.1 = best_self_snap; } diff --git a/crates/unified/src/ecs.rs b/crates/unified/src/ecs.rs index d6296febbe7e791ecbe661316d4e7d67cd43a900..6fd0aef0f9dfefe93332087f634c50db1681d32a 100644 --- a/crates/unified/src/ecs.rs +++ b/crates/unified/src/ecs.rs @@ -75,6 +75,8 @@ pub struct DragRequestEvent { pub set_rotation: Quat, #[entities] pub snap_target: Option, + #[entities] + pub peer_snap: Option } #[derive(Component, Serialize, Deserialize, Debug)] diff --git a/crates/unified/src/server/player.rs b/crates/unified/src/server/player.rs index aecb2846614d72c4c068926fdbcdf7d49bc3061a..d01cb606c57bb7aea466c73f69818bd945ed441a 100644 --- a/crates/unified/src/server/player.rs +++ b/crates/unified/src/server/player.rs @@ -5,9 +5,10 @@ use crate::server::system_sets::PlayerInputSet; use crate::server::world_config::WorldConfigResource; use crate::server::{ConnectedGameEntity, ConnectedNetworkEntity}; use bevy::prelude::*; -use bevy_rapier2d::prelude::ExternalForce; +use bevy_rapier2d::prelude::{ExternalForce, FixedJointBuilder, ImpulseJoint}; use bevy_replicon::prelude::FromClient; use std::f32::consts::PI; +use crate::attachment::{Joint, JointOf, PartInShip, Peer, SnapOf, SnapOfJoint}; pub fn player_management_plugin(app: &mut App) { app.add_systems( @@ -18,13 +19,114 @@ pub fn player_management_plugin(app: &mut App) { fn dragging( mut events: EventReader>, - mut parts: Query<&mut Transform, With>, + mut parts: Query<(&mut Transform, Option<&PartInShip>, Entity), (With, Without)>, + snaps: Query<(&SnapOf, &SnapOfJoint)>, + joints: Query<(&Joint, &JointOf, &Transform, Option<&Peer>, Entity)>, + clients: Query<&ConnectedNetworkEntity>, + mut commands: Commands ) { - for FromClient { event, .. } in events.read() { + for FromClient { event, client_entity } in events.read() { + let ConnectedNetworkEntity { game_entity: player_hearty_entity } = clients.get(*client_entity).unwrap(); + + debug!(?event, "got drag request event"); + + let mut teleport_to_translation = Vec2::new(0.0, 0.0); + let mut teleport_to_rotation = Quat::from_rotation_z(0.0); + + if let Some(snap_to) = event.snap_target && let Some(peer_snap) = event.peer_snap { + let Ok(snap_on_target) = snaps.get(snap_to) else { continue }; + let Ok(snap_on_source) = snaps.get(peer_snap) else { continue }; + + let Ok(target_joint) = joints.get(snap_on_target.1.0) else { continue }; + let Ok(source_joint) = joints.get(snap_on_source.1.0) else { continue }; + + // validation step 1: everything must match. if not, ignore the request + if snap_on_target.0.0 != target_joint.1.0 { + warn!("drag request: mismatched target entities (potential manipulation?), ignoring"); + continue; + } + if snap_on_source.0.0 != source_joint.1.0 { + warn!("drag request: mismatched source entities (potential manipulation?), ignoring request"); + continue; + } + + // we've passed initial validation. + // do not allow drags with the source or destination if they already have a peer (are attached) + if target_joint.3.is_some() { + warn!("drag request: cannot attach to a joint that already has a peer, ignoring request"); + continue; + } + if source_joint.3.is_some() { + warn!("drag request: dragging from a part that is already attached is currently not supported, ignoring request"); + continue; + } + + // great, the attachment appears to be valid + // let's make sure this player is allowed to drag onto this part + let target_part = { + let Ok(target_part) = parts.get(target_joint.1.0) else { continue }; + target_part.clone() + }; + + let source_part = { + let Ok(source_part) = parts.get(source_joint.1.0) else { continue }; + source_part.clone() + }; + + let allowed = target_joint.1.0 == *player_hearty_entity || target_part.1.is_some_and(|u| u.0 == *player_hearty_entity); + if !allowed { + warn!("drag request: this player cannot move this part, ignoring request"); + continue; + } + + // TODO - validate source_part? + + // great, we have a valid peering request + // create the peering component... + commands.entity(source_joint.4) + .insert(Peer(target_joint.4)); + commands.entity(target_joint.4) + .insert(Peer(source_joint.4)); + + // propagate PartInShip... + + let part_in_ship = if target_joint.1.0 == *player_hearty_entity { + PartInShip(*player_hearty_entity) + } else { + PartInShip(target_part.1.unwrap().0) // unwrap: checked above (during 'allowed' calculation) + }; + + commands.entity(source_part.2) + .insert(part_in_ship); + + let target_position = target_part.0.mul_transform(*target_joint.2); + + let rotation = event.set_rotation.to_scaled_axis().z; + let rotation = Rot2::radians(rotation); + + // create the joint... + let joint = FixedJointBuilder::new() + .local_anchor1(target_joint.2.translation.xy()) + .local_basis2(rotation.as_radians()); + + commands.entity(source_part.2) + .insert(ImpulseJoint::new(target_part.2, joint)); + + teleport_to_translation = target_position.translation.xy(); + teleport_to_rotation = event.set_rotation; + // and we're done! + } else { + warn!("blindly accepting non-attachment request, someone should change this eventually"); + warn!("dragging already attached entities may cause inconsistent behavior!!"); + teleport_to_translation = event.drag_to; + teleport_to_rotation = event.set_rotation; + } + let mut part = parts.get_mut(event.drag_target).unwrap(); - part.translation.x = event.drag_to.x; - part.translation.y = event.drag_to.y; - part.rotation = event.set_rotation; + part.0.translation.x = teleport_to_translation.x; + part.0.translation.y = teleport_to_translation.y; + part.0.rotation = teleport_to_rotation; // client calculates this; no reason to recalculate + // ( the math sucks ) } }