From 700a31013f5244ca23fabd75027aa03a9eebf62a Mon Sep 17 00:00:00 2001 From: Nathan Ruiz Date: Sat, 24 Sep 2022 08:46:44 +0000 Subject: [PATCH] Add digging to ECS --- src/entity/mod.rs | 38 ++++++++++++ src/entity/player.rs | 4 +- src/entity/systems.rs | 138 ++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 16 +++++ src/server/mod.rs | 32 ++++++++++ 5 files changed, 227 insertions(+), 1 deletion(-) diff --git a/src/entity/mod.rs b/src/entity/mod.rs index d5c80bf..5e942b6 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -1,3 +1,5 @@ +use steven_blocks as block; +use steven_protocol::protocol::packet; pub mod block_entity; pub mod player; @@ -23,6 +25,8 @@ pub fn add_systems(m: &mut ecs::Manager) { m.add_render_system(sys); let sys = systems::LightEntity::new(m); m.add_render_system(sys); + let sys = systems::ApplyDigging::new(m); + m.add_render_system(sys); block_entity::add_systems(m); } @@ -161,3 +165,37 @@ impl Light { Default::default() } } + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DiggingState { + pub block: block::Block, + pub position: shared::Position, + pub face: shared::Direction, + pub start: std::time::Instant, + pub finished: bool, +} + +#[derive(Default)] +pub struct Digging { + pub last: Option, + pub current: Option, + pub packets: std::collections::VecDeque, +} + +impl Digging { + pub fn new() -> Self { + Default::default() + } +} + +#[derive(Default)] +pub struct MouseButtons { + pub left: bool, + pub right: bool, +} + +impl MouseButtons { + pub fn new() -> Self { + Default::default() + } +} \ No newline at end of file diff --git a/src/entity/player.rs b/src/entity/player.rs index a0b6da8..40474a5 100644 --- a/src/entity/player.rs +++ b/src/entity/player.rs @@ -1,5 +1,5 @@ use super::{ - Bounds, GameInfo, Gravity, Light, Position, Rotation, TargetPosition, TargetRotation, Velocity, + Bounds, GameInfo, Gravity, Light, Position, Rotation, TargetPosition, TargetRotation, Velocity, MouseButtons, Digging }; use crate::ecs; use crate::format; @@ -43,6 +43,8 @@ pub fn create_local(m: &mut ecs::Manager) -> ecs::Entity { ); m.add_component_direct(entity, PlayerModel::new("", false, false, true)); m.add_component_direct(entity, Light::new()); + m.add_component_direct(entity, Digging::new()); + m.add_component_direct(entity, MouseButtons::new()); entity } diff --git a/src/entity/systems.rs b/src/entity/systems.rs index af3101f..8baf1c2 100644 --- a/src/entity/systems.rs +++ b/src/entity/systems.rs @@ -1,9 +1,12 @@ +use std::collections::VecDeque; + use super::*; use crate::ecs; use crate::render; use crate::shared::Position as BPos; use crate::world; use cgmath::InnerSpace; +use steven_protocol::protocol; pub struct ApplyVelocity { filter: ecs::Filter, @@ -285,3 +288,138 @@ impl ecs::System for LightEntity { } } } + +pub struct ApplyDigging { + filter: ecs::Filter, + mouse_buttons: ecs::Key, + digging: ecs::Key, +} + +impl ApplyDigging { + pub fn new(m: &mut ecs::Manager) -> Self { + let mouse_buttons = m.get_key(); + let digging = m.get_key(); + Self { + filter: ecs::Filter::new().with(mouse_buttons).with(digging), + mouse_buttons, + digging, + } + } + + fn send_packet(&self, + packets: &mut VecDeque, + target: &DiggingState, + state: i32 + ) { + match state { + 0 => println!("Send start dig packet {:?}", target), + 1 => println!("Send cancel dig packet {:?}", target), + 2 => println!("Send finish dig packet {:?}", target), + n => panic!("Invalid dig state {}", n), + } + + packets.push_back(packet::play::serverbound::PlayerDigging { + status: protocol::VarInt(state), + location: target.position, + face: target.face.index() as u8, + }); + } + + fn next_state(&self, + last: &Option, + mouse_buttons: &MouseButtons, + target: Option<(shared::Position, block::Block, shared::Direction, Vector3)> + ) -> Option { + // Figure out the next state + if !mouse_buttons.left { + return None; + } + + match (last, target) { + // Started digging + (None, Some((position, block, face, _))) => Some(DiggingState { + block, face, position, + start: std::time::Instant::now(), + finished: false, + }), + (Some(current), Some((position, block, face, _))) => { + // Continue digging + if position == current.position { + return last.clone(); + } + + // Start digging a different block. + Some(DiggingState { + block, face, position, + start: std::time::Instant::now(), + finished: false, + }) + } + // Not pointing at any target + (_, None) => None, + } + } + + fn is_finished(&self, state: &DiggingState) -> bool { + let mining_time = state.block.get_mining_time(&None); + match mining_time { + Some(mining_time) => { + let finish_time = state.start + mining_time; + finish_time > std::time::Instant::now() + }, + None => false, + } + } +} + +impl ecs::System for ApplyDigging { + fn filter(&self) -> &ecs::Filter { + &self.filter + } + + fn update(&mut self, m: &mut ecs::Manager, world: &mut world::World, renderer: &mut render::Renderer) { + use crate::server::target::{trace_ray, test_block}; + use cgmath::EuclideanSpace; + + let target = trace_ray( + world, + 4.0, + renderer.camera.pos.to_vec(), + renderer.view_vector.cast().unwrap(), + test_block, + ); + + for e in m.find(&self.filter) { + let mouse_buttons = m.get_component(e, self.mouse_buttons).unwrap(); + let digging = m.get_component_mut(e, self.digging).unwrap(); + let packets = &mut digging.packets; + + // Update last and current state + std::mem::swap(&mut digging.last, &mut digging.current); + digging.current = self.next_state(&digging.last, mouse_buttons, target); + + // Handle digging packets + match (&digging.last, &mut digging.current) { + // Start the new digging operation. + (None, Some(current)) => self.send_packet(packets, current, 0), + // Cancel the previous digging operation. + (Some(last), None) if !last.finished => self.send_packet(packets, last, 1), + // Move to digging a new block + (Some(last), Some(current)) if last.position != current.position => { + // Cancel the previous digging operation. + if !current.finished { + self.send_packet(packets, last, 1); + } + // Start the new digging operation. + self.send_packet(packets, current, 0); + }, + // Finish the new digging operation. + (Some(_), Some(current)) if !self.is_finished(current) && !current.finished => { + self.send_packet(packets, current, 2); + current.finished = true; + }, + _ => {}, + } + } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c3533d2..63bcc1b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -739,9 +739,25 @@ fn handle_window_event( height, ); } + + if game.focused { + game.server.on_left_mouse_button(false); + } + } + (ElementState::Pressed, MouseButton::Left) => { + if game.focused { + game.server.on_left_mouse_button(true); + } + } + (ElementState::Released, MouseButton::Right) => { + if game.focused { + game.server.on_right_mouse_button(false); + game.server.on_right_click(&mut game.renderer); + } } (ElementState::Pressed, MouseButton::Right) => { if game.focused { + game.server.on_right_mouse_button(true); game.server.on_right_click(&mut game.renderer); } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 6a8fed9..60860a2 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -61,6 +61,8 @@ pub struct Server { // Entity accessors game_info: ecs::Key, player_movement: ecs::Key, + mouse_buttons: ecs::Key, + digging: ecs::Key, gravity: ecs::Key, position: ecs::Key, target_position: ecs::Key, @@ -477,6 +479,8 @@ impl Server { // Entity accessors game_info, player_movement: entities.get_key(), + mouse_buttons: entities.get_key(), + digging: entities.get_key(), gravity: entities.get_key(), position: entities.get_key(), target_position: entities.get_key(), @@ -778,6 +782,12 @@ impl Server { }; self.write_packet(packet); } + + let digging = self.entities.get_component_mut(player, self.digging).unwrap(); + let packets = &mut digging.packets; + while let Some(packet) = packets.pop_front() { + self.write_packet(packet); + } } } @@ -792,6 +802,28 @@ impl Server { } } + pub fn on_left_mouse_button(&mut self, pressed: bool) { + if let Some(player) = self.player { + if let Some(mouse_buttons) = self + .entities + .get_component_mut(player, self.mouse_buttons) + { + mouse_buttons.left = pressed; + } + } + } + + pub fn on_right_mouse_button(&mut self, pressed: bool) { + if let Some(player) = self.player { + if let Some(mouse_buttons) = self + .entities + .get_component_mut(player, self.mouse_buttons) + { + mouse_buttons.right = pressed; + } + } + } + pub fn on_right_click(&mut self, renderer: &mut render::Renderer) { use crate::shared::Direction; if self.player.is_some() {