mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
commit
d0ac62d852
85 changed files with 1260 additions and 252 deletions
1
.gitignore
vendored
Executable file → Normal file
1
.gitignore
vendored
Executable file → Normal file
|
@ -4,6 +4,7 @@ flamegraph.svg
|
|||
perf.data
|
||||
perf.data.old
|
||||
|
||||
|
||||
code-generator/Burger
|
||||
code-generator/client.jar
|
||||
code-generator/burger.json
|
||||
|
|
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -100,6 +100,7 @@ dependencies = [
|
|||
"azalea-core",
|
||||
"azalea-crypto",
|
||||
"azalea-protocol",
|
||||
"azalea-world",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
@ -171,6 +172,15 @@ dependencies = [
|
|||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-world"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"azalea-core",
|
||||
"azalea-nbt",
|
||||
"azalea-protocol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -191,6 +201,7 @@ name = "bot"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"azalea-client",
|
||||
"azalea-core",
|
||||
"azalea-protocol",
|
||||
"tokio",
|
||||
]
|
||||
|
|
1
Cargo.toml
Executable file → Normal file
1
Cargo.toml
Executable file → Normal file
|
@ -9,6 +9,7 @@ members = [
|
|||
"azalea-nbt",
|
||||
"azalea-brigadier",
|
||||
"azalea-crypto",
|
||||
"azalea-world",
|
||||
"azalea-language",
|
||||
]
|
||||
|
||||
|
|
8
README.md
Executable file → Normal file
8
README.md
Executable file → Normal file
|
@ -1,13 +1,17 @@
|
|||
# Azalea
|
||||
|
||||
A Rust crate for creating Minecraft bots.
|
||||
|
||||
<p align="center">
|
||||
<img src="https://cdn.matdoes.dev/images/flowering_azalea.webp" alt="Azalea" height="200">
|
||||
</p>
|
||||
|
||||
A Rust crate for creating Minecraft bots.
|
||||
|
||||
I named this Azalea because it sounds like a cool word and this is a cool library. This project was heavily inspired by PrismarineJS.
|
||||
|
||||
## Why
|
||||
|
||||
I wanted a fun excuse to do something cool with Rust, and I also felt like I could do better than [Mineflayer](https://github.com/prismarinejs/mineflayer) in some areas.
|
||||
|
||||
## Goals
|
||||
|
||||
- Do everything a vanilla client can do.
|
||||
|
|
|
@ -10,4 +10,5 @@ azalea-auth = {path = "../azalea-auth"}
|
|||
azalea-core = {path = "../azalea-core"}
|
||||
azalea-crypto = {path = "../azalea-crypto"}
|
||||
azalea-protocol = {path = "../azalea-protocol"}
|
||||
azalea-world = {path = "../azalea-world"}
|
||||
tokio = {version = "1.18.0", features = ["sync"]}
|
||||
|
|
|
@ -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::{
|
||||
|
@ -17,7 +17,8 @@ use azalea_protocol::{
|
|||
},
|
||||
resolver, ServerAddress,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use azalea_world::{ChunkStorage, World};
|
||||
use std::{fmt::Debug, sync::Arc};
|
||||
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
|
@ -30,15 +31,15 @@ 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.
|
||||
pub struct Client {
|
||||
event_receiver: UnboundedReceiver<Event>,
|
||||
conn: Arc<Mutex<GameConnection>>,
|
||||
state: Arc<Mutex<ClientState>>,
|
||||
pub conn: Arc<Mutex<GameConnection>>,
|
||||
pub state: Arc<Mutex<ClientState>>,
|
||||
// game_loop
|
||||
}
|
||||
|
||||
|
@ -137,10 +138,9 @@ impl Client {
|
|||
// just start up the game loop and we're ready!
|
||||
// tokio::spawn(Self::game_loop(conn, tx, handler, state))
|
||||
|
||||
let game_loop_conn = conn.clone();
|
||||
let game_loop_state = client.state.clone();
|
||||
|
||||
tokio::spawn(Self::game_loop(game_loop_conn, tx, game_loop_state));
|
||||
tokio::spawn(Self::game_loop(conn, tx, game_loop_state));
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
|
@ -178,7 +178,37 @@ impl Client {
|
|||
GamePacket::ClientboundLoginPacket(p) => {
|
||||
println!("Got login packet {:?}", p);
|
||||
|
||||
state.lock().await.player.entity.id = p.player_id;
|
||||
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");
|
||||
let min_y = (*dimension_type
|
||||
.get("min_y")
|
||||
.expect("No min_y tag")
|
||||
.as_int()
|
||||
.expect("min_y tag is not int"))
|
||||
.try_into()
|
||||
.expect("min_y is not an i32");
|
||||
|
||||
state.world = Some(World {
|
||||
height,
|
||||
min_y,
|
||||
storage: ChunkStorage::new(16),
|
||||
});
|
||||
|
||||
conn.lock()
|
||||
.await
|
||||
.write(
|
||||
|
@ -202,7 +232,7 @@ impl Client {
|
|||
GamePacket::ClientboundChangeDifficultyPacket(p) => {
|
||||
println!("Got difficulty packet {:?}", p);
|
||||
}
|
||||
GamePacket::ClientboundDeclareCommandsPacket(p) => {
|
||||
GamePacket::ClientboundDeclareCommandsPacket(_p) => {
|
||||
println!("Got declare commands packet");
|
||||
}
|
||||
GamePacket::ClientboundPlayerAbilitiesPacket(p) => {
|
||||
|
@ -211,19 +241,19 @@ impl Client {
|
|||
GamePacket::ClientboundSetCarriedItemPacket(p) => {
|
||||
println!("Got set carried item packet {:?}", p);
|
||||
}
|
||||
GamePacket::ClientboundUpdateTagsPacket(p) => {
|
||||
GamePacket::ClientboundUpdateTagsPacket(_p) => {
|
||||
println!("Got update tags packet");
|
||||
}
|
||||
GamePacket::ClientboundDisconnectPacket(p) => {
|
||||
println!("Got disconnect packet {:?}", p);
|
||||
}
|
||||
GamePacket::ClientboundUpdateRecipesPacket(p) => {
|
||||
GamePacket::ClientboundUpdateRecipesPacket(_p) => {
|
||||
println!("Got update recipes packet");
|
||||
}
|
||||
GamePacket::ClientboundEntityEventPacket(p) => {
|
||||
// println!("Got entity event packet {:?}", p);
|
||||
}
|
||||
GamePacket::ClientboundRecipePacket(p) => {
|
||||
GamePacket::ClientboundRecipePacket(_p) => {
|
||||
println!("Got recipe packet");
|
||||
}
|
||||
GamePacket::ClientboundPlayerPositionPacket(p) => {
|
||||
|
@ -235,9 +265,27 @@ 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
|
||||
.as_mut()
|
||||
.expect("World doesn't exist! We should've gotten a login packet by now.")
|
||||
.replace_with_packet_data(&pos, &mut p.chunk_data.data.as_slice())
|
||||
.unwrap();
|
||||
}
|
||||
GamePacket::ClientboundLightUpdatePacket(p) => {
|
||||
println!("Got light update packet {:?}", p);
|
||||
|
@ -321,10 +369,21 @@ impl Client {
|
|||
}
|
||||
GamePacket::ClientboundBlockUpdatePacket(p) => {
|
||||
println!("Got block update packet {:?}", p);
|
||||
// TODO: update world
|
||||
}
|
||||
GamePacket::ClientboundAnimatePacket(p) => {
|
||||
println!("Got animate packet {:?}", p);
|
||||
}
|
||||
GamePacket::ClientboundSectionBlocksUpdatePacket(p) => {
|
||||
println!("Got section blocks update packet {:?}", p);
|
||||
// TODO: update world
|
||||
}
|
||||
GamePacket::ClientboundGameEventPacket(p) => {
|
||||
println!("Got game event packet {:?}", p);
|
||||
}
|
||||
GamePacket::ClientboundLevelParticlesPacket(p) => {
|
||||
println!("Got level particles packet {:?}", p);
|
||||
}
|
||||
_ => panic!("Unexpected packet {:?}", packet),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct BlockPos {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub z: i32,
|
||||
}
|
|
@ -83,7 +83,6 @@ mod tests {
|
|||
assert_eq!(1, Difficulty::EASY.id());
|
||||
assert_eq!(2, Difficulty::NORMAL.id());
|
||||
assert_eq!(3, Difficulty::HARD.id());
|
||||
assert_eq!(4, Difficulty::PEACEFUL.id());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! Random miscellaneous things like UUIDs that don't deserve their own crate.
|
||||
|
||||
#![feature(int_roundings)]
|
||||
|
||||
pub mod difficulty;
|
||||
pub mod game_type;
|
||||
pub mod resource_location;
|
||||
|
@ -8,8 +10,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, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos, ChunkSectionPos};
|
||||
|
||||
mod direction;
|
||||
pub use direction::Direction;
|
||||
|
|
157
azalea-core/src/position.rs
Normal file
157
azalea-core/src/position.rs
Normal file
|
@ -0,0 +1,157 @@
|
|||
use std::ops::Rem;
|
||||
|
||||
#[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 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Rem<i32> for BlockPos {
|
||||
type Output = Self;
|
||||
|
||||
fn rem(self, rhs: i32) -> Self {
|
||||
BlockPos {
|
||||
x: self.x % rhs,
|
||||
y: self.y % rhs,
|
||||
z: self.z % rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct ChunkPos {
|
||||
pub x: i32,
|
||||
pub z: i32,
|
||||
}
|
||||
|
||||
impl ChunkPos {
|
||||
pub fn new(x: i32, z: i32) -> Self {
|
||||
ChunkPos { x, z }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BlockPos> for ChunkPos {
|
||||
fn from(pos: &BlockPos) -> Self {
|
||||
ChunkPos {
|
||||
x: pos.x.div_floor(16),
|
||||
z: pos.z.div_floor(16),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The coordinates of a chunk section in the world.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct ChunkSectionPos {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub z: i32,
|
||||
}
|
||||
|
||||
impl ChunkSectionPos {
|
||||
pub fn new(x: i32, y: i32, z: i32) -> Self {
|
||||
ChunkSectionPos { x, y, z }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockPos> for ChunkSectionPos {
|
||||
fn from(pos: BlockPos) -> Self {
|
||||
ChunkSectionPos {
|
||||
x: pos.x.div_floor(16),
|
||||
y: pos.y.div_floor(16),
|
||||
z: pos.z.div_floor(16),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ChunkSectionPos> for ChunkPos {
|
||||
fn from(pos: ChunkSectionPos) -> Self {
|
||||
ChunkPos { x: pos.x, z: pos.z }
|
||||
}
|
||||
}
|
||||
|
||||
/// The coordinates of a block inside a chunk.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct ChunkBlockPos {
|
||||
pub x: u8,
|
||||
pub y: i32,
|
||||
pub z: u8,
|
||||
}
|
||||
|
||||
impl ChunkBlockPos {
|
||||
pub fn new(x: u8, y: i32, z: u8) -> Self {
|
||||
ChunkBlockPos { x, y, z }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BlockPos> for ChunkBlockPos {
|
||||
fn from(pos: &BlockPos) -> Self {
|
||||
ChunkBlockPos {
|
||||
x: pos.x.rem_euclid(16).abs() as u8,
|
||||
y: pos.y,
|
||||
z: pos.z.rem_euclid(16).abs() as u8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The coordinates of a block inside a chunk section.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct ChunkSectionBlockPos {
|
||||
/// A number between 0 and 16.
|
||||
pub x: u8,
|
||||
/// A number between 0 and 16.
|
||||
pub y: u8,
|
||||
/// A number between 0 and 16.
|
||||
pub z: u8,
|
||||
}
|
||||
|
||||
impl ChunkSectionBlockPos {
|
||||
pub fn new(x: u8, y: u8, z: u8) -> Self {
|
||||
ChunkSectionBlockPos { x, y, z }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BlockPos> for ChunkSectionBlockPos {
|
||||
fn from(pos: &BlockPos) -> Self {
|
||||
ChunkSectionBlockPos {
|
||||
x: pos.x.rem(16).abs() as u8,
|
||||
y: pos.y.rem(16).abs() as u8,
|
||||
z: pos.z.rem(16).abs() as u8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ChunkBlockPos> for ChunkSectionBlockPos {
|
||||
fn from(pos: &ChunkBlockPos) -> Self {
|
||||
ChunkSectionBlockPos {
|
||||
x: pos.x,
|
||||
y: pos.y.rem(16).abs() as u8,
|
||||
z: pos.z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_from_block_pos_to_chunk_pos() {
|
||||
let block_pos = BlockPos::new(5, 78, -2);
|
||||
let chunk_pos = ChunkPos::from(&block_pos);
|
||||
assert_eq!(chunk_pos, ChunkPos::new(0, -1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_block_pos_to_chunk_block_pos() {
|
||||
let block_pos = BlockPos::new(5, 78, -2);
|
||||
let chunk_block_pos = ChunkBlockPos::from(&block_pos);
|
||||
assert_eq!(chunk_block_pos, ChunkBlockPos::new(5, 78, 14));
|
||||
}
|
||||
}
|
|
@ -1,9 +1,6 @@
|
|||
use aes::cipher::inout::InOutBuf;
|
||||
use aes::cipher::BlockEncrypt;
|
||||
use aes::{
|
||||
cipher::{
|
||||
generic_array::GenericArray, AsyncStreamCipher, BlockDecryptMut, BlockEncryptMut, KeyIvInit,
|
||||
},
|
||||
cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit},
|
||||
Aes128,
|
||||
};
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
|
@ -15,7 +12,7 @@ fn generate_secret_key() -> [u8; 16] {
|
|||
key
|
||||
}
|
||||
|
||||
fn digest_data(server_id: &[u8], public_key: &[u8], private_key: &[u8]) -> Vec<u8> {
|
||||
pub fn digest_data(server_id: &[u8], public_key: &[u8], private_key: &[u8]) -> Vec<u8> {
|
||||
let mut digest = Sha1::new();
|
||||
digest.update(server_id);
|
||||
digest.update(public_key);
|
||||
|
@ -23,7 +20,7 @@ fn digest_data(server_id: &[u8], public_key: &[u8], private_key: &[u8]) -> Vec<u
|
|||
digest.finalize().to_vec()
|
||||
}
|
||||
|
||||
fn hex_digest(digest: &[u8]) -> String {
|
||||
pub fn hex_digest(digest: &[u8]) -> String {
|
||||
// Note that the Sha1.hexdigest() method used by minecraft is non standard.
|
||||
// It doesn't match the digest method found in most programming languages
|
||||
// and libraries. It works by treating the sha1 output bytes as one large
|
||||
|
@ -48,9 +45,8 @@ pub fn encrypt(public_key: &[u8], nonce: &[u8]) -> Result<EncryptResult, String>
|
|||
|
||||
// this.keybytes = Crypt.encryptUsingKey(publicKey, secretKey.getEncoded());
|
||||
// this.nonce = Crypt.encryptUsingKey(publicKey, arrby);
|
||||
let encrypted_public_key: Vec<u8> =
|
||||
rsa_public_encrypt_pkcs1::encrypt(&public_key, &secret_key)?;
|
||||
let encrypted_nonce: Vec<u8> = rsa_public_encrypt_pkcs1::encrypt(&public_key, &nonce)?;
|
||||
let encrypted_public_key: Vec<u8> = rsa_public_encrypt_pkcs1::encrypt(public_key, &secret_key)?;
|
||||
let encrypted_nonce: Vec<u8> = rsa_public_encrypt_pkcs1::encrypt(public_key, nonce)?;
|
||||
|
||||
Ok(EncryptResult {
|
||||
secret_key,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use lazy_static::lazy_static;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::{collections::HashMap, fs::File};
|
||||
// use tokio::fs::File;
|
||||
|
||||
// pub struct Language {
|
||||
|
@ -29,10 +30,26 @@ use std::collections::HashMap;
|
|||
// The code above is kept in case I come up with a better solution
|
||||
|
||||
lazy_static! {
|
||||
pub static ref STORAGE: HashMap<String, String> =
|
||||
serde_json::from_str(include_str!("en_us.json")).unwrap();
|
||||
pub static ref STORAGE: HashMap<String, String> = serde_json::from_str(&{
|
||||
let src_dir = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/en_us.json"));
|
||||
let mut file = File::open(src_dir).unwrap();
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents).unwrap();
|
||||
contents
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn get(key: &str) -> Option<&str> {
|
||||
STORAGE.get(key).map(|s| s.as_str())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_get() {
|
||||
assert_eq!(get("translation.test.none"), Some("Hello, world!"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ fn write_compound(
|
|||
if end_tag {
|
||||
writer.write_u8(Tag::End.id())?;
|
||||
}
|
||||
return Ok(());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -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 {
|
||||
|
|
4
azalea-protocol/README.md
Executable file → Normal file
4
azalea-protocol/README.md
Executable file → Normal file
|
@ -2,11 +2,11 @@
|
|||
|
||||
Sent and receive Minecraft packets. You should probably use `azalea` or `azalea-client` instead.
|
||||
|
||||
The goal is to **only** support the latest Minecraft version in order to ease development.
|
||||
The goal is to only support the latest Minecraft version in order to ease development.
|
||||
|
||||
This is not yet complete, search for `TODO` in the code for things that need to be done.
|
||||
|
||||
Unfortunately, compiling the crate requires Rust nightly because specialization is not stable yet.
|
||||
Unfortunately, using azalea-protocol requires Rust nightly because [specialization](https://github.com/rust-lang/rust/issues/31844) is not stable yet. Use `rustup default nightly` to enable it.
|
||||
|
||||
## Adding a new packet
|
||||
|
||||
|
|
|
@ -157,6 +157,19 @@ pub fn derive_mcbufwritable(input: TokenStream) -> TokenStream {
|
|||
create_impl_mcbufwritable(&ident, &data).into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(McBuf, attributes(var))]
|
||||
pub fn derive_mcbuf(input: TokenStream) -> TokenStream {
|
||||
let DeriveInput { ident, data, .. } = parse_macro_input!(input);
|
||||
|
||||
let writable = create_impl_mcbufwritable(&ident, &data);
|
||||
let readable = create_impl_mcbufreadable(&ident, &data);
|
||||
quote! {
|
||||
#writable
|
||||
#readable
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> TokenStream {
|
||||
let DeriveInput { ident, data, .. } = parse_macro_input!(input);
|
||||
|
||||
|
@ -169,8 +182,8 @@ fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> Toke
|
|||
_ => panic!("#[derive(*Packet)] can only be used on structs with named fields"),
|
||||
};
|
||||
|
||||
let mcbufreadable_impl = create_impl_mcbufreadable(&ident, &data);
|
||||
let mcbufwritable_impl = create_impl_mcbufwritable(&ident, &data);
|
||||
let _mcbufreadable_impl = create_impl_mcbufreadable(&ident, &data);
|
||||
let _mcbufwritable_impl = create_impl_mcbufwritable(&ident, &data);
|
||||
|
||||
let contents = quote! {
|
||||
impl #ident {
|
||||
|
@ -189,10 +202,6 @@ fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> Toke
|
|||
Ok(Self::read_into(buf)?.get())
|
||||
}
|
||||
}
|
||||
|
||||
#mcbufreadable_impl
|
||||
|
||||
#mcbufwritable_impl
|
||||
};
|
||||
|
||||
contents.into()
|
||||
|
@ -232,13 +241,12 @@ struct PacketIdMap {
|
|||
impl Parse for PacketIdMap {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut packets = vec![];
|
||||
loop {
|
||||
// 0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket,
|
||||
// 0x0e
|
||||
let packet_id: LitInt = match input.parse() {
|
||||
Ok(i) => i,
|
||||
Err(_) => break,
|
||||
};
|
||||
|
||||
// example:
|
||||
// 0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket,
|
||||
|
||||
// 0x0e
|
||||
while let Ok(packet_id) = input.parse::<LitInt>() {
|
||||
let packet_id = packet_id.base10_parse::<u32>()?;
|
||||
// :
|
||||
input.parse::<Token![:]>()?;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::mc_buf::read::{McBufReadable, Readable};
|
||||
use crate::mc_buf::write::{McBufWritable, Writable};
|
||||
use crate::mc_buf::McBufVarReadable;
|
||||
use azalea_chat::component::Component;
|
||||
use azalea_core::{BlockPos, Direction, Slot};
|
||||
use packet_macros::{McBufReadable, McBufWritable};
|
||||
use packet_macros::McBuf;
|
||||
use std::io::{Read, Write};
|
||||
use std::ops::Deref;
|
||||
use uuid::Uuid;
|
||||
|
@ -32,7 +33,7 @@ impl From<&str> for UnsizedByteArray {
|
|||
}
|
||||
|
||||
/// Represents Java's BitSet, a list of bits.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, McBufReadable, McBufWritable)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, McBuf)]
|
||||
pub struct BitSet {
|
||||
data: Vec<u64>,
|
||||
}
|
||||
|
@ -159,7 +160,7 @@ impl McBufWritable for EntityDataValue {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, Copy, McBuf)]
|
||||
pub enum Pose {
|
||||
Standing = 0,
|
||||
FallFlying = 1,
|
||||
|
@ -171,7 +172,7 @@ pub enum Pose {
|
|||
Dying = 7,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
|
||||
#[derive(Debug, Clone, McBuf)]
|
||||
pub struct VillagerData {
|
||||
#[var]
|
||||
type_: u32,
|
||||
|
@ -181,7 +182,7 @@ pub struct VillagerData {
|
|||
level: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
|
||||
#[derive(Debug, Clone, McBuf)]
|
||||
pub struct Particle {
|
||||
#[var]
|
||||
pub id: i32,
|
||||
|
@ -280,12 +281,12 @@ pub enum ParticleData {
|
|||
Scrape,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
|
||||
#[derive(Debug, Clone, McBuf)]
|
||||
pub struct BlockParticle {
|
||||
#[var]
|
||||
pub block_state: i32,
|
||||
}
|
||||
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
|
||||
#[derive(Debug, Clone, McBuf)]
|
||||
pub struct DustParticle {
|
||||
/// Red value, 0-1
|
||||
pub red: f32,
|
||||
|
@ -297,7 +298,7 @@ pub struct DustParticle {
|
|||
pub scale: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
|
||||
#[derive(Debug, Clone, McBuf)]
|
||||
pub struct DustColorTransitionParticle {
|
||||
/// Red value, 0-1
|
||||
pub from_red: f32,
|
||||
|
@ -315,12 +316,12 @@ pub struct DustColorTransitionParticle {
|
|||
pub to_blue: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
|
||||
#[derive(Debug, Clone, McBuf)]
|
||||
pub struct ItemParticle {
|
||||
pub item: Slot,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
|
||||
#[derive(Debug, Clone, McBuf)]
|
||||
pub struct VibrationParticle {
|
||||
pub origin: BlockPos,
|
||||
pub position_type: String,
|
||||
|
@ -331,9 +332,8 @@ pub struct VibrationParticle {
|
|||
pub ticks: u32,
|
||||
}
|
||||
|
||||
impl McBufReadable for ParticleData {
|
||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||
let id = buf.read_varint()?;
|
||||
impl ParticleData {
|
||||
pub fn read_from_particle_id(buf: &mut impl Read, id: u32) -> Result<Self, String> {
|
||||
Ok(match id {
|
||||
0 => ParticleData::AmbientEntityEffect,
|
||||
1 => ParticleData::AngryVillager,
|
||||
|
@ -428,6 +428,13 @@ impl McBufReadable for ParticleData {
|
|||
}
|
||||
}
|
||||
|
||||
impl McBufReadable for ParticleData {
|
||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||
let id = u32::var_read_into(buf)?;
|
||||
ParticleData::read_from_particle_id(buf, id)
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for ParticleData {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
todo!()
|
||||
|
|
6
azalea-protocol/src/mc_buf/mod.rs
Executable file → Normal file
6
azalea-protocol/src/mc_buf/mod.rs
Executable file → Normal file
|
@ -4,16 +4,16 @@ mod definitions;
|
|||
mod read;
|
||||
mod write;
|
||||
|
||||
pub use definitions::{BitSet, EntityMetadata, UnsizedByteArray};
|
||||
use packet_macros::{McBufReadable, McBufWritable};
|
||||
pub use definitions::{BitSet, EntityMetadata, ParticleData, UnsizedByteArray};
|
||||
pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable};
|
||||
use std::ops::Deref;
|
||||
pub use write::{McBufVarWritable, McBufWritable, Writable};
|
||||
|
||||
// const DEFAULT_NBT_QUOTA: u32 = 2097152;
|
||||
const MAX_STRING_LENGTH: u16 = 32767;
|
||||
// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144;
|
||||
|
||||
// TODO: maybe get rid of the readable/writable traits so there's not two ways to do the same thing and improve McBufReadable/McBufWritable
|
||||
|
||||
// TODO: have a definitions.rs in mc_buf that contains UnsizedByteArray and BitSet
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
20
azalea-protocol/src/mc_buf/read.rs
Executable file → Normal file
20
azalea-protocol/src/mc_buf/read.rs
Executable file → Normal file
|
@ -1,10 +1,10 @@
|
|||
use super::{BitSet, UnsizedByteArray, MAX_STRING_LENGTH};
|
||||
use super::{UnsizedByteArray, MAX_STRING_LENGTH};
|
||||
use azalea_chat::component::Component;
|
||||
use azalea_core::{
|
||||
difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation,
|
||||
serializable_uuid::SerializableUuid, BlockPos, Direction, Slot, SlotData,
|
||||
serializable_uuid::SerializableUuid, BlockPos, ChunkSectionPos, Direction, Slot, SlotData,
|
||||
};
|
||||
use byteorder::{ReadBytesExt, WriteBytesExt, BE};
|
||||
use byteorder::{ReadBytesExt, BE};
|
||||
use serde::Deserialize;
|
||||
use std::{collections::HashMap, hash::Hash, io::Read};
|
||||
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||
|
@ -466,7 +466,7 @@ impl McBufReadable for Component {
|
|||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||
let string = buf.read_utf()?;
|
||||
let json: serde_json::Value = serde_json::from_str(string.as_str())
|
||||
.map_err(|e| "Component isn't valid JSON".to_string())?;
|
||||
.map_err(|_| "Component isn't valid JSON".to_string())?;
|
||||
let component = Component::deserialize(json).map_err(|e| e.to_string())?;
|
||||
Ok(component)
|
||||
}
|
||||
|
@ -518,3 +518,15 @@ impl McBufReadable for Direction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ChunkSectionPos
|
||||
impl McBufReadable for ChunkSectionPos {
|
||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||
let long = i64::read_into(buf)?;
|
||||
Ok(ChunkSectionPos {
|
||||
x: (long >> 42) as i32,
|
||||
y: (long << 44 >> 44) as i32,
|
||||
z: (long << 22 >> 42) as i32,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
21
azalea-protocol/src/mc_buf/write.rs
Executable file → Normal file
21
azalea-protocol/src/mc_buf/write.rs
Executable file → Normal file
|
@ -2,7 +2,7 @@ use super::{UnsizedByteArray, MAX_STRING_LENGTH};
|
|||
use azalea_chat::component::Component;
|
||||
use azalea_core::{
|
||||
difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation,
|
||||
serializable_uuid::SerializableUuid, BlockPos, Direction, Slot,
|
||||
serializable_uuid::SerializableUuid, BlockPos, ChunkSectionPos, Direction, Slot,
|
||||
};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use std::{collections::HashMap, io::Write};
|
||||
|
@ -20,8 +20,8 @@ pub trait Writable: Write {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_int_id_list(&mut self, list: &Vec<i32>) -> Result<(), std::io::Error> {
|
||||
self.write_list(&list, |buf, n| buf.write_varint(*n))
|
||||
fn write_int_id_list(&mut self, list: &[i32]) -> Result<(), std::io::Error> {
|
||||
self.write_list(list, |buf, n| buf.write_varint(*n))
|
||||
}
|
||||
|
||||
fn write_map<KF, VF, KT, VT>(
|
||||
|
@ -47,7 +47,7 @@ pub trait Writable: Write {
|
|||
}
|
||||
|
||||
fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> {
|
||||
self.write_all(bytes);
|
||||
self.write_all(bytes)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -377,7 +377,7 @@ impl McBufWritable for Component {
|
|||
// let component = Component::deserialize(json).map_err(|e| e.to_string())?;
|
||||
// Ok(component)
|
||||
// }
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
// component doesn't have serialize implemented yet
|
||||
todo!()
|
||||
}
|
||||
|
@ -425,3 +425,14 @@ impl McBufWritable for Direction {
|
|||
buf.write_varint(*self as i32)
|
||||
}
|
||||
}
|
||||
|
||||
// ChunkSectionPos
|
||||
impl McBufWritable for ChunkSectionPos {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let long = (((self.x & 0x3FFFFF) as i64) << 42)
|
||||
| (self.y & 0xFFFFF) as i64
|
||||
| (((self.z & 0x3FFFFF) as i64) << 20);
|
||||
long.write_into(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundAddEntityPacket {
|
||||
#[var]
|
||||
pub id: i32,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundAddMobPacket {
|
||||
#[var]
|
||||
pub id: i32,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundAddPlayerPacket {
|
||||
#[var]
|
||||
pub id: i32,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::{GamePacket, McBufReadable, McBufWritable};
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundAnimatePacket {
|
||||
#[var]
|
||||
pub id: u32,
|
||||
|
@ -9,7 +9,7 @@ pub struct ClientboundAnimatePacket {
|
|||
|
||||
// minecraft actually uses a u8 for this, but a varint still works and makes it
|
||||
// so i don't have to add a special handler
|
||||
#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, Copy, McBuf)]
|
||||
pub enum AnimationAction {
|
||||
SwingMainHand = 0,
|
||||
Hurt = 1,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use azalea_core::BlockPos;
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundBlockUpdatePacket {
|
||||
pub pos: BlockPos,
|
||||
// TODO: in vanilla this is a BlockState, but here we just have it as a number.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use azalea_core::difficulty::Difficulty;
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundChangeDifficultyPacket {
|
||||
pub difficulty: Difficulty,
|
||||
pub locked: bool,
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
use azalea_chat::component::Component;
|
||||
use packet_macros::{GamePacket, McBufReadable, McBufWritable};
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundChatPacket {
|
||||
pub message: Component,
|
||||
pub type_: ChatType,
|
||||
pub sender: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, Copy, McBuf)]
|
||||
pub enum ChatType {
|
||||
Chat = 0,
|
||||
System = 1,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use azalea_core::Slot;
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundContainerSetContentPacket {
|
||||
pub container_id: u8,
|
||||
#[var]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::mc_buf::UnsizedByteArray;
|
||||
use azalea_core::resource_location::ResourceLocation;
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundCustomPayloadPacket {
|
||||
pub identifier: ResourceLocation,
|
||||
pub data: UnsizedByteArray,
|
||||
|
|
|
@ -281,7 +281,7 @@ impl McBufReadable for BrigadierParser {
|
|||
}
|
||||
}
|
||||
|
||||
// azalea_brigadier::tree::CommandNode
|
||||
// TODO: BrigadierNodeStub should have more stuff
|
||||
impl McBufReadable for BrigadierNodeStub {
|
||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||
let flags = u8::read_into(buf)?;
|
||||
|
@ -292,20 +292,18 @@ impl McBufReadable for BrigadierNodeStub {
|
|||
}
|
||||
|
||||
let node_type = flags & 0x03;
|
||||
let is_executable = flags & 0x04 != 0;
|
||||
let _is_executable = flags & 0x04 != 0;
|
||||
let has_redirect = flags & 0x08 != 0;
|
||||
let has_suggestions_type = flags & 0x10 != 0;
|
||||
|
||||
let children = buf.read_int_id_list()?;
|
||||
let redirect_node = if has_redirect { buf.read_varint()? } else { 0 };
|
||||
let _children = buf.read_int_id_list()?;
|
||||
let _redirect_node = if has_redirect { buf.read_varint()? } else { 0 };
|
||||
|
||||
// argument node
|
||||
if node_type == 2 {
|
||||
let name = buf.read_utf()?;
|
||||
|
||||
let parser = BrigadierParser::read_into(buf)?;
|
||||
|
||||
let suggestions_type = if has_suggestions_type {
|
||||
let _name = buf.read_utf()?;
|
||||
let _parser = BrigadierParser::read_into(buf)?;
|
||||
let _suggestions_type = if has_suggestions_type {
|
||||
Some(buf.read_resource_location()?)
|
||||
} else {
|
||||
None
|
||||
|
@ -314,7 +312,7 @@ impl McBufReadable for BrigadierNodeStub {
|
|||
}
|
||||
// literal node
|
||||
if node_type == 1 {
|
||||
let name = buf.read_utf()?;
|
||||
let _name = buf.read_utf()?;
|
||||
return Ok(BrigadierNodeStub {});
|
||||
}
|
||||
Ok(BrigadierNodeStub {})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use azalea_chat::component::Component;
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundDisconnectPacket {
|
||||
pub reason: Component,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
// we can't identify the status in azalea-protocol since they vary depending on the entity
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundEntityEventPacket {
|
||||
pub entity_id: i32,
|
||||
pub entity_status: i8,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundEntityVelocityPacket {
|
||||
#[var]
|
||||
pub entity_id: u32,
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundGameEventPacket {
|
||||
pub event: EventType,
|
||||
pub param: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Copy, McBuf)]
|
||||
pub enum EventType {
|
||||
NoRespawnBlockAvailable = 0,
|
||||
StartRaining = 1,
|
||||
StopRaining = 2,
|
||||
ChangeGameMode = 3,
|
||||
WinGame = 4,
|
||||
DemoEvent = 5,
|
||||
ArrowHitPlayer = 6,
|
||||
RainLevelChange = 7,
|
||||
ThunderLevelChange = 8,
|
||||
PufferFishSting = 9,
|
||||
GuardianElderEffect = 10,
|
||||
ImmediateRespawn = 11,
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundInitializeBorderPacket {
|
||||
pub new_center_x: f64,
|
||||
pub new_center_z: f64,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundKeepAlivePacket {
|
||||
pub id: u64,
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use packet_macros::{GamePacket, McBufReadable, McBufWritable};
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
use super::clientbound_light_update_packet::ClientboundLightUpdatePacketData;
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundLevelChunkWithLightPacket {
|
||||
pub x: i32,
|
||||
pub z: i32,
|
||||
|
@ -10,25 +10,19 @@ pub struct ClientboundLevelChunkWithLightPacket {
|
|||
pub light_data: ClientboundLightUpdatePacketData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
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)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct BlockEntity {
|
||||
packed_xz: u8,
|
||||
y: u16,
|
||||
pub packed_xz: u8,
|
||||
pub y: u16,
|
||||
#[var]
|
||||
type_: i32,
|
||||
data: azalea_nbt::Tag,
|
||||
}
|
||||
|
||||
pub struct ChunkSection {}
|
||||
|
||||
impl ClientboundLevelChunkPacketData {
|
||||
pub fn read(world_height: u32) {}
|
||||
pub type_: i32,
|
||||
pub data: azalea_nbt::Tag,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use azalea_core::BlockPos;
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundLevelEventPacket {
|
||||
pub type_: i32,
|
||||
pub pos: BlockPos,
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
use std::io::{Read, Write};
|
||||
|
||||
use crate::mc_buf::{McBufReadable, McBufWritable, ParticleData};
|
||||
use packet_macros::GamePacket;
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
pub struct ClientboundLevelParticlesPacket {
|
||||
pub particle_id: u32,
|
||||
pub override_limiter: bool,
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub z: f64,
|
||||
pub x_dist: f32,
|
||||
pub y_dist: f32,
|
||||
pub z_dist: f32,
|
||||
pub max_speed: f32,
|
||||
pub count: i32,
|
||||
pub data: ParticleData,
|
||||
}
|
||||
|
||||
impl McBufReadable for ClientboundLevelParticlesPacket {
|
||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||
let particle_id = u32::read_into(buf)?;
|
||||
let override_limiter = bool::read_into(buf)?;
|
||||
let x = f64::read_into(buf)?;
|
||||
let y = f64::read_into(buf)?;
|
||||
let z = f64::read_into(buf)?;
|
||||
let x_dist = f32::read_into(buf)?;
|
||||
let y_dist = f32::read_into(buf)?;
|
||||
let z_dist = f32::read_into(buf)?;
|
||||
let max_speed = f32::read_into(buf)?;
|
||||
let count = i32::read_into(buf)?;
|
||||
|
||||
let data = ParticleData::read_from_particle_id(buf, particle_id)?;
|
||||
|
||||
Ok(Self {
|
||||
particle_id,
|
||||
override_limiter,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
x_dist,
|
||||
y_dist,
|
||||
z_dist,
|
||||
max_speed,
|
||||
count,
|
||||
data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for ClientboundLevelParticlesPacket {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
todo!();
|
||||
}
|
||||
}
|
|
@ -1,15 +1,14 @@
|
|||
use crate::mc_buf::BitSet;
|
||||
use azalea_core::{game_type::GameType, resource_location::ResourceLocation};
|
||||
use packet_macros::{GamePacket, McBufReadable, McBufWritable};
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundLightUpdatePacket {
|
||||
pub x: i32,
|
||||
pub z: i32,
|
||||
pub light_data: ClientboundLightUpdatePacketData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct ClientboundLightUpdatePacketData {
|
||||
trust_edges: bool,
|
||||
sky_y_mask: BitSet,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use azalea_core::{game_type::GameType, resource_location::ResourceLocation};
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundLoginPacket {
|
||||
pub player_id: u32,
|
||||
pub hardcore: bool,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundMoveEntityPosPacket {
|
||||
#[var]
|
||||
pub entity_id: i32,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundMoveEntityPosRotPacket {
|
||||
#[var]
|
||||
pub entity_id: i32,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundMoveEntityRotPacket {
|
||||
#[var]
|
||||
pub entity_id: i32,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::mc_buf::{McBufReadable, McBufWritable, Readable};
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundPlayerAbilitiesPacket {
|
||||
pub flags: PlayerAbilitiesFlags,
|
||||
pub flying_speed: f32,
|
||||
|
@ -34,16 +34,16 @@ impl McBufWritable for PlayerAbilitiesFlags {
|
|||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let mut byte = 0;
|
||||
if self.invulnerable {
|
||||
byte = byte | 1;
|
||||
byte |= 0b1;
|
||||
}
|
||||
if self.flying {
|
||||
byte = byte | 2;
|
||||
byte |= 0b10;
|
||||
}
|
||||
if self.can_fly {
|
||||
byte = byte | 4;
|
||||
byte |= 0b100;
|
||||
}
|
||||
if self.instant_break {
|
||||
byte = byte | 8;
|
||||
byte |= 0b1000;
|
||||
}
|
||||
u8::write_into(&byte, buf)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable};
|
||||
use azalea_chat::component::Component;
|
||||
use packet_macros::{GamePacket, McBufReadable, McBufWritable};
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
use std::io::{Read, Write};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundPlayerInfoPacket {
|
||||
pub action: Action,
|
||||
}
|
||||
|
@ -18,14 +18,14 @@ pub enum Action {
|
|||
RemovePlayer(Vec<RemovePlayer>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct PlayerProperty {
|
||||
name: String,
|
||||
value: String,
|
||||
signature: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct AddPlayer {
|
||||
uuid: Uuid,
|
||||
name: String,
|
||||
|
@ -37,26 +37,26 @@ pub struct AddPlayer {
|
|||
display_name: Option<Component>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct UpdateGameMode {
|
||||
uuid: Uuid,
|
||||
#[var]
|
||||
gamemode: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct UpdateLatency {
|
||||
uuid: Uuid,
|
||||
#[var]
|
||||
ping: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct UpdateDisplayName {
|
||||
uuid: Uuid,
|
||||
display_name: Option<Component>,
|
||||
}
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct RemovePlayer {
|
||||
uuid: Uuid,
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::mc_buf::{McBufReadable, McBufWritable, Readable};
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundPlayerPositionPacket {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
|
@ -43,19 +43,19 @@ impl McBufWritable for RelativeArguments {
|
|||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let mut byte = 0;
|
||||
if self.x {
|
||||
byte = byte | 0b1;
|
||||
byte |= 0b1;
|
||||
}
|
||||
if self.y {
|
||||
byte = byte | 0b10;
|
||||
byte |= 0b10;
|
||||
}
|
||||
if self.z {
|
||||
byte = byte | 0b100;
|
||||
byte |= 0b100;
|
||||
}
|
||||
if self.y_rot {
|
||||
byte = byte | 0b1000;
|
||||
byte |= 0b1000;
|
||||
}
|
||||
if self.x_rot {
|
||||
byte = byte | 0b10000;
|
||||
byte |= 0b10000;
|
||||
}
|
||||
u8::write_into(&byte, buf)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable};
|
||||
use azalea_core::{resource_location::ResourceLocation, Slot};
|
||||
use packet_macros::{GamePacket, McBufReadable, McBufWritable};
|
||||
use azalea_core::resource_location::ResourceLocation;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundRecipePacket {
|
||||
pub action: State,
|
||||
pub settings: RecipeBookSettings,
|
||||
|
@ -11,7 +11,7 @@ pub struct ClientboundRecipePacket {
|
|||
pub to_highlight: Vec<ResourceLocation>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct RecipeBookSettings {
|
||||
pub gui_open: bool,
|
||||
pub filtering_craftable: bool,
|
||||
|
@ -41,7 +41,7 @@ impl McBufWritable for State {
|
|||
}
|
||||
impl McBufReadable for State {
|
||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||
let state = buf.read_varint()?.try_into().unwrap();
|
||||
let state = buf.read_varint()?;
|
||||
Ok(match state {
|
||||
0 => State::Init,
|
||||
1 => State::Add,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundRemoveEntitiesPacket {
|
||||
#[var]
|
||||
pub entity_ids: Vec<u32>,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundRotateHeadPacket {
|
||||
#[var]
|
||||
pub entity_id: u32,
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
use crate::mc_buf::{McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable};
|
||||
use azalea_core::{ChunkSectionBlockPos, ChunkSectionPos};
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundSectionBlocksUpdatePacket {
|
||||
pub section_pos: ChunkSectionPos,
|
||||
pub suppress_light_updates: bool,
|
||||
pub states: Vec<BlockStateWithPosition>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlockStateWithPosition {
|
||||
pub pos: ChunkSectionBlockPos,
|
||||
pub state: u32,
|
||||
}
|
||||
|
||||
impl McBufReadable for BlockStateWithPosition {
|
||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||
let data = u64::var_read_into(buf)?;
|
||||
let position_part = data & 4095;
|
||||
let state = (data >> 12) as u32;
|
||||
let position = ChunkSectionBlockPos {
|
||||
x: (position_part >> 8 & 15) as u8,
|
||||
y: (position_part >> 0 & 15) as u8,
|
||||
z: (position_part >> 4 & 15) as u8,
|
||||
};
|
||||
Ok(BlockStateWithPosition {
|
||||
pos: position,
|
||||
state: state,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for BlockStateWithPosition {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let data = (self.state as u64) << 12
|
||||
| ((self.pos.x as u64) << 8 | (self.pos.z as u64) << 4 | (self.pos.y as u64));
|
||||
u64::var_write_into(&data, buf)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
/// Sent to change the player's slot selection.
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundSetCarriedItemPacket {
|
||||
pub slot: u8,
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundSetChunkCacheCenterPacket {
|
||||
#[var]
|
||||
pub x: i32,
|
||||
#[var]
|
||||
pub y: i32,
|
||||
pub z: i32,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use azalea_core::BlockPos;
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundSetDefaultSpawnPositionPacket {
|
||||
pub pos: BlockPos,
|
||||
pub angle: f32,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::mc_buf::EntityMetadata;
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundSetEntityDataPacket {
|
||||
#[var]
|
||||
pub id: i32,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundSetEntityLinkPacket {
|
||||
pub source_id: u32,
|
||||
pub dest_id: u32,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundSetExperiencePacket {
|
||||
pub experience_progress: f32,
|
||||
#[var]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundSetHealthPacket {
|
||||
pub health: f32,
|
||||
#[var]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundSetTimePacket {
|
||||
pub game_time: u64,
|
||||
pub day_time: u64,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::{GamePacket, McBufReadable, McBufWritable};
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundSoundPacket {
|
||||
#[var]
|
||||
/// TODO: use the sound registry instead of just being a u32
|
||||
|
@ -13,7 +13,7 @@ pub struct ClientboundSoundPacket {
|
|||
pub pitch: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, Copy, McBuf)]
|
||||
pub enum SoundSource {
|
||||
Master = 0,
|
||||
Music = 1,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundTeleportEntityPacket {
|
||||
#[var]
|
||||
pub id: u32,
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use crate::packets::{McBufReadable, McBufWritable};
|
||||
use azalea_chat::component::Component;
|
||||
use azalea_core::{resource_location::ResourceLocation, Slot};
|
||||
use packet_macros::{GamePacket, McBufReadable, McBufWritable};
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::{Read, Write},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundUpdateAdvancementsPacket {
|
||||
pub reset: bool,
|
||||
pub added: HashMap<ResourceLocation, Advancement>,
|
||||
|
@ -15,7 +15,7 @@ pub struct ClientboundUpdateAdvancementsPacket {
|
|||
pub progress: HashMap<ResourceLocation, AdvancementProgress>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct Advancement {
|
||||
parent_id: Option<ResourceLocation>,
|
||||
display: Option<DisplayInfo>,
|
||||
|
@ -25,7 +25,7 @@ pub struct Advancement {
|
|||
// requirements_strategy: RequirementsStrategy.AND
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct DisplayInfo {
|
||||
pub title: Component,
|
||||
pub description: Component,
|
||||
|
@ -71,7 +71,7 @@ impl McBufWritable for DisplayFlags {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, Copy, McBuf)]
|
||||
pub enum FrameType {
|
||||
Task = 0,
|
||||
Challenge = 1,
|
||||
|
@ -79,12 +79,12 @@ pub enum FrameType {
|
|||
}
|
||||
|
||||
// nothing is written here
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct Criterion {}
|
||||
|
||||
pub type AdvancementProgress = HashMap<ResourceLocation, CriterionProgress>;
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct CriterionProgress {
|
||||
date: Option<u64>,
|
||||
}
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable};
|
||||
use azalea_core::resource_location::ResourceLocation;
|
||||
use packet_macros::{GamePacket, McBufReadable, McBufWritable};
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
use std::io::{Read, Write};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundUpdateAttributesPacket {
|
||||
#[var]
|
||||
pub entity_id: u32,
|
||||
pub attributes: Vec<AttributeSnapshot>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct AttributeSnapshot {
|
||||
pub attribute: ResourceLocation,
|
||||
pub base: f64,
|
||||
pub modifiers: Vec<Modifier>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct Modifier {
|
||||
pub uuid: Uuid,
|
||||
pub amount: f64,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::io::{Read, Write};
|
||||
|
||||
use azalea_core::{resource_location::ResourceLocation, Slot};
|
||||
use packet_macros::{GamePacket, McBufReadable, McBufWritable};
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundUpdateRecipesPacket {
|
||||
pub recipes: Vec<Recipe>,
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ pub struct Recipe {
|
|||
pub data: RecipeData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct ShapelessRecipe {
|
||||
/// Used to group similar recipes together in the recipe book.
|
||||
/// Tag is present in recipe JSON
|
||||
|
@ -68,7 +68,7 @@ impl McBufReadable for ShapedRecipe {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct CookingRecipe {
|
||||
group: String,
|
||||
ingredient: Ingredient,
|
||||
|
@ -77,13 +77,13 @@ pub struct CookingRecipe {
|
|||
#[var]
|
||||
cooking_time: u32,
|
||||
}
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct StoneCuttingRecipe {
|
||||
group: String,
|
||||
ingredient: Ingredient,
|
||||
result: Slot,
|
||||
}
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct SmithingRecipe {
|
||||
base: Ingredient,
|
||||
addition: Ingredient,
|
||||
|
@ -116,13 +116,13 @@ pub enum RecipeData {
|
|||
Smithing(SmithingRecipe),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct Ingredient {
|
||||
pub allowed: Vec<Slot>,
|
||||
}
|
||||
|
||||
impl McBufWritable for Recipe {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable};
|
||||
use azalea_core::resource_location::ResourceLocation;
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::{Read, Write},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundUpdateTagsPacket {
|
||||
pub tags: HashMap<ResourceLocation, Vec<Tags>>,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ClientboundUpdateViewDistancePacket {
|
||||
#[var]
|
||||
pub view_distance: i32,
|
||||
|
|
|
@ -11,10 +11,12 @@ pub mod clientbound_declare_commands_packet;
|
|||
pub mod clientbound_disconnect_packet;
|
||||
pub mod clientbound_entity_event_packet;
|
||||
pub mod clientbound_entity_velocity_packet;
|
||||
pub mod clientbound_game_event_packet;
|
||||
pub mod clientbound_initialize_border_packet;
|
||||
pub mod clientbound_keep_alive_packet;
|
||||
pub mod clientbound_level_chunk_with_light_packet;
|
||||
pub mod clientbound_level_event_packet;
|
||||
pub mod clientbound_level_particles_packet;
|
||||
pub mod clientbound_light_update_packet;
|
||||
pub mod clientbound_login_packet;
|
||||
pub mod clientbound_move_entity_pos_packet;
|
||||
|
@ -26,6 +28,7 @@ pub mod clientbound_player_position_packet;
|
|||
pub mod clientbound_recipe_packet;
|
||||
pub mod clientbound_remove_entities_packet;
|
||||
pub mod clientbound_rotate_head_packet;
|
||||
pub mod clientbound_section_blocks_update_packet;
|
||||
pub mod clientbound_set_carried_item_packet;
|
||||
pub mod clientbound_set_chunk_cache_center;
|
||||
pub mod clientbound_set_default_spawn_position_packet;
|
||||
|
@ -56,19 +59,21 @@ declare_state_packets!(
|
|||
0x00: clientbound_add_entity_packet::ClientboundAddEntityPacket,
|
||||
0x02: clientbound_add_mob_packet::ClientboundAddMobPacket,
|
||||
0x04: clientbound_add_player_packet::ClientboundAddPlayerPacket,
|
||||
0x6: clientbound_animate_packet::ClientboundAnimatePacket,
|
||||
0xc: clientbound_block_update_packet::ClientboundBlockUpdatePacket,
|
||||
0x06: clientbound_animate_packet::ClientboundAnimatePacket,
|
||||
0x0c: clientbound_block_update_packet::ClientboundBlockUpdatePacket,
|
||||
0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket,
|
||||
0xf: clientbound_chat_packet::ClientboundChatPacket,
|
||||
0x0f: clientbound_chat_packet::ClientboundChatPacket,
|
||||
0x12: clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket,
|
||||
0x14: clientbound_container_set_content_packet::ClientboundContainerSetContentPacket,
|
||||
0x1a: clientbound_disconnect_packet::ClientboundDisconnectPacket,
|
||||
0x1b: clientbound_entity_event_packet::ClientboundEntityEventPacket,
|
||||
0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket,
|
||||
0x1e: clientbound_game_event_packet::ClientboundGameEventPacket,
|
||||
0x20: clientbound_initialize_border_packet::ClientboundInitializeBorderPacket,
|
||||
0x21: clientbound_keep_alive_packet::ClientboundKeepAlivePacket,
|
||||
0x22: clientbound_level_chunk_with_light_packet::ClientboundLevelChunkWithLightPacket,
|
||||
0x23: clientbound_level_event_packet::ClientboundLevelEventPacket,
|
||||
0x24: clientbound_level_particles_packet::ClientboundLevelParticlesPacket,
|
||||
0x25: clientbound_light_update_packet::ClientboundLightUpdatePacket,
|
||||
0x26: clientbound_login_packet::ClientboundLoginPacket,
|
||||
0x29: clientbound_move_entity_pos_packet::ClientboundMoveEntityPosPacket,
|
||||
|
@ -80,6 +85,7 @@ declare_state_packets!(
|
|||
0x39: clientbound_recipe_packet::ClientboundRecipePacket,
|
||||
0x3a: clientbound_remove_entities_packet::ClientboundRemoveEntitiesPacket,
|
||||
0x3e: clientbound_rotate_head_packet::ClientboundRotateHeadPacket,
|
||||
0x3f: clientbound_section_blocks_update_packet::ClientboundSectionBlocksUpdatePacket,
|
||||
0x48: clientbound_set_carried_item_packet::ClientboundSetCarriedItemPacket,
|
||||
0x49: clientbound_set_chunk_cache_center::ClientboundSetChunkCacheCenterPacket,
|
||||
0x4a: clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::mc_buf::UnsizedByteArray;
|
||||
use azalea_core::resource_location::ResourceLocation;
|
||||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ServerboundCustomPayloadPacket {
|
||||
pub identifier: ResourceLocation,
|
||||
pub data: UnsizedByteArray,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use packet_macros::GamePacket;
|
||||
use packet_macros::{GamePacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, GamePacket)]
|
||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||
pub struct ServerboundKeepAlivePacket {
|
||||
pub id: u64,
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::packets::ConnectionProtocol;
|
||||
use packet_macros::HandshakePacket;
|
||||
use packet_macros::{HandshakePacket, McBuf};
|
||||
use std::hash::Hash;
|
||||
|
||||
#[derive(Hash, Clone, Debug, HandshakePacket)]
|
||||
#[derive(Hash, Clone, Debug, McBuf, HandshakePacket)]
|
||||
pub struct ClientIntentionPacket {
|
||||
#[var]
|
||||
pub protocol_version: u32,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::mc_buf::UnsizedByteArray;
|
||||
use azalea_core::resource_location::ResourceLocation;
|
||||
use packet_macros::LoginPacket;
|
||||
use packet_macros::{LoginPacket, McBuf};
|
||||
use std::hash::Hash;
|
||||
|
||||
#[derive(Hash, Clone, Debug, LoginPacket)]
|
||||
#[derive(Hash, Clone, Debug, McBuf, LoginPacket)]
|
||||
pub struct ClientboundCustomQueryPacket {
|
||||
#[var]
|
||||
pub transaction_id: u32,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use azalea_chat::component::Component;
|
||||
use packet_macros::LoginPacket;
|
||||
use packet_macros::{LoginPacket, McBuf};
|
||||
|
||||
#[derive(Clone, Debug, LoginPacket)]
|
||||
#[derive(Clone, Debug, McBuf, LoginPacket)]
|
||||
pub struct ClientboundLoginDisconnectPacket {
|
||||
pub reason: Component,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use packet_macros::LoginPacket;
|
||||
use packet_macros::{LoginPacket, McBuf};
|
||||
use std::hash::Hash;
|
||||
|
||||
#[derive(Hash, Clone, Debug, LoginPacket)]
|
||||
#[derive(Hash, Clone, Debug, McBuf, LoginPacket)]
|
||||
pub struct ServerboundHelloPacket {
|
||||
pub username: String,
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use super::LoginPacket;
|
||||
use crate::mc_buf::Writable;
|
||||
use packet_macros::LoginPacket;
|
||||
use packet_macros::{LoginPacket, McBuf};
|
||||
use std::hash::Hash;
|
||||
|
||||
#[derive(Hash, Clone, Debug, LoginPacket)]
|
||||
#[derive(Hash, Clone, Debug, McBuf, LoginPacket)]
|
||||
pub struct ServerboundKeyPacket {
|
||||
pub shared_secret: Vec<u8>,
|
||||
pub nonce: Vec<u8>,
|
||||
|
|
|
@ -43,7 +43,7 @@ where
|
|||
fn write(&self, buf: &mut impl Write) -> Result<(), std::io::Error>;
|
||||
}
|
||||
|
||||
impl McBufReadable for ConnectionProtocol {
|
||||
impl crate::mc_buf::McBufReadable for ConnectionProtocol {
|
||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||
ConnectionProtocol::from_i32(buf.read_varint()?)
|
||||
.ok_or_else(|| "Invalid intention".to_string())
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use packet_macros::StatusPacket;
|
||||
use packet_macros::{McBuf, StatusPacket};
|
||||
|
||||
#[derive(Clone, Debug, StatusPacket)]
|
||||
#[derive(Clone, Debug, McBuf, StatusPacket)]
|
||||
pub struct ServerboundStatusRequestPacket {}
|
||||
|
|
|
@ -2,10 +2,7 @@ use crate::{mc_buf::Writable, packets::ProtocolPacket, read::MAXIMUM_UNCOMPRESSE
|
|||
use async_compression::tokio::bufread::ZlibEncoder;
|
||||
use azalea_crypto::Aes128CfbEnc;
|
||||
use std::fmt::Debug;
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWrite, AsyncWriteExt},
|
||||
net::TcpStream,
|
||||
};
|
||||
use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
|
||||
fn frame_prepender(data: &mut Vec<u8>) -> Result<Vec<u8>, String> {
|
||||
let mut buf = Vec::new();
|
||||
|
|
11
azalea-world/Cargo.toml
Normal file
11
azalea-world/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
edition = "2021"
|
||||
name = "azalea-world"
|
||||
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"}
|
3
azalea-world/README.md
Normal file
3
azalea-world/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Azalea World
|
||||
|
||||
The Minecraft world representation used in Azalea.
|
209
azalea-world/src/bit_storage.rs
Normal file
209
azalea-world/src/bit_storage.rs
Normal file
|
@ -0,0 +1,209 @@
|
|||
use std::{error::Error, fmt};
|
||||
|
||||
// this is from minecraft's code
|
||||
// yeah idk either
|
||||
const MAGIC: [(i32, i32, i32); 64] = [
|
||||
(-1, -1, 0),
|
||||
(-2147483648, 0, 0),
|
||||
(1431655765, 1431655765, 0),
|
||||
(-2147483648, 0, 1),
|
||||
(858993459, 858993459, 0),
|
||||
(715827882, 715827882, 0),
|
||||
(613566756, 613566756, 0),
|
||||
(-2147483648, 0, 2),
|
||||
(477218588, 477218588, 0),
|
||||
(429496729, 429496729, 0),
|
||||
(390451572, 390451572, 0),
|
||||
(357913941, 357913941, 0),
|
||||
(330382099, 330382099, 0),
|
||||
(306783378, 306783378, 0),
|
||||
(286331153, 286331153, 0),
|
||||
(-2147483648, 0, 3),
|
||||
(252645135, 252645135, 0),
|
||||
(238609294, 238609294, 0),
|
||||
(226050910, 226050910, 0),
|
||||
(214748364, 214748364, 0),
|
||||
(204522252, 204522252, 0),
|
||||
(195225786, 195225786, 0),
|
||||
(186737708, 186737708, 0),
|
||||
(178956970, 178956970, 0),
|
||||
(171798691, 171798691, 0),
|
||||
(165191049, 165191049, 0),
|
||||
(159072862, 159072862, 0),
|
||||
(153391689, 153391689, 0),
|
||||
(148102320, 148102320, 0),
|
||||
(143165576, 143165576, 0),
|
||||
(138547332, 138547332, 0),
|
||||
(-2147483648, 0, 4),
|
||||
(130150524, 130150524, 0),
|
||||
(126322567, 126322567, 0),
|
||||
(122713351, 122713351, 0),
|
||||
(119304647, 119304647, 0),
|
||||
(116080197, 116080197, 0),
|
||||
(113025455, 113025455, 0),
|
||||
(110127366, 110127366, 0),
|
||||
(107374182, 107374182, 0),
|
||||
(104755299, 104755299, 0),
|
||||
(102261126, 102261126, 0),
|
||||
(99882960, 99882960, 0),
|
||||
(97612893, 97612893, 0),
|
||||
(95443717, 95443717, 0),
|
||||
(93368854, 93368854, 0),
|
||||
(91382282, 91382282, 0),
|
||||
(89478485, 89478485, 0),
|
||||
(87652393, 87652393, 0),
|
||||
(85899345, 85899345, 0),
|
||||
(84215045, 84215045, 0),
|
||||
(82595524, 82595524, 0),
|
||||
(81037118, 81037118, 0),
|
||||
(79536431, 79536431, 0),
|
||||
(78090314, 78090314, 0),
|
||||
(76695844, 76695844, 0),
|
||||
(75350303, 75350303, 0),
|
||||
(74051160, 74051160, 0),
|
||||
(72796055, 72796055, 0),
|
||||
(71582788, 71582788, 0),
|
||||
(70409299, 70409299, 0),
|
||||
(69273666, 69273666, 0),
|
||||
(68174084, 68174084, 0),
|
||||
(-2147483648, 0, 5),
|
||||
];
|
||||
|
||||
/// A compact list of integers with the given number of bits per entry.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct BitStorage {
|
||||
pub data: Vec<u64>,
|
||||
bits: usize,
|
||||
mask: u64,
|
||||
size: usize,
|
||||
values_per_long: u8,
|
||||
divide_mul: i32,
|
||||
divide_add: i32,
|
||||
divide_shift: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BitStorageError {
|
||||
InvalidLength { got: usize, expected: usize },
|
||||
}
|
||||
impl fmt::Display for BitStorageError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
BitStorageError::InvalidLength { got, expected } => write!(
|
||||
f,
|
||||
"Invalid length given for storage, got: {}, but expected: {}",
|
||||
got, expected
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Error for BitStorageError {}
|
||||
|
||||
impl BitStorage {
|
||||
/// Create a new BitStorage with the given number of bits per entry.
|
||||
/// `size` is the number of entries in the BitStorage.
|
||||
pub fn new(bits: usize, size: usize, data: Option<Vec<u64>>) -> Result<Self, BitStorageError> {
|
||||
// vanilla has this assert but it's not always true for some reason??
|
||||
// assert!(bits >= 1 && bits <= 32);
|
||||
|
||||
if let Some(data) = &data {
|
||||
if data.len() == 0 {
|
||||
// TODO: make 0 bit storage actually work
|
||||
return Ok(BitStorage::default());
|
||||
}
|
||||
}
|
||||
|
||||
let values_per_long = 64 / bits;
|
||||
let magic_index = values_per_long - 1;
|
||||
let (divide_mul, divide_add, divide_shift) = MAGIC[magic_index as usize];
|
||||
let calculated_length = (size + values_per_long - 1) / values_per_long;
|
||||
|
||||
let mask = (1 << bits) - 1;
|
||||
|
||||
let using_data = if let Some(data) = data {
|
||||
if data.len() != calculated_length as usize {
|
||||
return Err(BitStorageError::InvalidLength {
|
||||
got: data.len(),
|
||||
expected: calculated_length as usize,
|
||||
});
|
||||
}
|
||||
data
|
||||
} else {
|
||||
vec![0; calculated_length as usize]
|
||||
};
|
||||
|
||||
Ok(BitStorage {
|
||||
data: using_data,
|
||||
bits,
|
||||
mask,
|
||||
size,
|
||||
values_per_long: values_per_long as u8,
|
||||
divide_mul,
|
||||
divide_add,
|
||||
divide_shift,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cell_index(&self, index: u64) -> usize {
|
||||
// as unsigned wrap
|
||||
let first = self.divide_mul as u32 as u64;
|
||||
let second = self.divide_add as u64;
|
||||
|
||||
(((index * first) + second) >> 32 >> self.divide_shift)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn get(&self, index: usize) -> u64 {
|
||||
// Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)var1);
|
||||
// int var2 = this.cellIndex(var1);
|
||||
// long var3 = this.data[var2];
|
||||
// int var5 = (var1 - var2 * this.valuesPerLong) * this.bits;
|
||||
// return (int)(var3 >> var5 & this.mask);
|
||||
|
||||
assert!(
|
||||
index <= self.size - 1,
|
||||
"Index {} out of bounds (max is {})",
|
||||
index,
|
||||
self.size - 1
|
||||
);
|
||||
let cell_index = self.cell_index(index as u64);
|
||||
let cell = &self.data[cell_index as usize];
|
||||
let bit_index = (index - cell_index * self.values_per_long as usize) * self.bits;
|
||||
cell >> bit_index & self.mask
|
||||
}
|
||||
|
||||
pub fn set(&mut self, index: usize, value: u64) {
|
||||
// Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)var1);
|
||||
// Validate.inclusiveBetween(0L, this.mask, (long)var2);
|
||||
// int var3 = this.cellIndex(var1);
|
||||
// long var4 = this.data[var3];
|
||||
// int var6 = (var1 - var3 * this.valuesPerLong) * this.bits;
|
||||
// this.data[var3] = var4 & ~(this.mask << var6) | ((long)var2 & this.mask) << var6;
|
||||
|
||||
assert!(index <= self.size - 1);
|
||||
assert!(value <= self.mask);
|
||||
let cell_index = self.cell_index(index as u64);
|
||||
let cell = &mut self.data[cell_index as usize];
|
||||
let bit_index = (index - cell_index * self.values_per_long as usize) * self.bits;
|
||||
*cell = *cell & !(self.mask << bit_index) | (value & self.mask) << bit_index;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_wikivg_example() {
|
||||
let data = [
|
||||
1, 2, 2, 3, 4, 4, 5, 6, 6, 4, 8, 0, 7, 4, 3, 13, 15, 16, 9, 14, 10, 12, 0, 2,
|
||||
];
|
||||
let compact_data: [u64; 2] = [0x0020863148418841, 0x01018A7260F68C87];
|
||||
let storage = BitStorage::new(5, data.len(), Some(compact_data.to_vec())).unwrap();
|
||||
|
||||
for (i, expected) in data.iter().enumerate() {
|
||||
assert_eq!(storage.get(i), *expected);
|
||||
}
|
||||
}
|
||||
}
|
237
azalea-world/src/lib.rs
Normal file
237
azalea-world/src/lib.rs
Normal file
|
@ -0,0 +1,237 @@
|
|||
#![feature(int_roundings)]
|
||||
|
||||
mod bit_storage;
|
||||
mod palette;
|
||||
|
||||
use crate::palette::PalettedContainerType;
|
||||
use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos};
|
||||
use azalea_protocol::mc_buf::{McBufReadable, McBufWritable};
|
||||
pub use bit_storage::BitStorage;
|
||||
use palette::PalettedContainer;
|
||||
use std::{
|
||||
io::{Read, Write},
|
||||
ops::{Index, IndexMut},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = 2 + 2;
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
|
||||
const SECTION_HEIGHT: u32 = 16;
|
||||
|
||||
pub struct World {
|
||||
pub storage: ChunkStorage,
|
||||
pub height: u32,
|
||||
pub min_y: i32,
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
let chunk = Arc::new(Mutex::new(Chunk::read_with_world(data, self)?));
|
||||
println!("Loaded chunk {:?}", pos);
|
||||
self.storage[pos] = Some(chunk);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_view_center(&mut self, pos: &ChunkPos) {
|
||||
self.storage.view_center = *pos;
|
||||
}
|
||||
|
||||
pub fn get_block_state(&self, pos: &BlockPos) -> Option<u32> {
|
||||
self.storage.get_block_state(pos, self.min_y)
|
||||
}
|
||||
}
|
||||
impl Index<&ChunkPos> for World {
|
||||
type Output = Option<Arc<Mutex<Chunk>>>;
|
||||
|
||||
fn index(&self, pos: &ChunkPos) -> &Self::Output {
|
||||
&self.storage[pos]
|
||||
}
|
||||
}
|
||||
impl IndexMut<&ChunkPos> for World {
|
||||
fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output {
|
||||
&mut self.storage[pos]
|
||||
}
|
||||
}
|
||||
// impl Index<&BlockPos> for World {
|
||||
// type Output = Option<Arc<Mutex<Chunk>>>;
|
||||
|
||||
// fn index(&self, pos: &BlockPos) -> &Self::Output {
|
||||
// let chunk = &self[ChunkPos::from(pos)];
|
||||
// // chunk.
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub fn get_block_state(&self, pos: &BlockPos, min_y: i32) -> Option<u32> {
|
||||
let chunk_pos = ChunkPos::from(pos);
|
||||
println!("chunk_pos {:?} block_pos {:?}", chunk_pos, pos);
|
||||
let chunk = &self[&chunk_pos];
|
||||
match chunk {
|
||||
Some(chunk) => Some(chunk.lock().unwrap().get(&ChunkBlockPos::from(pos), min_y)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
for _ in 0..section_count {
|
||||
let section = Section::read_into(buf)?;
|
||||
sections.push(section);
|
||||
}
|
||||
Ok(Chunk { sections })
|
||||
}
|
||||
|
||||
pub fn section_index(&self, y: i32, min_y: i32) -> u32 {
|
||||
// TODO: check the build height and stuff, this code will be broken if the min build height is 0
|
||||
// (LevelHeightAccessor.getMinSection in vanilla code)
|
||||
assert!(y >= 0);
|
||||
let min_section_index = min_y.div_floor(16);
|
||||
(y.div_floor(16) - min_section_index) as u32
|
||||
}
|
||||
|
||||
pub fn get(&self, pos: &ChunkBlockPos, min_y: i32) -> u32 {
|
||||
let section_index = self.section_index(pos.y, min_y);
|
||||
// TODO: make sure the section exists
|
||||
let section = &self.sections[section_index as usize];
|
||||
let chunk_section_pos = ChunkSectionBlockPos::from(pos);
|
||||
let block_state = section.get(chunk_section_pos);
|
||||
block_state
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for Chunk {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
for section in &self.sections {
|
||||
section.write_into(buf)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Section {
|
||||
pub block_count: u16,
|
||||
pub states: PalettedContainer,
|
||||
pub biomes: PalettedContainer,
|
||||
}
|
||||
|
||||
impl McBufReadable for Section {
|
||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||
let block_count = u16::read_into(buf)?;
|
||||
// this is commented out because the vanilla server is wrong
|
||||
// assert!(
|
||||
// block_count <= 16 * 16 * 16,
|
||||
// "A section has more blocks than what should be possible. This is a bug!"
|
||||
// );
|
||||
let states = PalettedContainer::read_with_type(buf, &PalettedContainerType::BlockStates)?;
|
||||
let biomes = PalettedContainer::read_with_type(buf, &PalettedContainerType::Biomes)?;
|
||||
Ok(Section {
|
||||
block_count,
|
||||
states,
|
||||
biomes,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for Section {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
self.block_count.write_into(buf)?;
|
||||
self.states.write_into(buf)?;
|
||||
self.biomes.write_into(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Section {
|
||||
// TODO: return a BlockState instead of a u32
|
||||
fn get(&self, pos: ChunkSectionBlockPos) -> u32 {
|
||||
self.states
|
||||
.get(pos.x as usize, pos.y as usize, pos.z as usize)
|
||||
}
|
||||
}
|
139
azalea-world/src/palette.rs
Normal file
139
azalea-world/src/palette.rs
Normal file
|
@ -0,0 +1,139 @@
|
|||
use azalea_protocol::mc_buf::{McBufReadable, McBufVarReadable, McBufWritable, Readable, Writable};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use crate::BitStorage;
|
||||
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
pub enum PalettedContainerType {
|
||||
Biomes,
|
||||
BlockStates,
|
||||
}
|
||||
|
||||
#[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 storage: BitStorage,
|
||||
pub container_type: PalettedContainerType,
|
||||
}
|
||||
|
||||
impl PalettedContainer {
|
||||
pub fn read_with_type(
|
||||
buf: &mut impl Read,
|
||||
type_: &'static PalettedContainerType,
|
||||
) -> Result<Self, String> {
|
||||
let bits_per_entry = buf.read_byte()?;
|
||||
let palette = match type_ {
|
||||
PalettedContainerType::BlockStates => {
|
||||
Palette::block_states_read_with_bits_per_entry(buf, bits_per_entry)?
|
||||
}
|
||||
PalettedContainerType::Biomes => {
|
||||
Palette::biomes_read_with_bits_per_entry(buf, bits_per_entry)?
|
||||
}
|
||||
};
|
||||
let size = match type_ {
|
||||
PalettedContainerType::BlockStates => 4096,
|
||||
PalettedContainerType::Biomes => 64,
|
||||
};
|
||||
|
||||
let data = Vec::<u64>::read_into(buf)?;
|
||||
debug_assert!(
|
||||
bits_per_entry != 0 || data.is_empty(),
|
||||
"Bits per entry is 0 but data is not empty."
|
||||
);
|
||||
let storage = BitStorage::new(bits_per_entry.into(), size, Some(data)).unwrap();
|
||||
|
||||
Ok(PalettedContainer {
|
||||
bits_per_entry,
|
||||
palette,
|
||||
storage,
|
||||
container_type: *type_,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_index(&self, x: usize, y: usize, z: usize) -> usize {
|
||||
let size_bits = match self.container_type {
|
||||
PalettedContainerType::BlockStates => 4,
|
||||
PalettedContainerType::Biomes => 2,
|
||||
};
|
||||
|
||||
(((y << size_bits) | z) << size_bits) | x
|
||||
}
|
||||
|
||||
pub fn get(&self, x: usize, y: usize, z: usize) -> u32 {
|
||||
let paletted_value = self.storage.get(self.get_index(x, y, z));
|
||||
println!("palette: {:?}", self.palette);
|
||||
self.palette.value_for(paletted_value as usize)
|
||||
}
|
||||
}
|
||||
|
||||
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.storage.data.write_into(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Palette {
|
||||
/// ID of the corresponding entry in its global palette
|
||||
SingleValue(u32),
|
||||
Linear(Vec<u32>),
|
||||
Hashmap(Vec<u32>),
|
||||
Global,
|
||||
}
|
||||
|
||||
impl Palette {
|
||||
pub fn block_states_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::var_read_into(buf)?),
|
||||
1..=4 => Palette::Linear(Vec::<u32>::var_read_into(buf)?),
|
||||
5..=8 => Palette::Hashmap(Vec::<u32>::var_read_into(buf)?),
|
||||
_ => Palette::Global,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn biomes_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::var_read_into(buf)?),
|
||||
1..=3 => Palette::Linear(Vec::<u32>::var_read_into(buf)?),
|
||||
_ => Palette::Global,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn value_for(&self, value: usize) -> u32 {
|
||||
match self {
|
||||
Palette::SingleValue(v) => *v,
|
||||
Palette::Linear(v) => v[value],
|
||||
Palette::Hashmap(v) => v[value],
|
||||
Palette::Global => value as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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::Linear(values) => {
|
||||
values.write_into(buf)?;
|
||||
}
|
||||
Palette::Hashmap(values) => {
|
||||
values.write_into(buf)?;
|
||||
}
|
||||
Palette::Global => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -7,5 +7,6 @@ version = "0.1.0"
|
|||
|
||||
[dependencies]
|
||||
azalea-client = {path = "../azalea-client"}
|
||||
azalea-core = {path = "../azalea-core"}
|
||||
azalea-protocol = {path = "../azalea-protocol"}
|
||||
tokio = "^1.14.0"
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use azalea_client::{Account, Event};
|
||||
use azalea_core::BlockPos;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
println!("Hello, world!");
|
||||
|
||||
// let address = "95.111.249.143:10000";
|
||||
let address = "172.23.192.1:61385";
|
||||
let address = "192.168.2.234:50736";
|
||||
// let response = azalea_client::ping::ping_server(&address.try_into().unwrap())
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
@ -17,9 +18,16 @@ async fn main() {
|
|||
|
||||
while let Some(e) = client.next().await {
|
||||
match e {
|
||||
// TODO: have a "loaded" or "ready" event that fires when all chunks are loaded
|
||||
Event::Login => {}
|
||||
Event::Chat(p) => {
|
||||
println!("{}", p.message.to_ansi(None));
|
||||
if p.message.to_ansi(None) == "<py5> ok" {
|
||||
let state = client.state.lock().await;
|
||||
let world = state.world.as_ref().unwrap();
|
||||
let c = world.get_block_state(&BlockPos::new(5, 78, -2)).unwrap();
|
||||
println!("block state: {:?}", c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,8 +84,8 @@ def generate(burger_packets, mappings: Mappings, target_packet_id, target_packet
|
|||
generated_packet_code = []
|
||||
uses = set()
|
||||
generated_packet_code.append(
|
||||
f'#[derive(Clone, Debug, {to_camel_case(state)}Packet)]')
|
||||
uses.add(f'packet_macros::{to_camel_case(state)}Packet')
|
||||
f'#[derive(Clone, Debug, McBuf, {to_camel_case(state)}Packet)]')
|
||||
uses.add(f'packet_macros::{{{to_camel_case(state)}Packet, McBuf}}')
|
||||
|
||||
obfuscated_class_name = packet['class'].split('.')[0].split('$')[0]
|
||||
class_name = mappings.get_class(
|
||||
|
|
Loading…
Add table
Reference in a new issue