~starkingdoms/starkingdoms

d2dc2f8a62538214e085896a81874e1f98a1f9ec — core 5 months ago 1b6e187
??? ? ? ? ?????????????????????
M crates/unified/assets/config/parts/hearty.part.toml => crates/unified/assets/config/parts/hearty.part.toml +4 -4
@@ 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

M crates/unified/assets/config/parts/housing.part.toml => crates/unified/assets/config/parts/housing.part.toml +5 -5
@@ 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

M crates/unified/src/client/key_input.rs => crates/unified/src/client/key_input.rs +9 -4
@@ 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<ButtonInput<KeyCode>>, 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<Part>>,
    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 {

M crates/unified/src/client/parts.rs => crates/unified/src/client/parts.rs +4 -2
@@ 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<Entity>);
#[derive(Resource)]
struct SnapResource(Option<Entity>);
struct SnapResource(Option<Entity>, Option<Entity>);

#[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;
}

M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +2 -0
@@ 75,6 75,8 @@ pub struct DragRequestEvent {
    pub set_rotation: Quat,
    #[entities]
    pub snap_target: Option<Entity>,
    #[entities]
    pub peer_snap: Option<Entity>
}

#[derive(Component, Serialize, Deserialize, Debug)]

M crates/unified/src/server/player.rs => crates/unified/src/server/player.rs +108 -6
@@ 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<FromClient<DragRequestEvent>>,
    mut parts: Query<&mut Transform, With<Part>>,
    mut parts: Query<(&mut Transform, Option<&PartInShip>, Entity), (With<Part>, Without<Joint>)>,
    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 )
    }
}