diff --git a/Cargo.lock b/Cargo.lock index e0815f9..ea37987 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,15 +78,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "byteorder" version = "1.5.0" @@ -135,26 +126,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - [[package]] name = "env_filter" version = "0.1.2" @@ -188,16 +159,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -239,15 +200,6 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" -[[package]] -name = "md2" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4f0f3ed25ff4f8d8d102288d92f900efc202661c884cf67dfe4f0d07c43d1f" -dependencies = [ - "digest", -] - [[package]] name = "md5" version = "0.7.0" @@ -269,7 +221,6 @@ dependencies = [ "colog", "flate2", "log", - "md2", "md5", "num-derive", "num-traits", @@ -402,12 +353,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - [[package]] name = "unicode-ident" version = "1.0.13" @@ -420,12 +365,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index b9509eb..9d427d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ byteorder = "1.5.0" colog = "1.3.0" flate2 = "1.0.34" log = "0.4.22" -md2 = "0.10.2" md5 = "0.7.0" num-derive = "0.4.2" num-traits = "0.2.19" diff --git a/src/chunk.rs b/src/chunk.rs index 8a51d70..50dea0e 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -62,6 +62,45 @@ impl BlockArray { compressed_data: output_buf, } } + + pub fn new_superflat() -> Self { + let mut block_vec = vec![0; 16 * 16 * 128]; + + for x in 0..16 { + for y in 0..128 { + for z in 0..16 { + let pos = y + (z * (128)) + (x * (128) * (16)); + if y == 7 { + block_vec[pos] = BlockType::Grass as u8; + } else if y > 0 && y < 7 { + block_vec[pos] = BlockType::Dirt as u8; + } else if y == 0 { + block_vec[pos] = BlockType::Bedrock as u8; + } else { + block_vec[pos] = 0; + } + } + } + } + for _ in 0..(16 * 128 * 16) / 2 { + block_vec.push(0); + } + for _ in 0..(16 * 128 * 16) / 2 { + block_vec.push(0); + } + for _ in 0..(16 * 128 * 16) / 2 { + block_vec.push(0xFF); + } + + let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); + encoder.write(&block_vec).unwrap(); + let output_buf = encoder.finish().unwrap(); + + Self { + compressed_size: output_buf.len() as i32, + compressed_data: output_buf, + } + } } #[repr(u8)] diff --git a/src/main.rs b/src/main.rs index 3d902af..08d04bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ mod utils; mod byte_ops; mod chunk; +mod position; -use std::{io::{self, Read, Write}, net::{TcpListener, TcpStream}}; +use std::{cell::LazyCell, io::{self, Write}, net::{TcpListener, TcpStream}}; use base16ct::lower::encode_string; use chunk::{BlockArray, MapChunk, PreChunk}; @@ -11,9 +12,23 @@ use byteorder::{ReadBytesExt, WriteBytesExt, BE}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; use byte_ops::ToBytes; +use position::{PlayerLook, PlayerPosition, PlayerPositionAndLook}; use utils::{MCString, ReadMCString, WriteMCString}; use rand::random; +const CHUNKS: LazyCell> = LazyCell::new(|| { + let mut mapchunk = Vec::new(); + for i in -10..10 { + for o in -10..10 { + let x = i * 16; + let z = o * 16; + + mapchunk.push(MapChunk::new(x, z, BlockArray::new_superflat())); + } + } + + mapchunk +}); fn main() { colog::default_builder() @@ -26,7 +41,9 @@ fn main() { info!("Connected to client @ {}", connection.peer_addr().unwrap()); while let Some(cmd) = connection.read_u8().ok() { let command = Command::from_u8(cmd); - info!("COMMAND: {command:?} (0x{cmd:02X?})"); + if command.is_none() { + info!("COMMAND: {command:?} (0x{cmd:02X?})"); + } handle_command(&mut connection, command.unwrap()).unwrap(); } warn!("Lost connection to client"); @@ -35,7 +52,6 @@ fn main() { fn handle_command(mut connection: &mut TcpStream, command: Command) -> Result<(), io::Error> { match command { - Command::KeepAlive => todo!(), Command::Handshake => { let username = connection.read_mcstring()?; let random_number = random::(); @@ -61,10 +77,10 @@ fn handle_command(mut connection: &mut TcpStream, command: Command) -> Result<() info!("Dimension: {dimension}"); let login_packet = ServerLoginPacket { - entity_id: 1200, + entity_id: 1, unknown1: MCString::default(), unknown2: MCString::default(), - map_seed: 1715505462032542147, + map_seed: 0, dimension: 0, }; connection.write_u8(Command::Login as u8).unwrap(); @@ -72,16 +88,16 @@ fn handle_command(mut connection: &mut TcpStream, command: Command) -> Result<() info!("Responded to auth request"); - for i in 0..7 { - for o in 0..7 { - let x = (-4 + i) * 16; - let z = (-5 + o) * 16; + for i in -10..10 { + for o in -10..10 { + let x = i * 16; + let z = o * 16; connection.write_u8(Command::PreChunk as u8).unwrap(); connection.write_all(&PreChunk::new_load(x, z).to_bytes()).unwrap(); connection.write_u8(Command::MapChunk as u8)?; - connection.write_all(&MapChunk::new(x, z, BlockArray::new_air()).to_bytes())?; + connection.write_all(&MapChunk::new(x, z, BlockArray::new_superflat()).to_bytes())?; } } @@ -91,29 +107,74 @@ fn handle_command(mut connection: &mut TcpStream, command: Command) -> Result<() connection.write_u32::(0)?; let playerpos = PlayerPositionAndLook { - position_x: 0.0, - stance: 0.0, - position_y: 70.0, - position_z: 0.0, - yaw: 0.0, - pitch: 0.0, - on_ground: true, + position: PlayerPosition { + position_x: 1.0, + stance: 0.0, + position_y: 9.63, + position_z: 1.0, + }, + look: PlayerLook { + yaw: 0.0, + pitch: 0.0, + }, }; connection.write_u8(Command::PlayerPositionAndLook as u8)?; connection.write_all(&playerpos.to_bytes())?; }, - Command::PlayerPositionAndLook => { - let _poslook = PlayerPositionAndLook::from_bytes(&mut connection); + Command::ChatMessage => { + info!("Chat Message Recieved: {}", connection.read_mcstring().unwrap()); + } + Command::Player => { + connection.read_u8()?; + }, + Command::PlayerLook => { + let _look = PlayerLook::from_bytes(&mut connection); } Command::PlayerPosition => { let _pos = PlayerPosition::from_bytes(&mut connection); } + Command::PlayerPositionAndLook => { + let _poslook = PlayerPositionAndLook::from_bytes(&mut connection); + } + Command::PlayerDigging => { + let status = DiggingStatus::from_u8(connection.read_u8()?).unwrap(); + let pos_x = connection.read_i32::()?; + let pos_y = connection.read_u8()?; + let pos_z = connection.read_i32::()?; + let face = connection.read_u8()?; + } + Command::ArmAnimation => { + let eid = connection.read_i32::()?; + let animate = connection.read_u8()? != 0; + } + Command::Disconnect => { + let disconnect_string = connection.read_mcstring()?; + info!("Disconnecting client: {disconnect_string}"); + connection.shutdown(std::net::Shutdown::Both)?; + } + Command::KeepAlive => { + let _ = connection.write_u8(0x00); + // TODO: Feed keepalive watchdog for client + } + Command::UpdateHealth => { + connection.read_u8()?; + } c => unimplemented!("This command ({c:?}) is probably `Server -> Client` only") } Ok(()) } +#[repr(u8)] +#[derive(Debug, Clone, Copy)] +#[derive(FromPrimitive)] +enum DiggingStatus { + StartedDigging = 0, + Digging = 1, + StoppedDigging = 2, + BlockBroken = 3, +} + #[repr(u8)] #[derive(Debug, Clone, Copy)] #[derive(FromPrimitive)] @@ -127,18 +188,17 @@ enum Command { SpawnPosition = 0x06, UpdateHealth = 0x08, Respawn = 0x09, - PlayerPositionAndLook = 0x0D, + Player = 0x0A, PlayerPosition = 0x0B, + PlayerLook = 0x0C, + PlayerPositionAndLook = 0x0D, + PlayerDigging = 0x0E, + ArmAnimation = 0x12, PreChunk = 0x32, MapChunk = 0x33, Disconnect = 0xFF, } -#[derive(Debug, Clone, Copy)] -struct CommandError { - _id: u8, -} - struct ServerLoginPacket { entity_id: i32, unknown1: MCString, @@ -161,97 +221,3 @@ impl ToBytes for ServerLoginPacket { out_buf } } - -#[derive(Debug, Clone, Copy)] -struct PlayerPositionAndLook { - position_x: f64, - stance: f64, - position_y: f64, - position_z: f64, - yaw: f32, - pitch: f32, - on_ground: bool, -} - -impl ToBytes for PlayerPositionAndLook { - type Bytes = Vec; - - fn to_bytes(self) -> Self::Bytes { - let mut out_buf = Vec::new(); - out_buf.write_f64::(self.position_x).unwrap(); - out_buf.write_f64::(self.stance).unwrap(); - out_buf.write_f64::(self.position_y).unwrap(); - out_buf.write_f64::(self.position_z).unwrap(); - out_buf.write_f32::(self.yaw).unwrap(); - out_buf.write_f32::(self.pitch).unwrap(); - out_buf.write_u8(self.on_ground as u8).unwrap(); - - out_buf - } -} - -impl PlayerPositionAndLook { - fn from_bytes(stream: &mut R) -> Self { - let position_x = stream.read_f64::().unwrap(); - let position_y = stream.read_f64::().unwrap(); - let stance = stream.read_f64::().unwrap(); - let position_z = stream.read_f64::().unwrap(); - - let yaw = stream.read_f32::().unwrap(); - let pitch = stream.read_f32::().unwrap(); - let on_ground = stream.read_u8().unwrap() != 0; - - Self { - position_x, - stance, - position_y, - position_z, - yaw, - pitch, - on_ground - } - } -} - -#[derive(Debug, Clone, Copy)] -struct PlayerPosition { - position_x: f64, - position_y: f64, - stance: f64, - position_z: f64, - on_ground: bool, -} - -impl ToBytes for PlayerPosition { - type Bytes = Vec; - - fn to_bytes(self) -> Self::Bytes { - let mut out_buf = Vec::new(); - out_buf.write_f64::(self.position_x).unwrap(); - out_buf.write_f64::(self.position_y).unwrap(); - out_buf.write_f64::(self.stance).unwrap(); - out_buf.write_f64::(self.position_z).unwrap(); - out_buf.write_u8(self.on_ground as u8).unwrap(); - - out_buf - } -} - -impl PlayerPosition { - fn from_bytes(stream: &mut R) -> Self { - let position_x = stream.read_f64::().unwrap(); - let position_y = stream.read_f64::().unwrap(); - let stance = stream.read_f64::().unwrap(); - let position_z = stream.read_f64::().unwrap(); - - let on_ground = stream.read_u8().unwrap() != 0; - - Self { - position_x, - stance, - position_y, - position_z, - on_ground - } - } -} diff --git a/src/position.rs b/src/position.rs new file mode 100644 index 0000000..7a6299e --- /dev/null +++ b/src/position.rs @@ -0,0 +1,129 @@ +use std::io::Read; + +use byteorder::{ReadBytesExt, WriteBytesExt, BE}; + +use crate::byte_ops::ToBytes; + +#[derive(Debug, Clone, Copy)] +pub struct PlayerPositionAndLook { + pub position: PlayerPosition, + pub look: PlayerLook, +} + +impl ToBytes for PlayerPositionAndLook { + type Bytes = Vec; + + fn to_bytes(self) -> Self::Bytes { + let mut out_buf = Vec::new(); + out_buf.write_f64::(self.position.position_x).unwrap(); + out_buf.write_f64::(self.position.position_y).unwrap(); + out_buf.write_f64::(self.position.stance).unwrap(); + out_buf.write_f64::(self.position.position_z).unwrap(); + out_buf.write_f32::(self.look.yaw).unwrap(); + out_buf.write_f32::(self.look.pitch).unwrap(); + out_buf.write_u8(true as u8).unwrap(); + + out_buf + } +} + +impl PlayerPositionAndLook { + pub fn from_bytes(stream: &mut R) -> Self { + let position_x = stream.read_f64::().unwrap(); + let position_y = stream.read_f64::().unwrap(); + let stance = stream.read_f64::().unwrap(); + let position_z = stream.read_f64::().unwrap(); + + let yaw = stream.read_f32::().unwrap(); + let pitch = stream.read_f32::().unwrap(); + + let _on_ground = stream.read_u8().unwrap(); + + Self { + position: PlayerPosition { + position_x, + stance, + position_y, + position_z, + }, + look: PlayerLook { + yaw, + pitch, + }, + } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct PlayerPosition { + pub position_x: f64, + pub position_y: f64, + pub stance: f64, + pub position_z: f64, +} + +impl ToBytes for PlayerPosition { + type Bytes = Vec; + + fn to_bytes(self) -> Self::Bytes { + let mut out_buf = Vec::new(); + out_buf.write_f64::(self.position_x).unwrap(); + out_buf.write_f64::(self.position_y).unwrap(); + out_buf.write_f64::(self.stance).unwrap(); + out_buf.write_f64::(self.position_z).unwrap(); + out_buf.write_u8(1).unwrap(); + + out_buf + } +} + +impl PlayerPosition { + pub fn from_bytes(stream: &mut R) -> Self { + let position_x = stream.read_f64::().unwrap(); + let position_y = stream.read_f64::().unwrap(); + let stance = stream.read_f64::().unwrap(); + let position_z = stream.read_f64::().unwrap(); + + let _on_ground = stream.read_u8().unwrap() != 0; + + Self { + position_x, + stance, + position_y, + position_z, + } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct PlayerLook { + pub yaw: f32, + pub pitch: f32, +} + +impl ToBytes for PlayerLook { + type Bytes = Vec; + + fn to_bytes(self) -> Self::Bytes { + let mut out_buf = Vec::new(); + out_buf.write_f32::(self.yaw).unwrap(); + out_buf.write_f32::(self.pitch).unwrap(); + out_buf.write_u8(1).unwrap(); + + out_buf + } +} + +impl PlayerLook { + pub fn from_bytes(stream: &mut R) -> Self { + let yaw = stream.read_f32::().unwrap(); + let pitch = stream.read_f32::().unwrap(); + + let _on_ground = stream.read_u8().unwrap() != 0; + + Self { + yaw, + pitch, + } + } +}