Merge pull request #731 from nathanruiz/digging
Add the ability to break blocks
This commit is contained in:
commit
6ad43b3ccb
1040
blocks/src/lib.rs
1040
blocks/src/lib.rs
File diff suppressed because it is too large
Load Diff
|
@ -948,19 +948,36 @@ impl fmt::Debug for VarLong {
|
|||
impl Serializable for Position {
|
||||
fn read_from<R: io::Read>(buf: &mut R) -> Result<Position, Error> {
|
||||
let pos = buf.read_u64::<BigEndian>()?;
|
||||
let protocol_version = current_protocol_version();
|
||||
if protocol_version < 477 {
|
||||
Ok(Position::new(
|
||||
((pos as i64) >> 38) as i32,
|
||||
((pos as i64) & 0xFFF) as i32,
|
||||
(((pos as i64) >> 26) & 0xFFF) as i32,
|
||||
((pos as i64) << 38 >> 38) as i32,
|
||||
))
|
||||
} else {
|
||||
Ok(Position::new(
|
||||
((pos as i64) >> 38) as i32,
|
||||
((pos as i64) << 52 >> 52) as i32,
|
||||
((pos as i64) << 26 >> 38) as i32,
|
||||
))
|
||||
}
|
||||
}
|
||||
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
|
||||
let pos = (((self.x as u64) & 0x3FFFFFF) << 38)
|
||||
let pos;
|
||||
let protocol_version = current_protocol_version();
|
||||
if protocol_version < 477 {
|
||||
pos = (((self.x as u64) & 0x3FFFFFF) << 38)
|
||||
| (((self.y as u64) & 0xFFF) << 26)
|
||||
| ((self.z as u64) & 0x3FFFFFF);
|
||||
} else {
|
||||
pos = (((self.x as u64) & 0x3FFFFFF) << 38)
|
||||
| ((self.y as u64) & 0xFFF)
|
||||
| (((self.z as u64) & 0x3FFFFFF) << 12);
|
||||
}
|
||||
|
||||
buf.write_u64::<BigEndian>(pos)?;
|
||||
Result::Ok(())
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,36 @@ 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>,
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use super::{
|
||||
Bounds, GameInfo, Gravity, Light, Position, Rotation, TargetPosition, TargetRotation, Velocity,
|
||||
Bounds, Digging, GameInfo, Gravity, Light, MouseButtons, Position, Rotation, TargetPosition,
|
||||
TargetRotation, Velocity,
|
||||
};
|
||||
use crate::ecs;
|
||||
use crate::format;
|
||||
|
@ -43,6 +44,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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use super::*;
|
||||
use crate::ecs;
|
||||
use crate::render;
|
||||
use crate::shared::Position as BPos;
|
||||
use crate::world;
|
||||
use cgmath::InnerSpace;
|
||||
use log::debug;
|
||||
use steven_protocol::protocol;
|
||||
use steven_protocol::protocol::Conn;
|
||||
|
||||
pub struct ApplyVelocity {
|
||||
filter: ecs::Filter,
|
||||
|
@ -285,3 +291,186 @@ impl ecs::System for LightEntity {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ApplyDigging {
|
||||
filter: ecs::Filter,
|
||||
mouse_buttons: ecs::Key<MouseButtons>,
|
||||
digging: ecs::Key<Digging>,
|
||||
conn: ecs::Key<Arc<RwLock<Option<Conn>>>>,
|
||||
}
|
||||
|
||||
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,
|
||||
conn: m.get_key(),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_packet(&self, conn: &mut Conn, target: &DiggingState, state: i32) {
|
||||
match state {
|
||||
0 => debug!("Send start dig packet {:?}", target),
|
||||
1 => debug!("Send cancel dig packet {:?}", target),
|
||||
2 => debug!("Send finish dig packet {:?}", target),
|
||||
n => panic!("Invalid dig state {}", n),
|
||||
}
|
||||
|
||||
match conn.protocol_version {
|
||||
// 1.7.10
|
||||
5 => conn
|
||||
.write_packet(packet::play::serverbound::PlayerDigging_u8_u8y {
|
||||
status: state as u8,
|
||||
x: target.position.x,
|
||||
y: target.position.y as u8,
|
||||
z: target.position.z,
|
||||
face: target.face.index() as u8,
|
||||
})
|
||||
.unwrap(),
|
||||
// 1.8.9 or v15w39c
|
||||
47 | 74 => conn
|
||||
.write_packet(packet::play::serverbound::PlayerDigging_u8 {
|
||||
status: state as u8,
|
||||
location: target.position,
|
||||
face: target.face.index() as u8,
|
||||
})
|
||||
.unwrap(),
|
||||
// 1.9+
|
||||
_ => conn
|
||||
.write_packet(packet::play::serverbound::PlayerDigging {
|
||||
status: protocol::VarInt(state),
|
||||
location: target.position,
|
||||
face: target.face.index() as u8,
|
||||
})
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
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::{test_block, trace_ray};
|
||||
use cgmath::EuclideanSpace;
|
||||
|
||||
let world_entity = m.get_world();
|
||||
let mut conn = m
|
||||
.get_component(world_entity, self.conn)
|
||||
.unwrap()
|
||||
.write()
|
||||
.unwrap();
|
||||
let conn = match conn.as_mut() {
|
||||
Some(conn) => conn,
|
||||
// Don't keep processing digging operations if the connection was
|
||||
// closed.
|
||||
None => return,
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
// 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(conn, current, 0),
|
||||
// Cancel the previous digging operation.
|
||||
(Some(last), None) if !last.finished => self.send_packet(conn, 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(conn, last, 1);
|
||||
}
|
||||
// Start the new digging operation.
|
||||
self.send_packet(conn, current, 0);
|
||||
}
|
||||
// Finish the new digging operation.
|
||||
(Some(_), Some(current)) if !self.is_finished(current) && !current.finished => {
|
||||
self.send_packet(conn, current, 2);
|
||||
current.finished = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -738,9 +738,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ pub mod target;
|
|||
|
||||
pub struct Server {
|
||||
uuid: protocol::UUID,
|
||||
conn: Option<protocol::Conn>,
|
||||
conn: Arc<RwLock<Option<protocol::Conn>>>,
|
||||
protocol_version: i32,
|
||||
forge_mods: Vec<forge::ForgeMod>,
|
||||
read_queue: Option<mpsc::Receiver<Result<packet::Packet, protocol::Error>>>,
|
||||
|
@ -61,6 +61,7 @@ pub struct Server {
|
|||
// Entity accessors
|
||||
game_info: ecs::Key<entity::GameInfo>,
|
||||
player_movement: ecs::Key<entity::player::PlayerMovement>,
|
||||
mouse_buttons: ecs::Key<entity::MouseButtons>,
|
||||
gravity: ecs::Key<entity::Gravity>,
|
||||
position: ecs::Key<entity::Position>,
|
||||
target_position: ecs::Key<entity::TargetPosition>,
|
||||
|
@ -168,7 +169,7 @@ impl Server {
|
|||
forge_mods,
|
||||
protocol::UUID::from_str(&val.uuid).unwrap(),
|
||||
resources,
|
||||
Some(write),
|
||||
Arc::new(RwLock::new(Some(write))),
|
||||
Some(rx),
|
||||
));
|
||||
}
|
||||
|
@ -185,7 +186,7 @@ impl Server {
|
|||
forge_mods,
|
||||
val.uuid,
|
||||
resources,
|
||||
Some(write),
|
||||
Arc::new(RwLock::new(Some(write))),
|
||||
Some(rx),
|
||||
));
|
||||
}
|
||||
|
@ -329,7 +330,7 @@ impl Server {
|
|||
forge_mods,
|
||||
uuid,
|
||||
resources,
|
||||
Some(write),
|
||||
Arc::new(RwLock::new(Some(write))),
|
||||
Some(rx),
|
||||
))
|
||||
}
|
||||
|
@ -357,7 +358,7 @@ impl Server {
|
|||
vec![],
|
||||
protocol::UUID::default(),
|
||||
resources,
|
||||
None,
|
||||
Arc::new(RwLock::new(None)),
|
||||
None,
|
||||
);
|
||||
let mut rng = rand::thread_rng();
|
||||
|
@ -445,7 +446,7 @@ impl Server {
|
|||
forge_mods: Vec<forge::ForgeMod>,
|
||||
uuid: protocol::UUID,
|
||||
resources: Arc<RwLock<resources::Manager>>,
|
||||
conn: Option<protocol::Conn>,
|
||||
conn: Arc<RwLock<Option<protocol::Conn>>>,
|
||||
read_queue: Option<mpsc::Receiver<Result<packet::Packet, protocol::Error>>>,
|
||||
) -> Server {
|
||||
let mut entities = ecs::Manager::new();
|
||||
|
@ -454,6 +455,7 @@ impl Server {
|
|||
let world_entity = entities.get_world();
|
||||
let game_info = entities.get_key();
|
||||
entities.add_component(world_entity, game_info, entity::GameInfo::new());
|
||||
entities.add_component(world_entity, entities.get_key(), conn.clone());
|
||||
|
||||
let version = resources.read().unwrap().version();
|
||||
Server {
|
||||
|
@ -477,6 +479,7 @@ impl Server {
|
|||
// Entity accessors
|
||||
game_info,
|
||||
player_movement: entities.get_key(),
|
||||
mouse_buttons: entities.get_key(),
|
||||
gravity: entities.get_key(),
|
||||
position: entities.get_key(),
|
||||
target_position: entities.get_key(),
|
||||
|
@ -500,7 +503,7 @@ impl Server {
|
|||
}
|
||||
|
||||
pub fn disconnect(&mut self, reason: Option<format::Component>) {
|
||||
self.conn = None;
|
||||
self.conn.write().unwrap().take();
|
||||
self.disconnect_reason = reason;
|
||||
if let Some(player) = self.player.take() {
|
||||
self.entities.remove_entity(player);
|
||||
|
@ -509,7 +512,7 @@ impl Server {
|
|||
}
|
||||
|
||||
pub fn is_connected(&self) -> bool {
|
||||
self.conn.is_some()
|
||||
self.conn.read().unwrap().is_some()
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, renderer: &mut render::Renderer, delta: f64) {
|
||||
|
@ -656,12 +659,12 @@ impl Server {
|
|||
Err(err) => panic!("Err: {:?}", err),
|
||||
}
|
||||
// Disconnected
|
||||
if self.conn.is_none() {
|
||||
if self.conn.read().unwrap().is_none() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if self.conn.is_some() {
|
||||
if self.conn.read().unwrap().is_some() {
|
||||
self.read_queue = Some(rx);
|
||||
}
|
||||
}
|
||||
|
@ -793,6 +796,24 @@ 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() {
|
||||
|
@ -900,8 +921,9 @@ impl Server {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn write_packet<T: protocol::PacketType>(&mut self, p: T) {
|
||||
let _ = self.conn.as_mut().unwrap().write_packet(p); // TODO handle errors
|
||||
pub fn write_packet<T: protocol::PacketType>(&self, p: T) {
|
||||
let mut conn = self.conn.write().unwrap();
|
||||
let _ = conn.as_mut().unwrap().write_packet(p); // TODO handle errors
|
||||
}
|
||||
|
||||
fn on_keep_alive_i64(
|
||||
|
@ -1048,15 +1070,13 @@ impl Server {
|
|||
|
||||
// TODO: remove wrappers and directly call on Conn
|
||||
fn write_fmlhs_plugin_message(&mut self, msg: &forge::FmlHs) {
|
||||
let _ = self.conn.as_mut().unwrap().write_fmlhs_plugin_message(msg); // TODO handle errors
|
||||
let mut conn = self.conn.write().unwrap();
|
||||
let _ = conn.as_mut().unwrap().write_fmlhs_plugin_message(msg); // TODO handle errors
|
||||
}
|
||||
|
||||
fn write_plugin_message(&mut self, channel: &str, data: &[u8]) {
|
||||
let _ = self
|
||||
.conn
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.write_plugin_message(channel, data); // TODO handle errors
|
||||
let mut conn = self.conn.write().unwrap();
|
||||
let _ = conn.as_mut().unwrap().write_plugin_message(channel, data); // TODO handle errors
|
||||
}
|
||||
|
||||
fn on_game_join_worldnames_ishard_simdist(
|
||||
|
|
Loading…
Reference in New Issue