1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00

more azalea-world stuff

This commit is contained in:
mat 2022-05-03 00:33:32 -05:00
parent 8e42e1c5df
commit 0bd798045c
14 changed files with 4523 additions and 96 deletions

2
Cargo.lock generated
View file

@ -166,6 +166,8 @@ dependencies = [
name = "azalea-world"
version = "0.1.0"
dependencies = [
"azalea-core",
"azalea-nbt",
"azalea-protocol",
]

View file

@ -1,5 +1,5 @@
use crate::Player;
use azalea_core::resource_location::ResourceLocation;
use azalea_core::{resource_location::ResourceLocation, ChunkPos};
use azalea_protocol::{
connect::{GameConnection, HandshakeConnection},
packets::{
@ -13,8 +13,8 @@ use azalea_protocol::{
},
resolver, ServerAddress,
};
use azalea_world::Chunk;
use std::sync::Arc;
use azalea_world::{Chunk, ChunkStorage, World};
use std::{fmt::Debug, ops::Deref, sync::Arc};
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
use tokio::sync::Mutex;
@ -27,8 +27,8 @@ pub struct Account {
#[derive(Default)]
pub struct ClientState {
// placeholder
pub player: Player,
pub world: Option<World>,
}
/// A player that you can control that is currently in a Minecraft server.
@ -173,8 +173,31 @@ impl Client {
match packet {
GamePacket::ClientboundLoginPacket(p) => {
println!("Got login packet {:?}", p);
std::fs::write("login.txt", format!("{:#?}", p)).expect("Unable to write file");
let mut state = state.lock().await;
state.player.entity.id = p.player_id;
let dimension_type = p
.dimension_type
.as_compound()
.expect("Dimension type is not compound")
.get("")
.expect("No \"\" tag")
.as_compound()
.expect("\"\" tag is not compound");
let height = (*dimension_type
.get("height")
.expect("No height tag")
.as_int()
.expect("height tag is not int"))
.try_into()
.expect("height is not a u32");
state.world = Some(World {
height,
storage: ChunkStorage::new(16),
});
state.lock().await.player.entity.id = p.player_id;
conn.lock()
.await
.write(
@ -231,10 +254,25 @@ impl Client {
}
GamePacket::ClientboundSetChunkCacheCenterPacket(p) => {
println!("Got chunk cache center packet {:?}", p);
state
.lock()
.await
.world
.as_mut()
.unwrap()
.update_view_center(&ChunkPos::new(p.x, p.z));
}
GamePacket::ClientboundLevelChunkWithLightPacket(p) => {
println!("Got chunk with light packet {} {}", p.x, p.z);
let pos = ChunkPos::new(p.x, p.z);
// let chunk = Chunk::read_with_world_height(&mut p.chunk_data);
// println("chunk {:?}")
state
.lock()
.await
.world
.replace_with_packet_data(&pos, &mut p.chunk_data.data.as_slice())
.unwrap();
}
GamePacket::ClientboundLightUpdatePacket(p) => {
println!("Got light update packet {:?}", p);

View file

@ -1,4 +1,5 @@
use crate::Entity;
use azalea_world::World;
#[derive(Default)]
pub struct Player {

View file

@ -1,6 +0,0 @@
#[derive(Clone, Copy, Debug)]
pub struct BlockPos {
pub x: i32,
pub y: i32,
pub z: i32,
}

View file

@ -8,8 +8,8 @@ pub mod serializable_uuid;
mod slot;
pub use slot::{Slot, SlotData};
mod block_pos;
pub use block_pos::BlockPos;
mod position;
pub use position::{BlockPos, ChunkPos};
mod direction;
pub use direction::Direction;

View file

@ -0,0 +1,24 @@
#[derive(Clone, Copy, Debug, Default)]
pub struct BlockPos {
pub x: i32,
pub y: i32,
pub z: i32,
}
impl BlockPos {
pub fn new(x: i32, y: i32, z: i32) -> Self {
BlockPos { x, y, z }
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ChunkPos {
pub x: i32,
pub z: i32,
}
impl ChunkPos {
pub fn new(x: i32, z: i32) -> Self {
ChunkPos { x, z }
}
}

View file

@ -17,6 +17,12 @@ pub enum Tag {
LongArray(Vec<i64>), // 12
}
impl Default for Tag {
fn default() -> Self {
Tag::End
}
}
impl Tag {
#[inline]
pub fn id(&self) -> u8 {

View file

@ -12,18 +12,17 @@ pub struct ClientboundLevelChunkWithLightPacket {
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
pub struct ClientboundLevelChunkPacketData {
heightmaps: azalea_nbt::Tag,
pub heightmaps: azalea_nbt::Tag,
// we can't parse the data in azalea-protocol because it dependso on context from other packets
data: Vec<u8>,
block_entities: Vec<BlockEntity>,
pub data: Vec<u8>,
pub block_entities: Vec<BlockEntity>,
}
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
pub struct BlockEntity {
packed_xz: u8,
y: u16,
pub packed_xz: u8,
pub y: u16,
#[varint]
type_: i32,
data: azalea_nbt::Tag,
pub type_: i32,
pub data: azalea_nbt::Tag,
}

View file

@ -5,5 +5,5 @@ pub struct ClientboundSetChunkCacheCenterPacket {
#[varint]
pub x: i32,
#[varint]
pub y: i32,
pub z: i32,
}

View file

@ -6,4 +6,6 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-core = {path = "../azalea-core"}
azalea-nbt = {path = "../azalea-nbt"}
azalea-protocol = {path = "../azalea-protocol"}

View file

@ -1,5 +1,13 @@
use azalea_protocol::mc_buf::{McBufReadable, McBufWritable, Readable, Writable};
use std::io::{Read, Write};
mod palette;
use azalea_core::ChunkPos;
use azalea_protocol::mc_buf::{McBufReadable, McBufWritable};
use palette::PalettedContainer;
use std::{
io::{Read, Write},
ops::{Index, IndexMut},
sync::{Arc, Mutex},
};
#[cfg(test)]
mod tests {
@ -13,14 +21,107 @@ mod tests {
const SECTION_HEIGHT: u32 = 16;
pub struct World {
pub storage: ChunkStorage,
pub height: u32,
}
impl World {
pub fn replace_with_packet_data(
&mut self,
pos: &ChunkPos,
data: &mut impl Read,
) -> Result<(), String> {
if !self.storage.in_range(pos) {
println!(
"Ignoring chunk since it's not in the view range: {}, {}",
pos.x, pos.z
);
return Ok(());
}
let existing_chunk = &self.storage[pos];
if let Some(existing_chunk) = existing_chunk {
existing_chunk
.lock()
.expect("Couldn't get lock on existing chunk")
.replace_with_packet_data(data)?;
} else {
let chunk = Arc::new(Mutex::new(Chunk::read_with_world(data, self)?));
println!("Loaded chunk {:?}", chunk);
self.storage[pos] = Some(chunk);
}
Ok(())
}
pub fn update_view_center(&mut self, pos: &ChunkPos) {
self.storage.view_center = *pos;
}
}
pub struct ChunkStorage {
view_center: ChunkPos,
chunk_radius: u32,
view_range: u32,
// chunks is a list of size chunk_radius * chunk_radius
chunks: Vec<Option<Arc<Mutex<Chunk>>>>,
}
// java moment
// it might be possible to replace this with just a modulo, but i copied java's floorMod just in case
fn floor_mod(x: i32, y: u32) -> u32 {
if x < 0 {
y - ((-x) as u32 % y)
} else {
x as u32 % y
}
}
impl ChunkStorage {
pub fn new(chunk_radius: u32) -> Self {
let view_range = chunk_radius * 2 + 1;
ChunkStorage {
view_center: ChunkPos::new(0, 0),
chunk_radius,
view_range,
chunks: vec![None; (view_range * view_range) as usize],
}
}
fn get_index(&self, chunk_pos: &ChunkPos) -> usize {
(floor_mod(chunk_pos.x, self.view_range) * self.view_range
+ floor_mod(chunk_pos.z, self.view_range)) as usize
}
pub fn in_range(&self, chunk_pos: &ChunkPos) -> bool {
(chunk_pos.x - self.view_center.x).unsigned_abs() <= self.chunk_radius
&& (chunk_pos.z - self.view_center.z).unsigned_abs() <= self.chunk_radius
}
}
impl Index<&ChunkPos> for ChunkStorage {
type Output = Option<Arc<Mutex<Chunk>>>;
fn index(&self, pos: &ChunkPos) -> &Self::Output {
&self.chunks[self.get_index(pos)]
}
}
impl IndexMut<&ChunkPos> for ChunkStorage {
fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output {
let index = self.get_index(pos);
&mut self.chunks[index]
}
}
#[derive(Debug)]
pub struct Chunk {
pub sections: Vec<Section>,
}
impl Chunk {
pub fn read_with_world(buf: &mut impl Read, data: &World) -> Result<Self, String> {
Self::read_with_world_height(buf, data.height)
}
pub fn read_with_world_height(buf: &mut impl Read, world_height: u32) -> Result<Self, String> {
let section_count = world_height / SECTION_HEIGHT;
let mut sections = Vec::with_capacity(section_count as usize);
@ -30,6 +131,18 @@ impl Chunk {
}
Ok(Chunk { sections })
}
fn replace_with_packet_data(&mut self, data: &mut impl Read) -> Result<(), String> {
let section_count = self.sections.len();
// this should also replace block entities and set the heightmap
for i in 0..section_count {
self.sections[i] = Section::read_into(data)?;
}
Ok(())
}
}
impl McBufWritable for Chunk {
@ -69,73 +182,3 @@ impl McBufWritable for Section {
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct PalettedContainer {
pub bits_per_entry: u8,
pub palette: Palette,
/// Compacted list of indices pointing to entry IDs in the Palette.
pub data: Vec<i64>,
}
impl McBufReadable for PalettedContainer {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let bits_per_entry = buf.read_byte()?;
let palette = Palette::read_with_bits_per_entry(buf, bits_per_entry)?;
let data = Vec::<i64>::read_into(buf)?;
Ok(PalettedContainer {
bits_per_entry,
palette,
data,
})
}
}
impl McBufWritable for PalettedContainer {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
buf.write_byte(self.bits_per_entry)?;
self.palette.write_into(buf)?;
self.data.write_into(buf)?;
Ok(())
}
}
#[derive(Clone, Debug)]
pub enum Palette {
/// ID of the corresponding entry in its global palette
SingleValue(u32),
LinearPalette(Vec<u32>),
HashmapPalette(Vec<u32>),
GlobalPalette,
}
impl Palette {
pub fn read_with_bits_per_entry(
buf: &mut impl Read,
bits_per_entry: u8,
) -> Result<Palette, String> {
Ok(match bits_per_entry {
0 => Palette::SingleValue(u32::read_into(buf)?),
1..=4 => Palette::LinearPalette(Vec::<u32>::read_into(buf)?),
5..=8 => Palette::HashmapPalette(Vec::<u32>::read_into(buf)?),
_ => Palette::GlobalPalette,
})
}
}
impl McBufWritable for Palette {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
match self {
Palette::SingleValue(value) => {
value.write_into(buf)?;
}
Palette::LinearPalette(values) => {
values.write_into(buf)?;
}
Palette::HashmapPalette(values) => {
values.write_into(buf)?;
}
Palette::GlobalPalette => {}
}
Ok(())
}
}

View file

@ -0,0 +1,73 @@
use std::io::{Read, Write};
use azalea_protocol::mc_buf::{McBufReadable, McBufWritable, Readable, Writable};
#[derive(Clone, Debug)]
pub struct PalettedContainer {
pub bits_per_entry: u8,
pub palette: Palette,
/// Compacted list of indices pointing to entry IDs in the Palette.
pub data: Vec<i64>,
}
impl McBufReadable for PalettedContainer {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let bits_per_entry = buf.read_byte()?;
let palette = Palette::read_with_bits_per_entry(buf, bits_per_entry)?;
let data = Vec::<i64>::read_into(buf)?;
Ok(PalettedContainer {
bits_per_entry,
palette,
data,
})
}
}
impl McBufWritable for PalettedContainer {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
buf.write_byte(self.bits_per_entry)?;
self.palette.write_into(buf)?;
self.data.write_into(buf)?;
Ok(())
}
}
#[derive(Clone, Debug)]
pub enum Palette {
/// ID of the corresponding entry in its global palette
SingleValue(u32),
LinearPalette(Vec<u32>),
HashmapPalette(Vec<u32>),
GlobalPalette,
}
impl Palette {
pub fn read_with_bits_per_entry(
buf: &mut impl Read,
bits_per_entry: u8,
) -> Result<Palette, String> {
Ok(match bits_per_entry {
0 => Palette::SingleValue(u32::read_into(buf)?),
1..=4 => Palette::LinearPalette(Vec::<u32>::read_into(buf)?),
5..=8 => Palette::HashmapPalette(Vec::<u32>::read_into(buf)?),
_ => Palette::GlobalPalette,
})
}
}
impl McBufWritable for Palette {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
match self {
Palette::SingleValue(value) => {
value.write_into(buf)?;
}
Palette::LinearPalette(values) => {
values.write_into(buf)?;
}
Palette::HashmapPalette(values) => {
values.write_into(buf)?;
}
Palette::GlobalPalette => {}
}
Ok(())
}
}

View file

@ -5,7 +5,7 @@ async fn main() {
println!("Hello, world!");
// let address = "95.111.249.143:10000";
let address = "172.23.192.1:58024";
let address = "172.23.192.1:59152";
// let response = azalea_client::ping::ping_server(&address.try_into().unwrap())
// .await
// .unwrap();

4245
login.txt Normal file

File diff suppressed because it is too large Load diff