Compare commits

..

No commits in common. "a9677205ddfaf538af2e02857d5bc78993dd38da" and "81dc4ec3d9208af06b25c9649e43cd714dfc376e" have entirely different histories.

13 changed files with 246 additions and 385 deletions

View file

@ -1,4 +1,4 @@
# Alpha Server # Alpha Server
A server written in rust or something idk A server written in rust or something idk
Targeting Minecraft Beta `1.1_02`, protocol version `8`. Targeting Minecraft Alpha `1.2.6`, server version `0.2.8`.

View file

@ -1,54 +1,6 @@
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use std::fmt::Debug;
/// A trait to unify [`Block`]s and [`Item`]s.
pub trait BlockItemID: Debug + Clone + PartialEq {
/// The ID of the Block/Item
fn id(&self) -> i16;
/// The Block/Item corresponding to an ID
fn from_id(id: i16) -> Self;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlockItem {
Unknown,
Block(Block),
Item(Item),
}
impl BlockItemID for BlockItem {
fn id(&self) -> i16 {
match self {
Self::Unknown => -1,
BlockItem::Block(b) => *b as i16,
BlockItem::Item(i) => *i as i16 + 255,
}
}
fn from_id(id: i16) -> Self {
if id <= 255 {
if let Some(b) = Block::from_i16(id) {
Self::Block(b)
} else {
Self::Unknown
}
} else {
if let Some(b) = Item::from_i16(id - 255) {
Self::Item(b)
} else {
Self::Unknown
}
}
}
}
#[repr(i16)] #[repr(i16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(FromPrimitive)]
pub enum Block { pub enum Block {
Air = 0, Unknown = -1,
Stone = 1, Stone = 1,
Grass = 2, Grass = 2,
Dirt = 3, Dirt = 3,
@ -127,126 +79,110 @@ pub enum Block {
PumpkinLantern = 91, PumpkinLantern = 91,
} }
#[repr(i16)] #[repr(i16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(FromPrimitive)]
pub enum Item { pub enum Item {
ShovelSteel = 0, Unknown = -1,
PickaxeSteel = 1, ShovelSteel = 256,
AxeSteel = 2, PickaxeSteel = 257,
FlintAndSteel = 3, AxeSteel = 258,
AppleRed = 4, FlintAndSteel = 259,
Bow = 5, AppleRed = 260,
Arrow = 6, Bow = 261,
Coal = 7, Arrow = 262,
Diamond = 8, Coal = 263,
IngotIron = 9, Diamond = 264,
IngotGold = 10, IngotIron = 265,
SwordSteel = 11, IngotGold = 266,
SwordWood = 12, SwordSteel = 267,
ShovelWood = 13, SwordWood = 268,
PickaxeWood = 14, ShovelWood = 269,
AxeWood = 15, PickaxeWood = 270,
SwordStone = 16, AxeWood = 271,
ShovelStone = 17, SwordStone = 272,
PickaxeStone = 18, ShovelStone = 273,
AxeStone = 19, PickaxeStone = 274,
SwordDiamond = 20, AxeStone = 275,
ShovelDiamond = 21, SwordDiamond = 276,
PickaxeDiamond = 22, ShovelDiamond = 277,
AxeDiamond = 23, PickaxeDiamond = 278,
Stick = 24, AxeDiamond = 279,
BowlEmpty = 25, Stick = 280,
BowlSoup = 26, BowlEmpty = 281,
SwordGold = 27, BowlSoup = 282,
ShovelGold = 28, SwordGold = 283,
PickaxeGold = 29, ShovelGold = 284,
AxeGold = 30, PickaxeGold = 285,
Silk = 31, AxeGold = 286,
Feather = 32, Silk = 287,
Gunpowder = 33, Feather = 288,
HoeWood = 34, Gunpowder = 289,
HoeStone = 35, HoeWood = 290,
HoeSteel = 36, HoeStone = 291,
HoeDiamond = 37, HoeSteel = 292,
HoeGold = 38, HoeDiamond = 293,
Seeds = 39, HoeGold = 294,
Wheat = 40, Seeds = 295,
Bread = 41, Wheat = 296,
HelmetLeather = 42, Bread = 297,
PlateLeather = 43, HelmetLeather = 298,
LegsLeather = 44, PlateLeather = 299,
BootsLeather = 45, LegsLeather = 300,
HelmetChain = 46, BootsLeather = 301,
PlateChain = 47, HelmetChain = 302,
LegsChain = 48, PlateChain = 303,
BootsChain = 49, LegsChain = 304,
HelmetSteel = 50, BootsChain = 305,
PlateSteel = 51, HelmetSteel = 306,
LegsSteel = 52, PlateSteel = 307,
BootsSteel = 53, LegsSteel = 308,
HelmetDiamond = 54, BootsSteel = 309,
PlateDiamond = 55, HelmetDiamond = 310,
LegsDiamond = 56, PlateDiamond = 311,
BootsDiamond = 57, LegsDiamond = 312,
HelmetGold = 58, BootsDiamond = 313,
PlateGold = 59, HelmetGold = 314,
LegsGold = 60, PlateGold = 315,
BootsGold = 61, LegsGold = 316,
Flint = 62, BootsGold = 317,
PorkRaw = 63, Flint = 318,
PorkCooked = 64, PorkRaw = 319,
Painting = 65, PorkCooked = 320,
AppleGold = 66, Painting = 321,
Sign = 67, AppleGold = 322,
DoorWood = 68, Sign = 323,
BucketEmpty = 69, DoorWood = 324,
BucketWater = 70, BucketEmpty = 325,
BucketLava = 71, BucketWater = 326,
MinecartEmpty = 72, BucketLava = 327,
Saddle = 73, MinecartEmpty = 328,
DoorSteel = 74, Saddle = 329,
Redstone = 75, DoorSteel = 330,
Snowball = 76, Redstone = 331,
Boat = 77, Snowball = 332,
Leather = 78, Boat = 333,
BucketMilk = 79, Leather = 334,
Brick = 80, BucketMilk = 335,
Clay = 81, Brick = 336,
Reed = 82, Clay = 337,
Paper = 83, Reed = 338,
Book = 84, Paper = 339,
SlimeBall = 85, Book = 340,
MinecartCrate = 86, SlimeBall = 341,
MinecartPowered = 87, MinecartCrate = 342,
Egg = 88, MinecartPowered = 343,
Compass = 89, Egg = 344,
FishingRod = 90, Compass = 345,
PocketSundial = 91, FishingRod = 346,
LightStoneDust = 92, PocketSundial = 347,
FishRaw = 93, LightStoneDust = 348,
FishCooked = 94, FishRaw = 349,
FishCooked = 350,
Record13 = 2000, Record13 = 2000,
RecordCat = 2001, RecordCat = 2001,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] impl Item {
pub struct ItemStack { fn index() -> i16 {
pub stack_size: i32, 256
pub animations_to_go: i32,
pub item_id: BlockItem,
pub item_damage: i32,
}
impl ItemStack {
pub fn new(item_id: i32, stack_size: i32, item_damage: i32) -> Self {
Self {
stack_size,
item_id: BlockItem::from_id(item_id as i16),
item_damage,
animations_to_go: -1,
}
} }
} }

View file

@ -1,4 +1,4 @@
use std::{io::Read, ops}; use std::ops;
pub trait ToBytes: Sized { pub trait ToBytes: Sized {
/// A byte array which can store a packed representation of this type. /// A byte array which can store a packed representation of this type.
@ -7,12 +7,6 @@ pub trait ToBytes: Sized {
fn to_bytes(self) -> Self::Bytes; fn to_bytes(self) -> Self::Bytes;
} }
pub trait FromBytes: Sized {
type Bytes: ByteArray;
fn from_bytes<R: Read>(stream: &mut R) -> Self;
}
mod private { mod private {
pub trait ByteArray {} pub trait ByteArray {}

View file

@ -1,8 +1,9 @@
use byteorder::{WriteBytesExt, BE}; use byteorder::{WriteBytesExt, BE};
use flate2::{Compression, write::ZlibEncoder}; use flate2::write::ZlibEncoder;
use flate2::Compression;
use num_derive::FromPrimitive;
use std::io::prelude::*; use std::io::prelude::*;
use crate::blocks_items::Block;
use crate::byte_ops::ToBytes; use crate::byte_ops::ToBytes;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -70,13 +71,13 @@ impl BlockArray {
for z in 0..CHUNK_WIDTH_Z { for z in 0..CHUNK_WIDTH_Z {
let pos = y + (z * (CHUNK_HEIGHT_Y)) + (x * (CHUNK_HEIGHT_Y) * (CHUNK_WIDTH_X)); let pos = y + (z * (CHUNK_HEIGHT_Y)) + (x * (CHUNK_HEIGHT_Y) * (CHUNK_WIDTH_X));
if y == 7 { if y == 7 {
blocks[pos] = Block::Grass as u8; blocks[pos] = BlockType::Grass as u8;
} else if y > 0 && y < 7 { } else if y > 0 && y < 7 {
blocks[pos] = Block::Dirt as u8; blocks[pos] = BlockType::Dirt as u8;
} else if y == 0 { } else if y == 0 {
blocks[pos] = Block::Bedrock as u8; blocks[pos] = BlockType::Bedrock as u8;
} else { } else {
blocks[pos] = Block::Air as u8; blocks[pos] = BlockType::Air as u8;
} }
} }
} }
@ -91,6 +92,21 @@ impl BlockArray {
} }
} }
#[repr(i16)]
#[derive(Debug, Clone, Copy)]
#[derive(FromPrimitive)]
pub enum BlockType {
None = -1,
Air,
Stone,
Grass,
Dirt,
Cobblestone,
Planks,
Sapling,
Bedrock,
}
impl ToBytes for MapChunk { impl ToBytes for MapChunk {
type Bytes = Vec<u8>; type Bytes = Vec<u8>;

View file

@ -1,28 +0,0 @@
use std::sync::atomic::{self, AtomicI32};
/// The current Entity ID. Incremented by one every time there is a new entity.
///
/// This value should rarely be accessed directly, and definitely never updated.
pub static ENTITY_ID: EntityID = EntityID::new(0);
pub struct EntityID {
id: AtomicI32
}
impl EntityID {
/// Create a new entity ID
const fn new(id: i32) -> Self {
Self {
id: AtomicI32::new(id)
}
}
/// Get a new Entity ID and increment the global value by 1.
#[inline]
pub fn get(&self) -> i32 {
let eid = self.id.load(atomic::Ordering::Relaxed);
self.id.store(eid + 1, atomic::Ordering::Relaxed);
eid
}
}

View file

@ -1,45 +1,49 @@
mod mcstring; mod utils;
mod byte_ops; mod byte_ops;
mod chunk; mod chunk;
mod position; mod position;
mod state; mod state;
mod player; mod player;
mod blocks_items; mod blocks_items;
mod entity_id;
mod packets;
use std::{io::{self, Write}, net::{TcpListener, TcpStream}, process::exit, sync::{Arc, RwLock}, thread}; use std::{io::{self, Write}, net::{TcpListener, TcpStream}, sync::{atomic::{self, AtomicI32}, Arc, RwLock}, thread};
use base16ct::lower::encode_string; use base16ct::lower::encode_string;
use chunk::{BlockArray, MapChunk, PreChunk}; use chunk::{BlockArray, MapChunk, PreChunk};
use entity_id::ENTITY_ID; use log::{error, info, warn};
use log::{debug, error, info};
use byteorder::{ReadBytesExt, WriteBytesExt, BE}; use byteorder::{ReadBytesExt, WriteBytesExt, BE};
use num_derive::FromPrimitive; use num_derive::FromPrimitive;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use byte_ops::ToBytes; use byte_ops::ToBytes;
use packets::{packet15_place::Packet15Place, packet1_login, Packet}; use player::{DiggingStatus, PlayerBlockPlacement};
use player::DiggingStatus;
use position::{PlayerLook, PlayerPosition, PlayerPositionLook}; use position::{PlayerLook, PlayerPosition, PlayerPositionLook};
use state::{GameState, PlayerState}; use state::{GameState, PlayerState};
use mcstring::{MCString, ReadMCString, WriteMCString}; use utils::{MCString, ReadMCString, WriteMCString};
use rand::random; use rand::random;
/// The current Entity ID. Incremented by one every time there is a new entity.
///
/// This value should rarely be accessed directly, and definitely never updated.
static ENTITY_ID: AtomicI32 = AtomicI32::new(0);
/// Get an Entity ID and increment the global value by 1.
#[inline]
fn get_eid() -> i32 {
let eid = ENTITY_ID.load(atomic::Ordering::Relaxed);
ENTITY_ID.store(eid + 1, atomic::Ordering::Relaxed);
eid
}
fn main() { fn main() {
colog::default_builder() colog::default_builder()
.filter_level(log::LevelFilter::Debug) .filter_level(log::LevelFilter::Debug)
.init(); .init();
info!("Starting Minecraft server version Beta 1.1_02"); info!("Setting up game state");
let game_state: Arc<RwLock<GameState>> = Arc::new(RwLock::new(GameState::new())); let game_state: Arc<RwLock<GameState>> = Arc::new(RwLock::new(GameState::new()));
let listener = match TcpListener::bind("0.0.0.0:25565") { let listener = TcpListener::bind("0.0.0.0:25565").unwrap();
Ok(l) => l,
Err(e) => {
error!("Starting server failed: {e}");
exit(1)
},
};
info!("Server started and listening on {}", listener.local_addr().unwrap()); info!("Server started and listening on {}", listener.local_addr().unwrap());
for mut connection in listener.incoming().filter_map(|c| c.ok()) { for mut connection in listener.incoming().filter_map(|c| c.ok()) {
@ -78,16 +82,16 @@ fn player_loop(
break; break;
} }
if player_state.is_valid() if player_state.is_valid() {
&& (game_state.read().unwrap().player_list().get(player_state.username()).is_some_and(|p| *p != player_state) if game_state.read().unwrap().player_list().get(player_state.username()).is_some_and(|p| *p != player_state)
|| game_state.read().unwrap().player_list().get(player_state.username()).is_none()) || game_state.read().unwrap().player_list().get(player_state.username()).is_none()
{ {
game_state.write() game_state.write()
.unwrap() .unwrap()
.player_list_mut() .player_list_mut()
.insert(player_state.username().clone(), player_state.clone()); .insert(player_state.username().clone(), player_state.clone());
} }
}
} }
@ -111,19 +115,22 @@ fn handle_command(
info!("Handshake with {username} successful"); info!("Handshake with {username} successful");
}, },
Command::Login => { Command::Login => {
let login_info = packet1_login::Packet1Login::read_from(&mut connection)?; let protocol_version = connection.read_u32::<BE>()?;
let username = connection.read_mcstring()?;
// These are mostly useless
let _password = connection.read_mcstring()?;
let _map_seed = connection.read_i64::<BE>()?;
let _dimension = connection.read_i8()?;
// Return a successful login packet to the client // Return a successful login packet to the client
let eid = ENTITY_ID.get(); let eid = get_eid();
let login_packet = packet1_login::Packet1Login::new(eid, 0, 0); let login_packet = ServerLoginPacket::new(eid, 0, 0);
connection.write_u8(Command::Login as u8)?; connection.write_u8(Command::Login as u8).unwrap();
login_packet.write_into(&mut connection)?; connection.write_all(&login_packet.to_bytes())?;
info!("{} [{}] logged in with entity id {}", login_info.username, connection.peer_addr().unwrap(), eid); info!("{username} logged in. Protocol version {protocol_version}");
*player_state = PlayerState::new(username.to_string(), eid);
*player_state = PlayerState::new(login_info.username.to_string(), eid);
// Send "chunks" to the player. This simulates a flat-world of 20 by 20 chunks.
for i in -10..10 { for i in -10..10 {
for o in -10..10 { for o in -10..10 {
let x = i * 16; let x = i * 16;
@ -179,7 +186,7 @@ fn handle_command(
} }
Command::HoldingChange => { Command::HoldingChange => {
let _unused = connection.read_i32::<BE>()?; let _unused = connection.read_i32::<BE>()?;
let _block_id = connection.read_i16::<BE>()?; let block_id = connection.read_i16::<BE>()?;
} }
Command::PlayerDigging => { Command::PlayerDigging => {
let _status = DiggingStatus::from_u8(connection.read_u8()?).unwrap(); let _status = DiggingStatus::from_u8(connection.read_u8()?).unwrap();
@ -189,8 +196,7 @@ fn handle_command(
let _face = connection.read_u8()?; let _face = connection.read_u8()?;
} }
Command::PlayerBlockPlacement => { Command::PlayerBlockPlacement => {
let status = Packet15Place::read_from(&mut connection)?; let _status = PlayerBlockPlacement::from_bytes(&mut connection);
dbg!(status);
} }
Command::Animation => { Command::Animation => {
let _eid = connection.read_i32::<BE>()?; let _eid = connection.read_i32::<BE>()?;
@ -203,7 +209,6 @@ fn handle_command(
} }
Command::KeepAlive => { Command::KeepAlive => {
connection.write_u8(Command::KeepAlive as u8)?; connection.write_u8(Command::KeepAlive as u8)?;
info!("Keepalive!");
} }
Command::UpdateHealth => { Command::UpdateHealth => {
connection.read_u8()?; connection.read_u8()?;
@ -211,8 +216,6 @@ fn handle_command(
c => unimplemented!("This command ({c:?}) is probably `Server -> Client` only; thus it is unimplemented for the other way around!") c => unimplemented!("This command ({c:?}) is probably `Server -> Client` only; thus it is unimplemented for the other way around!")
} }
connection.write_u8(Command::KeepAlive as u8)?;
Ok(()) Ok(())
} }
@ -257,3 +260,38 @@ enum Command {
ComplexEntities = 0x3B, ComplexEntities = 0x3B,
Disconnect = 0xFF, Disconnect = 0xFF,
} }
struct ServerLoginPacket {
entity_id: i32,
unknown1: MCString,
unknown2: MCString,
map_seed: i64,
dimension: i8,
}
impl ServerLoginPacket {
pub fn new(entity_id: i32, map_seed: i64, dimension: i8) -> Self {
Self {
entity_id,
unknown1: MCString::default(),
unknown2: MCString::default(),
map_seed,
dimension,
}
}
}
impl ToBytes for ServerLoginPacket {
type Bytes = Vec<u8>;
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
}
}

View file

@ -1,19 +0,0 @@
use std::io::{self, Read, Write};
pub mod packet1_login;
pub mod packet15_place;
pub mod packet101;
/// A packet for communicating across the network.
pub trait Packet
where Self: Sized
{
/// Read the packet in from a stream
fn read_from<R: Read>(input: &mut R) -> Result<Self, io::Error>;
/// Write the packet out to a stream
fn write_into<W: Write>(&self, output: &mut W) -> Result<(), io::Error>;
/// The size of the packet in bytes
fn size(&self) -> usize;
}

View file

@ -1,8 +0,0 @@
use byteorder::{ReadBytesExt, WriteBytesExt, BE};
use num_traits::FromPrimitive;
use super::Packet;
pub struct Packet101 {
}

View file

@ -1,40 +0,0 @@
use crate::{blocks_items::{BlockItem, BlockItemID, ItemStack}, player::Direction};
use byteorder::{ReadBytesExt, WriteBytesExt, BE};
use num_traits::FromPrimitive;
use super::Packet;
#[derive(Debug, Clone, Copy)]
pub struct Packet15Place {
id: BlockItem,
x_position: i32,
y_position: u8,
z_position: i32,
direction: u8,
amount: Option<u8>,
health: Option<i16>,
}
impl Packet for Packet15Place {
fn read_from<R: std::io::Read>(input: &mut R) -> Result<Self, std::io::Error> {
let id = BlockItem::from_id(input.read_i16::<BE>()?);
Ok(Self {
id,
x_position: input.read_i32::<BE>()?,
y_position: input.read_u8()?,
z_position: input.read_i32::<BE>()?,
direction: input.read_u8()?,
amount: if id.id() <= 0 { None } else { Some(input.read_u8()?) },
health: if id.id() <= 0 { None } else { Some(input.read_i16::<BE>()?) }
})
}
fn write_into<W: std::io::Write>(&self, output: &mut W) -> Result<(), std::io::Error> {
unimplemented!()
}
fn size(&self) -> usize {
todo!()
}
}

View file

@ -1,58 +0,0 @@
use byteorder::{ReadBytesExt, WriteBytesExt, BE};
use crate::mcstring::{MCString, WriteMCString, ReadMCString};
use super::Packet;
#[derive(Debug, Clone)]
pub struct Packet1Login {
pub username: MCString,
pub password: MCString,
pub protocol_version: i32,
pub world_seed: i64,
pub dimension: i8,
}
impl Packet1Login {
pub fn new(protocol_version: i32, world_seed: i64, dimension: i8) -> Self {
Self {
username: MCString::default(),
password: MCString::default(),
protocol_version,
world_seed,
dimension,
}
}
}
impl Packet for Packet1Login {
fn read_from<R: std::io::Read>(input: &mut R) -> Result<Self, std::io::Error> {
let protocol_version = input.read_i32::<BE>()?;
let username = input.read_mcstring()?.into();
let password = input.read_mcstring()?.into();
let world_seed = input.read_i64::<BE>()?;
let dimension = input.read_i8()?;
Ok(Packet1Login {
username,
password,
protocol_version,
world_seed,
dimension,
})
}
fn write_into<W: std::io::Write>(&self, output: &mut W) -> Result<(), std::io::Error> {
output.write_i32::<BE>(self.protocol_version).unwrap();
output.write_mcstring(&self.username).unwrap();
output.write_mcstring(&self.password).unwrap();
output.write_i64::<BE>(self.world_seed).unwrap();
output.write_i8(self.dimension).unwrap();
Ok(())
}
fn size(&self) -> usize {
4 + self.username.len() + self.password.len() + 8 + 1
}
}

View file

@ -1,4 +1,10 @@
use std::io::Read;
use byteorder::{ReadBytesExt, BE};
use num_derive::FromPrimitive; use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use crate::chunk::BlockType;
#[repr(u8)] #[repr(u8)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -31,3 +37,32 @@ pub struct PlayerDigging {
position_z: i32, position_z: i32,
face: Direction, face: Direction,
} }
#[derive(Debug, Clone, Copy)]
pub struct PlayerBlockPlacement {
block_id: BlockType,
position_x: i32,
position_y: u8,
position_z: i32,
direction: Direction,
//amount: u8,
//health: u8,
}
impl PlayerBlockPlacement {
pub fn from_bytes<R: Read>(stream: &mut R) -> Self {
let block_id = BlockType::from_i16(stream.read_i16::<BE>().unwrap()).unwrap();
let position_x = stream.read_i32::<BE>().unwrap();
let position_y = stream.read_u8().unwrap();
let position_z = stream.read_i32::<BE>().unwrap();
let direction = Direction::from_u8(stream.read_u8().unwrap()).unwrap();
Self {
block_id,
position_x,
position_y,
position_z,
direction,
}
}
}

View file

@ -1,7 +1,8 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::{ use crate::{
blocks_items::BlockItem, chunk::MapChunk, position::{PlayerLook, PlayerPosition, PlayerPositionLook} chunk::{BlockType, MapChunk},
position::{PlayerLook, PlayerPosition, PlayerPositionLook},
}; };
pub struct GameState { pub struct GameState {
@ -28,7 +29,7 @@ impl GameState {
pub struct PlayerState { pub struct PlayerState {
eid: i32, eid: i32,
username: String, username: String,
holding: BlockItem, holding: i16,
position_look: PlayerPositionLook, position_look: PlayerPositionLook,
} }
@ -38,7 +39,7 @@ impl PlayerState {
Self { Self {
eid, eid,
username, username,
holding: BlockItem::Unknown, holding: -1,
position_look: PlayerPositionLook::default(), position_look: PlayerPositionLook::default(),
} }
} }
@ -47,7 +48,7 @@ impl PlayerState {
Self { Self {
eid: -1, eid: -1,
username: String::new(), username: String::new(),
holding: BlockItem::Unknown, holding: -1,
position_look: PlayerPositionLook::default(), position_look: PlayerPositionLook::default(),
} }
} }
@ -76,7 +77,7 @@ impl PlayerState {
&self.position_look &self.position_look
} }
pub fn holding(&self) -> &BlockItem { pub fn holding(&self) -> &BlockType {
&self.holding &self.holding
} }
@ -88,7 +89,7 @@ impl PlayerState {
self.position_look.look = look self.position_look.look = look
} }
pub fn set_holding(&mut self, holding: BlockItem) { pub fn set_holding(&mut self, holding: i16) {
self.holding = holding self.holding = holding
} }
} }

View file

@ -2,18 +2,12 @@ use std::{fmt::Display, io::{self, Read, Write}};
use byteorder::{ReadBytesExt, WriteBytesExt, BE}; use byteorder::{ReadBytesExt, WriteBytesExt, BE};
#[derive(Debug, Default, Clone, PartialEq, Eq)] #[derive(Debug, Default, Clone)]
pub struct MCString { pub struct MCString {
len: u16, len: u16,
chars: Vec<u8>, chars: Vec<u8>,
} }
impl MCString {
pub fn len(&self) -> usize {
self.len as usize
}
}
impl TryFrom<&str> for MCString { impl TryFrom<&str> for MCString {
type Error = (); type Error = ();