Skip to content
This repository has been archived by the owner on Jul 1, 2024. It is now read-only.

Commit

Permalink
Merge pull request #13 from Mubelotix/world
Browse files Browse the repository at this point in the history
Create world
  • Loading branch information
Mubelotix authored Nov 8, 2023
2 parents 4748f73 + 214aac0 commit 9e6c5f8
Show file tree
Hide file tree
Showing 20 changed files with 442 additions and 67 deletions.
3 changes: 1 addition & 2 deletions minecraft-positions/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@ name = "minecraft-positions"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
minecraft-protocol = { path = "../minecraft-protocol" }
34 changes: 33 additions & 1 deletion minecraft-positions/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod shards;

pub use minecraft_protocol::packets::Position as NetworkPosition;

#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct BlockPosition {
pub x: i32,
Expand Down Expand Up @@ -40,6 +42,26 @@ impl BlockPosition {
}
}

impl From<BlockPosition> for NetworkPosition {
fn from(value: BlockPosition) -> Self {
NetworkPosition {
x: value.x,
y: value.y as i16,
z: value.z,
}
}
}

impl From<NetworkPosition> for BlockPosition {
fn from(value: NetworkPosition) -> Self {
BlockPosition {
x: value.x,
y: value.y as i32,
z: value.z,
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct BlockPositionInChunk {
pub bx: u8,
Expand Down Expand Up @@ -112,7 +134,7 @@ pub struct Rotation {
pub z: f32,
}

#[derive(PartialEq, Eq, Hash)]
#[derive(PartialEq, Eq, Hash, Clone)]
pub struct ChunkPosition {
pub cx: i32,
pub cy: i32,
Expand Down Expand Up @@ -145,3 +167,13 @@ pub struct ChunkColumnPosition {
pub cx: i32,
pub cz: i32,
}

impl ChunkColumnPosition {
pub fn chunk(&self, cy: i32) -> ChunkPosition {
ChunkPosition {
cx: self.cx,
cy,
cz: self.cz,
}
}
}
2 changes: 1 addition & 1 deletion minecraft-protocol/src/components/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub enum PartialDiggingState {
}

/// See [the wiki](https://wiki.vg/Protocol#Player_Digging)
#[cfg_attr(test, derive(PartialEq))]
#[derive(PartialEq)]
#[minecraft_enum(VarInt)]
#[derive(Debug)]
pub enum DiggingState {
Expand Down
2 changes: 1 addition & 1 deletion minecraft-protocol/src/components/gamemode.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::*;

#[cfg_attr(test, derive(PartialEq))]
#[derive(PartialEq)]
#[minecraft_enum(u8)]
#[derive(Debug)]
pub enum Gamemode {
Expand Down
4 changes: 2 additions & 2 deletions minecraft-protocol/src/packets/play_clientbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,9 @@ pub enum ClientboundPacket<'a> {
/// It is legal to send this packet even if the given chunk is not currently loaded.
UnloadChunk {
/// Block coordinate divided by 16, rounded down.
chunk_x: i32,
chunk_z: i32,
/// Block coordinate divided by 16, rounded down.
chunk_y: i32,
chunk_x: i32,
},

/// Used for a wide variety of game state things, from weather to bed use to gamemode to demo messages.
Expand Down
1 change: 1 addition & 0 deletions minecraft-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ minecraft-protocol = { path="../minecraft-protocol" }
minecraft-entities = { path="../minecraft-entities" }
minecraft-positions = { path="../minecraft-positions" }
tags-macros = { path = "../tags-macros"}
rand = "0.8.4"
8 changes: 8 additions & 0 deletions minecraft-server/src/ecs/entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ pub struct Entities {
}

impl Entities {
pub fn new() -> Entities {
Entities {
entities: RwLock::new(HashMap::new()),
chunks: RwLock::new(HashMap::new()),
uuids: RwLock::new(HashMap::new()),
}
}

/// Observe an entity through a closure
pub async fn observe_entity<R>(&self, eid: Eid, observer: impl FnOnce(&AnyEntity) -> R) -> Option<R> {
self.entities.read().await.get(&eid).map(observer)
Expand Down
2 changes: 1 addition & 1 deletion minecraft-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
mod player_handler;
mod server_behavior;
mod prelude;
mod map;
mod ecs;
mod world;

use crate::prelude::*;

Expand Down
10 changes: 7 additions & 3 deletions minecraft-server/src/player_handler/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ pub async fn handle_connection(
mut stream: TcpStream,
addr: SocketAddr,
server_msg_rcvr: BroadcastReceiver<ServerMessage>,
world: Arc<World>,
) -> Result<(), ()> {
// Receive handshake
let packet = receive_packet(&mut stream).await;
let packet = receive_packet(&mut stream).await?;
let HandshakeServerbound::Hello { protocol_version, server_address, server_port, next_state } = HandshakeServerbound::deserialize_uncompressed_minecraft_packet(packet.as_slice()).unwrap();
match next_state {
ConnectionState::Login => {
let player_info = login(&mut stream, addr).await?;
let player_info = handshake(&mut stream, player_info).await?;
handle_player(stream, player_info, server_msg_rcvr).await
let (player_info, change_receiver) = handshake(&mut stream, player_info, Arc::clone(&world)).await?;
let uuid = player_info.uuid;
let r = handle_player(stream, player_info, server_msg_rcvr, Arc::clone(&world), change_receiver).await;
world.remove_loader(uuid).await;
r
},
ConnectionState::Status => {
status(&mut stream).await;
Expand Down
48 changes: 27 additions & 21 deletions minecraft-server/src/player_handler/handshake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ pub struct PlayerInfo {
pub(super) allow_server_listing: bool,
}

pub async fn handshake(stream: &mut TcpStream, logged_in_player_info: LoggedInPlayerInfo) -> Result<PlayerInfo, ()> {
pub async fn handshake(stream: &mut TcpStream, logged_in_player_info: LoggedInPlayerInfo, world: Arc<World>) -> Result<(PlayerInfo, MpscReceiver<WorldChange>), ()> {
// Receive client informations
let packet = receive_packet(stream).await;
let packet = receive_packet(stream).await?;
debug!("Packet received");
let packet = ConfigServerbound::deserialize_uncompressed_minecraft_packet(packet.as_slice()).unwrap();
let ConfigServerbound::ClientInformations { locale, render_distance, chat_mode, chat_colors, displayed_skin_parts, main_hand, enable_text_filtering, allow_server_listing } = packet else {
Expand Down Expand Up @@ -59,7 +59,7 @@ pub async fn handshake(stream: &mut TcpStream, logged_in_player_info: LoggedInPl
debug!("FinishConfiguration sent");

// Receive finish configuration
let packet = receive_packet(stream).await;
let packet = receive_packet(stream).await?;
let packet = ConfigServerbound::deserialize_uncompressed_minecraft_packet(packet.as_slice()).unwrap();
let ConfigServerbound::FinishConfiguration = packet else {
error!("Expected FinishConfiguration packet, got: {packet:?}");
Expand Down Expand Up @@ -306,28 +306,34 @@ pub async fn handshake(stream: &mut TcpStream, logged_in_player_info: LoggedInPl
send_packet(stream, chunk_data).await;
debug!("ChunkBatchStart sent");

let empty_chunk = NetworkChunk {
block_count: 0,
blocks: PalettedData::Single { value: 0 },
biomes: PalettedData::Single { value: 4 },
};
let dirt_chunk = NetworkChunk {
block_count: 4096,
blocks: PalettedData::Single { value: minecraft_protocol::ids::blocks::Block::GrassBlock.default_state_id() },
biomes: PalettedData::Single { value: 4 },
};
let mut flat_column = Vec::new();
flat_column.push(dirt_chunk);
for _ in 0..23 {
flat_column.push(empty_chunk.clone());
let change_receiver = world.add_loader(logged_in_player_info.uuid).await;
let mut loaded_chunks = HashSet::new();
for cx in -3..=3 {
for cz in -3..=3 {
loaded_chunks.insert(ChunkColumnPosition { cx, cz });
}
}
let serialized: Vec<u8> = NetworkChunk::into_data(flat_column).unwrap();
world.update_loaded_chunks(logged_in_player_info.uuid, loaded_chunks).await;

let mut heightmaps = HashMap::new();
heightmaps.insert(String::from("MOTION_BLOCKING"), NbtTag::LongArray(vec![0; 37]));
let heightmaps = NbtTag::Compound(heightmaps);

for cx in -3..=3 {
for cz in -3..=3 {
let mut column = Vec::new();
for cy in -4..20 {
let chunk = world.get_network_chunk(ChunkPosition { cx, cy, cz }).await.unwrap_or_else(|| {
error!("Chunk not loaded: {cx} {cy} {cz}");
NetworkChunk { // TODO hard error
block_count: 0,
blocks: PalettedData::Single { value: 0 },
biomes: PalettedData::Single { value: 4 },
}
});
column.push(chunk);
}
let serialized: Vec<u8> = NetworkChunk::into_data(column).unwrap();
let chunk_data = PlayClientbound::ChunkData {
value: ChunkData {
chunk_x: cx,
Expand All @@ -354,15 +360,15 @@ pub async fn handshake(stream: &mut TcpStream, logged_in_player_info: LoggedInPl
debug!("ChunkBatchFinished sent");

// Get chunk batch acknoledgement
let packet = receive_packet(stream).await;
let packet = receive_packet(stream).await?;
let packet = PlayServerbound::deserialize_uncompressed_minecraft_packet(packet.as_slice()).unwrap();
let PlayServerbound::ChunkBatchReceived { chunks_per_tick } = packet else {
error!("Expected ChunkBatchAcknoledgement packet, got: {packet:?}");
return Err(());
};
debug!("ChunkBatchAcknoledgement received");

Ok(PlayerInfo {
Ok((PlayerInfo {
addr: logged_in_player_info.addr,
username: logged_in_player_info.username,
uuid: logged_in_player_info.uuid,
Expand All @@ -374,5 +380,5 @@ pub async fn handshake(stream: &mut TcpStream, logged_in_player_info: LoggedInPl
main_hand,
enable_text_filtering,
allow_server_listing,
})
}, change_receiver))
}
8 changes: 4 additions & 4 deletions minecraft-server/src/player_handler/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub struct LoggedInPlayerInfo {

pub async fn login(stream: &mut TcpStream, addr: SocketAddr) -> Result<LoggedInPlayerInfo, ()> {
// Receive login start
let packet = receive_packet(stream).await;
let packet = receive_packet(stream).await?;
let packet = LoginServerbound::deserialize_uncompressed_minecraft_packet(packet.as_slice()).unwrap();
let LoginServerbound::LoginStart{ username, player_uuid } = packet else {
error!("Expected LoginStart packet, got: {packet:?}");
Expand All @@ -30,7 +30,7 @@ pub async fn login(stream: &mut TcpStream, addr: SocketAddr) -> Result<LoggedInP
debug!("LoginSuccess sent");

// Receive login acknowledged
let packet = receive_packet(stream).await;
let packet = receive_packet(stream).await?;
let packet = LoginServerbound::deserialize_uncompressed_minecraft_packet(packet.as_slice()).unwrap();
let LoginServerbound::LoginAcknowledged = packet else {
error!("Expected LoginAcknowledged packet, got: {packet:?}");
Expand All @@ -39,10 +39,10 @@ pub async fn login(stream: &mut TcpStream, addr: SocketAddr) -> Result<LoggedInP
debug!("LoginAcknowledged received");

// Ignore encryption response if any
let packet = receive_packet(stream).await;
let packet = receive_packet(stream).await?;
if let Ok(LoginServerbound::EncryptionResponse { .. }) = LoginServerbound::deserialize_uncompressed_minecraft_packet(packet.as_slice()) {
// Ignore for now (TODO)
//packet = receive_packet(stream).await;
//packet = receive_packet(stream).await?;
}
debug!("EncryptionResponse ignored");

Expand Down
20 changes: 10 additions & 10 deletions minecraft-server/src/player_handler/network.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
use super::*;

pub async fn receive_packet(stream: &mut TcpStream) -> Vec<u8> {
pub async fn receive_packet(stream: &mut TcpStream) -> Result<Vec<u8>, ()> {
let mut length: Vec<u8> = Vec::with_capacity(2);

loop {
if length.len() >= 5 {
//return Err("length too long".into());
}
let mut byte = [0];
stream.read_exact(&mut byte).await.unwrap();
stream.read_exact(&mut byte).await.map_err(|_| ())?;
length.push(byte[0]);
if byte[0] < 0b1000_0000 {
break;
}
}

let length = VarInt::deserialize_uncompressed_minecraft_packet(length.as_mut_slice()).unwrap();
let length = VarInt::deserialize_uncompressed_minecraft_packet(length.as_mut_slice()).map_err(|_| ())?;

let mut data = Vec::with_capacity(length.0 as usize);
unsafe { data.set_len(length.0 as usize); }
stream.read_exact(&mut data).await.unwrap();
stream.read_exact(&mut data).await.map_err(|_| ())?;

data
Ok(data)
}

pub async fn receive_packet_split(stream: &mut OwnedReadHalf) -> Vec<u8> {
pub async fn receive_packet_split(stream: &mut OwnedReadHalf) -> Result<Vec<u8>, ()> {
let mut length: Vec<u8> = Vec::with_capacity(2);

loop {
if length.len() >= 5 {
//return Err("length too long".into());
}
let mut byte = [0];
stream.read_exact(&mut byte).await.unwrap();
stream.read_exact(&mut byte).await.map_err(|_| ())?;
length.push(byte[0]);
if byte[0] < 0b1000_0000 {
break;
}
}

let length = VarInt::deserialize_uncompressed_minecraft_packet(length.as_mut_slice()).unwrap();
let length = VarInt::deserialize_uncompressed_minecraft_packet(length.as_mut_slice()).map_err(|_| ())?;

let mut data = Vec::with_capacity(length.0 as usize);
unsafe { data.set_len(length.0 as usize); }
stream.read_exact(&mut data).await.unwrap();
stream.read_exact(&mut data).await.map_err(|_| ())?;

data
Ok(data)
}

pub async fn send_packet_raw(stream: &mut TcpStream, packet: &[u8]) {
Expand Down
Loading

0 comments on commit 9e6c5f8

Please sign in to comment.