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:
parent
8e42e1c5df
commit
0bd798045c
14 changed files with 4523 additions and 96 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -166,6 +166,8 @@ dependencies = [
|
|||
name = "azalea-world"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"azalea-core",
|
||||
"azalea-nbt",
|
||||
"azalea-protocol",
|
||||
]
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::Entity;
|
||||
use azalea_world::World;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Player {
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct BlockPos {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub z: i32,
|
||||
}
|
|
@ -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;
|
||||
|
|
24
azalea-core/src/position.rs
Normal file
24
azalea-core/src/position.rs
Normal 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 }
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -5,5 +5,5 @@ pub struct ClientboundSetChunkCacheCenterPacket {
|
|||
#[varint]
|
||||
pub x: i32,
|
||||
#[varint]
|
||||
pub y: i32,
|
||||
pub z: i32,
|
||||
}
|
||||
|
|
|
@ -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"}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
73
azalea-world/src/palette.rs
Normal file
73
azalea-world/src/palette.rs
Normal 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(())
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue