~starkingdoms/starkingdoms

a2ff8f1ebb1deb91542e24cb73a025ef85253390 — core 5 months ago 4aabb4e
feat: drag
M Cargo.lock => Cargo.lock +27 -0
@@ 1012,6 1012,30 @@ dependencies = [
]

[[package]]
name = "bevy_dev_tools"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f29a3abf7b8e0148ef4f74a4ec63f4efb00a3d62aba361921a5d259410556d5"
dependencies = [
 "bevy_app 0.16.1",
 "bevy_asset 0.16.1",
 "bevy_color",
 "bevy_diagnostic 0.16.1",
 "bevy_ecs 0.16.1",
 "bevy_input 0.16.1",
 "bevy_picking",
 "bevy_reflect 0.16.1",
 "bevy_render 0.16.1",
 "bevy_state",
 "bevy_text",
 "bevy_time 0.16.1",
 "bevy_ui 0.16.1",
 "bevy_utils 0.16.1",
 "bevy_window 0.16.1",
 "tracing",
]

[[package]]
name = "bevy_diagnostic"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1427,6 1451,7 @@ dependencies = [
 "bevy_color",
 "bevy_core_pipeline 0.16.1",
 "bevy_derive 0.16.1",
 "bevy_dev_tools",
 "bevy_diagnostic 0.16.1",
 "bevy_ecs 0.16.1",
 "bevy_gizmos 0.16.1",


@@ 2043,11 2068,13 @@ dependencies = [
 "bevy_ecs 0.16.1",
 "bevy_image",
 "bevy_math 0.16.1",
 "bevy_picking",
 "bevy_platform",
 "bevy_reflect 0.16.1",
 "bevy_render 0.16.1",
 "bevy_transform 0.16.1",
 "bevy_utils 0.16.1",
 "bevy_window 0.16.1",
 "bitflags 2.9.1",
 "bytemuck",
 "derive_more 1.0.0",

M crates/unified/Cargo.toml => crates/unified/Cargo.toml +3 -1
@@ 24,7 24,9 @@ bevy = { version = "0.16", default-features = false, features = [
    "bevy_log",
    "x11",
    "wayland",
    "multi_threaded"
    "multi_threaded",
    "bevy_dev_tools",
    "bevy_sprite_picking_backend"
] }
bevy_rapier2d = { version = "0.30", features = ["serde-serialize", "simd-stable"] }
bevy_common_assets = { version = "0.13", features = ["toml"] }

M crates/unified/src/client/key_input.rs => crates/unified/src/client/key_input.rs +8 -1
@@ 3,6 3,8 @@ use bevy::{
    ecs::{event::EventWriter, system::Res},
    input::{ButtonInput, keyboard::KeyCode},
};
use bevy::dev_tools::picking_debug::DebugPickingMode;
use bevy::log::info;
use bevy::prelude::ResMut;
use bevy_rapier2d::render::DebugRenderContext;
use crate::ecs::ThrustEvent;


@@ 14,11 16,16 @@ pub fn key_input_plugin(app: &mut App) {

fn debug_render_keybind(
    keys: Res<ButtonInput<KeyCode>>,
    mut debug_render: ResMut<DebugRenderContext>
    mut debug_render: ResMut<DebugRenderContext>,
    mut picking_debug_mode: ResMut<DebugPickingMode>
) {
    if keys.just_pressed(KeyCode::F3) {
        debug_render.enabled = !debug_render.enabled;
    }
    if keys.just_pressed(KeyCode::F4) {
        *picking_debug_mode = DebugPickingMode::Noisy;
        info!("{:?}", picking_debug_mode);
    }
}



M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +6 -4
@@ 1,6 1,6 @@
mod colors;
mod incoming_particles;
mod incoming_parts;
mod parts;
mod key_input;
mod net;
mod starfield;


@@ 10,7 10,7 @@ mod zoom;
mod particles;

use crate::client::incoming_particles::replicated_particles_plugin;
use crate::client::incoming_parts::incoming_parts_plugin;
use crate::client::parts::parts_plugin;
use planet::incoming_planets::incoming_planets_plugin;
use crate::client::key_input::key_input_plugin;
use crate::client::starfield::starfield_plugin;


@@ 21,6 21,7 @@ use aeronet_websocket::client::WebSocketClient;
use bevy::core_pipeline::bloom::Bloom;
use bevy::core_pipeline::fxaa::Fxaa;
use bevy::core_pipeline::tonemapping::DebandDither;
use bevy::dev_tools::picking_debug::DebugPickingMode;
use bevy::prelude::*;
use bevy::window::PrimaryWindow;
use bevy_replicon::shared::server_entity_map::ServerEntityMap;


@@ 50,12 51,13 @@ impl Plugin for ClientPlugin {
            .add_systems(Update, find_me)
            .add_systems(Update, set_config)
            .add_plugins((incoming_planets_plugin, indicators_plugin))
            .add_plugins(incoming_parts_plugin)
            .add_plugins(parts_plugin)
            .add_plugins(key_input_plugin)
            .add_plugins(starfield_plugin)
            .add_plugins(ui_plugin)
            .add_plugins(replicated_particles_plugin)
            .add_plugins(zoom_plugin);
            .add_plugins(zoom_plugin)
            .insert_resource(DebugPickingMode::Disabled);
    }
}


R crates/unified/src/client/incoming_parts.rs => crates/unified/src/client/parts.rs +44 -4
@@ 1,10 1,15 @@
use crate::ecs::Part;
use std::fmt::Debug;
use crate::ecs::{CursorWorldCoordinates, Part};
use bevy::prelude::*;
use bevy_rapier2d::dynamics::MassProperties;
use bevy_rapier2d::prelude::{AdditionalMassProperties, ReadMassProperties, RigidBody};
use crate::client::Me;
use crate::clientevent::{PartDragControlEvent, PartDragEvent};

pub fn incoming_parts_plugin(app: &mut App) {
    app.add_systems(Update, (handle_incoming_parts, handle_updated_parts));
pub fn parts_plugin(app: &mut App) {
    app.insert_resource(DragResource(None));
    app.add_systems(Update, (handle_incoming_parts, handle_updated_parts, send_drag));
    app.add_observer(on_part_release);
}

fn handle_incoming_parts(


@@ 25,7 30,10 @@ fn handle_incoming_parts(
                principal_inertia: 7.5,
            }))
            .insert(ReadMassProperties::default())
            .insert(RigidBody::Dynamic);
            .insert(RigidBody::Dynamic)
            .insert(Pickable::default())
            .observe(on_part_click);

        info!(?new_part, ?new_entity, "prepared new part");
    }
}


@@ 51,3 59,35 @@ fn handle_updated_parts(
        info!(?updated_part, ?updated_entity, "updated part");
    }
}

#[derive(Resource)]
struct DragResource(Option<Entity>);


fn on_part_click(ev: Trigger<Pointer<Pressed>>, sprites: Query<&Sprite, Without<Me>>, mut drag: ResMut<DragResource>, mut events: EventWriter<PartDragControlEvent>) {
    if ev.button != PointerButton::Primary { return; };
    let Ok(sprite) = sprites.get(ev.target()) else { return; };

    drag.0 = Some(ev.target());

    events.write(PartDragControlEvent::Start(ev.target()));

    info!(?sprite, ?ev, "start drag");
}
fn on_part_release(ev: Trigger<Pointer<Released>>, mut drag: ResMut<DragResource>, mut events: EventWriter<PartDragControlEvent>) {
    if ev.button != PointerButton::Primary { return; };

    if let Some(e) = drag.0 {
        events.write(PartDragControlEvent::Stop(e));
    }

    drag.0 = None;

    info!(?ev, "stop drag");
}
fn send_drag(drag: ResMut<DragResource>, coords: Res<CursorWorldCoordinates>, mut events: EventWriter<PartDragEvent>) {
    let Some(dragging) = drag.0 else { return; };
    let Some(coordinates) = coords.0 else { return; };

    events.write(PartDragEvent(dragging, coordinates));
}
\ No newline at end of file

M crates/unified/src/client_plugins.rs => crates/unified/src/client_plugins.rs +5 -0
@@ 3,7 3,10 @@ use aeronet_replicon::client::AeronetRepliconClientPlugin;
use aeronet_websocket::client::WebSocketClientPlugin;
use bevy::DefaultPlugins;
use bevy::app::{PluginGroup, PluginGroupBuilder};
use bevy::dev_tools::picking_debug::DebugPickingPlugin;
use bevy::log::LogPlugin;
use bevy::prelude::MeshPickingPlugin;
use bevy::sprite::prelude::SpritePickingPlugin;
use bevy_enoki::EnokiPlugin;
use bevy_rapier2d::prelude::RapierDebugRenderPlugin;
use bevy_replicon::RepliconPlugins;


@@ 19,6 22,8 @@ impl PluginGroup for ClientPluginGroup {
            .add(WebSocketClientPlugin)
            .add(AeronetRepliconClientPlugin)
            .add(EnokiPlugin)
            .add(MeshPickingPlugin)
            .add(DebugPickingPlugin)
            .add(ClientPlugin {
                server: self.server,
            })

A crates/unified/src/clientevent.rs => crates/unified/src/clientevent.rs +25 -0
@@ 0,0 1,25 @@
use bevy::ecs::entity::MapEntities;
use bevy::math::Vec2;
use bevy::prelude::{Entity, EntityMapper, Event};
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Event, Serialize, Clone)]
pub struct PartDragEvent(pub Entity, pub Vec2);
impl MapEntities for PartDragEvent {
    fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
        self.0 = entity_mapper.get_mapped(self.0);
    }
}
#[derive(Debug, Deserialize, Event, Serialize, Clone)]
pub enum PartDragControlEvent {
    Start(Entity),
    Stop(Entity)
}
impl MapEntities for PartDragControlEvent {
    fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
        match self {
            PartDragControlEvent::Start(entity) => *entity = entity_mapper.get_mapped(*entity),
            PartDragControlEvent::Stop(entity) => *entity = entity_mapper.get_mapped(*entity),
        }
    }
}
\ No newline at end of file

M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +0 -2
@@ 21,8 21,6 @@ pub struct StarfieldBack;
#[derive(Resource, Default)]
pub struct CursorWorldCoordinates(pub Option<Vec2>);

#[derive(Debug, Default, Deserialize, Event, Serialize)]
pub struct SendBallHere(pub Vec2);

#[derive(Debug, Deserialize, Event, Serialize)]
pub enum ThrustEvent {

M crates/unified/src/lib.rs => crates/unified/src/lib.rs +1 -0
@@ 27,3 27,4 @@ pub mod server_plugins;
pub mod shared_plugins;
#[cfg(all(not(target_arch = "wasm32"), feature = "particle_editor"))]
pub mod particle_editor;
pub mod clientevent;

M crates/unified/src/server/gravity.rs => crates/unified/src/server/gravity.rs +2 -1
@@ 4,13 4,14 @@ use crate::server::world_config::WorldConfigResource;
use bevy::math::FloatPow;
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
use crate::server::part_dragging::BeingDragged;

pub fn newtonian_gravity_plugin(app: &mut App) {
    app.add_systems(Update, update_gravity);
}

fn update_gravity(
    mut part_query: Query<(&Transform, &ReadMassProperties, &mut ExternalForce), With<Part>>,
    mut part_query: Query<(&Transform, &ReadMassProperties, &mut ExternalForce), (With<Part>, Without<BeingDragged>)>,
    planet_query: Query<(&Transform, &ReadMassProperties), With<Planet>>,
    world_config: Res<WorldConfigResource>,
) {

M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +3 -0
@@ 3,6 3,7 @@ pub mod planets;
pub mod player;
mod world_config;
mod earth_parts;
mod part_dragging;

use crate::server::gravity::newtonian_gravity_plugin;
use crate::server::planets::planets_plugin;


@@ 17,6 18,7 @@ use bevy::prelude::*;
use bevy_replicon::prelude::Replicated;
use std::net::SocketAddr;
use crate::server::earth_parts::spawn_parts_plugin;
use crate::server::part_dragging::part_dragging_plugin;

pub struct ServerPlugin {
    pub bind: SocketAddr,


@@ 44,6 46,7 @@ impl Plugin for ServerPlugin {
            .add_plugins(world_config_plugin)
            .add_plugins(newtonian_gravity_plugin)
            .add_plugins(player_management_plugin)
            .add_plugins(part_dragging_plugin)
            .add_plugins(spawn_parts_plugin);
    }
}

A crates/unified/src/server/part_dragging.rs => crates/unified/src/server/part_dragging.rs +41 -0
@@ 0,0 1,41 @@
use bevy::prelude::*;
use bevy_rapier2d::prelude::{Collider, ColliderDisabled};
use bevy_replicon::prelude::FromClient;
use crate::clientevent::{PartDragControlEvent, PartDragEvent};
use crate::ecs::Part;

pub fn part_dragging_plugin(app: &mut App) {
    app.add_systems(Update, (handle_dragged_parts, handle_start_stop_drag));
}

// todo: attachment

#[derive(Component)]
pub struct BeingDragged;

fn handle_dragged_parts(mut transforms: Query<&mut Transform, (With<BeingDragged>)>, mut events: EventReader<FromClient<PartDragEvent>>) {
    for event in events.read() {
        let Ok(mut transform) = transforms.get_mut(event.0) else { continue; };
        transform.translation = (event.1, 0.0).into();
    }
}
fn handle_start_stop_drag(mut colliders: Query<&Collider, With<Part>>, mut events: EventReader<FromClient<PartDragControlEvent>>, mut commands: Commands) {
    for event in events.read() {
        let entity = match &event.event {
            PartDragControlEvent::Start(e) => *e,
            PartDragControlEvent::Stop(e) => *e,
        };
        let Ok(_) = colliders.get(entity) else { continue; };
        match &event.event {
            PartDragControlEvent::Start(_) => {
                commands.entity(entity)
                    .insert(BeingDragged)
                    .insert(ColliderDisabled);
            },
            PartDragControlEvent::Stop(_) => {
                commands.entity(entity).remove::<BeingDragged>()
                    .remove::<ColliderDisabled>();
            }
        }
    }
}
\ No newline at end of file

M crates/unified/src/shared_plugins.rs => crates/unified/src/shared_plugins.rs +5 -2
@@ 1,9 1,10 @@
use crate::config::planet::Planet;
use crate::ecs::{Ball, Ground, Part, Particles, Player, SendBallHere, ThrustEvent};
use crate::ecs::{Ball, Ground, Part, Particles, Player, ThrustEvent};
use bevy::app::{App, PluginGroup, PluginGroupBuilder};
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
use bevy_replicon::prelude::{AppRuleExt, Channel, ClientEventAppExt};
use crate::clientevent::{PartDragControlEvent, PartDragEvent};

pub struct SharedPluginGroup;



@@ 17,8 18,10 @@ impl PluginGroup for SharedPluginGroup {
}

pub fn register_everything(app: &mut App) {
    app.add_client_event::<SendBallHere>(Channel::Ordered)
    app
        .add_client_event::<ThrustEvent>(Channel::Ordered)
        .add_mapped_client_event::<PartDragEvent>(Channel::Unreliable)
        .add_mapped_client_event::<PartDragControlEvent>(Channel::Ordered)
        .replicate::<Transform>()
        .replicate::<Ball>()
        .replicate::<Ground>()