mirror of
https://github.com/G2-Games/minecraft_alpha_server.git
synced 2025-04-19 15:22:56 -05:00
Compare commits
2 commits
81dc4ec3d9
...
a9677205dd
Author | SHA1 | Date | |
---|---|---|---|
a9677205dd | |||
ad6293075f |
13 changed files with 385 additions and 246 deletions
|
@ -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 Alpha `1.2.6`, server version `0.2.8`.
|
Targeting Minecraft Beta `1.1_02`, protocol version `8`.
|
||||||
|
|
|
@ -1,6 +1,54 @@
|
||||||
|
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 {
|
||||||
Unknown = -1,
|
Air = 0,
|
||||||
Stone = 1,
|
Stone = 1,
|
||||||
Grass = 2,
|
Grass = 2,
|
||||||
Dirt = 3,
|
Dirt = 3,
|
||||||
|
@ -79,110 +127,126 @@ 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 {
|
||||||
Unknown = -1,
|
ShovelSteel = 0,
|
||||||
ShovelSteel = 256,
|
PickaxeSteel = 1,
|
||||||
PickaxeSteel = 257,
|
AxeSteel = 2,
|
||||||
AxeSteel = 258,
|
FlintAndSteel = 3,
|
||||||
FlintAndSteel = 259,
|
AppleRed = 4,
|
||||||
AppleRed = 260,
|
Bow = 5,
|
||||||
Bow = 261,
|
Arrow = 6,
|
||||||
Arrow = 262,
|
Coal = 7,
|
||||||
Coal = 263,
|
Diamond = 8,
|
||||||
Diamond = 264,
|
IngotIron = 9,
|
||||||
IngotIron = 265,
|
IngotGold = 10,
|
||||||
IngotGold = 266,
|
SwordSteel = 11,
|
||||||
SwordSteel = 267,
|
SwordWood = 12,
|
||||||
SwordWood = 268,
|
ShovelWood = 13,
|
||||||
ShovelWood = 269,
|
PickaxeWood = 14,
|
||||||
PickaxeWood = 270,
|
AxeWood = 15,
|
||||||
AxeWood = 271,
|
SwordStone = 16,
|
||||||
SwordStone = 272,
|
ShovelStone = 17,
|
||||||
ShovelStone = 273,
|
PickaxeStone = 18,
|
||||||
PickaxeStone = 274,
|
AxeStone = 19,
|
||||||
AxeStone = 275,
|
SwordDiamond = 20,
|
||||||
SwordDiamond = 276,
|
ShovelDiamond = 21,
|
||||||
ShovelDiamond = 277,
|
PickaxeDiamond = 22,
|
||||||
PickaxeDiamond = 278,
|
AxeDiamond = 23,
|
||||||
AxeDiamond = 279,
|
Stick = 24,
|
||||||
Stick = 280,
|
BowlEmpty = 25,
|
||||||
BowlEmpty = 281,
|
BowlSoup = 26,
|
||||||
BowlSoup = 282,
|
SwordGold = 27,
|
||||||
SwordGold = 283,
|
ShovelGold = 28,
|
||||||
ShovelGold = 284,
|
PickaxeGold = 29,
|
||||||
PickaxeGold = 285,
|
AxeGold = 30,
|
||||||
AxeGold = 286,
|
Silk = 31,
|
||||||
Silk = 287,
|
Feather = 32,
|
||||||
Feather = 288,
|
Gunpowder = 33,
|
||||||
Gunpowder = 289,
|
HoeWood = 34,
|
||||||
HoeWood = 290,
|
HoeStone = 35,
|
||||||
HoeStone = 291,
|
HoeSteel = 36,
|
||||||
HoeSteel = 292,
|
HoeDiamond = 37,
|
||||||
HoeDiamond = 293,
|
HoeGold = 38,
|
||||||
HoeGold = 294,
|
Seeds = 39,
|
||||||
Seeds = 295,
|
Wheat = 40,
|
||||||
Wheat = 296,
|
Bread = 41,
|
||||||
Bread = 297,
|
HelmetLeather = 42,
|
||||||
HelmetLeather = 298,
|
PlateLeather = 43,
|
||||||
PlateLeather = 299,
|
LegsLeather = 44,
|
||||||
LegsLeather = 300,
|
BootsLeather = 45,
|
||||||
BootsLeather = 301,
|
HelmetChain = 46,
|
||||||
HelmetChain = 302,
|
PlateChain = 47,
|
||||||
PlateChain = 303,
|
LegsChain = 48,
|
||||||
LegsChain = 304,
|
BootsChain = 49,
|
||||||
BootsChain = 305,
|
HelmetSteel = 50,
|
||||||
HelmetSteel = 306,
|
PlateSteel = 51,
|
||||||
PlateSteel = 307,
|
LegsSteel = 52,
|
||||||
LegsSteel = 308,
|
BootsSteel = 53,
|
||||||
BootsSteel = 309,
|
HelmetDiamond = 54,
|
||||||
HelmetDiamond = 310,
|
PlateDiamond = 55,
|
||||||
PlateDiamond = 311,
|
LegsDiamond = 56,
|
||||||
LegsDiamond = 312,
|
BootsDiamond = 57,
|
||||||
BootsDiamond = 313,
|
HelmetGold = 58,
|
||||||
HelmetGold = 314,
|
PlateGold = 59,
|
||||||
PlateGold = 315,
|
LegsGold = 60,
|
||||||
LegsGold = 316,
|
BootsGold = 61,
|
||||||
BootsGold = 317,
|
Flint = 62,
|
||||||
Flint = 318,
|
PorkRaw = 63,
|
||||||
PorkRaw = 319,
|
PorkCooked = 64,
|
||||||
PorkCooked = 320,
|
Painting = 65,
|
||||||
Painting = 321,
|
AppleGold = 66,
|
||||||
AppleGold = 322,
|
Sign = 67,
|
||||||
Sign = 323,
|
DoorWood = 68,
|
||||||
DoorWood = 324,
|
BucketEmpty = 69,
|
||||||
BucketEmpty = 325,
|
BucketWater = 70,
|
||||||
BucketWater = 326,
|
BucketLava = 71,
|
||||||
BucketLava = 327,
|
MinecartEmpty = 72,
|
||||||
MinecartEmpty = 328,
|
Saddle = 73,
|
||||||
Saddle = 329,
|
DoorSteel = 74,
|
||||||
DoorSteel = 330,
|
Redstone = 75,
|
||||||
Redstone = 331,
|
Snowball = 76,
|
||||||
Snowball = 332,
|
Boat = 77,
|
||||||
Boat = 333,
|
Leather = 78,
|
||||||
Leather = 334,
|
BucketMilk = 79,
|
||||||
BucketMilk = 335,
|
Brick = 80,
|
||||||
Brick = 336,
|
Clay = 81,
|
||||||
Clay = 337,
|
Reed = 82,
|
||||||
Reed = 338,
|
Paper = 83,
|
||||||
Paper = 339,
|
Book = 84,
|
||||||
Book = 340,
|
SlimeBall = 85,
|
||||||
SlimeBall = 341,
|
MinecartCrate = 86,
|
||||||
MinecartCrate = 342,
|
MinecartPowered = 87,
|
||||||
MinecartPowered = 343,
|
Egg = 88,
|
||||||
Egg = 344,
|
Compass = 89,
|
||||||
Compass = 345,
|
FishingRod = 90,
|
||||||
FishingRod = 346,
|
PocketSundial = 91,
|
||||||
PocketSundial = 347,
|
LightStoneDust = 92,
|
||||||
LightStoneDust = 348,
|
FishRaw = 93,
|
||||||
FishRaw = 349,
|
FishCooked = 94,
|
||||||
FishCooked = 350,
|
|
||||||
Record13 = 2000,
|
Record13 = 2000,
|
||||||
RecordCat = 2001,
|
RecordCat = 2001,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Item {
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
fn index() -> i16 {
|
pub struct ItemStack {
|
||||||
256
|
pub stack_size: i32,
|
||||||
|
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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::ops;
|
use std::{io::Read, 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,6 +7,12 @@ 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 {}
|
||||||
|
|
||||||
|
|
28
src/chunk.rs
28
src/chunk.rs
|
@ -1,9 +1,8 @@
|
||||||
use byteorder::{WriteBytesExt, BE};
|
use byteorder::{WriteBytesExt, BE};
|
||||||
use flate2::write::ZlibEncoder;
|
use flate2::{Compression, 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)]
|
||||||
|
@ -71,13 +70,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] = BlockType::Grass as u8;
|
blocks[pos] = Block::Grass as u8;
|
||||||
} else if y > 0 && y < 7 {
|
} else if y > 0 && y < 7 {
|
||||||
blocks[pos] = BlockType::Dirt as u8;
|
blocks[pos] = Block::Dirt as u8;
|
||||||
} else if y == 0 {
|
} else if y == 0 {
|
||||||
blocks[pos] = BlockType::Bedrock as u8;
|
blocks[pos] = Block::Bedrock as u8;
|
||||||
} else {
|
} else {
|
||||||
blocks[pos] = BlockType::Air as u8;
|
blocks[pos] = Block::Air as u8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,21 +91,6 @@ 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>;
|
||||||
|
|
||||||
|
|
28
src/entity_id.rs
Normal file
28
src/entity_id.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
110
src/main.rs
110
src/main.rs
|
@ -1,49 +1,45 @@
|
||||||
mod utils;
|
mod mcstring;
|
||||||
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}, sync::{atomic::{self, AtomicI32}, Arc, RwLock}, thread};
|
use std::{io::{self, Write}, net::{TcpListener, TcpStream}, process::exit, sync::{Arc, RwLock}, thread};
|
||||||
|
|
||||||
use base16ct::lower::encode_string;
|
use base16ct::lower::encode_string;
|
||||||
use chunk::{BlockArray, MapChunk, PreChunk};
|
use chunk::{BlockArray, MapChunk, PreChunk};
|
||||||
use log::{error, info, warn};
|
use entity_id::ENTITY_ID;
|
||||||
|
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 player::{DiggingStatus, PlayerBlockPlacement};
|
use packets::{packet15_place::Packet15Place, packet1_login, Packet};
|
||||||
|
use player::DiggingStatus;
|
||||||
use position::{PlayerLook, PlayerPosition, PlayerPositionLook};
|
use position::{PlayerLook, PlayerPosition, PlayerPositionLook};
|
||||||
use state::{GameState, PlayerState};
|
use state::{GameState, PlayerState};
|
||||||
use utils::{MCString, ReadMCString, WriteMCString};
|
use mcstring::{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!("Setting up game state");
|
info!("Starting Minecraft server version Beta 1.1_02");
|
||||||
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 = TcpListener::bind("0.0.0.0:25565").unwrap();
|
let listener = match TcpListener::bind("0.0.0.0:25565") {
|
||||||
|
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()) {
|
||||||
|
@ -82,16 +78,16 @@ fn player_loop(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if player_state.is_valid() {
|
if player_state.is_valid()
|
||||||
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_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());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,22 +111,19 @@ fn handle_command(
|
||||||
info!("Handshake with {username} successful");
|
info!("Handshake with {username} successful");
|
||||||
},
|
},
|
||||||
Command::Login => {
|
Command::Login => {
|
||||||
let protocol_version = connection.read_u32::<BE>()?;
|
let login_info = packet1_login::Packet1Login::read_from(&mut connection)?;
|
||||||
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 = get_eid();
|
let eid = ENTITY_ID.get();
|
||||||
let login_packet = ServerLoginPacket::new(eid, 0, 0);
|
let login_packet = packet1_login::Packet1Login::new(eid, 0, 0);
|
||||||
connection.write_u8(Command::Login as u8).unwrap();
|
connection.write_u8(Command::Login as u8)?;
|
||||||
connection.write_all(&login_packet.to_bytes())?;
|
login_packet.write_into(&mut connection)?;
|
||||||
|
|
||||||
info!("{username} logged in. Protocol version {protocol_version}");
|
info!("{} [{}] logged in with entity id {}", login_info.username, connection.peer_addr().unwrap(), eid);
|
||||||
*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;
|
||||||
|
@ -186,7 +179,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();
|
||||||
|
@ -196,7 +189,8 @@ fn handle_command(
|
||||||
let _face = connection.read_u8()?;
|
let _face = connection.read_u8()?;
|
||||||
}
|
}
|
||||||
Command::PlayerBlockPlacement => {
|
Command::PlayerBlockPlacement => {
|
||||||
let _status = PlayerBlockPlacement::from_bytes(&mut connection);
|
let status = Packet15Place::read_from(&mut connection)?;
|
||||||
|
dbg!(status);
|
||||||
}
|
}
|
||||||
Command::Animation => {
|
Command::Animation => {
|
||||||
let _eid = connection.read_i32::<BE>()?;
|
let _eid = connection.read_i32::<BE>()?;
|
||||||
|
@ -209,6 +203,7 @@ 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()?;
|
||||||
|
@ -216,6 +211,8 @@ 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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,38 +257,3 @@ 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,12 +2,18 @@ use std::{fmt::Display, io::{self, Read, Write}};
|
||||||
|
|
||||||
use byteorder::{ReadBytesExt, WriteBytesExt, BE};
|
use byteorder::{ReadBytesExt, WriteBytesExt, BE};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||||
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 = ();
|
||||||
|
|
19
src/packets/mod.rs
Normal file
19
src/packets/mod.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
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;
|
||||||
|
}
|
8
src/packets/packet101.rs
Normal file
8
src/packets/packet101.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use byteorder::{ReadBytesExt, WriteBytesExt, BE};
|
||||||
|
use num_traits::FromPrimitive;
|
||||||
|
|
||||||
|
use super::Packet;
|
||||||
|
|
||||||
|
pub struct Packet101 {
|
||||||
|
|
||||||
|
}
|
40
src/packets/packet15_place.rs
Normal file
40
src/packets/packet15_place.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
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!()
|
||||||
|
}
|
||||||
|
}
|
58
src/packets/packet1_login.rs
Normal file
58
src/packets/packet1_login.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,4 @@
|
||||||
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)]
|
||||||
|
@ -37,32 +31,3 @@ 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
13
src/state.rs
13
src/state.rs
|
@ -1,8 +1,7 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
chunk::{BlockType, MapChunk},
|
blocks_items::BlockItem, chunk::MapChunk, position::{PlayerLook, PlayerPosition, PlayerPositionLook}
|
||||||
position::{PlayerLook, PlayerPosition, PlayerPositionLook},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct GameState {
|
pub struct GameState {
|
||||||
|
@ -29,7 +28,7 @@ impl GameState {
|
||||||
pub struct PlayerState {
|
pub struct PlayerState {
|
||||||
eid: i32,
|
eid: i32,
|
||||||
username: String,
|
username: String,
|
||||||
holding: i16,
|
holding: BlockItem,
|
||||||
position_look: PlayerPositionLook,
|
position_look: PlayerPositionLook,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ impl PlayerState {
|
||||||
Self {
|
Self {
|
||||||
eid,
|
eid,
|
||||||
username,
|
username,
|
||||||
holding: -1,
|
holding: BlockItem::Unknown,
|
||||||
position_look: PlayerPositionLook::default(),
|
position_look: PlayerPositionLook::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +47,7 @@ impl PlayerState {
|
||||||
Self {
|
Self {
|
||||||
eid: -1,
|
eid: -1,
|
||||||
username: String::new(),
|
username: String::new(),
|
||||||
holding: -1,
|
holding: BlockItem::Unknown,
|
||||||
position_look: PlayerPositionLook::default(),
|
position_look: PlayerPositionLook::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +76,7 @@ impl PlayerState {
|
||||||
&self.position_look
|
&self.position_look
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn holding(&self) -> &BlockType {
|
pub fn holding(&self) -> &BlockItem {
|
||||||
&self.holding
|
&self.holding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +88,7 @@ impl PlayerState {
|
||||||
self.position_look.look = look
|
self.position_look.look = look
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_holding(&mut self, holding: i16) {
|
pub fn set_holding(&mut self, holding: BlockItem) {
|
||||||
self.holding = holding
|
self.holding = holding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue