mirror of
https://github.com/G2-Games/minecraft_alpha_server.git
synced 2025-04-19 23:32:57 -05:00
Compare commits
4 commits
afd0a48527
...
e898dfa859
Author | SHA1 | Date | |
---|---|---|---|
e898dfa859 | |||
ceafebac70 | |||
d059b9f160 | |||
33a0b37564 |
6 changed files with 203 additions and 98 deletions
4
README.md
Normal file
4
README.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Alpha Server
|
||||||
|
A server written in rust or something idk
|
||||||
|
|
||||||
|
Targeting Minecraft Alpha `1.2.6`, server version `0.2.8`.
|
94
src/chunk.rs
94
src/chunk.rs
|
@ -1,6 +1,7 @@
|
||||||
use byteorder::{WriteBytesExt, BE};
|
use byteorder::{WriteBytesExt, BE};
|
||||||
use flate2::write::ZlibEncoder;
|
use flate2::write::ZlibEncoder;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
|
use num_derive::FromPrimitive;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
|
||||||
use crate::byte_ops::ToBytes;
|
use crate::byte_ops::ToBytes;
|
||||||
|
@ -32,79 +33,71 @@ impl MapChunk {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BlockArray {
|
pub struct BlockArray {
|
||||||
compressed_size: i32,
|
blocks: Vec<u8>,
|
||||||
compressed_data: Vec<u8>,
|
metadata: Vec<u8>,
|
||||||
|
block_light: Vec<u8>,
|
||||||
|
sky_light: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CHUNK_WIDTH_X: usize = 16;
|
||||||
|
const CHUNK_WIDTH_Z: usize = 16;
|
||||||
|
const CHUNK_HEIGHT_Y: usize = 128;
|
||||||
|
const CHUNK_TOTAL_BLOCKS: usize = CHUNK_WIDTH_X * CHUNK_WIDTH_Z * CHUNK_HEIGHT_Y;
|
||||||
|
|
||||||
impl BlockArray {
|
impl BlockArray {
|
||||||
pub fn new_air() -> Self {
|
fn compress(self) -> Vec<u8> {
|
||||||
let mut block_vec = Vec::new();
|
|
||||||
|
|
||||||
for _ in 0..(16 * 127 * 15) {
|
|
||||||
block_vec.push(0);
|
|
||||||
}
|
|
||||||
for _ in 0..(16 * 127 * 15) / 2 {
|
|
||||||
block_vec.push(0);
|
|
||||||
}
|
|
||||||
for _ in 0..(16 * 127 * 15) / 2 {
|
|
||||||
block_vec.push(0);
|
|
||||||
}
|
|
||||||
for _ in 0..(16 * 127 * 15) / 2 {
|
|
||||||
block_vec.push(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
|
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
|
||||||
encoder.write(&block_vec).unwrap();
|
encoder.write(&self.blocks).unwrap();
|
||||||
|
encoder.write(&self.metadata).unwrap();
|
||||||
|
encoder.write(&self.block_light).unwrap();
|
||||||
|
encoder.write(&self.sky_light).unwrap();
|
||||||
let output_buf = encoder.finish().unwrap();
|
let output_buf = encoder.finish().unwrap();
|
||||||
|
|
||||||
|
output_buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_air() -> Self {
|
||||||
Self {
|
Self {
|
||||||
compressed_size: output_buf.len() as i32,
|
blocks: vec![0; CHUNK_TOTAL_BLOCKS],
|
||||||
compressed_data: output_buf,
|
metadata: vec![0; CHUNK_TOTAL_BLOCKS / 2],
|
||||||
|
block_light: vec![0; CHUNK_TOTAL_BLOCKS / 2],
|
||||||
|
sky_light: vec![0xFF; CHUNK_TOTAL_BLOCKS / 2],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_superflat() -> Self {
|
pub fn new_superflat() -> Self {
|
||||||
let mut block_vec = vec![0; 16 * 16 * 128];
|
let mut blocks = vec![0; CHUNK_TOTAL_BLOCKS];
|
||||||
|
for y in 0..CHUNK_HEIGHT_Y {
|
||||||
for x in 0..16 {
|
for x in 0..CHUNK_WIDTH_X {
|
||||||
for y in 0..128 {
|
for z in 0..CHUNK_WIDTH_Z {
|
||||||
for z in 0..16 {
|
let pos = y + (z * (CHUNK_HEIGHT_Y)) + (x * (CHUNK_HEIGHT_Y) * (CHUNK_WIDTH_X));
|
||||||
let pos = y + (z * (128)) + (x * (128) * (16));
|
|
||||||
if y == 7 {
|
if y == 7 {
|
||||||
block_vec[pos] = BlockType::Grass as u8;
|
blocks[pos] = BlockType::Grass as u8;
|
||||||
} else if y > 0 && y < 7 {
|
} else if y > 0 && y < 7 {
|
||||||
block_vec[pos] = BlockType::Dirt as u8;
|
blocks[pos] = BlockType::Dirt as u8;
|
||||||
} else if y == 0 {
|
} else if y == 0 {
|
||||||
block_vec[pos] = BlockType::Bedrock as u8;
|
blocks[pos] = BlockType::Bedrock as u8;
|
||||||
} else {
|
} else {
|
||||||
block_vec[pos] = 0;
|
blocks[pos] = BlockType::Air as u8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 {
|
Self {
|
||||||
compressed_size: output_buf.len() as i32,
|
blocks,
|
||||||
compressed_data: output_buf,
|
metadata: vec![0xFF; CHUNK_TOTAL_BLOCKS / 2],
|
||||||
|
block_light: vec![0; CHUNK_TOTAL_BLOCKS / 2],
|
||||||
|
sky_light: vec![0xFF; CHUNK_TOTAL_BLOCKS / 2],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(i16)]
|
||||||
enum BlockType {
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[derive(FromPrimitive)]
|
||||||
|
pub enum BlockType {
|
||||||
|
None = -1,
|
||||||
Air,
|
Air,
|
||||||
Stone,
|
Stone,
|
||||||
Grass,
|
Grass,
|
||||||
|
@ -127,8 +120,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();
|
let block_buf = self.compressed_data.compress();
|
||||||
buffer.write_all(&self.compressed_data.compressed_data).unwrap();
|
buffer.write_i32::<BE>(block_buf.len() as i32).unwrap();
|
||||||
|
buffer.write_all(&block_buf).unwrap();
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
}
|
}
|
||||||
|
|
101
src/main.rs
101
src/main.rs
|
@ -2,8 +2,10 @@ mod utils;
|
||||||
mod byte_ops;
|
mod byte_ops;
|
||||||
mod chunk;
|
mod chunk;
|
||||||
mod position;
|
mod position;
|
||||||
|
mod state;
|
||||||
|
mod player;
|
||||||
|
|
||||||
use std::{cell::LazyCell, io::{self, Write}, net::{TcpListener, TcpStream}};
|
use std::{io::{self, Write}, net::{TcpListener, TcpStream}, sync::RwLock};
|
||||||
|
|
||||||
use base16ct::lower::encode_string;
|
use base16ct::lower::encode_string;
|
||||||
use chunk::{BlockArray, MapChunk, PreChunk};
|
use chunk::{BlockArray, MapChunk, PreChunk};
|
||||||
|
@ -12,23 +14,24 @@ 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 position::{PlayerLook, PlayerPosition, PlayerPositionAndLook};
|
use position::{PlayerLook, PlayerPosition, PlayerPositionAndLook};
|
||||||
|
use state::PlayerState;
|
||||||
use utils::{MCString, ReadMCString, WriteMCString};
|
use utils::{MCString, ReadMCString, WriteMCString};
|
||||||
use rand::random;
|
use rand::random;
|
||||||
|
|
||||||
const CHUNKS: LazyCell<Vec<MapChunk>> = LazyCell::new(|| {
|
/// List of players.
|
||||||
let mut mapchunk = Vec::new();
|
const PLAYER_LIST: RwLock<Vec<PlayerState>> = RwLock::new(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()));
|
/// The current Entity ID. Incremented by one every time there is a new entity.
|
||||||
}
|
const ENTITY_ID: RwLock<i32> = RwLock::new(0);
|
||||||
}
|
|
||||||
|
|
||||||
mapchunk
|
fn get_eid() -> i32 {
|
||||||
});
|
let eid = ENTITY_ID.read().unwrap().clone();
|
||||||
|
*ENTITY_ID.write().unwrap() += 1;
|
||||||
|
|
||||||
|
eid
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
colog::default_builder()
|
colog::default_builder()
|
||||||
|
@ -36,13 +39,15 @@ fn main() {
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let listener = TcpListener::bind("0.0.0.0:25565").unwrap();
|
let listener = TcpListener::bind("0.0.0.0:25565").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()) {
|
||||||
info!("Connected to client @ {}", connection.peer_addr().unwrap());
|
info!("Player joined from {}", connection.peer_addr().unwrap());
|
||||||
while let Some(cmd) = connection.read_u8().ok() {
|
while let Some(cmd) = connection.read_u8().ok() {
|
||||||
let command = Command::from_u8(cmd);
|
let command = Command::from_u8(cmd);
|
||||||
if command.is_none() {
|
if command.is_none() {
|
||||||
info!("COMMAND: {command:?} (0x{cmd:02X?})");
|
info!("COMMAND: {command:?} (0x{cmd:02X?})");
|
||||||
|
panic!("This command isn't implemented yet");
|
||||||
}
|
}
|
||||||
handle_command(&mut connection, command.unwrap()).unwrap();
|
handle_command(&mut connection, command.unwrap()).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -57,27 +62,22 @@ fn handle_command(mut connection: &mut TcpStream, command: Command) -> Result<()
|
||||||
let random_number = random::<u128>();
|
let random_number = random::<u128>();
|
||||||
let random_hash = encode_string(md5::compute(random_number.to_le_bytes()).as_slice());
|
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(random_hash).unwrap())?;
|
connection.write_mcstring(&MCString::try_from(random_hash).unwrap())?;
|
||||||
|
|
||||||
|
info!("Handshake with {username} successful");
|
||||||
},
|
},
|
||||||
Command::Login => {
|
Command::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()?;
|
// These are mostly useless
|
||||||
let map_seed = connection.read_i64::<BE>()?;
|
let _password = connection.read_mcstring()?;
|
||||||
let dimension = connection.read_i8()?;
|
let _map_seed = connection.read_i64::<BE>()?;
|
||||||
|
let _dimension = connection.read_i8()?;
|
||||||
info!("Protocol Version: {protocol_version}");
|
|
||||||
info!("Username: {username}");
|
|
||||||
info!("Password: {password}");
|
|
||||||
info!("Map Seed: {map_seed}");
|
|
||||||
info!("Dimension: {dimension}");
|
|
||||||
|
|
||||||
|
let eid = get_eid();
|
||||||
let login_packet = ServerLoginPacket {
|
let login_packet = ServerLoginPacket {
|
||||||
entity_id: 1,
|
entity_id: eid,
|
||||||
unknown1: MCString::default(),
|
unknown1: MCString::default(),
|
||||||
unknown2: MCString::default(),
|
unknown2: MCString::default(),
|
||||||
map_seed: 0,
|
map_seed: 0,
|
||||||
|
@ -86,7 +86,9 @@ fn handle_command(mut connection: &mut TcpStream, command: Command) -> Result<()
|
||||||
connection.write_u8(Command::Login as u8).unwrap();
|
connection.write_u8(Command::Login as u8).unwrap();
|
||||||
connection.write(&login_packet.to_bytes())?;
|
connection.write(&login_packet.to_bytes())?;
|
||||||
|
|
||||||
info!("Responded to auth request");
|
PLAYER_LIST.write().unwrap().push(PlayerState::new(username.to_string(), eid));
|
||||||
|
|
||||||
|
info!("{username} logged in. Protocol version {protocol_version}");
|
||||||
|
|
||||||
for i in -10..10 {
|
for i in -10..10 {
|
||||||
for o in -10..10 {
|
for o in -10..10 {
|
||||||
|
@ -137,24 +139,28 @@ fn handle_command(mut connection: &mut TcpStream, command: Command) -> Result<()
|
||||||
let _poslook = PlayerPositionAndLook::from_bytes(&mut connection);
|
let _poslook = PlayerPositionAndLook::from_bytes(&mut connection);
|
||||||
}
|
}
|
||||||
Command::PlayerDigging => {
|
Command::PlayerDigging => {
|
||||||
let status = DiggingStatus::from_u8(connection.read_u8()?).unwrap();
|
let _status = DiggingStatus::from_u8(connection.read_u8()?).unwrap();
|
||||||
let pos_x = connection.read_i32::<BE>()?;
|
let _pos_x = connection.read_i32::<BE>()?;
|
||||||
let pos_y = connection.read_u8()?;
|
let _pos_y = connection.read_u8()?;
|
||||||
let pos_z = connection.read_i32::<BE>()?;
|
let _pos_z = connection.read_i32::<BE>()?;
|
||||||
let face = connection.read_u8()?;
|
let _face = connection.read_u8()?;
|
||||||
|
}
|
||||||
|
Command::PlayerBlockPlacement => {
|
||||||
|
let _status = PlayerBlockPlacement::from_bytes(&mut connection);
|
||||||
|
dbg!(_status);
|
||||||
}
|
}
|
||||||
Command::ArmAnimation => {
|
Command::ArmAnimation => {
|
||||||
let eid = connection.read_i32::<BE>()?;
|
let _eid = connection.read_i32::<BE>()?;
|
||||||
let animate = connection.read_u8()? != 0;
|
let _animate = connection.read_u8()? != 0;
|
||||||
|
dbg!(_animate);
|
||||||
}
|
}
|
||||||
Command::Disconnect => {
|
Command::Disconnect => {
|
||||||
let disconnect_string = connection.read_mcstring()?;
|
let disconnect_string = connection.read_mcstring()?;
|
||||||
info!("Disconnecting client: {disconnect_string}");
|
info!("Disconnecting client. Reason: {disconnect_string}");
|
||||||
connection.shutdown(std::net::Shutdown::Both)?;
|
connection.shutdown(std::net::Shutdown::Both)?;
|
||||||
}
|
}
|
||||||
Command::KeepAlive => {
|
Command::KeepAlive => {
|
||||||
let _ = connection.write_u8(0x00);
|
let _ = connection.write_u8(0x00);
|
||||||
// TODO: Feed keepalive watchdog for client
|
|
||||||
}
|
}
|
||||||
Command::UpdateHealth => {
|
Command::UpdateHealth => {
|
||||||
connection.read_u8()?;
|
connection.read_u8()?;
|
||||||
|
@ -165,16 +171,6 @@ fn handle_command(mut connection: &mut TcpStream, command: Command) -> Result<()
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
#[derive(FromPrimitive)]
|
|
||||||
enum DiggingStatus {
|
|
||||||
StartedDigging = 0,
|
|
||||||
Digging = 1,
|
|
||||||
StoppedDigging = 2,
|
|
||||||
BlockBroken = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
#[derive(FromPrimitive)]
|
#[derive(FromPrimitive)]
|
||||||
|
@ -193,6 +189,7 @@ enum Command {
|
||||||
PlayerLook = 0x0C,
|
PlayerLook = 0x0C,
|
||||||
PlayerPositionAndLook = 0x0D,
|
PlayerPositionAndLook = 0x0D,
|
||||||
PlayerDigging = 0x0E,
|
PlayerDigging = 0x0E,
|
||||||
|
PlayerBlockPlacement = 0x0F,
|
||||||
ArmAnimation = 0x12,
|
ArmAnimation = 0x12,
|
||||||
PreChunk = 0x32,
|
PreChunk = 0x32,
|
||||||
MapChunk = 0x33,
|
MapChunk = 0x33,
|
||||||
|
@ -207,6 +204,18 @@ struct ServerLoginPacket {
|
||||||
dimension: i8,
|
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 {
|
impl ToBytes for ServerLoginPacket {
|
||||||
type Bytes = Vec<u8>;
|
type Bytes = Vec<u8>;
|
||||||
|
|
||||||
|
|
68
src/player.rs
Normal file
68
src/player.rs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
use byteorder::{ReadBytesExt, WriteBytesExt, BE};
|
||||||
|
use num_derive::FromPrimitive;
|
||||||
|
use num_traits::FromPrimitive;
|
||||||
|
|
||||||
|
use crate::chunk::BlockType;
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[derive(FromPrimitive)]
|
||||||
|
pub enum DiggingStatus {
|
||||||
|
StartedDigging = 0,
|
||||||
|
Digging = 1,
|
||||||
|
StoppedDigging = 2,
|
||||||
|
BlockBroken = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The face of a block, a direction.
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[derive(FromPrimitive)]
|
||||||
|
pub enum Direction {
|
||||||
|
NegY = 0,
|
||||||
|
PosY = 1,
|
||||||
|
NegZ = 2,
|
||||||
|
PosZ = 3,
|
||||||
|
NegX = 4,
|
||||||
|
PosX = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct PlayerDigging {
|
||||||
|
status: DiggingStatus,
|
||||||
|
position_x: i32,
|
||||||
|
position_y: u8,
|
||||||
|
position_z: i32,
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,7 +54,7 @@ impl PlayerPositionAndLook {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
pub struct PlayerPosition {
|
pub struct PlayerPosition {
|
||||||
pub position_x: f64,
|
pub position_x: f64,
|
||||||
pub position_y: f64,
|
pub position_y: f64,
|
||||||
|
@ -95,7 +95,7 @@ impl PlayerPosition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
pub struct PlayerLook {
|
pub struct PlayerLook {
|
||||||
pub yaw: f32,
|
pub yaw: f32,
|
||||||
pub pitch: f32,
|
pub pitch: f32,
|
||||||
|
|
30
src/state.rs
Normal file
30
src/state.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
use crate::{
|
||||||
|
chunk::MapChunk, position::{PlayerLook, PlayerPosition},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct GameState {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PlayerState {
|
||||||
|
eid: i32,
|
||||||
|
username: String,
|
||||||
|
position: PlayerPosition,
|
||||||
|
look: PlayerLook,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerState {
|
||||||
|
/// Create a new player when they join
|
||||||
|
pub fn new(username: String, eid: i32,) -> Self {
|
||||||
|
Self {
|
||||||
|
eid,
|
||||||
|
username,
|
||||||
|
position: PlayerPosition::default(),
|
||||||
|
look: PlayerLook::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WorldState {
|
||||||
|
chunks: Vec<MapChunk>,
|
||||||
|
}
|
Loading…
Reference in a new issue