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

basic code generator

This commit is contained in:
mat 2022-05-07 16:54:02 -05:00
parent 71acb5b336
commit 9f496a89b5
10 changed files with 666 additions and 436 deletions

5
.gitignore vendored
View file

@ -3,3 +3,8 @@
flamegraph.svg
perf.data
perf.data.old
data-code-generator/Burger
data-code-generator/client.jar
data-code-generator/burger.json
__pycache__

View file

@ -0,0 +1,435 @@
use crate::mc_buf::read::{McBufReadable, Readable};
use crate::mc_buf::write::{McBufWritable, Writable};
use azalea_chat::component::Component;
use azalea_core::{BlockPos, Direction, Slot};
use packet_macros::{McBufReadable, McBufWritable};
use std::io::{Read, Write};
use std::ops::Deref;
use uuid::Uuid;
/// A Vec<u8> that isn't prefixed by a VarInt with the size.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UnsizedByteArray(Vec<u8>);
impl Deref for UnsizedByteArray {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Vec<u8>> for UnsizedByteArray {
fn from(vec: Vec<u8>) -> Self {
Self(vec)
}
}
impl From<&str> for UnsizedByteArray {
fn from(s: &str) -> Self {
Self(s.as_bytes().to_vec())
}
}
/// Represents Java's BitSet, a list of bits.
#[derive(Debug, Clone, PartialEq, Eq, Hash, McBufReadable, McBufWritable)]
pub struct BitSet {
data: Vec<u64>,
}
// the Index trait requires us to return a reference, but we can't do that
impl BitSet {
pub fn index(&self, index: usize) -> bool {
(self.data[index / 64] & (1u64 << (index % 64))) != 0
}
}
pub type EntityMetadata = Vec<EntityDataItem>;
#[derive(Clone, Debug)]
pub struct EntityDataItem {
// we can't identify what the index is for here because we don't know the
// entity type
pub index: u8,
pub value: EntityDataValue,
}
impl McBufReadable for Vec<EntityDataItem> {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let mut metadata = Vec::new();
loop {
let index = buf.read_byte()?;
if index == 0xff {
break;
}
let value = EntityDataValue::read_into(buf)?;
metadata.push(EntityDataItem { index, value });
}
Ok(metadata)
}
}
impl McBufWritable for Vec<EntityDataItem> {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
for item in self {
buf.write_byte(item.index)?;
item.value.write_into(buf)?;
}
buf.write_byte(0xff)?;
Ok(())
}
}
#[derive(Clone, Debug)]
pub enum EntityDataValue {
Byte(u8),
// varint
Int(i32),
Float(f32),
String(String),
Component(Component),
OptionalComponent(Option<Component>),
ItemStack(Slot),
Boolean(bool),
Rotations { x: f32, y: f32, z: f32 },
BlockPos(BlockPos),
OptionalBlockPos(Option<BlockPos>),
Direction(Direction),
OptionalUuid(Option<Uuid>),
// 0 for absent (implies air); otherwise, a block state ID as per the global palette
// this is a varint
OptionalBlockState(Option<i32>),
CompoundTag(azalea_nbt::Tag),
Particle(Particle),
VillagerData(VillagerData),
// 0 for absent; 1 + actual value otherwise. Used for entity IDs.
OptionalUnsignedInt(Option<u32>),
Pose(Pose),
}
impl McBufReadable for EntityDataValue {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let type_ = buf.read_varint()?;
Ok(match type_ {
0 => EntityDataValue::Byte(buf.read_byte()?),
1 => EntityDataValue::Int(buf.read_varint()?),
2 => EntityDataValue::Float(buf.read_float()?),
3 => EntityDataValue::String(buf.read_utf()?),
4 => EntityDataValue::Component(Component::read_into(buf)?),
5 => EntityDataValue::OptionalComponent(Option::<Component>::read_into(buf)?),
6 => EntityDataValue::ItemStack(Slot::read_into(buf)?),
7 => EntityDataValue::Boolean(buf.read_boolean()?),
8 => EntityDataValue::Rotations {
x: buf.read_float()?,
y: buf.read_float()?,
z: buf.read_float()?,
},
9 => EntityDataValue::BlockPos(BlockPos::read_into(buf)?),
10 => EntityDataValue::OptionalBlockPos(Option::<BlockPos>::read_into(buf)?),
11 => EntityDataValue::Direction(Direction::read_into(buf)?),
12 => EntityDataValue::OptionalUuid(Option::<Uuid>::read_into(buf)?),
13 => EntityDataValue::OptionalBlockState({
let val = i32::read_into(buf)?;
if val == 0 {
None
} else {
Some(val)
}
}),
14 => EntityDataValue::CompoundTag(azalea_nbt::Tag::read_into(buf)?),
15 => EntityDataValue::Particle(Particle::read_into(buf)?),
16 => EntityDataValue::VillagerData(VillagerData::read_into(buf)?),
17 => EntityDataValue::OptionalUnsignedInt({
let val = buf.read_varint()?;
if val == 0 {
None
} else {
Some((val - 1) as u32)
}
}),
18 => EntityDataValue::Pose(Pose::read_into(buf)?),
_ => return Err(format!("Unknown entity data type: {}", type_)),
})
}
}
impl McBufWritable for EntityDataValue {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
todo!();
}
}
#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)]
pub enum Pose {
Standing = 0,
FallFlying = 1,
Sleeping = 2,
Swimming = 3,
SpinAttack = 4,
Sneaking = 5,
LongJumping = 6,
Dying = 7,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct VillagerData {
#[var]
type_: u32,
#[var]
profession: u32,
#[var]
level: u32,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct Particle {
#[var]
pub id: i32,
pub data: ParticleData,
}
#[derive(Clone, Debug)]
pub enum ParticleData {
AmbientEntityEffect,
AngryVillager,
Block(BlockParticle),
BlockMarker(BlockParticle),
Bubble,
Cloud,
Crit,
DamageIndicator,
DragonBreath,
DrippingLava,
FallingLava,
LandingLava,
DrippingWater,
FallingWater,
Dust(DustParticle),
DustColorTransition(DustColorTransitionParticle),
Effect,
ElderGuardian,
EnchantedHit,
Enchant,
EndRod,
EntityEffect,
ExplosionEmitter,
Explosion,
FallingDust(BlockParticle),
Firework,
Fishing,
Flame,
SoulFireFlame,
Soul,
Flash,
HappyVillager,
Composter,
Heart,
InstantEffect,
Item(ItemParticle),
Vibration(VibrationParticle),
ItemSlime,
ItemSnowball,
LargeSmoke,
Lava,
Mycelium,
Note,
Poof,
Portal,
Rain,
Smoke,
Sneeze,
Spit,
SquidInk,
SweepAttack,
TotemOfUndying,
Underwater,
Splash,
Witch,
BubblePop,
CurrentDown,
BubbleColumnUp,
Nautilus,
Dolphin,
CampfireCozySmoke,
CampfireSignalSmoke,
DrippingHoney,
FallingHoney,
LandingHoney,
FallingNectar,
FallingSporeBlossom,
Ash,
CrimsonSpore,
WarpedSpore,
SporeBlossomAir,
DrippingObsidianTear,
FallingObsidianTear,
LandingObsidianTear,
ReversePortal,
WhiteAsh,
SmallFlame,
Snowflake,
DrippingDripstoneLava,
FallingDripstoneLava,
DrippingDripstoneWater,
FallingDripstoneWater,
GlowSquidInk,
Glow,
WaxOn,
WaxOff,
ElectricSpark,
Scrape,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct BlockParticle {
#[var]
pub block_state: i32,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct DustParticle {
/// Red value, 0-1
pub red: f32,
/// Green value, 0-1
pub green: f32,
/// Blue value, 0-1
pub blue: f32,
/// The scale, will be clamped between 0.01 and 4.
pub scale: f32,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct DustColorTransitionParticle {
/// Red value, 0-1
pub from_red: f32,
/// Green value, 0-1
pub from_green: f32,
/// Blue value, 0-1
pub from_blue: f32,
/// The scale, will be clamped between 0.01 and 4.
pub scale: f32,
/// Red value, 0-1
pub to_red: f32,
/// Green value, 0-1
pub to_green: f32,
/// Blue value, 0-1
pub to_blue: f32,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct ItemParticle {
pub item: Slot,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct VibrationParticle {
pub origin: BlockPos,
pub position_type: String,
pub block_position: BlockPos,
#[var]
pub entity_id: u32,
#[var]
pub ticks: u32,
}
impl McBufReadable for ParticleData {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let id = buf.read_varint()?;
Ok(match id {
0 => ParticleData::AmbientEntityEffect,
1 => ParticleData::AngryVillager,
2 => ParticleData::Block(BlockParticle::read_into(buf)?),
3 => ParticleData::BlockMarker(BlockParticle::read_into(buf)?),
4 => ParticleData::Bubble,
5 => ParticleData::Cloud,
6 => ParticleData::Crit,
7 => ParticleData::DamageIndicator,
8 => ParticleData::DragonBreath,
9 => ParticleData::DrippingLava,
10 => ParticleData::FallingLava,
11 => ParticleData::LandingLava,
12 => ParticleData::DrippingWater,
13 => ParticleData::FallingWater,
14 => ParticleData::Dust(DustParticle::read_into(buf)?),
15 => ParticleData::DustColorTransition(DustColorTransitionParticle::read_into(buf)?),
16 => ParticleData::Effect,
17 => ParticleData::ElderGuardian,
18 => ParticleData::EnchantedHit,
19 => ParticleData::Enchant,
20 => ParticleData::EndRod,
21 => ParticleData::EntityEffect,
22 => ParticleData::ExplosionEmitter,
23 => ParticleData::Explosion,
24 => ParticleData::FallingDust(BlockParticle::read_into(buf)?),
25 => ParticleData::Firework,
26 => ParticleData::Fishing,
27 => ParticleData::Flame,
28 => ParticleData::SoulFireFlame,
29 => ParticleData::Soul,
30 => ParticleData::Flash,
31 => ParticleData::HappyVillager,
32 => ParticleData::Composter,
33 => ParticleData::Heart,
34 => ParticleData::InstantEffect,
35 => ParticleData::Item(ItemParticle::read_into(buf)?),
36 => ParticleData::Vibration(VibrationParticle::read_into(buf)?),
37 => ParticleData::ItemSlime,
38 => ParticleData::ItemSnowball,
39 => ParticleData::LargeSmoke,
40 => ParticleData::Lava,
41 => ParticleData::Mycelium,
42 => ParticleData::Note,
43 => ParticleData::Poof,
44 => ParticleData::Portal,
45 => ParticleData::Rain,
46 => ParticleData::Smoke,
47 => ParticleData::Sneeze,
48 => ParticleData::Spit,
49 => ParticleData::SquidInk,
50 => ParticleData::SweepAttack,
51 => ParticleData::TotemOfUndying,
52 => ParticleData::Underwater,
53 => ParticleData::Splash,
54 => ParticleData::Witch,
55 => ParticleData::BubblePop,
56 => ParticleData::CurrentDown,
57 => ParticleData::BubbleColumnUp,
58 => ParticleData::Nautilus,
59 => ParticleData::Dolphin,
60 => ParticleData::CampfireCozySmoke,
61 => ParticleData::CampfireSignalSmoke,
62 => ParticleData::DrippingHoney,
63 => ParticleData::FallingHoney,
64 => ParticleData::LandingHoney,
65 => ParticleData::FallingNectar,
66 => ParticleData::FallingSporeBlossom,
67 => ParticleData::Ash,
68 => ParticleData::CrimsonSpore,
69 => ParticleData::WarpedSpore,
70 => ParticleData::SporeBlossomAir,
71 => ParticleData::DrippingObsidianTear,
72 => ParticleData::FallingObsidianTear,
73 => ParticleData::LandingObsidianTear,
74 => ParticleData::ReversePortal,
75 => ParticleData::WhiteAsh,
76 => ParticleData::SmallFlame,
77 => ParticleData::Snowflake,
78 => ParticleData::DrippingDripstoneLava,
79 => ParticleData::FallingDripstoneLava,
80 => ParticleData::DrippingDripstoneWater,
81 => ParticleData::FallingDripstoneWater,
82 => ParticleData::GlowSquidInk,
83 => ParticleData::Glow,
84 => ParticleData::WaxOn,
85 => ParticleData::WaxOff,
86 => ParticleData::ElectricSpark,
87 => ParticleData::Scrape,
_ => return Err(format!("Unknown particle id: {}", id)),
})
}
}
impl McBufWritable for ParticleData {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
todo!()
}
}

View file

@ -1,8 +1,10 @@
//! Utilities for reading and writing for the Minecraft protocol
mod definitions;
mod read;
mod write;
pub use definitions::{BitSet, EntityMetadata, UnsizedByteArray};
use packet_macros::{McBufReadable, McBufWritable};
pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable};
use std::ops::Deref;
@ -14,43 +16,6 @@ const MAX_STRING_LENGTH: u16 = 32767;
// TODO: have a definitions.rs in mc_buf that contains UnsizedByteArray and BitSet
/// A Vec<u8> that isn't prefixed by a VarInt with the size.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UnsizedByteArray(Vec<u8>);
impl Deref for UnsizedByteArray {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Vec<u8>> for UnsizedByteArray {
fn from(vec: Vec<u8>) -> Self {
Self(vec)
}
}
impl From<&str> for UnsizedByteArray {
fn from(s: &str) -> Self {
Self(s.as_bytes().to_vec())
}
}
/// Represents Java's BitSet, a list of bits.
#[derive(Debug, Clone, PartialEq, Eq, Hash, McBufReadable, McBufWritable)]
pub struct BitSet {
data: Vec<u64>,
}
// the Index trait requires us to return a reference, but we can't do that
impl BitSet {
pub fn index(&self, index: usize) -> bool {
(self.data[index / 64] & (1u64 << (index % 64))) != 0
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -279,7 +279,7 @@ impl McBufVarReadable for u64 {
impl McBufReadable for UnsizedByteArray {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
Ok(UnsizedByteArray(buf.read_bytes()?))
Ok(buf.read_bytes()?.into())
}
}

View file

@ -1,404 +1,9 @@
use crate::{
mc_buf::{Readable, Writable},
packets::{McBufReadable, McBufWritable},
};
use azalea_chat::component::Component;
use azalea_core::{BlockPos, Direction, Slot};
use packet_macros::{GamePacket, McBufReadable, McBufWritable};
use std::io::{Read, Write};
use uuid::Uuid;
use crate::mc_buf::EntityMetadata;
use packet_macros::GamePacket;
#[derive(Clone, Debug, GamePacket)]
pub struct ClientboundSetEntityDataPacket {
#[var]
pub id: i32,
pub metadata: Vec<EntityDataItem>,
}
#[derive(Clone, Debug)]
pub struct EntityDataItem {
// we can't identify what the index is for here because we don't know the
// entity type
pub index: u8,
pub value: EntityDataValue,
}
impl McBufReadable for Vec<EntityDataItem> {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let mut metadata = Vec::new();
loop {
let index = buf.read_byte()?;
if index == 0xff {
break;
}
let value = EntityDataValue::read_into(buf)?;
metadata.push(EntityDataItem { index, value });
}
Ok(metadata)
}
}
impl McBufWritable for Vec<EntityDataItem> {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
for item in self {
buf.write_byte(item.index)?;
item.value.write_into(buf)?;
}
buf.write_byte(0xff)?;
Ok(())
}
}
#[derive(Clone, Debug)]
pub enum EntityDataValue {
Byte(u8),
// varint
Int(i32),
Float(f32),
String(String),
Component(Component),
OptionalComponent(Option<Component>),
ItemStack(Slot),
Boolean(bool),
Rotations { x: f32, y: f32, z: f32 },
BlockPos(BlockPos),
OptionalBlockPos(Option<BlockPos>),
Direction(Direction),
OptionalUuid(Option<Uuid>),
// 0 for absent (implies air); otherwise, a block state ID as per the global palette
// this is a varint
OptionalBlockState(Option<i32>),
CompoundTag(azalea_nbt::Tag),
Particle(Particle),
VillagerData(VillagerData),
// 0 for absent; 1 + actual value otherwise. Used for entity IDs.
OptionalUnsignedInt(Option<u32>),
Pose(Pose),
}
impl McBufReadable for EntityDataValue {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let type_ = buf.read_varint()?;
Ok(match type_ {
0 => EntityDataValue::Byte(buf.read_byte()?),
1 => EntityDataValue::Int(buf.read_varint()?),
2 => EntityDataValue::Float(buf.read_float()?),
3 => EntityDataValue::String(buf.read_utf()?),
4 => EntityDataValue::Component(Component::read_into(buf)?),
5 => EntityDataValue::OptionalComponent(Option::<Component>::read_into(buf)?),
6 => EntityDataValue::ItemStack(Slot::read_into(buf)?),
7 => EntityDataValue::Boolean(buf.read_boolean()?),
8 => EntityDataValue::Rotations {
x: buf.read_float()?,
y: buf.read_float()?,
z: buf.read_float()?,
},
9 => EntityDataValue::BlockPos(BlockPos::read_into(buf)?),
10 => EntityDataValue::OptionalBlockPos(Option::<BlockPos>::read_into(buf)?),
11 => EntityDataValue::Direction(Direction::read_into(buf)?),
12 => EntityDataValue::OptionalUuid(Option::<Uuid>::read_into(buf)?),
13 => EntityDataValue::OptionalBlockState({
let val = i32::read_into(buf)?;
if val == 0 {
None
} else {
Some(val)
}
}),
14 => EntityDataValue::CompoundTag(azalea_nbt::Tag::read_into(buf)?),
15 => EntityDataValue::Particle(Particle::read_into(buf)?),
16 => EntityDataValue::VillagerData(VillagerData::read_into(buf)?),
17 => EntityDataValue::OptionalUnsignedInt({
let val = buf.read_varint()?;
if val == 0 {
None
} else {
Some((val - 1) as u32)
}
}),
18 => EntityDataValue::Pose(Pose::read_into(buf)?),
_ => return Err(format!("Unknown entity data type: {}", type_)),
})
}
}
impl McBufWritable for EntityDataValue {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
todo!();
}
}
#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)]
pub enum Pose {
Standing = 0,
FallFlying = 1,
Sleeping = 2,
Swimming = 3,
SpinAttack = 4,
Sneaking = 5,
LongJumping = 6,
Dying = 7,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct VillagerData {
#[var]
type_: u32,
#[var]
profession: u32,
#[var]
level: u32,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct Particle {
#[var]
pub id: i32,
pub data: ParticleData,
}
#[derive(Clone, Debug)]
pub enum ParticleData {
AmbientEntityEffect,
AngryVillager,
Block(BlockParticle),
BlockMarker(BlockParticle),
Bubble,
Cloud,
Crit,
DamageIndicator,
DragonBreath,
DrippingLava,
FallingLava,
LandingLava,
DrippingWater,
FallingWater,
Dust(DustParticle),
DustColorTransition(DustColorTransitionParticle),
Effect,
ElderGuardian,
EnchantedHit,
Enchant,
EndRod,
EntityEffect,
ExplosionEmitter,
Explosion,
FallingDust(BlockParticle),
Firework,
Fishing,
Flame,
SoulFireFlame,
Soul,
Flash,
HappyVillager,
Composter,
Heart,
InstantEffect,
Item(ItemParticle),
Vibration(VibrationParticle),
ItemSlime,
ItemSnowball,
LargeSmoke,
Lava,
Mycelium,
Note,
Poof,
Portal,
Rain,
Smoke,
Sneeze,
Spit,
SquidInk,
SweepAttack,
TotemOfUndying,
Underwater,
Splash,
Witch,
BubblePop,
CurrentDown,
BubbleColumnUp,
Nautilus,
Dolphin,
CampfireCozySmoke,
CampfireSignalSmoke,
DrippingHoney,
FallingHoney,
LandingHoney,
FallingNectar,
FallingSporeBlossom,
Ash,
CrimsonSpore,
WarpedSpore,
SporeBlossomAir,
DrippingObsidianTear,
FallingObsidianTear,
LandingObsidianTear,
ReversePortal,
WhiteAsh,
SmallFlame,
Snowflake,
DrippingDripstoneLava,
FallingDripstoneLava,
DrippingDripstoneWater,
FallingDripstoneWater,
GlowSquidInk,
Glow,
WaxOn,
WaxOff,
ElectricSpark,
Scrape,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct BlockParticle {
#[var]
pub block_state: i32,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct DustParticle {
/// Red value, 0-1
pub red: f32,
/// Green value, 0-1
pub green: f32,
/// Blue value, 0-1
pub blue: f32,
/// The scale, will be clamped between 0.01 and 4.
pub scale: f32,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct DustColorTransitionParticle {
/// Red value, 0-1
pub from_red: f32,
/// Green value, 0-1
pub from_green: f32,
/// Blue value, 0-1
pub from_blue: f32,
/// The scale, will be clamped between 0.01 and 4.
pub scale: f32,
/// Red value, 0-1
pub to_red: f32,
/// Green value, 0-1
pub to_green: f32,
/// Blue value, 0-1
pub to_blue: f32,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct ItemParticle {
pub item: Slot,
}
#[derive(Debug, Clone, McBufReadable, McBufWritable)]
pub struct VibrationParticle {
pub origin: BlockPos,
pub position_type: String,
pub block_position: BlockPos,
#[var]
pub entity_id: u32,
#[var]
pub ticks: u32,
}
impl McBufReadable for ParticleData {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let id = buf.read_varint()?;
Ok(match id {
0 => ParticleData::AmbientEntityEffect,
1 => ParticleData::AngryVillager,
2 => ParticleData::Block(BlockParticle::read_into(buf)?),
3 => ParticleData::BlockMarker(BlockParticle::read_into(buf)?),
4 => ParticleData::Bubble,
5 => ParticleData::Cloud,
6 => ParticleData::Crit,
7 => ParticleData::DamageIndicator,
8 => ParticleData::DragonBreath,
9 => ParticleData::DrippingLava,
10 => ParticleData::FallingLava,
11 => ParticleData::LandingLava,
12 => ParticleData::DrippingWater,
13 => ParticleData::FallingWater,
14 => ParticleData::Dust(DustParticle::read_into(buf)?),
15 => ParticleData::DustColorTransition(DustColorTransitionParticle::read_into(buf)?),
16 => ParticleData::Effect,
17 => ParticleData::ElderGuardian,
18 => ParticleData::EnchantedHit,
19 => ParticleData::Enchant,
20 => ParticleData::EndRod,
21 => ParticleData::EntityEffect,
22 => ParticleData::ExplosionEmitter,
23 => ParticleData::Explosion,
24 => ParticleData::FallingDust(BlockParticle::read_into(buf)?),
25 => ParticleData::Firework,
26 => ParticleData::Fishing,
27 => ParticleData::Flame,
28 => ParticleData::SoulFireFlame,
29 => ParticleData::Soul,
30 => ParticleData::Flash,
31 => ParticleData::HappyVillager,
32 => ParticleData::Composter,
33 => ParticleData::Heart,
34 => ParticleData::InstantEffect,
35 => ParticleData::Item(ItemParticle::read_into(buf)?),
36 => ParticleData::Vibration(VibrationParticle::read_into(buf)?),
37 => ParticleData::ItemSlime,
38 => ParticleData::ItemSnowball,
39 => ParticleData::LargeSmoke,
40 => ParticleData::Lava,
41 => ParticleData::Mycelium,
42 => ParticleData::Note,
43 => ParticleData::Poof,
44 => ParticleData::Portal,
45 => ParticleData::Rain,
46 => ParticleData::Smoke,
47 => ParticleData::Sneeze,
48 => ParticleData::Spit,
49 => ParticleData::SquidInk,
50 => ParticleData::SweepAttack,
51 => ParticleData::TotemOfUndying,
52 => ParticleData::Underwater,
53 => ParticleData::Splash,
54 => ParticleData::Witch,
55 => ParticleData::BubblePop,
56 => ParticleData::CurrentDown,
57 => ParticleData::BubbleColumnUp,
58 => ParticleData::Nautilus,
59 => ParticleData::Dolphin,
60 => ParticleData::CampfireCozySmoke,
61 => ParticleData::CampfireSignalSmoke,
62 => ParticleData::DrippingHoney,
63 => ParticleData::FallingHoney,
64 => ParticleData::LandingHoney,
65 => ParticleData::FallingNectar,
66 => ParticleData::FallingSporeBlossom,
67 => ParticleData::Ash,
68 => ParticleData::CrimsonSpore,
69 => ParticleData::WarpedSpore,
70 => ParticleData::SporeBlossomAir,
71 => ParticleData::DrippingObsidianTear,
72 => ParticleData::FallingObsidianTear,
73 => ParticleData::LandingObsidianTear,
74 => ParticleData::ReversePortal,
75 => ParticleData::WhiteAsh,
76 => ParticleData::SmallFlame,
77 => ParticleData::Snowflake,
78 => ParticleData::DrippingDripstoneLava,
79 => ParticleData::FallingDripstoneLava,
80 => ParticleData::DrippingDripstoneWater,
81 => ParticleData::FallingDripstoneWater,
82 => ParticleData::GlowSquidInk,
83 => ParticleData::Glow,
84 => ParticleData::WaxOn,
85 => ParticleData::WaxOff,
86 => ParticleData::ElectricSpark,
87 => ParticleData::Scrape,
_ => return Err(format!("Unknown particle id: {}", id)),
})
}
}
impl McBufWritable for ParticleData {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
todo!()
}
pub metadata: EntityMetadata,
}

View file

@ -0,0 +1,4 @@
Generate code for reading/writing packets from [Burger](https://github.com/pokechu22/Burger). The only dependency is `requests`.
The directory name doesn't start with `azalea-` because it's not a Rust crate.

View file

@ -0,0 +1,41 @@
from mappings import Mappings
import packetcodegen
import requests
import json
import os
# enable this if you already have the burger.json and don't want to wait
SKIP_BURGER = True
print(
f'\033[92mFinding Minecraft version...\033[m')
version_manifest_data = requests.get(
'https://launchermeta.mojang.com/mc/game/version_manifest.json').json()
minecraft_version = version_manifest_data['latest']['snapshot']
print(
f'\033[92mUsing \033[1m{minecraft_version}..\033[m')
package_url = next(
filter(lambda v: v['id'] == minecraft_version, version_manifest_data['versions']))['url']
package_data = requests.get(package_url).json()
client_jar_url = package_data['downloads']['client']['url']
if not SKIP_BURGER:
print('\033[92mDownloading Burger...\033[m')
r = os.system('git clone https://github.com/pokechu22/Burger')
os.system('git pull')
print('\033[92mDownloading client jar...\033[m')
with open('client.jar', 'wb') as f:
f.write(requests.get(client_jar_url).content)
print(f'\033[92mExtracting data with Burger...\033[m')
os.system('cd Burger && python munch.py ../client.jar --output ../burger.json')
client_mappings_url = package_data['downloads']['client_mappings']['url']
mappings = Mappings.parse(requests.get(client_mappings_url).text)
with open('burger.json', 'r') as f:
burger_data = json.load(f)
burger_packets_data = burger_data[0]['packets']['packet']
packetcodegen.generate(burger_packets_data, mappings)

View file

@ -0,0 +1,60 @@
class Mappings:
__slots__ = ('classes', 'fields', 'methods')
def __init__(self, classes, fields, methods):
self.classes = classes
self.fields = fields
self.methods = methods
@staticmethod
def parse(mappings_txt):
classes = {}
fields = {}
methods = {}
current_obfuscated_class_name = None
for line in mappings_txt.splitlines():
if line.startswith('#') or line == '':
continue
if line.startswith(' '):
# if a line starts with 4 spaces, that means it's a method or a field
if '(' in line:
# if it has an opening parenthesis, it's a method
real_name_with_parameters_and_line, obfuscated_name = line.strip().split(' -> ')
real_name_with_parameters = real_name_with_parameters_and_line.split(
':')[-1]
real_name = real_name_with_parameters.split('(')[0]
parameters = real_name_with_parameters.split('(')[1]
if current_obfuscated_class_name not in methods:
methods[current_obfuscated_class_name] = {}
methods[current_obfuscated_class_name][
f'{obfuscated_name}({parameters})'] = real_name
else:
# otherwise, it's a field
real_name_with_type, obfuscated_name = line.strip().split(' -> ')
real_name = real_name_with_type.split(' ')[1]
if current_obfuscated_class_name not in fields:
fields[current_obfuscated_class_name] = {}
fields[current_obfuscated_class_name][obfuscated_name] = real_name
else:
# otherwise it's a class
real_name, obfuscated_name = line.strip(':').split(' -> ')
current_obfuscated_class_name = obfuscated_name
classes[obfuscated_name] = real_name
return Mappings(classes, fields, methods)
def get_field(self, obfuscated_class_name, obfuscated_field_name):
return self.fields.get(obfuscated_class_name, {}).get(obfuscated_field_name)
def get_class(self, obfuscated_class_name):
return self.classes[obfuscated_class_name]
def get_method(self, obfuscated_class_name, obfuscated_method_name, obfuscated_signature):
return self.methods[obfuscated_class_name][f'{obfuscated_method_name}({obfuscated_signature})']

View file

@ -0,0 +1,100 @@
from utils import to_snake_case, to_camel_case
from mappings import Mappings
def burger_type_to_rust_type(burger_type):
is_var = False
uses = set()
if burger_type == 'byte':
field_type_rs = 'i8'
elif burger_type == 'short':
field_type_rs = 'i16'
elif burger_type == 'int':
field_type_rs = 'i32'
elif burger_type == 'long':
field_type_rs = 'i64'
elif burger_type == 'float':
field_type_rs = 'f32'
elif burger_type == 'double':
field_type_rs = 'f64'
elif burger_type == 'varint':
is_var = True
field_type_rs = 'i32'
elif burger_type == 'varlong':
is_var = True
field_type_rs = 'i64'
elif burger_type == 'boolean':
field_type_rs = 'bool'
elif burger_type == 'string':
field_type_rs = 'String'
elif burger_type == 'chatcomponent':
field_type_rs = 'Component'
elif burger_type == 'identifier':
field_type_rs = 'ResourceLocation'
elif burger_type == 'uuid':
field_type_rs = 'Uuid'
elif burger_type == 'position':
field_type_rs = 'BlockPos'
elif burger_type == 'nbtcompound':
field_type_rs = 'azalea_nbt::Tag'
elif burger_type == 'itemstack':
field_type_rs = 'Slot'
elif burger_type == 'metadata':
field_type_rs = 'EntityMetadata'
elif burger_type == 'enum':
# enums are too complicated, leave those to the user
field_type_rs = 'todo!()'
elif burger_type.endswith('[]'):
field_type_rs, is_var, uses = burger_type_to_rust_type(
burger_type[:-2])
field_type_rs = f'Vec<{field_type_rs}>'
else:
print('Unknown field type:', burger_type)
exit()
return field_type_rs, is_var, uses
def generate(burger_packets, mappings: Mappings):
for packet in burger_packets.values():
direction = packet['direction'].lower() # serverbound or clientbound
state = packet['state'].lower()
generated_packet_code = []
generated_packet_code.append(
f'#[derive(Clone, Debug, {to_camel_case(state)}Packet)]')
obfuscated_class_name = packet['class'].split('.')[0]
class_name = mappings.get_class(obfuscated_class_name).split('.')[-1]
generated_packet_code.append(
f'pub struct {to_camel_case(class_name)} {{')
for instruction in packet.get('instructions', []):
if instruction['operation'] == 'write':
obfuscated_field_name = instruction['field']
if '.' in obfuscated_field_name or ' ' in obfuscated_field_name or '(' in obfuscated_field_name:
continue
field_name = mappings.get_field(
obfuscated_class_name, obfuscated_field_name)
if not field_name:
generated_packet_code.append(f'// TODO: {instruction}')
continue
field_type = instruction['type']
field_type_rs, is_var, uses = burger_type_to_rust_type(
field_type)
if is_var:
generated_packet_code.append('#[var]')
generated_packet_code.append(
f'pub {to_snake_case(field_name)}: {field_type_rs},')
else:
generated_packet_code.append(f'// TODO: {instruction}')
continue
generated_packet_code.append('}')
print(generated_packet_code)
print()

View file

@ -0,0 +1,15 @@
import urllib.request
import gzip
import json
import re
import io
def to_snake_case(name):
s = re.sub('([A-Z])', r'_\1', name)
return s.lower()
def to_camel_case(name):
s = re.sub('_([a-z])', lambda m: m.group(1).upper(), name)
return s[0].upper() + s[1:]