Merge pull request #731 from nathanruiz/digging

Add the ability to break blocks
This commit is contained in:
iceiix 2022-10-30 16:10:45 -07:00 committed by GitHub
commit 6ad43b3ccb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1350 additions and 28 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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>()?;
Ok(Position::new(
((pos as i64) >> 38) as i32,
((pos as i64) & 0xFFF) as i32,
((pos as i64) << 26 >> 38) as i32,
))
let protocol_version = current_protocol_version();
if protocol_version < 477 {
Ok(Position::new(
((pos as i64) >> 38) 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)
| ((self.y as u64) & 0xFFF)
| (((self.z as u64) & 0x3FFFFFF) << 12);
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(())
}
}

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,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()
}
}

View File

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

View File

@ -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;
}
_ => {}
}
}
}
}

View File

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

View File

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