Client now gets to near the end of loading in

This commit is contained in:
G2-Games 2024-10-10 01:59:00 -05:00
parent b43c372cc4
commit ca67f15d46
6 changed files with 455 additions and 65 deletions

222
Cargo.lock generated
View file

@ -66,6 +66,27 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "base16ct"
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]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.5.0" version = "1.5.0"
@ -114,6 +135,26 @@ dependencies = [
"cfg-if", "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]] [[package]]
name = "env_filter" name = "env_filter"
version = "0.1.2" version = "0.1.2"
@ -147,6 +188,27 @@ dependencies = [
"miniz_oxide", "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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]] [[package]]
name = "humantime" name = "humantime"
version = "2.1.0" version = "2.1.0"
@ -165,12 +227,33 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.22" version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.4" version = "2.7.4"
@ -181,10 +264,16 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
name = "minecraft_server_impl" name = "minecraft_server_impl"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"base16ct",
"byteorder", "byteorder",
"colog", "colog",
"flate2", "flate2",
"log", "log",
"md2",
"md5",
"num-derive",
"num-traits",
"rand",
] ]
[[package]] [[package]]
@ -196,6 +285,83 @@ dependencies = [
"adler2", "adler2",
] ]
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "ppv-lite86"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.11.0" version = "1.11.0"
@ -225,12 +391,47 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "syn"
version = "2.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
dependencies = [
"proc-macro2",
"quote",
"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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"
@ -369,3 +570,24 @@ name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"byteorder",
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View file

@ -4,7 +4,13 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
base16ct = { version = "0.2.0", features = ["std"] }
byteorder = "1.5.0" byteorder = "1.5.0"
colog = "1.3.0" colog = "1.3.0"
flate2 = "1.0.34" flate2 = "1.0.34"
log = "0.4.22" log = "0.4.22"
md2 = "0.10.2"
md5 = "0.7.0"
num-derive = "0.4.2"
num-traits = "0.2.19"
rand = "0.8.5"

View file

@ -3,10 +3,10 @@ use flate2::write::ZlibEncoder;
use flate2::Compression; use flate2::Compression;
use std::io::prelude::*; use std::io::prelude::*;
use crate::to_bytes::ToBytes; use crate::byte_ops::ToBytes;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct MapChunk { pub struct MapChunk {
chunk_x: i32, chunk_x: i32,
chunk_y: i16, chunk_y: i16,
chunk_z: i32, chunk_z: i32,
@ -17,7 +17,7 @@ struct MapChunk {
} }
impl MapChunk { impl MapChunk {
fn new(chunk_x: i32, chunk_z: i32, compressed_data: BlockArray) -> Self { pub fn new(chunk_x: i32, chunk_z: i32, compressed_data: BlockArray) -> Self {
Self { Self {
chunk_x, chunk_x,
chunk_y: 0, chunk_y: 0,
@ -31,34 +31,35 @@ impl MapChunk {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct BlockArray { pub struct BlockArray {
compressed_size: i32, compressed_size: i32,
compressed_data: Vec<u8>, compressed_data: Vec<u8>,
} }
impl BlockArray { impl BlockArray {
fn new_air() -> Self { pub fn new_air() -> Self {
let mut output_vec = Vec::new(); let mut block_vec = Vec::new();
for _ in 0..(16 * 127 * 15) { for _ in 0..(16 * 127 * 15) {
output_vec.push(0); block_vec.push(0);
} }
for _ in 0..(16 * 127 * 15) / 2 { for _ in 0..(16 * 127 * 15) / 2 {
output_vec.push(0); block_vec.push(0);
} }
for _ in 0..(16 * 127 * 15) / 2 { for _ in 0..(16 * 127 * 15) / 2 {
output_vec.push(0); block_vec.push(0);
} }
for _ in 0..(16 * 127 * 15) / 2 { for _ in 0..(16 * 127 * 15) / 2 {
output_vec.push(0); block_vec.push(0);
} }
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
encoder.write(&output_vec).unwrap(); encoder.write(&block_vec).unwrap();
let output_buf = encoder.finish().unwrap();
Self { Self {
compressed_size: 1, compressed_size: output_buf.len() as i32,
compressed_data: encoder.finish().unwrap(), compressed_data: output_buf,
} }
} }
} }
@ -87,6 +88,9 @@ impl ToBytes for MapChunk {
buffer.write_u8(self.size_y).unwrap(); buffer.write_u8(self.size_y).unwrap();
buffer.write_u8(self.size_z).unwrap(); buffer.write_u8(self.size_z).unwrap();
buffer.write_i32::<BE>(self.compressed_data.compressed_size).unwrap();
buffer.write_all(&self.compressed_data.compressed_data).unwrap();
buffer buffer
} }
} }
@ -98,6 +102,24 @@ pub struct PreChunk {
pub mode: bool, // True to load, False to unload pub mode: bool, // True to load, False to unload
} }
impl PreChunk {
pub fn new_load(x_coord: i32, z_coord: i32) -> Self {
Self {
x_coord,
z_coord,
mode: true
}
}
pub fn new_unload(x_coord: i32, z_coord: i32) -> Self {
Self {
x_coord,
z_coord,
mode: true
}
}
}
impl ToBytes for PreChunk { impl ToBytes for PreChunk {
type Bytes = [u8; 9]; type Bytes = [u8; 9];

View file

@ -1,14 +1,18 @@
mod utils; mod utils;
mod to_bytes; mod byte_ops;
mod chunk; mod chunk;
use std::{io::{self, Write}, net::{TcpListener, TcpStream}}; use std::{io::{self, Read, Write}, net::{TcpListener, TcpStream}};
use chunk::PreChunk; use base16ct::lower::encode_string;
use chunk::{BlockArray, MapChunk, PreChunk};
use log::{info, warn}; use log::{info, warn};
use byteorder::{ReadBytesExt, WriteBytesExt, BE}; use byteorder::{ReadBytesExt, WriteBytesExt, BE};
use to_bytes::ToBytes; use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use byte_ops::ToBytes;
use utils::{MCString, ReadMCString, WriteMCString}; use utils::{MCString, ReadMCString, WriteMCString};
use rand::random;
fn main() { fn main() {
@ -21,71 +25,113 @@ fn main() {
for mut connection in listener.incoming().filter_map(|c| c.ok()) { for mut connection in listener.incoming().filter_map(|c| c.ok()) {
info!("Connected to client @ {}", connection.peer_addr().unwrap()); info!("Connected to client @ {}", connection.peer_addr().unwrap());
while let Some(cmd) = connection.read_u8().ok() { while let Some(cmd) = connection.read_u8().ok() {
let command = Command::try_from(cmd); let command = Command::from_u8(cmd);
info!("COMMAND: {command:?}"); info!("COMMAND: {command:?} (0x{cmd:02X?})");
handle_command(&mut connection, command.unwrap()).unwrap(); handle_command(&mut connection, command.unwrap()).unwrap();
} }
warn!("Lost connection to client"); warn!("Lost connection to client");
} }
} }
fn handle_command(connection: &mut TcpStream, command: Command) -> Result<(), io::Error> { fn handle_command(mut connection: &mut TcpStream, command: Command) -> Result<(), io::Error> {
match command { match command {
Command::KeepAlive => todo!(), Command::KeepAlive => todo!(),
Command::Handshake => { Command::Handshake => {
let username = connection.read_mcstring()?; let username = connection.read_mcstring()?;
info!("Handshake: {username}"); let random_number = random::<u128>();
let random_hash = encode_string(md5::compute(random_number.to_le_bytes()).as_slice());
info!("Handshake with {username} successful. Providing hash: {random_hash:?}");
connection.write_u8(0x02)?; connection.write_u8(0x02)?;
connection.write_mcstring(&MCString::try_from("-").unwrap())?; connection.write_mcstring(&MCString::try_from(random_hash).unwrap())?;
}, },
Command::Login => { Command::Login => {
info!("---");
info!("Initiating login"); info!("Initiating login");
let protocol_version = connection.read_u32::<BE>()?; let protocol_version = connection.read_u32::<BE>()?;
let username = connection.read_mcstring()?; let username = connection.read_mcstring()?;
let _password = connection.read_mcstring()?; let password = connection.read_mcstring()?;
let _map_seed = connection.read_i64::<BE>()?; let map_seed = connection.read_i64::<BE>()?;
let _dimension = connection.read_i8()?; let dimension = connection.read_i8()?;
info!("Protocol Version: {protocol_version}"); info!("Protocol Version: {protocol_version}");
info!("Username: {username}"); info!("Username: {username}");
info!("Password: {password}");
info!("Map Seed: {map_seed}");
info!("Dimension: {dimension}");
let login_packet = ServerLoginPacket { let login_packet = ServerLoginPacket {
entity_id: 1, entity_id: 1200,
unknown1: MCString::default(), unknown1: MCString::default(),
unknown2: MCString::default(), unknown2: MCString::default(),
map_seed: 1715505462032542147, map_seed: 1715505462032542147,
dimension: 0, dimension: 0,
}; };
login_packet.write_into(connection)?; connection.write_u8(Command::Login as u8).unwrap();
connection.write(&login_packet.to_bytes())?;
info!("Responded to auth request"); info!("Responded to auth request");
let prechunk = PreChunk { for i in 0..7 {
x_coord: 0, for o in 0..7 {
z_coord: 0, let x = (-4 + i) * 16;
mode: true, let z = (-5 + o) * 16;
};
connection.write_u8(Command::PreChunk as u8)?; connection.write_u8(Command::PreChunk as u8).unwrap();
connection.write_all(&prechunk.to_bytes())?; 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_u8(Command::SpawnPosition as u8)?;
connection.write_u32::<BE>(0)?;
connection.write_u32::<BE>(70)?;
connection.write_u32::<BE>(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,
};
connection.write_u8(Command::PlayerPositionAndLook as u8)?;
connection.write_all(&playerpos.to_bytes())?;
}, },
_ => unimplemented!("This command is probably `Server -> Client` only") Command::PlayerPositionAndLook => {
let _poslook = PlayerPositionAndLook::from_bytes(&mut connection);
}
Command::PlayerPosition => {
let _pos = PlayerPosition::from_bytes(&mut connection);
}
c => unimplemented!("This command ({c:?}) is probably `Server -> Client` only")
} }
Ok(()) Ok(())
} }
#[repr(u8)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[derive(FromPrimitive)]
enum Command { enum Command {
KeepAlive = 0x00, KeepAlive = 0x00,
Login = 0x01, Login = 0x01,
Handshake = 0x02, Handshake = 0x02,
ChatMessage = 0x03,
TimeUpdate = 0x04,
PlayerInventory = 0x05,
SpawnPosition = 0x06,
UpdateHealth = 0x08,
Respawn = 0x09,
PlayerPositionAndLook = 0x0D,
PlayerPosition = 0x0B,
PreChunk = 0x32, PreChunk = 0x32,
ChunkData = 0x33, MapChunk = 0x33,
Kick = 0xFF, Disconnect = 0xFF,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -93,24 +139,6 @@ struct CommandError {
_id: u8, _id: u8,
} }
impl TryFrom<u8> for Command {
type Error = CommandError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(match value {
0x00 => Self::KeepAlive,
0x01 => Self::Login,
0x02 => Self::Handshake,
0x32 => Self::PreChunk,
0x33 => Self::ChunkData,
0xFF => Self::Kick,
v => return Err(CommandError{
_id: v,
}),
})
}
}
struct ServerLoginPacket { struct ServerLoginPacket {
entity_id: i32, entity_id: i32,
unknown1: MCString, unknown1: MCString,
@ -119,14 +147,111 @@ struct ServerLoginPacket {
dimension: i8, dimension: i8,
} }
impl ServerLoginPacket { impl ToBytes for ServerLoginPacket {
fn write_into<W: Write>(&self, stream: &mut W) -> Result<(), io::Error> { type Bytes = Vec<u8>;
stream.write_i32::<BE>(self.entity_id)?;
stream.write_mcstring(&self.unknown1)?;
stream.write_mcstring(&self.unknown2)?;
stream.write_i64::<BE>(self.map_seed)?;
stream.write_i8(self.dimension)?;
Ok(()) fn to_bytes(self) -> Self::Bytes {
let mut out_buf = Vec::new();
out_buf.write_i32::<BE>(self.entity_id).unwrap();
out_buf.write_mcstring(&self.unknown1).unwrap();
out_buf.write_mcstring(&self.unknown2).unwrap();
out_buf.write_i64::<BE>(self.map_seed).unwrap();
out_buf.write_i8(self.dimension).unwrap();
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<u8>;
fn to_bytes(self) -> Self::Bytes {
let mut out_buf = Vec::new();
out_buf.write_f64::<BE>(self.position_x).unwrap();
out_buf.write_f64::<BE>(self.stance).unwrap();
out_buf.write_f64::<BE>(self.position_y).unwrap();
out_buf.write_f64::<BE>(self.position_z).unwrap();
out_buf.write_f32::<BE>(self.yaw).unwrap();
out_buf.write_f32::<BE>(self.pitch).unwrap();
out_buf.write_u8(self.on_ground as u8).unwrap();
out_buf
}
}
impl PlayerPositionAndLook {
fn from_bytes<R: Read>(stream: &mut R) -> Self {
let position_x = stream.read_f64::<BE>().unwrap();
let position_y = stream.read_f64::<BE>().unwrap();
let stance = stream.read_f64::<BE>().unwrap();
let position_z = stream.read_f64::<BE>().unwrap();
let yaw = stream.read_f32::<BE>().unwrap();
let pitch = stream.read_f32::<BE>().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<u8>;
fn to_bytes(self) -> Self::Bytes {
let mut out_buf = Vec::new();
out_buf.write_f64::<BE>(self.position_x).unwrap();
out_buf.write_f64::<BE>(self.position_y).unwrap();
out_buf.write_f64::<BE>(self.stance).unwrap();
out_buf.write_f64::<BE>(self.position_z).unwrap();
out_buf.write_u8(self.on_ground as u8).unwrap();
out_buf
}
}
impl PlayerPosition {
fn from_bytes<R: Read>(stream: &mut R) -> Self {
let position_x = stream.read_f64::<BE>().unwrap();
let position_y = stream.read_f64::<BE>().unwrap();
let stance = stream.read_f64::<BE>().unwrap();
let position_z = stream.read_f64::<BE>().unwrap();
let on_ground = stream.read_u8().unwrap() != 0;
Self {
position_x,
stance,
position_y,
position_z,
on_ground
}
} }
} }

View file

@ -23,6 +23,21 @@ impl TryFrom<&str> for MCString {
} }
} }
impl TryFrom<String> for MCString {
type Error = ();
fn try_from(value: String) -> Result<Self, Self::Error> {
if value.len() > u16::MAX as usize {
return Err(())
}
Ok(Self {
len: value.len() as u16,
chars: value.as_bytes().to_vec(),
})
}
}
impl Display for MCString { impl Display for MCString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", String::from_utf8(self.chars.clone()).unwrap()) write!(f, "{}", String::from_utf8(self.chars.clone()).unwrap())