Add digging to ECS

This commit is contained in:
Nathan Ruiz 2022-09-24 08:46:44 +00:00
parent d601ec3907
commit 700a31013f
5 changed files with 227 additions and 1 deletions

View File

@ -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<DiggingState>,
pub current: Option<DiggingState>,
pub packets: std::collections::VecDeque<packet::play::serverbound::PlayerDigging>,
}
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()
}
}

View File

@ -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
}

View File

@ -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<MouseButtons>,
digging: ecs::Key<Digging>,
}
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<packet::play::serverbound::PlayerDigging>,
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<DiggingState>,
mouse_buttons: &MouseButtons,
target: Option<(shared::Position, block::Block, shared::Direction, Vector3<f64>)>
) -> Option<DiggingState> {
// 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;
},
_ => {},
}
}
}
}

View File

@ -739,9 +739,25 @@ fn handle_window_event<T>(
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);
}
}

View File

@ -61,6 +61,8 @@ pub struct Server {
// Entity accessors
game_info: ecs::Key<entity::GameInfo>,
player_movement: ecs::Key<entity::player::PlayerMovement>,
mouse_buttons: ecs::Key<entity::MouseButtons>,
digging: ecs::Key<entity::Digging>,
gravity: ecs::Key<entity::Gravity>,
position: ecs::Key<entity::Position>,
target_position: ecs::Key<entity::TargetPosition>,
@ -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() {