├── .github └── workflows │ ├── rust.yml │ └── todo.yml ├── .gitignore ├── Cargo.toml ├── minecraft-entities-derive ├── Cargo.toml ├── examples │ └── main.rs └── src │ └── lib.rs ├── minecraft-positions ├── Cargo.toml └── src │ ├── lib.rs │ └── shards.rs ├── minecraft-protocol-derive ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── minecraft-protocol ├── Cargo.toml ├── README.md ├── build │ ├── blocks.rs │ ├── build.rs │ ├── entities.rs │ ├── items.rs │ └── recipes.rs ├── examples │ └── proxy.rs ├── src │ ├── components │ │ ├── advancements.rs │ │ ├── animations.rs │ │ ├── auto_completion.rs │ │ ├── biomes.rs │ │ ├── blocks.rs │ │ ├── boss_bar.rs │ │ ├── chat.rs │ │ ├── chunk.rs │ │ ├── command_block.rs │ │ ├── difficulty.rs │ │ ├── effect.rs │ │ ├── entity.rs │ │ ├── game_state.rs │ │ ├── gamemode.rs │ │ ├── mod.rs │ │ ├── paintings.rs │ │ ├── particle.rs │ │ ├── players.rs │ │ ├── recipes.rs │ │ ├── resource_pack.rs │ │ ├── slots.rs │ │ ├── sound.rs │ │ ├── tags.rs │ │ ├── teams.rs │ │ └── trades.rs │ ├── ids │ │ ├── .gitignore │ │ └── mod.rs │ ├── lib.rs │ ├── nbt │ │ ├── arrays.rs │ │ ├── compound.rs │ │ ├── mod.rs │ │ ├── numbers.rs │ │ ├── serializer.rs │ │ └── test_data │ │ │ ├── bigtest.nbt │ │ │ ├── hello_world.nbt │ │ │ ├── level.dat │ │ │ └── servers.dat │ ├── network.rs │ └── packets │ │ ├── config.rs │ │ ├── handshake.rs │ │ ├── login.rs │ │ ├── mod.rs │ │ ├── play_clientbound.rs │ │ ├── play_serverbound.rs │ │ ├── serializer.rs │ │ └── status.rs └── tests │ ├── auto_play_clientbound_000.rs │ ├── auto_play_clientbound_001.rs │ ├── auto_play_clientbound_002.rs │ ├── auto_play_clientbound_00A.rs │ ├── auto_play_clientbound_00B.rs │ ├── auto_play_clientbound_00C.rs │ ├── auto_play_clientbound_00D.rs │ ├── auto_play_clientbound_011.rs │ ├── auto_play_clientbound_013.rs │ ├── auto_play_clientbound_01D.rs │ ├── auto_play_clientbound_01E.rs │ ├── auto_play_clientbound_020.rs │ ├── auto_play_clientbound_023.rs │ ├── auto_play_clientbound_026.rs │ ├── auto_play_clientbound_028.rs │ ├── auto_play_clientbound_029.rs │ ├── auto_play_clientbound_02C.rs │ ├── auto_play_clientbound_02D.rs │ ├── auto_play_clientbound_02E.rs │ ├── auto_play_clientbound_036.rs │ ├── auto_play_clientbound_03C.rs │ ├── auto_play_clientbound_03E.rs │ ├── auto_play_clientbound_03F.rs │ ├── auto_play_clientbound_040.rs │ ├── auto_play_clientbound_044.rs │ ├── auto_play_clientbound_045.rs │ ├── auto_play_clientbound_047.rs │ ├── auto_play_clientbound_04F.rs │ ├── auto_play_clientbound_050.rs │ ├── auto_play_clientbound_052.rs │ ├── auto_play_clientbound_054.rs │ ├── auto_play_clientbound_056.rs │ ├── auto_play_clientbound_057.rs │ ├── auto_play_clientbound_058.rs │ ├── auto_play_clientbound_059.rs │ ├── auto_play_clientbound_060.rs │ ├── auto_play_clientbound_067.rs │ ├── auto_play_clientbound_06B.rs │ ├── auto_play_clientbound_06C.rs │ ├── auto_play_clientbound_06D.rs │ ├── auto_play_clientbound_06F.rs │ ├── auto_play_serverbound_000.rs │ ├── auto_play_serverbound_007.rs │ ├── auto_play_serverbound_016.rs │ ├── auto_play_serverbound_017.rs │ ├── auto_play_serverbound_018.rs │ ├── auto_play_serverbound_02B.rs │ ├── chunk2.dump │ └── chunk2.dump.tags └── minecraft-server ├── Cargo.toml └── src ├── entities ├── animals │ ├── axolotl.rs │ ├── bee.rs │ ├── cat.rs │ ├── chicken.rs │ ├── cow.rs │ ├── fox.rs │ ├── frog.rs │ ├── goat.rs │ ├── hoglin.rs │ ├── horses.rs │ ├── mod.rs │ ├── ocelot.rs │ ├── panda.rs │ ├── parrot.rs │ ├── pig.rs │ ├── polar_bear.rs │ ├── rabbit.rs │ ├── sheep.rs │ ├── sniffer.rs │ ├── strider.rs │ ├── turtle.rs │ ├── water_animal.rs │ └── wolf.rs ├── arrow.rs ├── block.rs ├── boat.rs ├── display.rs ├── entity.rs ├── fire_entities.rs ├── interaction.rs ├── item.rs ├── living_entity.rs ├── mobs │ ├── bat.rs │ ├── ender_dragon.rs │ ├── flying.rs │ ├── golems.rs │ ├── mod.rs │ ├── slime.rs │ └── villagers.rs ├── mod.rs ├── monsters │ ├── blaze.rs │ ├── creeper.rs │ ├── enderman.rs │ ├── endermite.rs │ ├── giant.rs │ ├── guardian.rs │ ├── mod.rs │ ├── piglin.rs │ ├── raider.rs │ ├── silverfish.rs │ ├── skeleton.rs │ ├── spider.rs │ ├── vex.rs │ ├── warden.rs │ ├── wither.rs │ ├── zoglin.rs │ └── zombies.rs ├── particles.rs ├── player.rs ├── shulker.rs ├── tasks │ ├── mod.rs │ └── newton.rs └── thrown_item_projectile.rs ├── main.rs ├── player_handler ├── connect.rs ├── handshake.rs ├── login.rs ├── mod.rs ├── network.rs └── status.rs ├── prelude.rs ├── raw ├── registry_codec.mc_packet └── status_response.json ├── server_behavior.rs └── world ├── change.rs ├── collisions.rs ├── ecs.rs ├── loading_manager.rs ├── map.rs └── mod.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ '**' ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | Tests: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/cache@v3 18 | with: 19 | path: | 20 | ~/.cargo/bin/ 21 | ~/.cargo/registry/index/ 22 | ~/.cargo/registry/cache/ 23 | ~/.cargo/git/db/ 24 | target/ 25 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/rust.yml') }} 26 | - name: Build 27 | run: cargo build 28 | - name: Run tests 29 | run: cargo test 30 | -------------------------------------------------------------------------------- /.github/workflows/todo.yml: -------------------------------------------------------------------------------- 1 | name: "Run TODO to Issue" 2 | on: ["push"] 3 | jobs: 4 | build: 5 | runs-on: "ubuntu-latest" 6 | steps: 7 | - uses: "actions/checkout@v3" 8 | - name: "TODO to Issue" 9 | uses: "alstr/todo-to-issue-action@v4" 10 | with: 11 | IDENTIFIERS: '[{"name": "TODO", "labels": []}, {"name": "FIXME", "labels": ["bug"]}]' 12 | ISSUE_TEMPLATE: "{{ title }}\n\n{{ body }}\n\n{{ url }}\n\n" 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | logs 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "minecraft-protocol", 4 | "minecraft-protocol-derive", 5 | "minecraft-server", 6 | "minecraft-entities-derive", 7 | "minecraft-positions" 8 | ] 9 | 10 | workspace.resolver = "2" 11 | -------------------------------------------------------------------------------- /minecraft-entities-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minecraft-entities-derive" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | proc-macro = true 8 | 9 | [dependencies] 10 | convert_case = "0.6" 11 | proc-macro-error = "1.0.4" 12 | quote = "1.0" 13 | proc-macro2 = "1.0" 14 | -------------------------------------------------------------------------------- /minecraft-entities-derive/examples/main.rs: -------------------------------------------------------------------------------- 1 | use minecraft_entities_derive::*; 2 | 3 | use std::{pin::Pin, future::Future, sync::{Mutex, Arc}}; 4 | type CallBack = fn(O) -> Pin + Sync + Send>>; 5 | type CallBack1 = fn(O, I) -> Pin + Sync + Send>>; 6 | type CallBack2 = fn(O, I, J) -> Pin + Sync + Send>>; 7 | type Eid = u32; 8 | 9 | trait TryAsEntityRef { 10 | fn try_as_entity_ref(&self) -> Option<&T>; 11 | fn try_as_entity_mut(&mut self) -> Option<&mut T>; 12 | } 13 | 14 | enum AnyEntity { 15 | Entity(Entity), 16 | Animal(Animal), 17 | Cow(Cow), 18 | } 19 | 20 | pub struct Handler { 21 | uuid: Eid, 22 | world: Arc>, 23 | entity: std::marker::PhantomData, 24 | } 25 | 26 | impl Handler { 27 | fn assume(uuid: Eid, world: Arc>) -> Self { 28 | Self { 29 | uuid, 30 | world, 31 | entity: std::marker::PhantomData, 32 | } 33 | } 34 | 35 | fn assume_other(self) -> Handler { 36 | Handler { 37 | uuid: self.uuid, 38 | world: self.world, 39 | entity: std::marker::PhantomData, 40 | } 41 | } 42 | } 43 | 44 | // Entity 45 | 46 | #[MinecraftEntity( 47 | inheritable, 48 | ancestors { }, 49 | descendants { Animal... }, 50 | defines { 51 | on_moved(self, from: f32, to: f32); 52 | on_spawned(self); 53 | } 54 | )] 55 | pub struct Entity { 56 | 57 | } 58 | 59 | impl Handler { 60 | async fn on_moved(self, from: f32, to: f32) { 61 | println!("Entity moved from {} to {}", from, to); 62 | } 63 | 64 | async fn on_spawned(self) { 65 | println!("Entity spawned"); 66 | } 67 | } 68 | 69 | // Animal 70 | 71 | #[MinecraftEntity( 72 | inheritable, 73 | ancestors { Entity }, 74 | descendants { Cow }, 75 | defines { 76 | Entity.on_spawned(self); 77 | on_hit(self, damage: usize); 78 | on_jump(self); 79 | } 80 | )] 81 | pub struct Animal { 82 | entity: Entity, 83 | } 84 | 85 | impl Handler { 86 | async fn on_hit(self, damage: usize) { 87 | println!("Animal hit with {} damage", damage); 88 | } 89 | 90 | async fn on_jump(self) { 91 | println!("Animal jumped"); 92 | } 93 | 94 | async fn on_spawned(self) { 95 | println!("Animal spawned"); 96 | } 97 | } 98 | 99 | // Cow 100 | 101 | #[MinecraftEntity( 102 | ancestors { Animal, Entity }, 103 | defines { 104 | Entity.on_spawned(self); 105 | Animal.on_hit(self, damage: usize); 106 | on_milked(self); 107 | } 108 | )] 109 | pub struct Cow { 110 | animal: Animal, 111 | } 112 | 113 | impl Handler { 114 | async fn on_milked(self) { 115 | println!("Cow milked"); 116 | } 117 | 118 | async fn on_hit(self, damage: usize) { 119 | println!("Cow hit with {} damage", damage); 120 | } 121 | 122 | async fn on_spawned(self) { 123 | println!("Cow spawned"); 124 | } 125 | } 126 | 127 | fn main() { 128 | } 129 | 130 | #[test] 131 | fn test() { 132 | 133 | } 134 | -------------------------------------------------------------------------------- /minecraft-positions/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minecraft-positions" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | minecraft-protocol = { path = "../minecraft-protocol" } 8 | -------------------------------------------------------------------------------- /minecraft-positions/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod shards; 2 | 3 | pub use minecraft_protocol::packets::Position as NetworkPosition; 4 | 5 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] 6 | pub struct BlockPosition { 7 | pub x: i32, 8 | pub y: i32, 9 | pub z: i32, 10 | } 11 | 12 | impl BlockPosition { 13 | pub fn chunk(&self) -> ChunkPosition { 14 | ChunkPosition { 15 | cx: self.x.div_euclid(16), 16 | cy: self.y.div_euclid(16), 17 | cz: self.z.div_euclid(16), 18 | } 19 | } 20 | 21 | pub fn in_chunk(&self) -> BlockPositionInChunk { 22 | BlockPositionInChunk { 23 | bx: self.x.rem_euclid(16) as u8, 24 | by: self.y.rem_euclid(16) as u8, 25 | bz: self.z.rem_euclid(16) as u8, 26 | } 27 | } 28 | 29 | pub fn in_chunk_column(&self) -> BlockPositionInChunkColumn { 30 | BlockPositionInChunkColumn { 31 | bx: self.x.rem_euclid(16) as u8, 32 | y: self.y, 33 | bz: self.z.rem_euclid(16) as u8, 34 | } 35 | } 36 | 37 | pub fn chunk_column(&self) -> ChunkColumnPosition { 38 | ChunkColumnPosition { 39 | cx: self.x.div_euclid(16), 40 | cz: self.z.div_euclid(16), 41 | } 42 | } 43 | } 44 | 45 | impl From for NetworkPosition { 46 | fn from(value: BlockPosition) -> Self { 47 | NetworkPosition { 48 | x: value.x, 49 | y: value.y as i16, 50 | z: value.z, 51 | } 52 | } 53 | } 54 | 55 | impl From for BlockPosition { 56 | fn from(value: NetworkPosition) -> Self { 57 | BlockPosition { 58 | x: value.x, 59 | y: value.y as i32, 60 | z: value.z, 61 | } 62 | } 63 | } 64 | 65 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 66 | pub struct BlockPositionInChunk { 67 | pub bx: u8, 68 | pub by: u8, 69 | pub bz: u8, 70 | } 71 | 72 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 73 | pub struct BlockPositionInChunkColumn { 74 | pub bx: u8, 75 | pub y: i32, 76 | pub bz: u8, 77 | } 78 | 79 | impl BlockPositionInChunkColumn { 80 | pub fn in_chunk(&self) -> BlockPositionInChunk { 81 | BlockPositionInChunk { 82 | bx: self.bx, 83 | by: self.y.rem_euclid(16) as u8, 84 | bz: self.bz, 85 | } 86 | } 87 | 88 | pub fn cy(&self) -> i32 { 89 | self.y.div_euclid(16) 90 | } 91 | } 92 | 93 | #[derive(Debug, Clone, PartialEq)] 94 | pub struct Position { 95 | pub x: f64, 96 | pub y: f64, 97 | pub z: f64, 98 | } 99 | 100 | impl Position { 101 | pub fn chunk(&self) -> ChunkPosition { 102 | ChunkPosition { 103 | cx: (self.x.floor() as i32).div_euclid(16), 104 | cy: (self.y.floor() as i32).div_euclid(16), 105 | cz: (self.z.floor() as i32).div_euclid(16), 106 | } 107 | } 108 | 109 | pub fn chunk_column(&self) -> ChunkColumnPosition { 110 | ChunkColumnPosition { 111 | cx: (self.x.floor() as i32).div_euclid(16), 112 | cz: (self.z.floor() as i32).div_euclid(16), 113 | } 114 | } 115 | } 116 | 117 | impl std::ops::Add for Position { 118 | type Output = Position; 119 | 120 | fn add(self, rhs: Position) -> Self::Output { 121 | Position { 122 | x: self.x + rhs.x, 123 | y: self.y + rhs.y, 124 | z: self.z + rhs.z, 125 | } 126 | } 127 | } 128 | 129 | impl std::ops::AddAssign for Position { 130 | fn add_assign(&mut self, rhs: Position) { 131 | self.x += rhs.x; 132 | self.y += rhs.y; 133 | self.z += rhs.z; 134 | } 135 | } 136 | 137 | #[derive(Clone, Default)] 138 | pub struct Rotation { 139 | pub x: f32, 140 | pub y: f32, 141 | pub z: f32, 142 | } 143 | 144 | #[derive(PartialEq, Eq, Hash, Clone)] 145 | pub struct ChunkPosition { 146 | pub cx: i32, 147 | pub cy: i32, 148 | pub cz: i32, 149 | } 150 | 151 | impl ChunkPosition { 152 | pub fn chunk_column(&self) -> ChunkColumnPosition { 153 | ChunkColumnPosition { 154 | cx: self.cx, 155 | cz: self.cz, 156 | } 157 | } 158 | } 159 | 160 | impl std::ops::Add for ChunkPosition { 161 | type Output = BlockPosition; 162 | 163 | fn add(self, rhs: BlockPositionInChunk) -> Self::Output { 164 | BlockPosition { 165 | x: self.cx * 16 + rhs.bx as i32, 166 | y: self.cy * 16 + rhs.by as i32, 167 | z: self.cz * 16 + rhs.bz as i32, 168 | } 169 | } 170 | } 171 | 172 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 173 | pub struct ChunkColumnPosition { 174 | pub cx: i32, 175 | pub cz: i32, 176 | } 177 | 178 | impl ChunkColumnPosition { 179 | pub fn chunk(&self, cy: i32) -> ChunkPosition { 180 | ChunkPosition { 181 | cx: self.cx, 182 | cy, 183 | cz: self.cz, 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /minecraft-positions/src/shards.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | impl ChunkColumnPosition { 4 | pub fn shard(&self, shard_count: usize) -> usize { 5 | (self.cx + self.cz).unsigned_abs() as usize % shard_count 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /minecraft-protocol-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minecraft-protocol-derive" 3 | version = "0.1.0" 4 | authors = ["Mubelotix "] 5 | edition = "2018" 6 | 7 | [lib] 8 | proc-macro = true 9 | 10 | [dependencies] 11 | syn = {version="1.0", features=["extra-traits", "full"]} 12 | quote = "1.0" 13 | -------------------------------------------------------------------------------- /minecraft-protocol-derive/README.md: -------------------------------------------------------------------------------- 1 | # minecraft-protocol-derive 2 | 3 | Procedural macros to make your structs compatible with the Minecraft protocol. 4 | 5 | This crate aims to make the development of Minecraft protocol libraries easier. 6 | There is already [a complete Minecraft protocol implementation](https://github.com/Mubelotix/minecraft-protocol) using this crate, but you could also [make your own](https://wiki.vg/Main_Page). 7 | 8 | ## Usage 9 | 10 | This crate requires you to declare a `MinecraftPacketPart` trait (see [tests](https://github.com/Mubelotix/minecraft-protocol-derive/tree/main/tests) for examples). 11 | The name of the derive macros is the same and can be used to implement the trait automatically. 12 | It can still be implemented manually for complex types. 13 | 14 | ```rust 15 | #[derive(MinecraftPacketPart)] 16 | struct Entity { 17 | entity_id: VarInt, 18 | x: f64, 19 | y: f64, 20 | z: f64, 21 | } 22 | ``` 23 | 24 | You can also nest your different structures. 25 | 26 | ```rust 27 | #[derive(MinecraftPacketPart)] 28 | struct Entity { 29 | entity_id: VarInt, 30 | position: Position, 31 | } 32 | 33 | #[derive(MinecraftPacketPart)] 34 | struct Position { 35 | x: f64, 36 | y: f64, 37 | z: f64, 38 | } 39 | ``` 40 | 41 | Note that you need to implement the `MinecraftPacketPart` trait for all the primitive types. 42 | The `VarInt` type is a common signed integer type. 43 | See [the wiki](https://wiki.vg/Protocol#VarInt_and_VarLong) for help with the implementation. 44 | 45 | ## Enums 46 | 47 | Enums are supported as long as you use named fields. 48 | 49 | ```rust 50 | #[derive(MinecraftPacketPart)] 51 | enum Packet { 52 | Login { 53 | username: String, 54 | }, 55 | MoveTo { 56 | x: f64, 57 | y: f64, 58 | z: f64, 59 | } 60 | Ping, 61 | } 62 | ``` 63 | 64 | For enums exclusively composed of unit variants, using `#[minecraft_enum]` is recommended since its implementation is more lightweight and optimized. 65 | 66 | Since numeric IDs represent enums in the protocol, each variant has an associated value (its corresponding ID). 67 | It can be specified explicitly or inferred. 68 | A variant with no specified ID will take the ID of the previous variant, incremented by 1. 69 | The first variant matches 0 if no ID is specified. 70 | 71 | You can specify the type of the numeric ID in the macro parameters. 72 | If missing, `VarInt` will be used. 73 | 74 | ```rust 75 | #[minecraft_enum(u8)] 76 | enum EntityType { 77 | Player, // = 0 (inferred) 78 | Villager, 79 | Animal = 3, // Specify the corresponding ID like this. 80 | Monster, // = 4 (inferred, 3+1) 81 | Minecart, 82 | } 83 | ``` 84 | 85 | It is also possible to set the type and value of the numeric IDs with the derive syntax. 86 | 87 | The same inference rules are applied. 88 | 89 | 90 | ```rust 91 | #[derive(MinecraftPacketPart)] 92 | #[discriminant(u8)] // define the type of the numeric ID of the variants 93 | enum Packet { 94 | Login { 95 | username: String, 96 | }, 97 | #[value = 5] // define the ID corresponding to the MoveTo variant 98 | MoveTo { 99 | x: f64, 100 | y: f64, 101 | z: f64, 102 | } 103 | Ping, 104 | } 105 | ``` 106 | 107 | ## Lifetimes 108 | 109 | Lifetimes are supported everywhere. 110 | The only limitation is that you cannot have more than one lifetime. 111 | 112 | It is easy to implement zero-copy deserialization by using references. 113 | 114 | ```rust 115 | #[derive(MinecraftPacketPart)] 116 | struct Player<'a> { 117 | username: &'a str, 118 | data: &'a [u8], 119 | } 120 | ``` 121 | -------------------------------------------------------------------------------- /minecraft-protocol/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minecraft-protocol" 3 | version = "0.1.0" 4 | authors = ["Mubelotix "] 5 | edition = "2018" 6 | build = "build/build.rs" 7 | 8 | [dependencies] 9 | minecraft-protocol-derive = {path="../minecraft-protocol-derive"} 10 | 11 | [build-dependencies] 12 | minreq = {version="2.3", features=["https"]} 13 | serde_json = "1.0" 14 | serde = {version="1.0", features=["derive"]} 15 | convert_case = "0.6" 16 | 17 | [features] 18 | all-packets = [] 19 | -------------------------------------------------------------------------------- /minecraft-protocol/README.md: -------------------------------------------------------------------------------- 1 | # minecraft-protocol 2 | 3 | A library crate empowering everyone to build Minecraft clients and servers. 4 | 5 | This crate is capable of handling the parsing and creation of Minecraft packets. 6 | It contains a complete implementation of the latest Minecraft protocol, including [the NBT data format](https://wiki.vg/NBT). 7 | This crate also embeds [exhaustive lists](https://github.com/PrismarineJS/minecraft-data) of blocks and entities, along with their properties. 8 | 9 | The work relies on and contributes to a [community reverse engineering effort](https://wiki.vg). 10 | I have tested the whole crate on real-world Minecraft servers and successfully made a miner bot. 11 | 12 | ## Usage 13 | 14 | This crate is low-level since you will have to manage the network and learn about [how the protocol works](https://wiki.vg). 15 | There are WIP helper functions for reading and writing packets over a `TcpStream`. 16 | There is currently no support for encryption and compression. 17 | 18 | You can serialize and deserialize any struct of this library like this: 19 | 20 | ```rust 21 | // Serialize 22 | let packet = ClientboundPacket::UpdateHealth { 23 | health: 20.0, 24 | food: VarInt(10), 25 | food_saturation: 2.0, 26 | }; 27 | let serialized_packet: Vec = packet.serialize_minecraft_packet().unwrap(); 28 | 29 | // Deserialize 30 | let raw_packet = [68, 160, 129, 2, 0, 0, 0, 2, 5, 0, 6, 18, 0, 4, 7, 0, 15, 13, 10, 14, 0, 0, 12, 1, 0, 13, 10, 0, 8, 2, 66, 32, 0, 0, 9, 1, 0, 11, 1, 0, 10, 7, 0, 1, 1, 172, 2, 3, 7, 0, 7, 0, 0, 5, 7, 0, 16, 7, 0, 17, 7, 0, 255]; 31 | let parsed_packet = ClientboundPacket::deserialize_minecraft_packet(&raw_packet).unwrap(); 32 | ``` 33 | 34 | ## Internal design 35 | 36 | This crate uses procedural macros to generate most of the parsing of composed structs. 37 | These macros are defined in [this crate](https://github.com/Mubelotix/minecraft-protocol-derive). Note that this is **the only dependency**. 38 | 39 | As you can see, specifying new types is child's play: 40 | 41 | ```rust 42 | #[derive(MinecraftPacketPart)] 43 | struct Entity { 44 | entity_id: VarInt, 45 | x: f64, 46 | y: f64, 47 | z: f64, 48 | } 49 | ``` 50 | 51 | This library consists of a bunch of structs (like this one) nested in each other. 52 | See [Mubelotix/minecraft-protocol-derive](https://github.com/Mubelotix/minecraft-protocol-derive) for more information. 53 | 54 | ## State the Minecraft + Rust ecosystem 55 | 56 | There are many library crates for Minecraft, but they are often incomplete and outdated. 57 | This crate aims to be complete and easy to maintain. Thanks to the use of macros and build scripts, there is little parsing to implement by hand. 58 | 59 | However, there is currently no high-level Minecraft client or server library. 60 | It would be amazing to have a batteries-included Minecraft client framework. 61 | It could handle packets, environment state, and provide easy-to-use functions. 62 | That would allow client and bot creators to focus on the logic. 63 | Minecraft isn't that complex, but there is a surprisingly large amount of content to understand in the protocol. 64 | Despite the quality of [the wiki](https://wiki.vg), it may still be a pain point for beginners. 65 | Thus, feel free to pursue the efforts on Minecraft in Rust as it will be highly appreciated. 66 | -------------------------------------------------------------------------------- /minecraft-protocol/build/entities.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Debug, Serialize, Deserialize)] 4 | #[serde(rename_all = "camelCase")] 5 | struct Entity { 6 | id: u32, 7 | #[serde(rename = "name")] 8 | text_id: String, 9 | display_name: String, 10 | width: f32, 11 | height: f32, 12 | #[serde(rename = "type")] 13 | category: String, 14 | } 15 | 16 | pub fn generate_entity_enum(data: serde_json::Value) { 17 | let mut entities: Vec = serde_json::from_value(data).expect("Invalid entity data"); 18 | entities.sort_by_key(|entity| entity.id); 19 | 20 | // Look for missing items in the array 21 | let mut expected = 0; 22 | for entity in &entities { 23 | if entity.id != expected { 24 | panic!("The entity with id {} is missing.", expected) 25 | } 26 | expected += 1; 27 | } 28 | 29 | // Generate the categories array 30 | let mut categories = String::new(); 31 | categories.push('['); 32 | for entity in &entities { 33 | let variant_name = match entity.category.as_str() { 34 | "other" => "Other", 35 | "living" => "Living", 36 | "projectile" => "Projectile", 37 | "animal" => "Animal", 38 | "ambient" => "Ambient", 39 | "hostile" => "Hostile", 40 | "water_creature" => "WaterCreature", 41 | "mob" => "Mob", 42 | "passive" => "Passive", 43 | "player" => "Player", 44 | unknown_category => panic!("Unknown entity category {}", unknown_category), 45 | }; 46 | categories.push_str("EntityCategory::"); 47 | categories.push_str(variant_name); 48 | categories.push_str(", "); 49 | } 50 | categories.push(']'); 51 | 52 | // Generate the variants of the Item enum 53 | let mut variants = String::new(); 54 | for entity in &entities { 55 | let name = entity 56 | .text_id 57 | .from_case(Case::Snake) 58 | .to_case(Case::UpperCamel); 59 | variants.push_str(&format!("\t{} = {},\n", name, entity.id)); 60 | } 61 | 62 | // Generate the code 63 | let code = format!( 64 | r#"use crate::*; 65 | 66 | /// See [implementations](#implementations) for useful methods. 67 | #[repr(u32)] 68 | #[derive(Debug, Clone, Copy, PartialEq)] 69 | pub enum Entity {{ 70 | {variants} 71 | }} 72 | 73 | #[derive(Debug, Clone, Copy, PartialEq)] 74 | pub enum EntityCategory {{ 75 | Other, 76 | Living, 77 | Projectile, 78 | Animal, 79 | Ambient, 80 | Hostile, 81 | WaterCreature, 82 | Mob, 83 | Passive, 84 | Player, 85 | }} 86 | 87 | impl Entity {{ 88 | #[inline] 89 | pub fn from_id(id: u32) -> Option {{ 90 | if id < {max_value} {{ 91 | Some(unsafe{{std::mem::transmute(id)}}) 92 | }} else {{ 93 | None 94 | }} 95 | }} 96 | 97 | #[inline] 98 | pub fn text_id(self) -> &'static str {{ 99 | unsafe {{*TEXT_IDS.get_unchecked((self as u32) as usize)}} 100 | }} 101 | 102 | #[inline] 103 | pub fn display_name(self) -> &'static str {{ 104 | unsafe {{*DISPLAY_NAMES.get_unchecked((self as u32) as usize)}} 105 | }} 106 | 107 | #[inline] 108 | pub fn category(self) -> EntityCategory {{ 109 | unsafe {{*CATEGORIES.get_unchecked((self as u32) as usize)}} 110 | }} 111 | 112 | #[inline] 113 | pub fn height(self) -> f32 {{ 114 | unsafe {{*HEIGHTS.get_unchecked((self as u32) as usize)}} 115 | }} 116 | 117 | #[inline] 118 | pub fn width(self) -> f32 {{ 119 | unsafe {{*WIDTHS.get_unchecked((self as u32) as usize)}} 120 | }} 121 | }} 122 | 123 | impl<'a> MinecraftPacketPart<'a> for Entity {{ 124 | #[inline] 125 | fn serialize_minecraft_packet_part(self, output: &mut Vec) -> Result<(), &'static str> {{ 126 | VarInt((self as u32) as i32).serialize_minecraft_packet_part(output) 127 | }} 128 | 129 | #[inline] 130 | fn deserialize_minecraft_packet_part(input: &'a[u8]) -> Result<(Self, &'a[u8]), &'static str> {{ 131 | let (id, input) = VarInt::deserialize_minecraft_packet_part(input)?; 132 | let id = std::cmp::max(id.0, 0) as u32; 133 | let entity = Entity::from_id(id).ok_or("No entity corresponding to the specified numeric ID.")?; 134 | Ok((entity, input)) 135 | }} 136 | }} 137 | 138 | const HEIGHTS: [f32; {max_value}] = {heights:?}; 139 | 140 | const WIDTHS: [f32; {max_value}] = {widths:?}; 141 | 142 | const DISPLAY_NAMES: [&str; {max_value}] = {display_names:?}; 143 | 144 | const TEXT_IDS: [&str; {max_value}] = {text_ids:?}; 145 | 146 | const CATEGORIES: [EntityCategory; {max_value}] = {categories}; 147 | "#, 148 | variants = variants, 149 | max_value = expected, 150 | heights = entities.iter().map(|e| e.height).collect::>(), 151 | widths = entities.iter().map(|e| e.width).collect::>(), 152 | display_names = entities.iter().map(|e| &e.display_name).collect::>(), 153 | text_ids = entities.iter().map(|e| &e.text_id).collect::>(), 154 | categories = categories, 155 | ); 156 | 157 | File::create("src/ids/entities.rs") 158 | .unwrap() 159 | .write_all(code.as_bytes()) 160 | .unwrap() 161 | } 162 | -------------------------------------------------------------------------------- /minecraft-protocol/build/items.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Debug, Serialize, Deserialize)] 4 | #[serde(rename_all = "camelCase")] 5 | pub struct Item { 6 | pub id: u32, 7 | display_name: String, 8 | #[serde(rename = "name")] 9 | pub text_id: String, 10 | stack_size: u8, 11 | max_durability: Option, 12 | } 13 | 14 | #[allow(clippy::explicit_counter_loop)] 15 | pub fn generate_item_enum(data: serde_json::Value) -> Vec { 16 | let mut items: Vec = serde_json::from_value(data).expect("Invalid block data"); 17 | items.sort_by_key(|item| item.id); 18 | 19 | // Patch the missing Air 20 | if items.first().map(|i| i.id) != Some(0) { 21 | items.insert( 22 | 0, 23 | Item { 24 | id: 0, 25 | display_name: String::from("Air"), 26 | text_id: String::from("air"), 27 | stack_size: 64, 28 | max_durability: None, 29 | }, 30 | ); 31 | } 32 | 33 | // Look for missing items in the array 34 | let mut expected = 0; 35 | for item in &items { 36 | if item.id != expected { 37 | panic!("The item with id {} is missing.", expected) 38 | } 39 | expected += 1; 40 | } 41 | 42 | // Generate the variants of the Item enum 43 | let mut variants = String::new(); 44 | for item in &items { 45 | let name = item 46 | .text_id 47 | .from_case(Case::Snake) 48 | .to_case(Case::UpperCamel); 49 | variants.push_str(&format!("\t{} = {},\n", name, item.id)); 50 | } 51 | 52 | // Generate the code 53 | let code = format!( 54 | r#"use crate::*; 55 | 56 | /// See [implementations](#implementations) for useful methods. 57 | #[repr(u32)] 58 | #[derive(Debug, Clone, Copy, PartialEq)] 59 | pub enum Item {{ 60 | {variants} 61 | }} 62 | 63 | impl Item {{ 64 | #[inline] 65 | pub fn from_id(id: u32) -> Option {{ 66 | if id < {max_value} {{ 67 | Some(unsafe{{std::mem::transmute(id)}}) 68 | }} else {{ 69 | None 70 | }} 71 | }} 72 | 73 | #[inline] 74 | pub fn text_id(self) -> &'static str {{ 75 | unsafe {{*TEXT_IDS.get_unchecked((self as u32) as usize)}} 76 | }} 77 | 78 | #[inline] 79 | pub fn display_name(self) -> &'static str {{ 80 | unsafe {{*DISPLAY_NAMES.get_unchecked((self as u32) as usize)}} 81 | }} 82 | 83 | #[inline] 84 | pub fn max_stack_size(self) -> u8 {{ 85 | unsafe {{*STACK_SIZES.get_unchecked((self as u32) as usize)}} 86 | }} 87 | 88 | #[inline] 89 | pub fn durability(self) -> Option {{ 90 | unsafe {{*DURABILITIES.get_unchecked((self as u32) as usize)}} 91 | }} 92 | 93 | #[inline] 94 | pub fn crafting_recipes(&self) -> &'static [crate::ids::recipes::Recipe] {{ 95 | crate::ids::recipes::Recipe::get_recipes_for_item(*self) 96 | }} 97 | }} 98 | 99 | impl<'a> MinecraftPacketPart<'a> for Item {{ 100 | #[inline] 101 | fn serialize_minecraft_packet_part(self, output: &mut Vec) -> Result<(), &'static str> {{ 102 | VarInt(self as i32).serialize_minecraft_packet_part(output) 103 | }} 104 | 105 | #[inline] 106 | fn deserialize_minecraft_packet_part(input: &'a[u8]) -> Result<(Self, &'a[u8]), &'static str> {{ 107 | let (id, input) = VarInt::deserialize_minecraft_packet_part(input)?; 108 | let id = std::cmp::max(id.0, 0) as u32; 109 | let item = Item::from_id(id).ok_or("No item corresponding to the specified numeric ID.")?; 110 | Ok((item, input)) 111 | }} 112 | }} 113 | 114 | const STACK_SIZES: [u8; {max_value}] = {max_stack_sizes:?}; 115 | 116 | const DURABILITIES: [Option; {max_value}] = {durabilities:?}; 117 | 118 | const DISPLAY_NAMES: [&str; {max_value}] = {display_names:?}; 119 | 120 | const TEXT_IDS: [&str; {max_value}] = {text_ids:?}; 121 | "#, 122 | variants = variants, 123 | max_value = expected, 124 | max_stack_sizes = items.iter().map(|i| i.stack_size).collect::>(), 125 | durabilities = items.iter().map(|i| i.max_durability).collect::>(), 126 | display_names = items.iter().map(|i| &i.display_name).collect::>(), 127 | text_ids = items.iter().map(|i| &i.text_id).collect::>(), 128 | ); 129 | 130 | File::create("src/ids/items.rs") 131 | .unwrap() 132 | .write_all(code.as_bytes()) 133 | .unwrap(); 134 | 135 | items 136 | } 137 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/animations.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[minecraft_enum(u8)] 5 | #[derive(Debug)] 6 | pub enum Animation { 7 | SwingMainArm = 0, 8 | TakeDamage, /// Not in the wiki maybe disapeared? 9 | LeaveBed, 10 | SwingOffhand, 11 | CriticalEffect, 12 | MagicCriticalEffect = 5, 13 | } 14 | 15 | #[cfg_attr(test, derive(PartialEq))] 16 | #[minecraft_enum(i32)] 17 | #[derive(Debug)] 18 | pub enum Effect { 19 | DispenserDispenses = 1000, 20 | DispenserFailToDispense, 21 | DispenserShoots, 22 | EnderEyeLaunched, 23 | FireworkShot, 24 | IronDoorOpened, 25 | WoodenDoorOpened, 26 | WoodenTrapdoorOpened, 27 | FenceGateOpened, 28 | FireExtinguished, 29 | /// Play record: This is actually a special case within the packet [packets::play_clientbound::ClientboundPacket::Effect]. 30 | /// You can start/stop a record at a specific location. 31 | /// Use a valid Record ID to start a record (or overwrite a currently playing one), any other value will stop the record. 32 | /// See [item IDs](crate::ids::items::Item). 33 | PlayRecord, 34 | IronDoorClosed, 35 | WoodenDoorClosed, 36 | WoodenTrapdoorClosed, 37 | FenceGateClosed, 38 | GhastWarns, 39 | GhastShoots, 40 | EnderdragonShoots, 41 | BlazeShoots, 42 | ZombieAttacksWoodDoor, 43 | ZombieAttacksIronDoor, 44 | ZombieBreackWoodDoor, 45 | WitherBreacksBlock, 46 | WitherSpawn, 47 | WitherShoots, 48 | BatTakesOff, 49 | ZoombieInfects, 50 | ZoombieVillagerConverted, 51 | EnderdragonDeath, 52 | AnvilDestroyed, 53 | AnvilUsed, 54 | AnvilLanded, 55 | PortalTravel, 56 | ChorusFlowerGrown, 57 | ChorusFlowerDied, 58 | BrewingStandBrewed, 59 | IronTrapdoorOpened, 60 | IronTrapdoorClosed, 61 | EndPortalOpening, 62 | PhantomBites, 63 | ZombieConvertsToDrowned, 64 | HuskConvertsToZombieByDrowning, 65 | GrindStoneUsed, 66 | BookPageTurned, 67 | 68 | ComposerComposts = 1500, 69 | /// either water to stone, or removes existing blocks such as torches 70 | LavaConvertsBlock, 71 | RedstoneTorchBurnsOut, 72 | EnderEyePlaced, 73 | 74 | /// Spawns 10 smoke particles, e.g. from a fire. 75 | SmokeParticules = 2000, 76 | /// Block state, as an index into the global palette. 77 | BlockBreack, 78 | /// Particle effect + glass break sound. 79 | /// RGB color as an integer (e.g. 8364543 for #7FA1FF). 80 | SplashPotion, 81 | /// particles and sound 82 | EyeOfEnderBreak, 83 | /// particle effect: smoke + flames 84 | MobSpawn, 85 | /// How many particles to spawn (if set to 0, 15 are spawned). 86 | BonemealParticules, 87 | DragonBreath, 88 | /// Particle effect + glass break sound. 89 | InstantSplashPotion, 90 | EnderdragonDestroysBlock, 91 | WetSpongeVaporizesInNether, 92 | 93 | EndGatewaySpawn = 3000, 94 | EnderdragonGrowl, 95 | ElectricSpark, 96 | CopperRemoveWax, 97 | CopperScrapeOxidation, 98 | } 99 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/auto_completion.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// One eligible value to insert 4 | #[cfg_attr(test, derive(PartialEq))] 5 | #[derive(Debug, MinecraftPacketPart)] 6 | pub struct Match<'a> { 7 | /// The value. Note that for instance this doesn't include a leading / on commands. 8 | pub value: &'a str, 9 | /// Tooltip to display 10 | pub tooltip: Option>, 11 | } 12 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/biomes.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[derive(Debug, MinecraftPacketPart)] 5 | pub struct ChunkBiomeData<'a> { 6 | /// Chunk coordinate (block coordinate divided by 16, rounded down) 7 | pub chunk_x: i32, 8 | /// Chunk coordinate (block coordinate divided by 16, rounded down) 9 | pub chunk_z: i32, 10 | /// Chunk [data structure](https://wiki.vg/Chunk_Format#Data_structure), with [sections](https://wiki.vg/Chunk_Format#Chunk_Section) containing only the `Biomes` field 11 | pub data: Array<'a, u8, VarInt>, 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/boss_bar.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[derive(Debug, MinecraftPacketPart)] 5 | #[discriminant(VarInt)] 6 | pub enum BossBarAction<'a> { 7 | Add { 8 | title: Chat<'a>, 9 | /// From 0 to 1. Values greater than 1 do not crash a Notchian client, and start [rendering part of a second health bar](https://i.johni0702.de/nA.png) at around 1.5. 10 | health: f32, 11 | color: Color, 12 | division: Division, 13 | /// Bit mask. 0x1: should darken sky, 0x2: is dragon bar (used to play end music), 0x04: create fog (previously was also controlled by 0x02). 14 | flags: u8, 15 | }, 16 | Remove, 17 | UpdateHealth { 18 | /// From 0 to 1. Values greater than 1 do not crash a Notchian client, and start [rendering part of a second health bar](https://i.johni0702.de/nA.png) at around 1.5. 19 | health: f32, 20 | }, 21 | UpdateTitle { 22 | title: Chat<'a>, 23 | }, 24 | UpdateStyle { 25 | color: Color, 26 | division: Division, 27 | }, 28 | UpdateFlages { 29 | /// Bit mask. 0x1: should darken sky, 0x2: is dragon bar (used to play end music), 0x04: create fog (previously was also controlled by 0x02). 30 | flags: u8, 31 | }, 32 | } 33 | 34 | #[cfg_attr(test, derive(PartialEq))] 35 | #[minecraft_enum(VarInt)] 36 | #[derive(Debug)] 37 | pub enum Color { 38 | Pink, 39 | Blue, 40 | Red, 41 | Green, 42 | Yellow, 43 | Purple, 44 | White, 45 | } 46 | 47 | #[cfg_attr(test, derive(PartialEq))] 48 | #[minecraft_enum(VarInt)] 49 | #[derive(Debug)] 50 | pub enum Division { 51 | NoDivision, 52 | SixNotches, 53 | TenNotches, 54 | TwelveNotches, 55 | TwentyNotches, 56 | } 57 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/chat.rs: -------------------------------------------------------------------------------- 1 | use crate::{*, packets::serializer::BitSet}; 2 | 3 | /// See [processing chat](https://wiki.vg/Chat#Processing_chat) for more information 4 | #[cfg_attr(test, derive(PartialEq))] 5 | #[minecraft_enum(u8)] 6 | #[derive(Debug)] 7 | pub enum Position { 8 | /// A common chat (chat box) 9 | Chat, 10 | /// A system message (chat box) 11 | System, 12 | /// Game info displayed above the hotbar 13 | GameInfo, 14 | } 15 | 16 | /// See [processing chat](https://wiki.vg/Chat#Processing_chat) for more information 17 | #[cfg_attr(test, derive(PartialEq))] 18 | #[minecraft_enum(VarInt)] 19 | #[derive(Debug)] 20 | pub enum ChatMode { 21 | Enabled, 22 | CommandsOnly, 23 | Hidden, 24 | } 25 | 26 | #[cfg_attr(test, derive(PartialEq))] 27 | #[minecraft_enum(VarInt)] 28 | #[derive(Debug)] 29 | pub enum ChatAction { 30 | Add, 31 | Remove, 32 | Set 33 | } 34 | 35 | #[cfg_attr(test, derive(PartialEq))] 36 | #[derive(Debug, MinecraftPacketPart)] 37 | pub struct PreviousMessage<'a> { 38 | /// The message Id + 1, used for validating message signature. The next field is present only when value of this field is equal to 0. 39 | pub message_id: VarInt, 40 | /// The previous message's signature. Contains the same type of data as Message Signature bytes (256 bytes) above. Not length-prefxied. 41 | pub signature: RawBytes<'a>, 42 | } 43 | 44 | #[cfg_attr(test, derive(PartialEq))] 45 | #[derive(Debug, MinecraftPacketPart)] 46 | #[discriminant(VarInt)] 47 | pub enum FilterType<'a> { 48 | /// No filters applied 49 | PassThrough, 50 | /// All filters applied 51 | FullyFiltered, 52 | /// Only some filters are applied. 53 | PartiallyFiltered { 54 | filter_mask: BitSet<'a>, 55 | }, 56 | } 57 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/command_block.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[minecraft_enum(VarInt)] 5 | #[derive(Debug)] 6 | pub enum CommandBlockMode { 7 | Sequence, 8 | Auto, 9 | Redstone, 10 | } 11 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/difficulty.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[minecraft_enum(u8)] 5 | #[derive(Debug)] 6 | pub enum Difficulty { 7 | Peaceful, 8 | Easy, 9 | Normal, 10 | Hard, 11 | } 12 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/effect.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// [Read about effects](https://minecraft.fandom.com/wiki/Effect) 4 | #[minecraft_enum(VarInt)] 5 | #[derive(Debug, Clone, Copy, PartialEq)] 6 | pub enum Effect { 7 | Speed = 1, 8 | Slowness, 9 | Haste, 10 | MiningFatigue, 11 | Strength, 12 | InstantHealth, 13 | InstantDamage, 14 | JumpBoost, 15 | Nausea, 16 | Regeneration, 17 | Resistance, 18 | FireResistance, 19 | WaterBreathing, 20 | Invisibility, 21 | Blindness, 22 | NightVision, 23 | Hunger, 24 | Weakness, 25 | Poison, 26 | Wither, 27 | HealthBoost, 28 | Absorption, 29 | Saturation, 30 | Glowing, 31 | Levitation, 32 | Luck, 33 | BadLuck, 34 | SlowFalling, 35 | ConduitPower, 36 | DolphinsGrace, 37 | BadOmen, 38 | HeroOfTheVillage, 39 | } 40 | 41 | impl Effect { 42 | pub fn get_name(&self) -> &'static str { 43 | match self { 44 | Effect::Speed => "speed", 45 | Effect::Slowness => "slowness", 46 | Effect::Haste => "haste", 47 | Effect::MiningFatigue => "mining_fatigue", 48 | Effect::Strength => "strength", 49 | Effect::InstantHealth => "instant_health", 50 | Effect::InstantDamage => "instant_damage", 51 | Effect::JumpBoost => "jump_boost", 52 | Effect::Nausea => "nausea", 53 | Effect::Regeneration => "regeneration", 54 | Effect::Resistance => "resistance", 55 | Effect::FireResistance => "fire_resistance", 56 | Effect::WaterBreathing => "water_breathing", 57 | Effect::Invisibility => "invisibility", 58 | Effect::Blindness => "blindness", 59 | Effect::NightVision => "night_vision", 60 | Effect::Hunger => "hunger", 61 | Effect::Weakness => "weakness", 62 | Effect::Poison => "poison", 63 | Effect::Wither => "wither", 64 | Effect::HealthBoost => "health_boost", 65 | Effect::Absorption => "absorption", 66 | Effect::Saturation => "saturation", 67 | Effect::Glowing => "glowing", 68 | Effect::Levitation => "levitation", 69 | Effect::Luck => "luck", 70 | Effect::BadLuck => "unluck", 71 | Effect::SlowFalling => "slow_falling", 72 | Effect::ConduitPower => "conduit_power", 73 | Effect::DolphinsGrace => "dolphins_grace", 74 | Effect::BadOmen => "bad_omen", 75 | Effect::HeroOfTheVillage => "hero_of_the_village", 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/game_state.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[minecraft_enum(u8)] 5 | #[derive(Debug)] 6 | pub enum GameState { 7 | /// Sends "You have no home bed or charged respawn anchor, or it was obstructed" to the client 8 | NoRespawn, 9 | BeginRaining, 10 | EndRaining, 11 | ChangeGamemode, 12 | Win, 13 | DemoEvent, 14 | ArrowHitPlayer, 15 | RainLevelChange, 16 | ThunderLevelChange, 17 | PlayPufferfishStingSound, 18 | ElderGuardianMob, 19 | EnableRespawnScreen, 20 | LimitedCrafting 21 | } 22 | 23 | #[cfg_attr(test, derive(PartialEq))] 24 | #[minecraft_enum(VarInt)] 25 | #[derive(Debug)] 26 | pub enum ClientStatus { 27 | /// Sent when the client is ready to complete login and when the client is ready to respawn after death 28 | PerformRespawn, 29 | /// Sent when the client opens the Statistics menu 30 | RequestStats, 31 | } 32 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/gamemode.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[derive(PartialEq)] 4 | #[minecraft_enum(u8)] 5 | #[derive(Debug, Clone)] 6 | pub enum Gamemode { 7 | Survival, 8 | Creative, 9 | Adventure, 10 | Spectator, 11 | } 12 | 13 | #[cfg_attr(test, derive(PartialEq))] 14 | #[minecraft_enum(i8)] 15 | #[derive(Debug)] 16 | pub enum PreviousGamemode { 17 | None = -1, 18 | 19 | Survival = 0, 20 | Creative, 21 | Adventure, 22 | Spectator, 23 | } 24 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod advancements; 2 | pub mod animations; 3 | pub mod auto_completion; 4 | pub mod biomes; 5 | pub mod blocks; 6 | pub mod boss_bar; 7 | pub mod chat; 8 | pub mod chunk; 9 | pub mod command_block; 10 | pub mod difficulty; 11 | pub mod effect; 12 | pub mod entity; 13 | pub mod game_state; 14 | pub mod gamemode; 15 | pub mod paintings; 16 | pub mod particle; 17 | pub mod players; 18 | pub mod recipes; 19 | pub mod resource_pack; 20 | pub mod slots; 21 | pub mod sound; 22 | pub mod tags; 23 | pub mod teams; 24 | pub mod trades; 25 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/paintings.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[minecraft_enum(VarInt)] 5 | #[derive(Debug, Clone, Copy)] 6 | pub enum Painting { 7 | Kebab = 0, 8 | Aztec, 9 | Alban, 10 | Aztec2, 11 | Bomb, 12 | Plant, 13 | Wasteland, 14 | Pool, 15 | Courbet, 16 | Sea, 17 | Sunset, 18 | Creebet, 19 | Wanderer, 20 | Graham, 21 | Match, 22 | Bust, 23 | Stage, 24 | Void, 25 | SkullAndRoses, 26 | Wither, 27 | Fighters, 28 | Pointer, 29 | Pigscene, 30 | BurningSkull, 31 | Skeleton, 32 | DonkeyKong, 33 | } 34 | 35 | impl Default for Painting { 36 | fn default() -> Self { 37 | Self::Kebab 38 | } 39 | } 40 | 41 | 42 | impl Painting { 43 | pub fn get_id(&self) -> i32 { 44 | *self as i32 45 | } 46 | 47 | /// Get the rect position on `paintings_kristoffer_zetterstrand.png`. 48 | /// Return (x, y, width, height). 49 | /// See [the wiki](https://wiki.vg/Protocol#Spawn_Painting). 50 | pub fn get_position(&self) -> (u8, u8, u8, u8) { 51 | match self { 52 | Painting::Kebab => (0, 0, 16, 16), 53 | Painting::Aztec => (16, 0, 16, 16), 54 | Painting::Alban => (32, 0, 16, 16), 55 | Painting::Aztec2 => (48, 0, 16, 16), 56 | Painting::Bomb => (64, 0, 16, 16), 57 | Painting::Plant => (80, 0, 16, 16), 58 | Painting::Wasteland => (96, 0, 16, 16), 59 | Painting::Pool => (0, 32, 32, 16), 60 | Painting::Courbet => (32, 32, 32, 16), 61 | Painting::Sea => (64, 32, 32, 16), 62 | Painting::Sunset => (96, 32, 32, 16), 63 | Painting::Creebet => (128, 32, 32, 16), 64 | Painting::Wanderer => (0, 64, 16, 32), 65 | Painting::Graham => (16, 64, 16, 32), 66 | Painting::Match => (0, 128, 32, 32), 67 | Painting::Bust => (32, 128, 32, 32), 68 | Painting::Stage => (64, 128, 32, 32), 69 | Painting::Void => (96, 128, 32, 32), 70 | Painting::SkullAndRoses => (128, 128, 32, 32), 71 | Painting::Wither => (160, 128, 32, 32), 72 | Painting::Fighters => (0, 96, 64, 32), 73 | Painting::Pointer => (0, 192, 64, 64), 74 | Painting::Pigscene => (64, 192, 64, 64), 75 | Painting::BurningSkull => (128, 192, 64, 64), 76 | Painting::Skeleton => (192, 64, 64, 48), 77 | Painting::DonkeyKong => (192, 112, 64, 48), 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/particle.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[derive(Debug, Clone, MinecraftPacketPart)] 5 | #[discriminant(VarInt)] 6 | pub enum Particle { 7 | AmbiantEntityEffect, 8 | AngryVillager, 9 | Barrier, 10 | Block { 11 | /// Use [Block::from_state_id](crate::ids::blocks::Block::from_state_id) to get the block. 12 | block_state_id: VarInt, 13 | }, 14 | Bubble, 15 | Cloud, 16 | Crit, 17 | DamageIndicator, 18 | DragonBreath, 19 | DrippingLava, 20 | FallingLava, 21 | LandingLava, 22 | DrippingWater, 23 | FallingWater, 24 | Dust { 25 | /// Red value, between 0.0 and 1.0 26 | red: f32, 27 | /// Green value, between 0.0 and 1.0 28 | green: f32, 29 | /// Blue value, between 0.0 and 1.0 30 | blue: f32, 31 | /// The scale, will be clamped between 0.01 and 4 32 | scale: f32, 33 | }, 34 | Effect, 35 | ElderGuardian, 36 | EnchantedHit, 37 | Enchant, 38 | EndRod, 39 | EntityEffect, 40 | ExplosionEmitter, 41 | Explosion, 42 | FallingDust { 43 | /// Use [Block::from_state_id](crate::ids::blocks::Block::from_state_id) to get the block. 44 | block_state_id: VarInt, 45 | }, 46 | Firework, 47 | Fishing, 48 | Flame, 49 | Flash, 50 | HappyVillager, 51 | Composter, 52 | Heart, 53 | InstantEffect, 54 | Item { 55 | /// The item that will be used 56 | item: super::slots::Slot, 57 | }, 58 | ItemSlime, 59 | IemSnowball, 60 | LargeSmoke, 61 | Lava, 62 | Mycelium, 63 | Note, 64 | Poof, 65 | Portal, 66 | Rain, 67 | Smoke, 68 | Sneeze, 69 | Spit, 70 | SquidInk, 71 | SweepAttack, 72 | TotemOfUndying, 73 | Underwater, 74 | Splash, 75 | Witch, 76 | BubblePop, 77 | CurrentDown, 78 | BubbleColumnUp, 79 | Nautilus, 80 | Dolphin, 81 | CampfireCosySmoke, 82 | CampfireSignalSmoke, 83 | DrippingHoney, 84 | FallingHoney, 85 | LandingHoney, 86 | FallingNectar, 87 | } 88 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/recipes.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[derive(Debug, MinecraftPacketPart)] 5 | #[discriminant(VarInt)] 6 | pub enum UnlockRecipesAction<'a> { 7 | Init { 8 | crafting_recipe_book_open: bool, 9 | crafting_recipe_book_filter_active: bool, 10 | smelting_recipe_book_open: bool, 11 | smelting_recipe_book_filter_active: bool, 12 | blast_furnace_recipe_book_open: bool, 13 | blast_furnace_recipe_book_filter_active: bool, 14 | smoker_recipe_book_open: bool, 15 | smoker_recipe_book_filter_active: bool, 16 | /// Recipes that will be tagged as displayed 17 | displayed_recipes: Array<'a, Identifier<'a>, VarInt>, 18 | /// Recipes that will be added to the recipe book 19 | added_recipes: Array<'a, Identifier<'a>, VarInt>, 20 | }, 21 | Add { 22 | crafting_recipe_book_open: bool, 23 | crafting_recipe_book_filter_active: bool, 24 | smelting_recipe_book_open: bool, 25 | smelting_recipe_book_filter_active: bool, 26 | blast_furnace_recipe_book_open: bool, 27 | blast_furnace_recipe_book_filter_active: bool, 28 | smoker_recipe_book_open: bool, 29 | smoker_recipe_book_filter_active: bool, 30 | /// Recipes that will be added to the recipe book and have their icon shown in the notification 31 | recipes: Array<'a, Identifier<'a>, VarInt>, 32 | }, 33 | Remove { 34 | crafting_recipe_book_open: bool, 35 | crafting_recipe_book_filter_active: bool, 36 | smelting_recipe_book_open: bool, 37 | smelting_recipe_book_filter_active: bool, 38 | blast_furnace_recipe_book_open: bool, 39 | blast_furnace_recipe_book_filter_active: bool, 40 | smoker_recipe_book_open: bool, 41 | smoker_recipe_book_filter_active: bool, 42 | /// Recipes that will be removed 43 | /// This allows them to be re-displayed when they are re-added. 44 | recipes: Array<'a, Identifier<'a>, VarInt>, 45 | }, 46 | } 47 | 48 | #[cfg_attr(test, derive(PartialEq))] 49 | #[derive(Debug, MinecraftPacketPart)] 50 | #[discriminant(VarInt)] 51 | pub enum RecipeBook { 52 | Crafting, 53 | Furnace, 54 | BlastFurnace, 55 | Smoker, 56 | } 57 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/resource_pack.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[minecraft_enum(VarInt)] 5 | #[derive(Debug)] 6 | pub enum ResourcePackStatus { 7 | Loaded, 8 | Declined, 9 | FailedDownload, 10 | Accepted, 11 | } 12 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/slots.rs: -------------------------------------------------------------------------------- 1 | use crate::{nbt::NbtTag, *}; 2 | 3 | /// The [Slot] data structure is how Minecraft represents an item and its associated data in the [Minecraft Protocol](https://wiki.vg/Protocol). 4 | #[cfg_attr(test, derive(PartialEq))] 5 | #[derive(Debug, Clone, Default, MinecraftPacketPart)] 6 | pub struct Slot { 7 | /// `Some(item)` if there is an item in this slot; `None` if it is empty. 8 | pub item: Option, 9 | } 10 | 11 | #[cfg_attr(test, derive(PartialEq))] 12 | #[derive(Debug, Clone, MinecraftPacketPart)] 13 | pub struct SlotItem { 14 | /// The [item](crate::ids::items::Item). 15 | /// Item IDs are distinct from [block IDs](crate::ids::blocks::Block); see [crate::ids] for more information. 16 | pub item_id: crate::ids::items::Item, 17 | pub item_count: i8, 18 | /// Things like enchantements and durability are encoded in this field. 19 | pub nbt_data: NbtTag, 20 | } 21 | 22 | #[cfg_attr(test, derive(PartialEq))] 23 | #[minecraft_enum(VarInt)] 24 | #[derive(Debug)] 25 | pub enum Hand { 26 | MainHand, 27 | OffHand, 28 | } 29 | 30 | #[cfg_attr(test, derive(PartialEq))] 31 | #[minecraft_enum(VarInt)] 32 | #[derive(Debug)] 33 | pub enum MainHand { 34 | Left, 35 | Right, 36 | } 37 | 38 | #[minecraft_enum(u8)] 39 | #[derive(Debug, Clone, Copy)] 40 | pub enum EquipmentSlot { 41 | MainHand, 42 | OffHand, 43 | Boots, 44 | Leggings, 45 | Chestplate, 46 | Helmet, 47 | } 48 | 49 | impl std::cmp::PartialEq for EquipmentSlot { 50 | fn eq(&self, other: &Self) -> bool { 51 | (*self as u8).eq(&(*other as u8)) 52 | } 53 | } 54 | impl std::cmp::Eq for EquipmentSlot {} 55 | impl std::cmp::PartialOrd for EquipmentSlot { 56 | fn partial_cmp(&self, other: &Self) -> Option { 57 | Some(self.cmp(other)) 58 | } 59 | } 60 | impl std::cmp::Ord for EquipmentSlot { 61 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 62 | (*self as u8).cmp(&(*other as u8)) 63 | } 64 | } 65 | 66 | use std::collections::BTreeMap; 67 | #[cfg_attr(test, derive(PartialEq))] 68 | #[derive(Debug)] 69 | pub struct EquipmentSlotArray { 70 | pub slots: BTreeMap, 71 | } 72 | 73 | 74 | impl<'a> MinecraftPacketPart<'a> for EquipmentSlotArray { 75 | fn serialize_minecraft_packet_part(self, output: &mut Vec) -> Result<(), &'static str> { 76 | let len = self.slots.len(); 77 | for (idx, (slot_index, slot)) in self.slots.into_iter().enumerate() { 78 | let mut slot_index = slot_index as u8; 79 | if idx + 1 < len { 80 | slot_index += 0b1000_0000; 81 | } 82 | output.push(slot_index); 83 | slot.serialize_minecraft_packet_part(output)?; 84 | } 85 | Ok(()) 86 | } 87 | 88 | fn deserialize_minecraft_packet_part( 89 | mut input: &'a [u8], 90 | ) -> Result<(Self, &'a [u8]), &'static str> { 91 | let mut slots = BTreeMap::new(); 92 | loop { 93 | let (number, new_input) = u8::deserialize_minecraft_packet_part(input)?; 94 | let (slot, new_input) = Slot::deserialize_minecraft_packet_part(new_input)?; 95 | input = new_input; 96 | 97 | let slot_index = 0b0111_1111 & number; 98 | let slot_index_variant: EquipmentSlot = if slot_index <= 5 { 99 | unsafe { std::mem::transmute(slot_index) } 100 | } else { 101 | return Err("The slot index cannot be higher than 5."); 102 | }; 103 | slots.insert(slot_index_variant, slot); 104 | 105 | if number < 0b1000_0000 { 106 | break; 107 | } 108 | } 109 | Ok((EquipmentSlotArray { slots }, input)) 110 | } 111 | } 112 | 113 | #[cfg_attr(test, derive(PartialEq))] 114 | #[minecraft_enum(VarInt)] 115 | #[derive(Debug)] 116 | pub enum WindowType { 117 | OneRow, 118 | TwoRows, 119 | ThreeRows, 120 | FourRows, 121 | FiveRows, 122 | SixRows, 123 | ThreeByThree, 124 | Anvil, 125 | Beacon, 126 | BlastFurnace, 127 | BrewingStand, 128 | Crafting, 129 | Enchantment, 130 | Furnace, 131 | Grindstone, 132 | Hopper, 133 | Lectern, 134 | Loom, 135 | Merchant, 136 | ShulkerBox, 137 | Smithing, 138 | Smoker, 139 | Cartography, 140 | Stonecutter, 141 | } 142 | 143 | #[cfg(test)] 144 | mod tests { 145 | use super::*; 146 | 147 | #[test] 148 | fn test_slot() { 149 | let serialized = &mut [0x01, 0x01, 0x01, 0x00]; 150 | let deserialized = Slot::deserialize_uncompressed_minecraft_packet(serialized) 151 | .unwrap() 152 | .item 153 | .unwrap(); 154 | assert_eq!(deserialized.item_id, crate::ids::items::Item::Stone); 155 | assert_eq!(deserialized.item_count, 1); 156 | assert!(matches!(deserialized.nbt_data, NbtTag::Null)); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/sound.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[derive(Debug, MinecraftPacketPart)] 5 | #[discriminant(VarInt)] 6 | pub enum SoundCategory { 7 | Master, 8 | Music, 9 | Record, 10 | Weather, 11 | Block, 12 | Hostile, 13 | Neutral, 14 | Player, 15 | Ambiant, 16 | Voice, 17 | } 18 | 19 | #[cfg_attr(test, derive(PartialEq))] 20 | #[derive(Debug)] 21 | pub struct StopSoundPacket<'a> { 22 | /// If not present, then sounds from all sources are cleared 23 | pub sound_category: Option, 24 | /// A sound effect name, see [ClientboundPacket::NamedSoundEffect]. 25 | /// If not present, then all sounds are cleared. 26 | pub sound_effect_name: Option>, 27 | } 28 | 29 | impl<'a> MinecraftPacketPart<'a> for StopSoundPacket<'a> { 30 | fn serialize_minecraft_packet_part(self, output: &mut Vec) -> Result<(), &'static str> { 31 | let mut flags: u8 = 0; 32 | if self.sound_category.is_some() { 33 | flags += 0b0000_0001; 34 | } 35 | if self.sound_category.is_some() { 36 | flags += 0b0000_0010; 37 | } 38 | output.push(flags); 39 | if let Some(sound_category) = self.sound_category { 40 | sound_category.serialize_minecraft_packet_part(output)?; 41 | } 42 | if let Some(sound_effect_name) = self.sound_effect_name { 43 | sound_effect_name.serialize_minecraft_packet_part(output)?; 44 | } 45 | Ok(()) 46 | } 47 | fn deserialize_minecraft_packet_part( 48 | input: &'a [u8], 49 | ) -> Result<(Self, &'a [u8]), &'static str> { 50 | let (flags, input) = u8::deserialize_minecraft_packet_part(input)?; 51 | let (sound_category, input) = match flags & 0b0000_0001 == 1 { 52 | true => { 53 | let (sound_category, input) = 54 | MinecraftPacketPart::deserialize_minecraft_packet_part(input)?; 55 | (Some(sound_category), input) 56 | } 57 | false => (None, input), 58 | }; 59 | let (sound_effect_name, input) = match flags & 0b0000_0010 == 0b0000_0010 { 60 | true => { 61 | let (sound_effect_name, input) = 62 | MinecraftPacketPart::deserialize_minecraft_packet_part(input)?; 63 | (Some(sound_effect_name), input) 64 | } 65 | false => (None, input), 66 | }; 67 | Ok(( 68 | StopSoundPacket { 69 | sound_category, 70 | sound_effect_name, 71 | }, 72 | input, 73 | )) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/tags.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// A tag 4 | /// 5 | /// More information on tags is available at: https://minecraft.gamepedia.com/Tag 6 | /// And a list of all tags is here: https://minecraft.gamepedia.com/Tag#List_of_tags 7 | #[cfg_attr(test, derive(PartialEq))] 8 | #[derive(Debug, MinecraftPacketPart)] 9 | pub struct Tag<'a> { 10 | pub tag_name: Identifier<'a>, 11 | pub data: Array<'a, VarInt, VarInt>, 12 | } 13 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/teams.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[derive(Debug, MinecraftPacketPart)] 5 | #[discriminant(VarInt)] 6 | pub enum ScoreboardAction<'a> { 7 | Create { 8 | text: Chat<'a>, 9 | scoreboard_type: ScoreboardType, 10 | }, 11 | Remove, 12 | Update { 13 | text: Chat<'a>, 14 | scoreboard_type: ScoreboardType, 15 | }, 16 | } 17 | 18 | #[cfg_attr(test, derive(PartialEq))] 19 | #[derive(Debug, MinecraftPacketPart)] 20 | #[discriminant(u8)] 21 | pub enum ScoreboardScoreAction<'a> { 22 | /// Update or Create 23 | Update { 24 | /// The name of the objective the score belongs to 25 | objective_name: Chat<'a>, 26 | /// The score to be displayed next to the entry 27 | value: VarInt, 28 | }, 29 | Remove { 30 | /// The name of the objective the score belongs to 31 | objective_name: Chat<'a>, 32 | }, 33 | } 34 | 35 | #[cfg_attr(test, derive(PartialEq))] 36 | #[minecraft_enum(VarInt)] 37 | #[derive(Debug)] 38 | pub enum ScoreboardType { 39 | Integer, 40 | Hearts, 41 | } 42 | 43 | #[cfg_attr(test, derive(PartialEq))] 44 | #[minecraft_enum(u8)] 45 | #[derive(Debug)] 46 | pub enum ScoreboardPosition { 47 | List, 48 | Sidebar, 49 | BelowName, 50 | BlackTeam, 51 | DarkBlueTeam, 52 | DarkGreenTeam, 53 | DarkCyanTeam, 54 | DarkRedTeam, 55 | PurpleTeam, 56 | GoldTeam, 57 | GrayTeam, 58 | DarkGrayTeam, 59 | BlueTeam, 60 | GreenTeam, 61 | CyanTeam, 62 | RedTeam, 63 | PinkTeam, 64 | YellowTeam, 65 | WhiteTeam, 66 | } 67 | 68 | #[cfg_attr(test, derive(PartialEq))] 69 | #[derive(Debug, MinecraftPacketPart)] 70 | #[discriminant(u8)] 71 | pub enum TeamAction<'a> { 72 | Create { 73 | team_display_name: Chat<'a>, 74 | /// Bit mask. 0x01: Allow friendly fire, 0x02: can see invisible players on same team. 75 | friendly_flags: u8, 76 | /// One of the following: always, hideForOtherTeams, hideForOwnTeam, never 77 | name_tag_visibility: &'a str, 78 | /// One of the following: always, pushOtherTeams, pushOwnTeam, never 79 | collision_rule: &'a str, 80 | /// Used to color the name of players on the team 81 | team_color: TeamColor, 82 | /// Displayed before the names of players that are part of this team 83 | team_prefix: Chat<'a>, 84 | /// Displayed after the names of players that are part of this team 85 | team_suffix: Chat<'a>, 86 | /// Identifiers for the entities in this team. For players, this is their username; for other entities, it is their UUID. 87 | entities: Array<'a, &'a str, VarInt>, 88 | }, 89 | Remove, 90 | Update { 91 | team_display_name: Chat<'a>, 92 | /// Bit mask. 0x01: Allow friendly fire, 0x02: can see invisible players on same team. 93 | friendly_flags: u8, 94 | /// One of the following: always, hideForOtherTeams, hideForOwnTeam, never 95 | name_tag_visibility: &'a str, 96 | /// One of the following: always, pushOtherTeams, pushOwnTeam, never 97 | collision_rule: &'a str, 98 | /// Used to color the name of players on the team 99 | team_color: TeamColor, 100 | /// Displayed before the names of players that are part of this team 101 | team_prefix: Chat<'a>, 102 | /// Displayed after the names of players that are part of this team 103 | team_suffix: Chat<'a>, 104 | }, 105 | AddEntities { 106 | /// Identifiers for the added entities. For players, this is their username; for other entities, it is their UUID. 107 | entities: Array<'a, &'a str, VarInt>, 108 | }, 109 | RemoveEntities { 110 | /// Identifiers for the removed entities. For players, this is their username; for other entities, it is their UUID. 111 | entities: Array<'a, &'a str, VarInt>, 112 | }, 113 | } 114 | 115 | /// The color of a team defines how the names of the team members are visualized; any formatting code can be used. 116 | #[cfg_attr(test, derive(PartialEq))] 117 | #[minecraft_enum(VarInt)] 118 | #[derive(Debug)] 119 | pub enum TeamColor { 120 | Black, 121 | DarkBlue, 122 | DarkGreen, 123 | DarkCyan, 124 | DarkRed, 125 | Purple, 126 | Gold, 127 | Gray, 128 | DarkGray, 129 | Blue, 130 | Green, 131 | Cyan, 132 | Red, 133 | Pink, 134 | Yellow, 135 | White, 136 | Obfuscated, 137 | Bold, 138 | Strikethrough, 139 | Underlined, 140 | Italic, 141 | Reset, 142 | } 143 | -------------------------------------------------------------------------------- /minecraft-protocol/src/components/trades.rs: -------------------------------------------------------------------------------- 1 | use super::slots::Slot; 2 | use crate::*; 3 | 4 | /// Represents a single trade offer 5 | /// 6 | /// Notes: Modifiers can increase or decrease the number of items for the first input slot. The second input slot and the output slot never change the nubmer of items. The number of items may never be less than 1, and never more than the stack size. If special price and demand are both zero, only the default price is displayed. If either is non-zero, then the adjusted price is displayed next to the crossed-out default price. The adjusted prices is calculated as follows: 7 | /// `Adjusted price = default price + floor(default price x multiplier x demand) + special price` 8 | #[cfg_attr(test, derive(PartialEq))] 9 | #[derive(Debug, MinecraftPacketPart)] 10 | pub struct Trade { 11 | /// The first item the player has to supply for this villager trade. 12 | /// The count of the item stack is the default "price" of this trade. 13 | pub input_item1: Slot, 14 | /// The item the player will receive from this villager trade 15 | pub output_item: Slot, 16 | /// The second item the player has to supply for this villager trade. May be an empty slot. 17 | pub input_item2: Slot, 18 | /// True if the trade is disabled; false if the trade is enabled 19 | pub disabled: bool, 20 | /// Number of times the trade has been used so far. 21 | /// If equal to the maximum number of trades, the client will display a red X. 22 | pub use_count: i32, 23 | /// Number of times this trade can be used before it's exhausted 24 | pub max_use_count: i32, 25 | /// Amount of XP both the player and the villager will earn each time the trade is used 26 | pub xp: i32, 27 | /// Can be zero or negative. 28 | /// The number is added to the price when an item is discounted due to player reputation or other effects. 29 | pub special_price: i32, 30 | /// Can be low (0.05) or high (0.2). 31 | /// Determines how much demand, player reputation, and temporary effects will adjust the price. 32 | pub price_multiplier: f32, 33 | /// Can be zero or a positive number. 34 | /// Causes the price to increase when a trade is used repeatedly. 35 | pub demand: i32, 36 | } 37 | 38 | #[minecraft_enum(VarInt)] 39 | #[derive(Debug, PartialEq, Clone, Copy)] 40 | pub enum VillagerType { 41 | Desert, 42 | Jungle, 43 | Plains, 44 | Savanna, 45 | Snow, 46 | Swamp, 47 | Taiga, 48 | } 49 | 50 | #[minecraft_enum(VarInt)] 51 | #[derive(Debug, PartialEq, Clone, Copy)] 52 | pub enum VillagerProfession { 53 | None, 54 | Armorer, 55 | Butcher, 56 | Cartographer, 57 | Cleric, 58 | Farmer, 59 | Fisherman, 60 | Fletcher, 61 | Leatherworker, 62 | Librarian, 63 | Mason, 64 | Nitwit, 65 | Shepherd, 66 | Toolsmith, 67 | Weaponsmith, 68 | } 69 | 70 | /// Appears on the trade GUI; meaning comes from the translation key `merchant.level.` + level. 71 | #[minecraft_enum(VarInt)] 72 | #[derive(Debug, PartialEq, Clone, Copy)] 73 | pub enum VillagerLevel { 74 | Novice = 1, 75 | Apprentice, 76 | Journeyman, 77 | Expert, 78 | Master, 79 | } 80 | -------------------------------------------------------------------------------- /minecraft-protocol/src/ids/.gitignore: -------------------------------------------------------------------------------- 1 | blocks.rs 2 | block_states.rs 3 | items.rs 4 | entities.rs 5 | recipes.rs 6 | 7 | -------------------------------------------------------------------------------- /minecraft-protocol/src/ids/mod.rs: -------------------------------------------------------------------------------- 1 | //! All the submodules of this module are generated automatically. 2 | //! See the build.rs file. 3 | 4 | pub mod blocks; 5 | pub mod entities; 6 | pub mod items; 7 | pub mod block_states; 8 | pub mod recipes; 9 | -------------------------------------------------------------------------------- /minecraft-protocol/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A library crate empowering everyone to build Minecraft clients and servers. 2 | //! 3 | //! This crate is capable of handling the parsing and creation of Minecraft packets. 4 | //! It contains a complete implementation of the latest Minecraft protocol, including [the NBT data format](https://wiki.vg/NBT). 5 | //! This crate also embeds [exhaustive lists](https://github.com/PrismarineJS/minecraft-data) of blocks and entities, along with their properties. 6 | //! 7 | //! The work relies on and contributes to a [community reverse engineering effort](https://wiki.vg). 8 | //! I have tested the whole crate on real-world Minecraft servers and successfully made a miner bot. 9 | //! 10 | //! # Usage 11 | //! 12 | //! This crate is low-level since you will have to manage the network and learn about [how the protocol works](https://wiki.vg). 13 | //! There are WIP helper functions for reading and writing packets over a `TcpStream`. 14 | //! There is currently no support for encryption and compression. 15 | //! 16 | //! You can serialize and deserialize any struct of this library like this: 17 | //! 18 | //! ```ignore 19 | //! // Serialize 20 | //! let packet = ClientboundPacket::UpdateHealth { 21 | //! health: 20.0, 22 | //! food: VarInt(10), 23 | //! food_saturation: 2.0, 24 | //! }; 25 | //! let serialized_packet: Vec = packet.serialize_minecraft_packet().unwrap(); 26 | //! 27 | //! // Deserialize 28 | //! let raw_packet = [68, 160, 129, 2, 0, 0, 0, 2, 5, 0, 6, 18, 0, 4, 7, 0, 15, 13, 10, 14, 0, 0, 12, 1, 0, 13, 10, 0, 8, 2, 66, 32, 0, 0, 9, 1, 0, 11, 1, 0, 10, 7, 0, 1, 1, 172, 2, 3, 7, 0, 7, 0, 0, 5, 7, 0, 16, 7, 0, 17, 7, 0, 255]; 29 | //! let parsed_packet = ClientboundPacket::deserialize_minecraft_packet(&raw_packet).unwrap(); 30 | //! ``` 31 | //! 32 | //! # Internal design 33 | //! 34 | //! This crate uses procedural macros to generate most of the parsing of composed structs. 35 | //! These macros are defined in [this crate](https://github.com/Mubelotix/minecraft-protocol-derive). Note that this is **the only dependency**. 36 | //! 37 | //! As you can see, specifying new types is child's play: 38 | //! 39 | //! ```ignore 40 | //! #[derive(MinecraftPacketPart)] 41 | //! struct Entity { 42 | //! entity_id: VarInt, 43 | //! x: f64, 44 | //! y: f64, 45 | //! z: f64, 46 | //! } 47 | //! ``` 48 | //! 49 | //! This library consists of a bunch of structs (like this one) nested in each other. 50 | //! See [Mubelotix/minecraft-protocol-derive](https://github.com/Mubelotix/minecraft-protocol-derive) for more information. 51 | //! 52 | //! # State of the Minecraft + Rust ecosystem 53 | //! 54 | //! There are many library crates for Minecraft, but they are often incomplete and outdated. 55 | //! This crate aims to be complete and easy to maintain. Thanks to the use of macros and build scripts, there is little parsing to implement by hand. 56 | //! 57 | //! However, there is currently no high-level Minecraft client or server library. 58 | //! It would be amazing to have a batteries-included Minecraft client framework. 59 | //! It could handle packets, environment state, and provide easy-to-use functions. 60 | //! That would allow client and bot creators to focus on the logic. 61 | //! Minecraft isn't that complex, but there is a surprisingly large amount of content to understand in the protocol. 62 | //! Despite the quality of [the wiki](https://wiki.vg), it may still be a pain point for beginners. 63 | //! Thus, feel free to pursue the efforts on Minecraft in Rust as it will be highly appreciated. 64 | 65 | #![allow(clippy::upper_case_acronyms)] 66 | 67 | pub mod components; 68 | pub mod ids; 69 | pub mod nbt; 70 | pub mod network; 71 | pub mod packets; 72 | 73 | pub use crate::packets::serializer::MinecraftPacketPart; 74 | pub(crate) use crate::packets::*; 75 | -------------------------------------------------------------------------------- /minecraft-protocol/src/nbt/compound.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::collections::HashMap; 3 | 4 | #[inline] 5 | pub fn parse_compound(mut input: &[u8]) -> Result<(HashMap, &[u8]), &'static str> { 6 | let mut content = HashMap::new(); 7 | 8 | loop { 9 | if input.first() == Some(&0) { 10 | input = &input[1..]; 11 | break; 12 | } 13 | if input.len() < 3 { 14 | return Err("A tag in a compound should be introduced by three bytes."); 15 | } 16 | let (tag_id, len): (u8, u16) = unsafe { 17 | ( 18 | *input.get_unchecked(0), 19 | u16::from_be_bytes(*(input.as_ptr().add(1) as *mut [u8; 2])), 20 | ) 21 | }; 22 | 23 | let len = len as usize; 24 | let new_input = &input[3..]; 25 | let (bytes, new_input) = new_input.split_at(len); 26 | let name = String::from_utf8(bytes.to_vec()) 27 | .map_err(|_| "A tag name should contain valid utf8 characters.")?; 28 | let (tag, new_input) = parse_nbt_tag(new_input, tag_id)?; 29 | input = new_input; 30 | content.insert(name, tag); 31 | } 32 | 33 | Ok((content, input)) 34 | } 35 | 36 | #[allow(clippy::type_complexity)] 37 | pub fn parse_root_compound( 38 | mut input: &[u8], 39 | ) -> Result<((String, HashMap), &[u8]), &'static str> { 40 | if input.first() != Some(&10) { 41 | return Err("The root compound tag should start with the compound ID (10)."); 42 | } 43 | input = &input[1..]; 44 | if input.len() < 2 { 45 | return Err("A root compound tag should contain two bytes."); 46 | } 47 | let len: u16 = unsafe { u16::from_be_bytes(*(input.as_ptr() as *mut [u8; 2])) }; 48 | let len = len as usize; 49 | input = &input[2..]; 50 | let (bytes, new_input) = input.split_at(len); 51 | let name = String::from_utf8(bytes.to_vec()) 52 | .map_err(|_| "A compound tag name should contain valid utf8 characters.")?; 53 | input = new_input; 54 | 55 | let (content, input) = parse_compound(input)?; 56 | 57 | Ok(((name, content), input)) 58 | } 59 | 60 | pub fn parse_root_compound_complete( 61 | input: &[u8], 62 | ) -> Result<(String, HashMap), &'static str> { 63 | let (value, input) = parse_root_compound(input)?; 64 | 65 | if !input.is_empty() { 66 | return Err("There should be no data after a root compound."); 67 | } 68 | 69 | Ok(value) 70 | } 71 | -------------------------------------------------------------------------------- /minecraft-protocol/src/nbt/numbers.rs: -------------------------------------------------------------------------------- 1 | /// A single signed byte 2 | #[inline] 3 | pub fn parse_byte(input: &[u8]) -> Result<(i8, &[u8]), &'static str> { 4 | let byte = *input.first().ok_or("A byte tag should contain a byte.")?; 5 | let byte = i8::from_be_bytes([byte]); 6 | Ok((byte, &input[1..])) 7 | } 8 | 9 | /// A single signed, big endian 16 bit integer 10 | #[inline] 11 | pub fn parse_short(input: &[u8]) -> Result<(i16, &[u8]), &'static str> { 12 | if input.len() < 2 { 13 | return Err("A short tag should contain two bytes."); 14 | } 15 | Ok(unsafe { 16 | ( 17 | i16::from_be_bytes(*(input.as_ptr() as *mut [u8; 2])), 18 | input.get_unchecked(2..), 19 | ) 20 | }) 21 | } 22 | 23 | /// A single signed, big endian 32 bit integer 24 | #[inline] 25 | pub fn parse_int(input: &[u8]) -> Result<(i32, &[u8]), &'static str> { 26 | if input.len() < 4 { 27 | return Err("A int tag should contain four bytes."); 28 | } 29 | Ok(unsafe { 30 | ( 31 | i32::from_be_bytes(*(input.as_ptr() as *mut [u8; 4])), 32 | input.get_unchecked(4..), 33 | ) 34 | }) 35 | } 36 | 37 | /// A single signed, big endian 64 bit integer 38 | #[inline] 39 | pub fn parse_long(input: &[u8]) -> Result<(i64, &[u8]), &'static str> { 40 | if input.len() < 8 { 41 | return Err("A long tag should contain height bytes."); 42 | } 43 | Ok(unsafe { 44 | ( 45 | i64::from_be_bytes(*(input.as_ptr() as *mut [u8; 8])), 46 | input.get_unchecked(8..), 47 | ) 48 | }) 49 | } 50 | 51 | /// A single, big endian IEEE-754 single-precision floating point number (NaN possible) 52 | #[inline] 53 | pub fn parse_float(input: &[u8]) -> Result<(f32, &[u8]), &'static str> { 54 | if input.len() < 4 { 55 | return Err("A float tag should contain four bytes."); 56 | } 57 | unsafe { 58 | let number = input.get_unchecked(..4); 59 | #[cfg(target_endian = "little")] 60 | let number = f32::from_be_bytes([ 61 | *number.get_unchecked(0), 62 | *number.get_unchecked(1), 63 | *number.get_unchecked(2), 64 | *number.get_unchecked(3), 65 | ]); 66 | 67 | Ok((number, input.get_unchecked(4..))) 68 | } 69 | } 70 | 71 | /// A single, big endian IEEE-754 double-precision floating point number (NaN possible) 72 | #[inline] 73 | pub fn parse_double(input: &[u8]) -> Result<(f64, &[u8]), &'static str> { 74 | if input.len() < 8 { 75 | return Err("A double tag should contain four bytes."); 76 | } 77 | unsafe { 78 | let number = input.get_unchecked(..8); 79 | #[cfg(target_endian = "little")] 80 | let number = f64::from_be_bytes([ 81 | *number.get_unchecked(0), 82 | *number.get_unchecked(1), 83 | *number.get_unchecked(2), 84 | *number.get_unchecked(3), 85 | *number.get_unchecked(4), 86 | *number.get_unchecked(5), 87 | *number.get_unchecked(6), 88 | *number.get_unchecked(7), 89 | ]); 90 | 91 | Ok((number, input.get_unchecked(8..))) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /minecraft-protocol/src/nbt/serializer.rs: -------------------------------------------------------------------------------- 1 | use super::NbtTag; 2 | use crate::*; 3 | 4 | impl<'a> MinecraftPacketPart<'a> for NbtTag { 5 | fn serialize_minecraft_packet_part(self, output: &mut Vec) -> Result<(), &'static str> { 6 | self.serialize(output); 7 | Ok(()) 8 | } 9 | 10 | fn deserialize_minecraft_packet_part( 11 | input: &'a [u8], 12 | ) -> Result<(Self, &'a [u8]), &'static str> { 13 | super::parse_network_nbt(input) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /minecraft-protocol/src/nbt/test_data/bigtest.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mubelotix/minecraft-protocol/69c7bef61dfa06a58287cf979781c4a61098c094/minecraft-protocol/src/nbt/test_data/bigtest.nbt -------------------------------------------------------------------------------- /minecraft-protocol/src/nbt/test_data/hello_world.nbt: -------------------------------------------------------------------------------- 1 | 2 | hello worldname Bananrama -------------------------------------------------------------------------------- /minecraft-protocol/src/nbt/test_data/level.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mubelotix/minecraft-protocol/69c7bef61dfa06a58287cf979781c4a61098c094/minecraft-protocol/src/nbt/test_data/level.dat -------------------------------------------------------------------------------- /minecraft-protocol/src/nbt/test_data/servers.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mubelotix/minecraft-protocol/69c7bef61dfa06a58287cf979781c4a61098c094/minecraft-protocol/src/nbt/test_data/servers.dat -------------------------------------------------------------------------------- /minecraft-protocol/src/packets/handshake.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[cfg_attr(test, derive(PartialEq))] 4 | #[derive(Debug, MinecraftPacketPart)] 5 | #[discriminant(VarInt)] 6 | pub enum ServerboundPacket<'a> { 7 | /// This causes the server to switch into the target state 8 | Hello { 9 | /// See [protocol version numbers](https://wiki.vg/Protocol_version_numbers) (currently 754 in Minecraft 1.16.5). 10 | protocol_version: VarInt, 11 | /// Hostname or IP, e.g. localhost or 127.0.0.1, that was used to connect. 12 | /// The Notchian server does not use this information. 13 | /// Note that SRV records are a complete redirect, e.g. if _minecraft._tcp.example.com points to mc.example.org, users connecting to example.com will provide mc.example.org as server address in addition to connecting to it. 14 | server_address: &'a str, 15 | /// Default is 25565. 16 | /// The Notchian server does not use this information. 17 | server_port: u16, 18 | next_state: ConnectionState, 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /minecraft-protocol/src/packets/login.rs: -------------------------------------------------------------------------------- 1 | //! For general information about login, see [the wiki](https://wiki.vg/Protocol#Login). 2 | 3 | use crate::*; 4 | 5 | #[cfg_attr(test, derive(PartialEq))] 6 | #[derive(Debug, MinecraftPacketPart)] 7 | #[discriminant(VarInt)] 8 | pub enum ClientboundPacket<'a> { 9 | Disconnect { 10 | reason: Chat<'a>, 11 | }, 12 | 13 | /// See [Protocol Encryption](https://wiki.vg/Protocol_Encryption) for details. 14 | /// 15 | /// *Request for [ServerboundPacket::EncryptionResponse]* 16 | EncryptionRequest { 17 | /// Appears to be empty 18 | server_id: &'a str, 19 | /// The server's public key in bytes 20 | public_key: Array<'a, u8, VarInt>, 21 | /// A sequence of random bytes generated by the server 22 | verify_token: Array<'a, u8, VarInt>, 23 | }, 24 | 25 | /// This packet switches the connection state to [ConnectionState::Play]. 26 | /// 27 | /// **Warning**: The (notchian) server might take a bit to fully transition to the `Play` state, so it's recommended to wait before sending `Play` packets, either by setting a timeout, or waiting for Play packets from the server (usually [PlayerInfo](super::play_clientbound::ClientboundPacket::PlayerInfo)). 28 | /// The notchian client doesn't send any (non-keep alive) packets until the next tick/time update packet. 29 | LoginSuccess { 30 | uuid: UUID, 31 | username: &'a str, 32 | properties: Array<'a, components::players::Property<'a>, VarInt>, 33 | }, 34 | 35 | /// Enables compression. 36 | /// If compression is enabled, all following packets are encoded in the compressed packet format. 37 | /// Negative or zero values will disable compression, meaning the packet format should remain in the uncompressed packet format. 38 | /// However, this packet is entirely optional, and if not sent, compression will also not be enabled (the notchian server does not send the packet when compression is disabled). 39 | SetCompression { 40 | /// Maximum size of a packet before it is compressed 41 | threshold: VarInt, 42 | }, 43 | 44 | /// Used to implement a custom handshaking flow. 45 | /// Unlike plugin messages in "play" mode, these messages follow a lock-step request/response scheme, where the client is expected to respond to a request indicating whether it understood. The notchian client always responds that it hasn't understood, and sends an empty payload. 46 | /// 47 | /// *Request for [ServerboundPacket::LoginPluginResponse]* 48 | LoginPluginRequest { 49 | /// Generated by the server - should be unique to the connection 50 | message_id: VarInt, 51 | /// Name of the plugin channel used to send the data 52 | channel: Identifier<'a>, 53 | /// Any data, depending on the channel 54 | data: RawBytes<'a>, 55 | }, 56 | } 57 | 58 | #[cfg_attr(test, derive(PartialEq))] 59 | #[derive(Debug, MinecraftPacketPart)] 60 | #[discriminant(VarInt)] 61 | pub enum ServerboundPacket<'a> { 62 | LoginStart { 63 | /// Player's Username 64 | username: &'a str, 65 | /// The UUID of the player logging in. Unused by the Notchian server. 66 | player_uuid: UUID, 67 | }, 68 | 69 | /// See [Protocol Encryption](https://wiki.vg/Protocol_Encryption) for details. 70 | /// 71 | /// *Response for [ClientboundPacket::EncryptionRequest]* 72 | EncryptionResponse { 73 | /// Shared Secret value, encrypted with the [server's public key](ClientboundPacket::EncryptionRequest::public_key). 74 | shared_secret: Array<'a, u8, VarInt>, 75 | /// Verify Token value, encrypted with the `shared_secret`. 76 | verify_token: Array<'a, u8, VarInt>, 77 | }, 78 | 79 | /// *Response for [ClientboundPacket::LoginPluginRequest]* 80 | LoginPluginResponse { 81 | /// Should match ID from server. 82 | message_id: VarInt, 83 | /// `Some(data)` if the client understands the [request](ClientboundPacket::LoginPluginRequest), `None` otherwise 84 | data: Option>, 85 | }, 86 | 87 | LoginAcknowledged, 88 | } 89 | -------------------------------------------------------------------------------- /minecraft-protocol/src/packets/status.rs: -------------------------------------------------------------------------------- 1 | //! For general information about pinging servers, see [the wiki](https://wiki.vg/Server_List_Ping). 2 | 3 | use crate::*; 4 | 5 | #[cfg_attr(test, derive(PartialEq))] 6 | #[derive(Debug, MinecraftPacketPart)] 7 | #[discriminant(VarInt)] 8 | pub enum ClientboundPacket<'a> { 9 | /// *Response to [ServerboundPacket::Request]* 10 | Response { 11 | /// See [Server List Ping#Response](https://wiki.vg/Server_List_Ping#Response); as with all strings this is prefixed by its length as a VarInt. 12 | json_response: &'a str, 13 | }, 14 | 15 | /// *Response to [ServerboundPacket::Ping]* 16 | Pong { 17 | /// Should be the same as sent by the client 18 | payload: i64, 19 | }, 20 | } 21 | #[cfg_attr(test, derive(PartialEq))] 22 | #[derive(Debug, MinecraftPacketPart)] 23 | #[discriminant(VarInt)] 24 | pub enum ServerboundPacket { 25 | /// *Request for [ClientboundPacket::Response]* 26 | Request, 27 | 28 | /// *Request for [ClientboundPacket::Pong]* 29 | Ping { 30 | /// May be any number. Notchian clients use a system-dependent time value which is counted in milliseconds. 31 | payload: i64, 32 | }, 33 | } 34 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_000.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_0() { 12 | let input = &[0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_001.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_1() { 12 | let input = &[1, 34, 234, 6, 197, 73, 217, 127, 79, 232, 151, 167, 112, 71, 86, 187, 106, 141, 118, 64, 134, 172, 0, 0, 0, 0, 0, 64, 63, 0, 0, 0, 0, 0, 0, 64, 130, 68, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 253, 141, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_002.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_2() { 12 | let input = &[2, 36, 64, 134, 157, 5, 90, 71, 44, 173, 64, 80, 64, 0, 0, 0, 0, 0, 64, 130, 94, 125, 255, 193, 222, 191, 0, 1]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_00A.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_a() { 12 | let input = &[10, 51, 221, 3, 69, 59, 196, 67, 92, 134, 219, 94, 232, 56, 79, 240, 36, 0, 130, 2, 123, 34, 105, 110, 115, 101, 114, 116, 105, 111, 110, 34, 58, 34, 56, 100, 55, 55, 97, 100, 98, 101, 45, 102, 56, 54, 56, 45, 52, 101, 97, 51, 45, 56, 57, 48, 57, 45, 55, 50, 102, 53, 49, 51, 57, 53, 51, 48, 100, 98, 34, 44, 34, 104, 111, 118, 101, 114, 69, 118, 101, 110, 116, 34, 58, 123, 34, 97, 99, 116, 105, 111, 110, 34, 58, 34, 115, 104, 111, 119, 95, 101, 110, 116, 105, 116, 121, 34, 44, 34, 99, 111, 110, 116, 101, 110, 116, 115, 34, 58, 123, 34, 116, 121, 112, 101, 34, 58, 34, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 119, 105, 116, 104, 101, 114, 34, 44, 34, 105, 100, 34, 58, 34, 56, 100, 55, 55, 97, 100, 98, 101, 45, 102, 56, 54, 56, 45, 52, 101, 97, 51, 45, 56, 57, 48, 57, 45, 55, 50, 102, 53, 49, 51, 57, 53, 51, 48, 100, 98, 34, 44, 34, 110, 97, 109, 101, 34, 58, 123, 34, 116, 114, 97, 110, 115, 108, 97, 116, 101, 34, 58, 34, 101, 110, 116, 105, 116, 121, 46, 109, 105, 110, 101, 99, 114, 97, 102, 116, 46, 119, 105, 116, 104, 101, 114, 34, 125, 125, 125, 44, 34, 116, 114, 97, 110, 115, 108, 97, 116, 101, 34, 58, 34, 101, 110, 116, 105, 116, 121, 46, 109, 105, 110, 101, 99, 114, 97, 102, 116, 46, 119, 105, 116, 104, 101, 114, 34, 125, 63, 128, 0, 0, 5, 0, 1]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_00B.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_b() { 12 | let input = &[11, 1, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_00C.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_c() { 12 | let input = &[12, 1]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_00D.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_d() { 12 | let input = &[13]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_013.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_13() { 12 | let input = &[19, 0, 1, 46, 0, 0, 0, 0, 0, 0, 1, 189, 6, 1, 10, 3, 0, 6, 68, 97, 109, 97, 103, 101, 0, 0, 0, 4, 0, 0, 0, 1, 250, 4, 1, 0, 1, 161, 3, 3, 0, 1, 172, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 201, 6, 13, 0, 1, 145, 5, 1, 0, 1, 246, 5, 1, 10, 3, 0, 6, 68, 97, 109, 97, 103, 101, 0, 0, 0, 4, 0, 1, 167, 6, 11, 0, 1, 237, 7, 1, 0, 1, 162, 6, 1, 10, 9, 0, 12, 69, 110, 99, 104, 97, 110, 116, 109, 101, 110, 116, 115, 10, 0, 0, 0, 1, 2, 0, 3, 108, 118, 108, 0, 5, 8, 0, 2, 105, 100, 0, 19, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 115, 104, 97, 114, 112, 110, 101, 115, 115, 0, 3, 0, 6, 68, 97, 109, 97, 103, 101, 0, 0, 0, 0, 0, 1, 184, 3, 6, 0, 1, 153, 3, 1, 0, 1, 44, 1, 0, 1, 250, 4, 64, 0, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_01D.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_1d() { 12 | let input = &[29, 0, 0, 0, 20, 28]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_01E.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_1e() { 12 | let input = &[30, 64, 135, 187, 58, 44, 169, 90, 246, 64, 81, 83, 224, 161, 142, 185, 216, 64, 130, 169, 47, 107, 158, 13, 171, 63, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_020.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_20() { 12 | let input = &[32, 2, 0, 0, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_023.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_23() { 12 | let input = &[35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 140, 156, 55, 0, 0, 0, 0, 65, 140, 156, 55, 0, 0, 0, 0, 0, 240, 134, 167, 14, 5, 15]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_026.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_26() { 12 | let input = &[38, 0, 0, 4, 0, 0, 0, 189, 128, 0, 37, 64, 73, 0, 0, 0, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_029.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_29() { 12 | let input = &[41, 0, 0, 0, 20, 0, 3, 19, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 111, 118, 101, 114, 119, 111, 114, 108, 100, 20, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 116, 104, 101, 95, 110, 101, 116, 104, 101, 114, 17, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 116, 104, 101, 95, 101, 110, 100, 20, 10, 10, 0, 1, 0, 19, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 111, 118, 101, 114, 119, 111, 114, 108, 100, 19, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 111, 118, 101, 114, 119, 111, 114, 108, 100, 116, 58, 202, 32, 182, 71, 111, 68, 1, 255, 0, 0, 1, 19, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 111, 118, 101, 114, 119, 111, 114, 108, 100, 0, 0, 32, 128, 0, 25, 208, 52, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_02C.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_2c() { 12 | let input = &[44, 75, 0, 26, 4, 4, 1, 158, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_02D.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_2d() { 12 | let input = &[45, 51, 4, 26, 2, 210, 7, 109, 213, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_02E.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_2e() { 12 | let input = &[46, 83, 3, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_036.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_36() { 12 | let input = &[54, 15, 61, 76, 204, 205, 61, 204, 204, 205]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_03C.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_3c() { 12 | let input = &[60, 63, 1, 195, 134, 29, 39, 164, 208, 51, 25, 151, 73, 18, 61, 57, 230, 183, 71, 8, 65, 101, 108, 111, 114, 105, 117, 115, 0, 0, 1, 1, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_03E.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_3e() { 12 | let input = &[62, 64, 134, 214, 23, 121, 189, 105, 23, 64, 82, 126, 225, 211, 109, 241, 109, 64, 130, 63, 231, 15, 182, 88, 161, 66, 245, 179, 47, 65, 102, 101, 143, 0, 1]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_040.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_40() { 12 | let input = &[64, 1, 132, 1]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_044.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_44() { 12 | let input = &[68, 48, 53]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_045.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_45() { 12 | let input = &[69, 0, 0, 188, 0, 2, 80, 0, 4, 3, 195, 16, 195, 14, 211, 14]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_047.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_47() { 12 | let input = &[71, 29, 123, 34, 116, 101, 120, 116, 34, 58, 34, 65, 32, 77, 105, 110, 101, 99, 114, 97, 102, 116, 32, 83, 101, 114, 118, 101, 114, 34, 125, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_04F.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_4f() { 12 | let input = &[79, 4]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_050.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_50() { 12 | let input = &[80, 45, 36]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_052.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_52() { 12 | let input = &[82, 0, 0, 28, 0, 0, 27, 0, 65, 0, 0, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_054.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_54() { 12 | let input = &[84, 20, 9, 3, 65, 30, 31, 100, 16, 1, 76, 17, 0, 127, 255]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_056.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_56() { 12 | let input = &[86, 35, 0, 0, 227, 64, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_057.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_57() { 12 | let input = &[87, 49, 0, 1, 248, 5, 1, 10, 3, 0, 6, 68, 97, 109, 97, 103, 101, 0, 0, 0, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_058.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_58() { 12 | let input = &[88, 62, 87, 148, 50, 6, 76]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_059.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_59() { 12 | let input = &[89, 65, 30, 31, 100, 16, 0, 0, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_060.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_60() { 12 | let input = &[96, 0, 0, 0, 0, 0, 22, 211, 233, 0, 0, 0, 0, 0, 22, 211, 233]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_067.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_67() { 12 | let input = &[103, 162, 1, 123, 34, 101, 120, 116, 114, 97, 34, 58, 91, 123, 34, 98, 111, 108, 100, 34, 58, 102, 97, 108, 115, 101, 44, 34, 105, 116, 97, 108, 105, 99, 34, 58, 102, 97, 108, 115, 101, 44, 34, 117, 110, 100, 101, 114, 108, 105, 110, 101, 100, 34, 58, 102, 97, 108, 115, 101, 44, 34, 115, 116, 114, 105, 107, 101, 116, 104, 114, 111, 117, 103, 104, 34, 58, 102, 97, 108, 115, 101, 44, 34, 111, 98, 102, 117, 115, 99, 97, 116, 101, 100, 34, 58, 102, 97, 108, 115, 101, 44, 34, 99, 111, 108, 111, 114, 34, 58, 34, 121, 101, 108, 108, 111, 119, 34, 44, 34, 116, 101, 120, 116, 34, 58, 34, 65, 101, 108, 111, 114, 105, 117, 115, 32, 106, 111, 105, 110, 101, 100, 32, 116, 104, 101, 32, 103, 97, 109, 101, 34, 125, 93, 44, 34, 116, 101, 120, 116, 34, 58, 34, 34, 125, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_06B.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_6b() { 12 | let input = &[107, 75, 64, 134, 101, 82, 60, 186, 253, 194, 192, 64, 128, 0, 0, 0, 0, 0, 64, 130, 119, 79, 245, 90, 214, 254, 6, 0, 1]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_clientbound_06D.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_clientbound::ClientboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_clientbound_6d() { 12 | let input = &[109, 20, 5, 30, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 103, 101, 110, 101, 114, 105, 99, 46, 97, 116, 116, 97, 99, 107, 95, 115, 112, 101, 101, 100, 64, 16, 0, 0, 0, 0, 0, 0, 0, 33, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 103, 101, 110, 101, 114, 105, 99, 46, 97, 114, 109, 111, 114, 95, 116, 111, 117, 103, 104, 110, 101, 115, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 103, 101, 110, 101, 114, 105, 99, 46, 109, 111, 118, 101, 109, 101, 110, 116, 95, 115, 112, 101, 101, 100, 63, 185, 153, 153, 160, 0, 0, 0, 0, 28, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 103, 101, 110, 101, 114, 105, 99, 46, 109, 97, 120, 95, 104, 101, 97, 108, 116, 104, 64, 52, 0, 0, 0, 0, 0, 0, 0, 23, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 103, 101, 110, 101, 114, 105, 99, 46, 97, 114, 109, 111, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 13 | let packet_deserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | //reserialize let _reserialized = ClientboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | //reserialize assert!(matches!(ClientboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_serverbound_000.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_serverbound::ServerboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_serverbound_0() { 12 | let input = &[0, 1]; 13 | let packet_deserialized = ServerboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | let _reserialized = ServerboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | assert!(matches!(ServerboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_serverbound_007.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_serverbound::ServerboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_serverbound_7() { 12 | let input = &[7, 64, 22, 100, 172]; 13 | let packet_deserialized = ServerboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | let _reserialized = ServerboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | assert!(matches!(ServerboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_serverbound_016.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_serverbound::ServerboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_serverbound_16() { 12 | let input = &[22, 64, 134, 214, 23, 121, 189, 105, 23, 64, 82, 126, 225, 211, 109, 241, 109, 64, 130, 63, 231, 15, 182, 88, 161, 0]; 13 | let packet_deserialized = ServerboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | let _reserialized = ServerboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | assert!(matches!(ServerboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_serverbound_017.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_serverbound::ServerboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_serverbound_17() { 12 | let input = &[23, 64, 134, 214, 23, 121, 189, 105, 23, 64, 82, 126, 225, 211, 109, 241, 109, 64, 130, 63, 231, 15, 182, 88, 161, 66, 245, 179, 47, 65, 102, 101, 143, 0]; 13 | let packet_deserialized = ServerboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | let _reserialized = ServerboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | assert!(matches!(ServerboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_serverbound_018.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_serverbound::ServerboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_serverbound_18() { 12 | let input = &[24, 66, 245, 179, 47, 65, 97, 152, 195, 0]; 13 | let packet_deserialized = ServerboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | let _reserialized = ServerboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | assert!(matches!(ServerboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/auto_play_serverbound_02B.rs: -------------------------------------------------------------------------------- 1 | 2 | //! This test was automatically generated. Please run the proxy example to regenerate it. 3 | //! 4 | //! ``` 5 | //! cargo run --example proxy 6 | //! ``` 7 | 8 | use minecraft_protocol::{MinecraftPacketPart, packets::play_serverbound::ServerboundPacket}; 9 | 10 | #[test] 11 | fn auto_play_serverbound_2b() { 12 | let input = &[43, 0, 4]; 13 | let packet_deserialized = ServerboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(); 14 | 15 | match packet_deserialized.serialize_minecraft_packet() { 16 | Ok(packet) => { 17 | let _reserialized = ServerboundPacket::deserialize_uncompressed_minecraft_packet(&packet).unwrap(); 18 | assert!(matches!(ServerboundPacket::deserialize_uncompressed_minecraft_packet(input).unwrap(), _reserialized)); 19 | } 20 | Err(e) => panic!("Failed to serialize packet: {:?}", e), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-protocol/tests/chunk2.dump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mubelotix/minecraft-protocol/69c7bef61dfa06a58287cf979781c4a61098c094/minecraft-protocol/tests/chunk2.dump -------------------------------------------------------------------------------- /minecraft-protocol/tests/chunk2.dump.tags: -------------------------------------------------------------------------------- 1 | [{"from":0,"to":1,"color":"red","caption":"block count"},{"from":2,"to":2,"color":"orange","caption":"bits per entry"},{"from":3,"to":3,"color":"green","caption":"palette lenght"},{"from":4,"to":17,"color":"lime","caption":"palette"},{"from":18,"to":19,"color":"green","caption":"indexed data lenght"},{"from":20,"to":2755,"color":"aqua","caption":"indexed block data"},{"from":2756,"to":2756,"color":"orange","caption":"bits per entry (biome)"},{"from":2757,"to":2757,"color":"green","caption":"palette lenght (biomes)"},{"from":2758,"to":2759,"color":"lime","caption":"palette (biomes)"},{"from":2760,"to":2760,"color":"green","caption":"indexed data lenght (biomes)"},{"from":2761,"to":2768,"color":"yellow","caption":"indexed data (biomes)"},{"from":2769,"to":2770,"color":"red","caption":"block count"},{"from":2771,"to":2771,"color":"orange","caption":"bits per entry"},{"from":2772,"to":2772,"color":"green","caption":"palette lenght"},{"from":2773,"to":2781,"color":"lime","caption":"palette"},{"from":2782,"to":2783,"color":"green","caption":"indexed data lenght"},{"from":2784,"to":5519,"color":"aqua","caption":"indexed data"},{"from":5520,"to":5520,"color":"orange","caption":"bits per entry (biomes)"},{"from":5521,"to":5521,"color":"green","caption":"palette lenght (biomes)"},{"from":5522,"to":5523,"color":"lime","caption":"palette (biomes)"},{"from":5524,"to":5524,"color":"green","caption":"indexed data lenght (biomes)"},{"from":5525,"to":5532,"color":"yellow","caption":"indexed data (biomes)"},{"from":11061,"to":11062,"color":"red","caption":"block count"},{"from":11063,"to":11063,"color":"orange","caption":"bits per entry"},{"from":11064,"to":11064,"color":"green","caption":"palette lenght"},{"from":11065,"to":11093,"color":"lime","caption":"palette"},{"from":11094,"to":11096,"color":"green","caption":"indexed data lenght"},{"from":11097,"to":13846,"color":"aqua","caption":"indexed data"},{"from":13847,"to":13847,"color":"orange","caption":"bits per entry"},{"from":13848,"to":13848,"color":"green","caption":"palette lenght (biomes)"},{"from":13849,"to":13850,"color":"lime","caption":"palette (biomes)"},{"from":13851,"to":13851,"color":"green","caption":"indexed data lenght (biomes)"},{"from":13852,"to":13859,"color":"yellow","caption":"indexed data (biomes)"},{"from":24231,"to":24232,"color":"red","caption":"block count"},{"from":24233,"to":24233,"color":"orange","caption":"bits per entry"},{"from":24234,"to":24234,"color":"lime","caption":"palette"},{"from":24235,"to":24235,"color":"green","caption":"indexed data lenght (always 0 when bits per entry = 0)"},{"from":24236,"to":24236,"color":"orange","caption":"bits per entry (biomes)"},{"from":24237,"to":24237,"color":"green","caption":"palette lenght (biomes)"},{"from":24238,"to":24239,"color":"lime","caption":"palette (biomes)"},{"from":24240,"to":24240,"color":"green","caption":"indexed data lenght (biomes)"},{"from":24241,"to":24248,"color":"yellow","caption":"indexed data (biomes)"}] -------------------------------------------------------------------------------- /minecraft-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minecraft-server" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | env_logger = "0.10.0" 10 | log = "0.4.20" 11 | tokio = { version = "1.33.0", features = ["full"] } 12 | futures = "0.3.29" 13 | minecraft-protocol = { path="../minecraft-protocol" } 14 | minecraft-positions = { path="../minecraft-positions" } 15 | minecraft-entities-derive = { path="../minecraft-entities-derive" } 16 | rand = "0.8.4" 17 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/axolotl.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Axolotl { 8 | pub animal: Animal, 9 | pub variant: u8, 10 | pub playing_dead: bool, 11 | pub spawn_from_bucket: bool, 12 | } 13 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/bee.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Bee { 8 | pub animal: Animal, 9 | pub flag: u8, 10 | pub anger_time: usize, 11 | } 12 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/cat.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[MinecraftEntity( 4 | ancestors { TameableAnimal, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 5 | )] 6 | pub struct Cat { 7 | pub tameable_animal: TameableAnimal, 8 | pub variant: u8, 9 | pub is_lying: bool, 10 | pub is_relaxed: bool, 11 | pub collar_color: u8, 12 | } 13 | 14 | impl Default for Cat { 15 | fn default() -> Self { 16 | Self { 17 | tameable_animal: Default::default(), 18 | variant: 0, 19 | is_lying: false, 20 | is_relaxed: false, 21 | collar_color: 14, 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/chicken.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Chicken { 8 | pub animal: Animal, 9 | } 10 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/cow.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | inheritable, 6 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 7 | descendants { Mooshroom }, 8 | )] 9 | pub struct Cow { 10 | pub animal: Animal, 11 | } 12 | 13 | #[derive(Default)] 14 | #[MinecraftEntity( 15 | ancestors { Cow, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 16 | )] 17 | pub struct Mooshroom { 18 | pub cow: Cow, 19 | pub variant: u8, // In the doc it is a string 20 | } 21 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/fox.rs: -------------------------------------------------------------------------------- 1 | use minecraft_protocol::packets::UUID; 2 | use super::*; 3 | 4 | #[derive(Default)] 5 | #[MinecraftEntity( 6 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 7 | )] 8 | pub struct Fox { 9 | pub animal: Animal, 10 | pub variant: u8, 11 | pub mask: u8, 12 | pub first_uuid: Option, 13 | pub second_uuid: Option, 14 | } 15 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/frog.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Frog { 8 | pub animal: Animal, 9 | pub variant: u8, 10 | pub tongue_target: Option, 11 | } 12 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/goat.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[MinecraftEntity( 4 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 5 | )] 6 | pub struct Goat { 7 | pub animal: Animal, 8 | pub is_screaming: bool, 9 | pub has_left_horn: bool, 10 | pub has_right_horn: bool, 11 | } 12 | 13 | impl Default for Goat { 14 | fn default() -> Self { 15 | Self { 16 | animal: Animal::default(), 17 | is_screaming: false, 18 | has_left_horn: true, 19 | has_right_horn: true, 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/hoglin.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Hoglin { 8 | pub animal: Animal, 9 | pub is_immune: bool, 10 | } 11 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/horses.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | inheritable, 6 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 7 | descendants { Horse, ZombieHorse, SkeletonHorse, Camel, ChestedHorse... }, 8 | )] 9 | pub struct AbstractHorse { 10 | pub animal: Animal, 11 | pub mask: u8, 12 | } 13 | 14 | #[derive(Default)] 15 | #[MinecraftEntity( 16 | ancestors { AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 17 | )] 18 | pub struct Horse { 19 | pub abstract_horse: AbstractHorse, 20 | pub variant: usize, 21 | } 22 | 23 | #[derive(Default)] 24 | #[MinecraftEntity( 25 | ancestors { AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 26 | )] 27 | pub struct ZombieHorse { 28 | pub abstract_horse: AbstractHorse, 29 | } 30 | 31 | #[derive(Default)] 32 | #[MinecraftEntity( 33 | ancestors { AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 34 | )] 35 | pub struct SkeletonHorse { 36 | pub abstract_horse: AbstractHorse, 37 | } 38 | 39 | #[derive(Default)] 40 | #[MinecraftEntity( 41 | ancestors { AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 42 | )] 43 | pub struct Camel { 44 | pub abstract_horse: AbstractHorse, 45 | pub is_dashing: bool, 46 | pub last_pose_change_tick: usize, 47 | } 48 | 49 | #[derive(Default)] 50 | #[MinecraftEntity( 51 | inheritable, 52 | ancestors { AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 53 | descendants { Mule, Donkey, Llama... }, 54 | )] 55 | pub struct ChestedHorse { 56 | pub abstract_horse: AbstractHorse, 57 | pub has_chest: bool, 58 | } 59 | 60 | #[derive(Default)] 61 | #[MinecraftEntity( 62 | ancestors { ChestedHorse, AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 63 | )] 64 | pub struct Mule { 65 | pub chested_horse: ChestedHorse, 66 | } 67 | 68 | #[derive(Default)] 69 | #[MinecraftEntity( 70 | ancestors { ChestedHorse, AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 71 | )] 72 | pub struct Donkey { 73 | pub chested_horse: ChestedHorse, 74 | } 75 | 76 | #[MinecraftEntity( 77 | inheritable, 78 | ancestors { ChestedHorse, AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 79 | descendants { TraderLlama }, 80 | )] 81 | pub struct Llama { 82 | pub chested_horse: ChestedHorse, 83 | /// Strength (number of columns of 3 slots in the llama's inventory once a chest is equipped) 84 | pub stength: u8, 85 | /// Carpet color (a dye color, or -1 if no carpet equipped) 86 | pub carpet_color: i16, 87 | pub variant: u8, 88 | } 89 | 90 | impl Default for Llama { 91 | fn default() -> Self { 92 | Self { 93 | chested_horse: ChestedHorse::default(), 94 | stength: 0, 95 | carpet_color: -1, 96 | variant: 0, 97 | } 98 | } 99 | } 100 | 101 | #[derive(Default)] 102 | #[MinecraftEntity( 103 | ancestors { Llama, ChestedHorse, AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 104 | )] 105 | pub struct TraderLlama { 106 | pub llama: Llama, 107 | } 108 | 109 | 110 | #[derive(Default)] 111 | #[MinecraftEntity( 112 | ancestors { Entity }, 113 | )] 114 | pub struct LlamaSpit { 115 | pub entity: Entity, 116 | } 117 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/mod.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | mod sniffer; 4 | pub use sniffer::*; 5 | mod horses; 6 | pub use horses::*; 7 | mod axolotl; 8 | pub use axolotl::*; 9 | mod bee; 10 | pub use bee::*; 11 | mod fox; 12 | pub use fox::*; 13 | mod frog; 14 | pub use frog::*; 15 | mod ocelot; 16 | pub use ocelot::*; 17 | mod panda; 18 | pub use panda::*; 19 | mod pig; 20 | pub use pig::*; 21 | mod rabbit; 22 | pub use rabbit::*; 23 | mod turtle; 24 | pub use turtle::*; 25 | mod polar_bear; 26 | pub use polar_bear::*; 27 | mod chicken; 28 | pub use chicken::*; 29 | mod cow; 30 | pub use cow::*; 31 | mod hoglin; 32 | pub use hoglin::*; 33 | mod sheep; 34 | pub use sheep::*; 35 | mod strider; 36 | pub use strider::*; 37 | mod cat; 38 | pub use cat::*; 39 | mod wolf; 40 | pub use wolf::*; 41 | mod parrot; 42 | pub use parrot::*; 43 | mod goat; 44 | pub use goat::*; 45 | mod water_animal; 46 | pub use water_animal::*; 47 | 48 | #[derive(Default)] 49 | #[MinecraftEntity( 50 | inheritable, 51 | ancestors { AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 52 | descendants { Axolotl, Bee, Chicken, Cow, Fox, Frog, Goat, Hoglin, Ocelot, Panda, Pig, PolarBear, Rabbit, Sheep, Sniffer, Strider, Turtle, AbstractHorse..., TameableAnimal... }, 53 | )] 54 | pub struct Animal { 55 | pub ageable_mob: AgeableMob, 56 | } 57 | 58 | #[derive(Default)] 59 | #[MinecraftEntity( 60 | inheritable, 61 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 62 | descendants { Cat, Parrot, Wolf } 63 | )] 64 | pub struct TameableAnimal { 65 | pub animal: Animal, 66 | pub action_mask: u8, 67 | pub owner: Option, 68 | } 69 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/ocelot.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Ocelot { 8 | pub animal: Animal, 9 | pub is_trusting: bool, 10 | } 11 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/panda.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Panda { 8 | pub animal: Animal, 9 | pub breed_timer: u16, 10 | pub sneeze_timer: u16, 11 | pub eat_timer: u16, 12 | pub main_gene: u8, 13 | pub hidden_gene: u8, 14 | pub action_mask: u8, 15 | } 16 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/parrot.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { TameableAnimal, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Parrot { 8 | pub tameable_animal: TameableAnimal, 9 | pub variant: u8, 10 | } 11 | 12 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/pig.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Pig { 8 | pub animal: Animal, 9 | pub has_saddle: bool, 10 | pub boost_time: u16, 11 | } 12 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/polar_bear.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct PolarBear { 8 | pub animal: Animal, 9 | pub is_standing: bool, 10 | } 11 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/rabbit.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Rabbit { 8 | pub animal: Animal, 9 | pub variant: u16, 10 | } 11 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/sheep.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Sheep { 8 | pub animal: Animal, 9 | pub mask_style: u8, 10 | } 11 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/sniffer.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Sniffer { 8 | pub animal: Animal, 9 | pub sniffer_state: u8, 10 | pub drop_seed_at_tick: usize, 11 | } 12 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/strider.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Strider { 8 | pub animal: Animal, 9 | pub boost_time: u16, 10 | pub is_shaking: bool, 11 | pub has_saddle: bool, 12 | } 13 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/turtle.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Turtle { 8 | pub animal: Animal, 9 | pub block_position: BlockPosition, 10 | pub has_egg: bool, 11 | pub is_laying_egg: bool, 12 | pub travel_position: Option, 13 | pub is_going_home: bool, 14 | } 15 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/water_animal.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | inheritable, 6 | ancestors { PathfinderMob, Mob, LivingEntity, Entity }, 7 | descendants { Dolphin, Squid, AbstractFish... }, 8 | )] 9 | pub struct WaterAnimal { 10 | pub pathfinder_mob: PathfinderMob, 11 | } 12 | 13 | #[derive(Default)] 14 | #[MinecraftEntity( 15 | ancestors { WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, 16 | )] 17 | pub struct Dolphin { 18 | pub water_animal: WaterAnimal, 19 | pub treasure_position: Option, 20 | pub has_fish: bool, 21 | pub moisture_level: usize, 22 | } 23 | 24 | #[derive(Default)] 25 | #[MinecraftEntity( 26 | ancestors { WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, 27 | )] 28 | pub struct Squid { 29 | pub water_animal: WaterAnimal, 30 | } 31 | 32 | #[derive(Default)] 33 | #[MinecraftEntity( 34 | inheritable, 35 | ancestors { WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, 36 | descendants { Cod, Pufferfish, Salmon, TropicalFish, Tadpole... }, 37 | )] 38 | pub struct AbstractFish { 39 | pub water_animal: WaterAnimal, 40 | pub from_bucket: bool, 41 | } 42 | 43 | #[derive(Default)] 44 | #[MinecraftEntity( 45 | ancestors { AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, 46 | )] 47 | pub struct Cod { 48 | pub abstract_fish: AbstractFish, 49 | } 50 | 51 | #[derive(Default)] 52 | #[MinecraftEntity( 53 | ancestors { AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, 54 | )] 55 | pub struct Pufferfish { 56 | pub abstract_fish: AbstractFish, 57 | pub puff_state: usize, 58 | } 59 | 60 | #[derive(Default)] 61 | #[MinecraftEntity( 62 | ancestors { AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, 63 | )] 64 | pub struct Salmon { 65 | pub abstract_fish: AbstractFish, 66 | } 67 | 68 | #[derive(Default)] 69 | #[MinecraftEntity( 70 | ancestors { AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, 71 | )] 72 | pub struct TropicalFish { 73 | pub abstract_fish: AbstractFish, 74 | pub variant: usize, 75 | } 76 | 77 | #[derive(Default)] 78 | #[MinecraftEntity( 79 | ancestors { AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, 80 | )] 81 | pub struct Tadpole { 82 | pub abstract_fish: AbstractFish, 83 | } 84 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/animals/wolf.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[MinecraftEntity( 4 | ancestors { TameableAnimal, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 5 | )] 6 | pub struct Wolf { 7 | pub tameable_animal: TameableAnimal, 8 | pub is_begging: bool, 9 | pub collar_color: u8, 10 | pub anger: u16, 11 | } 12 | 13 | impl Default for Wolf { 14 | fn default() -> Self { 15 | Self { 16 | tameable_animal: Default::default(), 17 | is_begging: false, 18 | collar_color: 14, 19 | anger: 0, 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/arrow.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[MinecraftEntity( 4 | inheritable, 5 | ancestors { Entity }, 6 | descendants { Arrow, SpectralArrow, ThrownTrident }, 7 | )] 8 | pub struct AbstractArrow { 9 | pub entity: Entity, 10 | pub is_critical: bool, 11 | pub is_no_clip: bool, 12 | pub piercing_level: isize, 13 | } 14 | 15 | impl Default for AbstractArrow { 16 | fn default() -> Self { 17 | Self { 18 | entity: Entity::default(), 19 | is_critical: false, 20 | is_no_clip: false, 21 | piercing_level: 0, 22 | } 23 | } 24 | } 25 | 26 | #[MinecraftEntity( 27 | ancestors { AbstractArrow, Entity }, 28 | )] 29 | pub struct Arrow { 30 | pub abstract_arrow: AbstractArrow, 31 | pub color: isize, 32 | } 33 | 34 | impl Default for Arrow { 35 | fn default() -> Self { 36 | Self { 37 | abstract_arrow: AbstractArrow::default(), 38 | color: -1, 39 | } 40 | } 41 | } 42 | 43 | #[MinecraftEntity( 44 | ancestors { AbstractArrow, Entity }, 45 | )] 46 | pub struct SpectralArrow { 47 | pub abstract_arrow: AbstractArrow, 48 | pub loyalty_level: isize, 49 | pub has_enchantment_glint: bool, 50 | } 51 | 52 | impl Default for SpectralArrow { 53 | fn default() -> Self { 54 | Self { 55 | abstract_arrow: AbstractArrow::default(), 56 | loyalty_level: 0, 57 | has_enchantment_glint: false, 58 | } 59 | } 60 | } 61 | 62 | #[MinecraftEntity( 63 | ancestors { AbstractArrow, Entity }, 64 | )] 65 | pub struct ThrownTrident { 66 | pub abstract_arrow: AbstractArrow, 67 | pub loyalty_level: isize, 68 | pub has_enchantment_glint: bool, 69 | } 70 | 71 | impl Default for ThrownTrident { 72 | fn default() -> Self { 73 | Self { 74 | abstract_arrow: AbstractArrow::default(), 75 | loyalty_level: 0, 76 | has_enchantment_glint: false, 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/block.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Entity }, 6 | )] 7 | pub struct FallingBlock { 8 | pub entity: Entity, 9 | pub spawn_position: BlockPosition, 10 | } 11 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/boat.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[MinecraftEntity( 4 | inheritable, 5 | ancestors { Entity }, 6 | descendants { ChestBoat }, 7 | )] 8 | pub struct Boat { 9 | pub entity: Entity, 10 | pub time_since_last_hit: usize, 11 | pub forward_direction: usize, 12 | pub damage_taken: f32, 13 | /// Type (0=oak, 1=spruce, 2=birch, 3=jungle, 4=acacia, 5=dark oak) 14 | pub ty: usize, 15 | pub is_left_paddle_turning: bool, 16 | pub is_right_paddle_turning: bool, 17 | pub splash_timer: usize, 18 | } 19 | 20 | impl Default for Boat { 21 | fn default() -> Self { 22 | Boat { 23 | entity: Entity::default(), 24 | time_since_last_hit: 0, 25 | forward_direction: 1, 26 | damage_taken: 0.0, 27 | ty: 0, 28 | is_left_paddle_turning: false, 29 | is_right_paddle_turning: false, 30 | splash_timer: 0, 31 | } 32 | } 33 | } 34 | 35 | #[derive(Default)] 36 | #[MinecraftEntity( 37 | ancestors { Boat, Entity }, 38 | )] 39 | pub struct ChestBoat { 40 | pub boat: Boat, 41 | } 42 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/display.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use minecraft_protocol::components::paintings::Painting as PaintingType; 3 | 4 | #[MinecraftEntity( 5 | inheritable, 6 | ancestors { Entity }, 7 | descendants { BlockDisplay, ItemDisplay, TextDisplay }, 8 | )] 9 | pub struct Display { 10 | pub entity: Entity, 11 | pub interpolation_delay: u32, 12 | pub transformation_interpolation_duration: u32, 13 | pub position_interpolation_duration: u32, 14 | pub translation: (f64, f64, f64), 15 | pub scale: (f64, f64, f64), 16 | pub rotation_left: (f64, f64, f64, f64), 17 | pub rotation_right: (f64, f64, f64, f64), 18 | pub fixed_constraint: bool, 19 | pub vertical_constraint: bool, 20 | pub horizontal_constraint: bool, 21 | pub center_constraint: bool, 22 | pub brightness: isize, 23 | pub view_range: f32, 24 | pub shadow_radius: f32, 25 | pub shadow_strenght: f32, 26 | pub width: f32, 27 | pub height: f32, 28 | pub glow_color: isize, 29 | } 30 | 31 | impl Default for Display { 32 | fn default() -> Self { 33 | Self { 34 | entity: Entity::default(), 35 | interpolation_delay: 0, 36 | transformation_interpolation_duration: 0, 37 | position_interpolation_duration: 0, 38 | translation: (0., 0., 0.), 39 | scale: (0., 0., 0.), 40 | rotation_left: (0., 0., 0., 1.), 41 | rotation_right: (0., 0., 0., 1.), 42 | fixed_constraint: false, 43 | vertical_constraint: false, 44 | horizontal_constraint: false, 45 | center_constraint: false, 46 | brightness: -1, 47 | view_range: 1., 48 | shadow_radius: 0., 49 | shadow_strenght: 1., 50 | width: 0., 51 | height: 0., 52 | glow_color: -1, 53 | } 54 | } 55 | } 56 | 57 | #[MinecraftEntity( 58 | ancestors { Display, Entity }, 59 | )] 60 | pub struct BlockDisplay { 61 | pub display: Display, 62 | pub block: BlockWithState, 63 | } 64 | 65 | impl Default for BlockDisplay { 66 | fn default() -> Self { 67 | Self { 68 | display: Display::default(), 69 | block: BlockWithState::Air, 70 | } 71 | } 72 | } 73 | 74 | #[MinecraftEntity( 75 | ancestors { Display, Entity }, 76 | )] 77 | pub struct ItemDisplay { 78 | pub display: Display, 79 | pub item: Slot, 80 | pub display_type: u8, 81 | } 82 | 83 | impl Default for ItemDisplay { 84 | fn default() -> Self { 85 | Self { 86 | display: Display::default(), 87 | item: Slot { item: None }, 88 | display_type: 0, 89 | } 90 | } 91 | } 92 | 93 | #[MinecraftEntity( 94 | ancestors { Display, Entity }, 95 | )] 96 | pub struct TextDisplay { 97 | pub display: Display, 98 | pub text: String, 99 | pub line_width: usize, 100 | pub background_color: isize, 101 | pub text_opacity: i8, 102 | pub has_shadow: bool, 103 | pub is_seethrough: bool, 104 | pub use_default_background: bool, 105 | pub alignement: u8, 106 | } 107 | 108 | impl Default for TextDisplay { 109 | fn default() -> Self { 110 | Self { 111 | display: Display::default(), 112 | text: String::new(), 113 | line_width: 0, 114 | background_color: 1073741824, 115 | text_opacity: -1, 116 | has_shadow: false, 117 | is_seethrough: false, 118 | use_default_background: false, 119 | alignement: 0, 120 | } 121 | } 122 | } 123 | 124 | #[derive(Default)] 125 | #[MinecraftEntity( 126 | ancestors { Entity }, 127 | )] 128 | pub struct Painting { 129 | pub entity: Entity, 130 | pub painting_type: PaintingType, 131 | } 132 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/entity.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[MinecraftEntity( 4 | inheritable, 5 | descendants { AbstractArrow..., Boat..., Display, FallingBlock, LlamaSpit, Painting, DragonFireball, Fireball..., FireworkRocket, SmallFireball, Interaction..., ItemEntity, ItemFrame..., LivingEntity... EndCrystal, EvokerFangs, WitherSkull, AreaEffectCloud, FishingHook, EyeOfEnder, ThrownItemProjectile... }, 6 | defines { 7 | init(self, server_msg_rcvr: BroadcastReceiver); 8 | } 9 | )] 10 | pub struct Entity { 11 | pub position: Position, 12 | pub velocity: Translation, 13 | pub pitch: f32, 14 | pub yaw: f32, 15 | pub is_on_fire: bool, 16 | pub is_crouching: bool, 17 | pub is_sprinting: bool, 18 | pub is_swimming: bool, 19 | pub is_invisible: bool, 20 | pub is_glowing: bool, 21 | pub is_fying_with_elytra: bool, 22 | pub air_ticks: u32, 23 | pub name: Option, 24 | pub is_name_visible: bool, 25 | pub is_silent: bool, 26 | pub has_no_gravity: bool, 27 | pub pose: Pose, 28 | pub ticks_frozen: u32, 29 | } 30 | 31 | impl Handler { 32 | pub async fn init(self, server_msg_rcvr: BroadcastReceiver) { 33 | self.insert_task("newton", tokio::spawn(newton_task(self.clone(), server_msg_rcvr))).await; 34 | } 35 | } 36 | 37 | impl Default for Entity { 38 | fn default() -> Self { 39 | Entity { 40 | position: Position { x: 0.0, y: 0.0, z: 0.0 }, 41 | velocity: Translation { x: 0.0, y: 0.0, z: 0.0 }, 42 | pitch: 0.0, 43 | yaw: 0.0, 44 | is_on_fire: false, 45 | is_crouching: false, 46 | is_sprinting: false, 47 | is_swimming: false, 48 | is_invisible: false, 49 | is_glowing: false, 50 | is_fying_with_elytra: false, 51 | air_ticks: 300, 52 | name: None, 53 | is_name_visible: false, 54 | is_silent: false, 55 | has_no_gravity: false, 56 | pose: Pose::Standing, 57 | ticks_frozen: 0, 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/fire_entities.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Entity }, 6 | )] 7 | pub struct DragonFireball { 8 | pub entity: Entity, 9 | } 10 | 11 | #[derive(Default)] 12 | #[MinecraftEntity( 13 | ancestors { Entity }, 14 | )] 15 | pub struct SmallFireball { 16 | pub entity: Entity, 17 | pub item: Slot, 18 | } 19 | 20 | #[derive(Default)] 21 | #[MinecraftEntity( 22 | ancestors { Entity }, 23 | )] 24 | pub struct Fireball { 25 | pub entity: Entity, 26 | pub item: Slot, 27 | } 28 | 29 | #[derive(Default)] 30 | #[MinecraftEntity( 31 | ancestors { Entity }, 32 | )] 33 | pub struct FireworkRocket { 34 | pub entity: Entity, 35 | pub item: Slot, 36 | pub used_by: Option, 37 | pub is_shot_at_angle: bool, 38 | } 39 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/interaction.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[MinecraftEntity( 4 | ancestors { Entity }, 5 | )] 6 | pub struct Interaction { 7 | pub entity: Entity, 8 | pub width: f32, 9 | pub height: f32, 10 | pub responsive: bool, 11 | } 12 | 13 | impl Default for Interaction { 14 | fn default() -> Self { 15 | Interaction { 16 | entity: Entity::default(), 17 | width: 1.0, 18 | height: 1.0, 19 | responsive: false, 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/item.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | inheritable, 6 | ancestors { Entity }, 7 | descendants { GlowingItemFrame }, 8 | )] 9 | pub struct ItemFrame { 10 | pub entity: Entity, 11 | pub item: Slot, 12 | pub rotation: u8, 13 | } 14 | 15 | #[derive(Default)] 16 | #[MinecraftEntity( 17 | ancestors { ItemFrame, Entity }, 18 | )] 19 | pub struct GlowingItemFrame { 20 | pub item_frame: ItemFrame, 21 | } 22 | 23 | #[derive(Default)] 24 | #[MinecraftEntity( 25 | ancestors { Entity }, 26 | )] 27 | pub struct ItemEntity { 28 | pub entity: Entity, 29 | pub item: Slot, 30 | } 31 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/living_entity.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[MinecraftEntity( 4 | inheritable, 5 | ancestors { Entity }, 6 | descendants { Player, ArmorStand, Mob... }, 7 | )] 8 | pub struct LivingEntity { 9 | pub entity: Entity, 10 | pub head_yaw: f32, 11 | pub is_hand_active: bool, 12 | pub active_hand: Hand, 13 | pub is_riptide_spinning: bool, 14 | pub health: f32, 15 | pub potion_effect_color: usize, 16 | pub is_potion_effect_ambient: bool, 17 | pub arrows_count: usize, 18 | pub bee_stinger_count: usize, 19 | pub bed: Option, 20 | } 21 | 22 | impl Default for LivingEntity { 23 | fn default() -> Self { 24 | LivingEntity { 25 | entity: Entity::default(), 26 | head_yaw: 0.0, 27 | is_hand_active: false, 28 | active_hand: Hand::MainHand, 29 | is_riptide_spinning: false, 30 | health: 1.0, 31 | potion_effect_color: 0, 32 | is_potion_effect_ambient: false, 33 | arrows_count: 0, 34 | bee_stinger_count: 0, 35 | bed: None, 36 | } 37 | } 38 | } 39 | 40 | #[MinecraftEntity( 41 | ancestors { LivingEntity, Entity }, 42 | )] 43 | pub struct ArmorStand { 44 | pub living_entity: LivingEntity, 45 | pub apparence_mask: u8, 46 | pub head_rotation: Rotation, 47 | pub body_rotation: Rotation, 48 | pub left_arm_rotation: Rotation, 49 | pub right_arm_rotation: Rotation, 50 | pub left_leg_rotation: Rotation, 51 | pub right_leg_rotation: Rotation, 52 | } 53 | 54 | impl Default for ArmorStand { 55 | fn default() -> Self { 56 | Self { 57 | living_entity: LivingEntity::default(), 58 | apparence_mask: 0, 59 | head_rotation: Rotation::default(), 60 | body_rotation: Rotation::default(), 61 | left_arm_rotation: Rotation { 62 | x: -10.0, 63 | y: 0.0, 64 | z: -10.0, 65 | }, 66 | right_arm_rotation: Rotation { 67 | x: -15.0, 68 | y: 0.0, 69 | z: 10.0, 70 | }, 71 | left_leg_rotation: Rotation { 72 | x: -1.0, 73 | y: 0.0, 74 | z: -1.0, 75 | }, 76 | right_leg_rotation: Rotation { 77 | x: 1.0, 78 | y: 0.0, 79 | z: 1.0, 80 | }, 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/mobs/bat.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { AmbientCreature, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Bat { 8 | pub ambient_creature: AmbientCreature, 9 | pub is_hanging: bool, 10 | } 11 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/mobs/ender_dragon.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[MinecraftEntity( 4 | ancestors { Mob, LivingEntity, Entity }, 5 | )] 6 | pub struct EnderDragon { 7 | pub mob: Mob, 8 | pub phase: usize, 9 | } 10 | 11 | impl Default for EnderDragon { 12 | fn default() -> Self { 13 | Self { 14 | mob: Mob::default(), 15 | phase: 10, 16 | } 17 | } 18 | } 19 | 20 | #[MinecraftEntity( 21 | ancestors { Entity }, 22 | )] 23 | pub struct EndCrystal { 24 | pub entity: Entity, 25 | pub block_position: Option, 26 | pub show_bottom: bool, 27 | } 28 | 29 | impl Default for EndCrystal { 30 | fn default() -> Self { 31 | Self { 32 | entity: Entity::default(), 33 | block_position: None, 34 | show_bottom: true, 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/mobs/flying.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | inheritable, 6 | ancestors { Mob, LivingEntity, Entity }, 7 | descendants { Ghast, Phantom }, 8 | )] 9 | pub struct Flying { 10 | pub mob: Mob, 11 | } 12 | 13 | #[derive(Default)] 14 | #[MinecraftEntity( 15 | ancestors { Flying, Mob, LivingEntity, Entity }, 16 | )] 17 | pub struct Ghast { 18 | pub flying: Flying, 19 | pub is_attacking: bool, 20 | } 21 | 22 | #[derive(Default)] 23 | #[MinecraftEntity( 24 | ancestors { Flying, Mob, LivingEntity, Entity }, 25 | )] 26 | pub struct Phantom { 27 | pub flying: Flying, 28 | pub size: usize, 29 | } 30 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/mobs/golems.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | inheritable, 6 | ancestors { PathfinderMob, Mob, LivingEntity, Entity }, 7 | descendants { IronGolem, SnowGolem, Shulker }, 8 | )] 9 | pub struct AbstractGolem { 10 | pub pathfinder_mob: PathfinderMob, 11 | } 12 | 13 | #[derive(Default)] 14 | #[MinecraftEntity( 15 | ancestors { AbstractGolem, PathfinderMob, Mob, LivingEntity, Entity }, 16 | )] 17 | pub struct IronGolem { 18 | pub abstract_golem: AbstractGolem, 19 | pub is_player_created: bool, 20 | } 21 | 22 | #[derive(Default)] 23 | #[MinecraftEntity( 24 | ancestors { AbstractGolem, PathfinderMob, Mob, LivingEntity, Entity }, 25 | )] 26 | pub struct SnowGolem { 27 | pub abstract_golem: AbstractGolem, 28 | pub has_pumpkin_hat: bool, 29 | } 30 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/mobs/mod.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | mod villagers; 4 | pub use villagers::*; 5 | mod golems; 6 | pub use golems::*; 7 | mod ender_dragon; 8 | pub use ender_dragon::*; 9 | mod slime; 10 | pub use slime::*; 11 | mod flying; 12 | pub use flying::*; 13 | mod bat; 14 | pub use bat::*; 15 | 16 | #[derive(Default)] 17 | #[MinecraftEntity( 18 | inheritable, 19 | ancestors { LivingEntity, Entity }, 20 | descendants { AmbientCreature..., PathfinderMob..., EnderDragon, Flying..., Slime }, 21 | )] 22 | pub struct Mob { 23 | pub living_entity: LivingEntity, 24 | pub no_ai: bool, 25 | pub is_left_handed: bool, 26 | pub is_aggressive: bool, 27 | } 28 | 29 | #[derive(Default)] 30 | #[MinecraftEntity( 31 | inheritable, 32 | ancestors { Mob, LivingEntity, Entity }, 33 | descendants { Bat }, 34 | )] 35 | pub struct AmbientCreature { 36 | pub mob: Mob, 37 | } 38 | 39 | #[derive(Default)] 40 | #[MinecraftEntity( 41 | inheritable, 42 | ancestors { Mob, LivingEntity, Entity }, 43 | descendants { WaterAnimal..., AgeableMob..., Monster..., AbstractGolem... }, 44 | )] 45 | pub struct PathfinderMob { 46 | pub mob: Mob, 47 | } 48 | 49 | #[derive(Default)] 50 | #[MinecraftEntity( 51 | inheritable, 52 | ancestors { PathfinderMob, Mob, LivingEntity, Entity }, 53 | descendants { Animal..., AbstractVillager... }, 54 | )] 55 | pub struct AgeableMob { 56 | pub pathfinder_mob: PathfinderMob, 57 | pub is_baby: bool, 58 | } 59 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/mobs/slime.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Slime { 8 | pub mob: Mob, 9 | pub size: usize, 10 | } 11 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/mobs/villagers.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | inheritable, 6 | ancestors { AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 7 | descendants { Villager, WanderingTrader }, 8 | )] 9 | pub struct AbstractVillager { 10 | pub ageable_mob: AgeableMob, 11 | pub head_shake_timer: u32, 12 | } 13 | 14 | #[derive(Default)] 15 | #[MinecraftEntity( 16 | ancestors { AbstractVillager, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 17 | )] 18 | pub struct Villager { 19 | pub abstract_villager: AbstractVillager, 20 | pub villager_data: Vec, 21 | } 22 | 23 | #[derive(Default)] 24 | #[MinecraftEntity( 25 | ancestors { AbstractVillager, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, 26 | )] 27 | pub struct WanderingTrader { 28 | pub abstract_villager: AbstractVillager, 29 | } 30 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/blaze.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Blaze { 8 | pub monster: Monster, 9 | pub is_on_fire: bool, 10 | } 11 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/creeper.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[MinecraftEntity( 4 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 5 | )] 6 | pub struct Creeper { 7 | pub monster: Monster, 8 | pub state: i8, 9 | pub is_charged: bool, 10 | pub is_ignited: bool, 11 | } 12 | 13 | impl Default for Creeper { 14 | fn default() -> Self { 15 | Self { 16 | monster: Monster::default(), 17 | state: -1, 18 | is_charged: false, 19 | is_ignited: false, 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/enderman.rs: -------------------------------------------------------------------------------- 1 | use minecraft_protocol::ids::blocks::Block; 2 | 3 | use super::*; 4 | 5 | #[derive(Default)] 6 | #[MinecraftEntity( 7 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 8 | )] 9 | pub struct Enderman { 10 | pub monster: Monster, 11 | pub block_id: Option, 12 | pub is_screaming: bool, 13 | pub is_staring: bool, 14 | } 15 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/endermite.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Endermite { 8 | pub monster: Monster, 9 | } 10 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/giant.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Giant { 8 | pub monster: Monster, 9 | } 10 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/guardian.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | inheritable, 6 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 7 | descendants { ElderGuardian }, 8 | )] 9 | pub struct Guardian { 10 | pub monster: Monster, 11 | pub is_retracting_spikes: bool, 12 | pub target_eid: Eid, 13 | } 14 | 15 | #[derive(Default)] 16 | #[MinecraftEntity( 17 | ancestors { Guardian, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 18 | )] 19 | pub struct ElderGuardian { 20 | pub guardian: Guardian, 21 | } 22 | 23 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/mod.rs: -------------------------------------------------------------------------------- 1 | pub use super::*; 2 | 3 | mod piglin; 4 | pub use piglin::*; 5 | mod blaze; 6 | pub use blaze::*; 7 | mod creeper; 8 | pub use creeper::*; 9 | mod endermite; 10 | pub use endermite::*; 11 | mod giant; 12 | pub use giant::*; 13 | mod guardian; 14 | pub use guardian::*; 15 | mod silverfish; 16 | pub use silverfish::*; 17 | mod raider; 18 | pub use raider::*; 19 | mod vex; 20 | pub use vex::*; 21 | mod skeleton; 22 | pub use skeleton::*; 23 | mod spider; 24 | pub use spider::*; 25 | mod warden; 26 | pub use warden::*; 27 | mod wither; 28 | pub use wither::*; 29 | mod zoglin; 30 | pub use zoglin::*; 31 | mod zombies; 32 | pub use zombies::*; 33 | mod enderman; 34 | pub use enderman::*; 35 | 36 | #[derive(Default)] 37 | #[MinecraftEntity( 38 | inheritable, 39 | ancestors { PathfinderMob, Mob, LivingEntity, Entity }, 40 | descendants { Blaze, Creeper, Enderman, Endermite, Giant, Raider, Silverfish, Spider, Vex, Warden, Wither, Zoglin, Zombie, AbstractSkeleton...,BasePiglin..., Guardian... }, 41 | )] 42 | pub struct Monster { 43 | pub pathfinder_mob: PathfinderMob, 44 | } 45 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/piglin.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | inheritable, 6 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 7 | descendants { Piglin, PiglinBrute }, 8 | )] 9 | pub struct BasePiglin { 10 | pub monster: Monster, 11 | pub is_immune: bool, 12 | } 13 | 14 | #[derive(Default)] 15 | #[MinecraftEntity( 16 | ancestors { BasePiglin, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 17 | )] 18 | pub struct Piglin { 19 | pub base_piglin: BasePiglin, 20 | pub is_baby: bool, 21 | pub is_charging_crossbow: bool, 22 | pub is_dancing: bool, 23 | } 24 | 25 | #[derive(Default)] 26 | #[MinecraftEntity( 27 | ancestors { BasePiglin, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 28 | )] 29 | pub struct PiglinBrute { 30 | pub base_piglin: BasePiglin, 31 | } 32 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/raider.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | inheritable, 6 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 7 | descendants { Witch, AbstractIllager... }, 8 | )] 9 | pub struct Raider { 10 | pub monster: Monster, 11 | pub is_celebrating: bool, 12 | } 13 | 14 | #[derive(Default)] 15 | #[MinecraftEntity( 16 | ancestors { Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 17 | )] 18 | pub struct Witch { 19 | pub raider: Raider, 20 | pub is_drinking_potion: bool, 21 | } 22 | 23 | #[derive(Default)] 24 | #[MinecraftEntity( 25 | inheritable, 26 | ancestors { Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 27 | descendants { Vindicator, Pillager, SpellcasterIllager... }, 28 | )] 29 | pub struct AbstractIllager { 30 | pub raider: Raider, 31 | } 32 | 33 | #[derive(Default)] 34 | #[MinecraftEntity( 35 | ancestors { AbstractIllager, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 36 | )] 37 | pub struct Vindicator { 38 | pub abstract_illager: AbstractIllager, 39 | } 40 | 41 | #[derive(Default)] 42 | #[MinecraftEntity( 43 | ancestors { AbstractIllager, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 44 | )] 45 | pub struct Pillager { 46 | pub abstract_illager: AbstractIllager, 47 | pub is_charging: bool, 48 | } 49 | 50 | #[derive(Default)] 51 | #[MinecraftEntity( 52 | inheritable, 53 | ancestors { AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 54 | descendants { Illusioner, Ravager, Evoker }, 55 | )] 56 | pub struct SpellcasterIllager { 57 | pub abstract_illager: AbstractIllager, 58 | pub spell: u8, 59 | } 60 | 61 | #[derive(Default)] 62 | #[MinecraftEntity( 63 | ancestors { SpellcasterIllager, AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 64 | )] 65 | pub struct Illusioner { 66 | pub spellcaster_illager: SpellcasterIllager, 67 | } 68 | 69 | #[derive(Default)] 70 | #[MinecraftEntity( 71 | ancestors { SpellcasterIllager, AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 72 | )] 73 | pub struct Ravager { 74 | pub spellcaster_illager: SpellcasterIllager, 75 | } 76 | 77 | #[derive(Default)] 78 | #[MinecraftEntity( 79 | ancestors { SpellcasterIllager, AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 80 | )] 81 | pub struct Evoker { 82 | pub spellcaster_illager: SpellcasterIllager, 83 | } 84 | 85 | #[derive(Default)] 86 | #[MinecraftEntity( 87 | ancestors { Entity }, 88 | )] 89 | pub struct EvokerFangs { 90 | pub entity: Entity, 91 | } 92 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/silverfish.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Silverfish { 8 | pub monster: Monster, 9 | } 10 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/skeleton.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | inheritable, 6 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 7 | descendants { Skeleton, WitherSkeleton, Stray }, 8 | )] 9 | pub struct AbstractSkeleton { 10 | pub monster: Monster, 11 | } 12 | 13 | #[derive(Default)] 14 | #[MinecraftEntity( 15 | ancestors { AbstractSkeleton, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 16 | )] 17 | pub struct Skeleton { 18 | pub abstract_skeleton: AbstractSkeleton, 19 | } 20 | 21 | #[derive(Default)] 22 | #[MinecraftEntity( 23 | ancestors { AbstractSkeleton, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 24 | )] 25 | pub struct WitherSkeleton { 26 | pub abstract_skeleton: AbstractSkeleton, 27 | } 28 | 29 | #[derive(Default)] 30 | #[MinecraftEntity( 31 | ancestors { AbstractSkeleton, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 32 | )] 33 | pub struct Stray { 34 | pub abstract_skeleton: AbstractSkeleton, 35 | } 36 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/spider.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Spider { 8 | pub monster: Monster, 9 | pub is_climbing_mask: u8, 10 | } 11 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/vex.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Vex { 8 | pub monster: Monster, 9 | } 10 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/warden.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Warden { 8 | pub monster: Monster, 9 | pub anger_level: usize, 10 | } 11 | 12 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/wither.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Wither { 8 | pub monster: Monster, 9 | pub center_head_target: Option, 10 | pub left_head_target: Option, 11 | pub right_head: Option, 12 | pub invulnerable_time: usize, 13 | } 14 | 15 | #[derive(Default)] 16 | #[MinecraftEntity( 17 | ancestors { Entity }, 18 | )] 19 | pub struct WitherSkull { 20 | pub entity: Entity, 21 | pub is_invulnerable: bool, 22 | } 23 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/zoglin.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Zoglin { 8 | pub monster: Monster, 9 | pub is_baby: bool, 10 | } 11 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/monsters/zombies.rs: -------------------------------------------------------------------------------- 1 | use minecraft_protocol::network; 2 | 3 | use super::*; 4 | 5 | #[derive(Default)] 6 | #[MinecraftEntity( 7 | inheritable, 8 | ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, 9 | descendants { ZombieVillager, Husk, Drowned, ZombifiedPiglin }, 10 | defines { 11 | Entity.init(self, server_msg_rcvr: BroadcastReceiver); 12 | } 13 | )] 14 | pub struct Zombie { 15 | pub monster: Monster, 16 | pub is_baby: bool, 17 | pub unused: isize, 18 | pub is_becoming_drowned: bool, 19 | } 20 | 21 | impl Handler { 22 | pub async fn init(self, server_msg_rcvr: BroadcastReceiver) { 23 | self.insert_task("newton", tokio::spawn(newton_task(self.clone(), server_msg_rcvr.resubscribe()))).await; 24 | self.insert_task("zombie-ai", tokio::spawn(zombie_ai_task(self.clone(), server_msg_rcvr))).await; 25 | } 26 | } 27 | 28 | pub async fn sleep_ticks(server_msg_rcvr: &mut BroadcastReceiver, t: usize) { 29 | let mut i = 0; 30 | while i < t { 31 | let Ok(msg) = server_msg_rcvr.recv().await else {continue}; 32 | if matches!(&msg, &ServerMessage::Tick(_)) { i += 1; } 33 | } 34 | } 35 | 36 | const ZOOMBIE_SPEED: f64 = 0.2; // Arbitrary value 37 | 38 | pub async fn zombie_ai_task(h: Handler, mut server_msg_rcvr: BroadcastReceiver) where AnyEntity: TryAsEntityRef { 39 | loop { 40 | sleep_ticks(&mut server_msg_rcvr, 1).await; 41 | 42 | let mut self_position = h.observe(|e| e.get_entity().position.clone()).await.unwrap(); 43 | let chunk = self_position.chunk_column(); 44 | let player_positions = h.world.observe_entities(chunk, |entity| { 45 | let network_entity = entity.to_network().unwrap(); 46 | TryAsEntityRef::::try_as_entity_ref(entity).map(|player| { 47 | (player.get_entity().position.clone(), network_entity) 48 | }) 49 | }).await; 50 | 51 | let Some((target_position, network_entity)) = player_positions.get(0) else { sleep_ticks(&mut server_msg_rcvr, 100).await; continue }; 52 | let target_object = CollisionShape { 53 | x1: target_position.x - network_entity.width() as f64 / 2.0, 54 | y1: target_position.y, 55 | z1: target_position.z - network_entity.width() as f64 / 2.0, 56 | x2: target_position.x + network_entity.width() as f64 / 2.0, 57 | y2: target_position.y + network_entity.height() as f64, 58 | z2: target_position.z + network_entity.width() as f64 / 2.0, 59 | }; 60 | 61 | for _ in 0..50 { 62 | let mut translation = Translation { 63 | x: target_position.x - self_position.x, 64 | y: target_position.y - self_position.y, 65 | z: target_position.z - self_position.z, 66 | }; 67 | translation.set_norm(ZOOMBIE_SPEED); 68 | 69 | let authorized_translation = h.world.try_move(&target_object, &translation).await; 70 | 71 | let new_pos = h.mutate(|e| { 72 | e.get_entity_mut().position += authorized_translation; 73 | (e.get_entity().position.clone(), EntityChanges::position()) 74 | }).await; 75 | self_position = match new_pos { 76 | Some(pos) => pos, 77 | None => break, 78 | }; 79 | 80 | sleep_ticks(&mut server_msg_rcvr, 1).await; // TODO: do while 81 | } 82 | 83 | } 84 | } 85 | 86 | #[derive(Default)] 87 | #[MinecraftEntity( 88 | ancestors { Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 89 | )] 90 | pub struct ZombieVillager { 91 | pub zombie: Zombie, 92 | pub is_converting: bool, 93 | pub villager_data: Vec, 94 | } 95 | 96 | #[derive(Default)] 97 | #[MinecraftEntity( 98 | ancestors { Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 99 | )] 100 | pub struct Husk { 101 | pub zombie: Zombie, 102 | } 103 | 104 | #[derive(Default)] 105 | #[MinecraftEntity( 106 | ancestors { Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 107 | )] 108 | pub struct Drowned { 109 | pub zombie: Zombie, 110 | } 111 | 112 | #[derive(Default)] 113 | #[MinecraftEntity( 114 | ancestors { Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity }, 115 | )] 116 | pub struct ZombifiedPiglin { 117 | pub zombie: Zombie, 118 | } 119 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/particles.rs: -------------------------------------------------------------------------------- 1 | use minecraft_protocol::components::particle::Particle; 2 | use super::*; 3 | 4 | #[MinecraftEntity( 5 | ancestors { Entity }, 6 | )] 7 | pub struct AreaEffectCloud { 8 | pub entity: Entity, 9 | pub radius: f32, 10 | pub color: Option, 11 | pub ignore_radius: bool, 12 | pub particle: Particle, 13 | } 14 | 15 | impl Default for AreaEffectCloud { 16 | fn default() -> Self { 17 | Self { 18 | entity: Entity::default(), 19 | radius: 0.5, 20 | color: None, 21 | ignore_radius: false, 22 | particle: Particle::Effect, 23 | } 24 | } 25 | } 26 | 27 | #[derive(Default)] 28 | #[MinecraftEntity( 29 | ancestors { Entity }, 30 | )] 31 | pub struct FishingHook{ 32 | pub entity: Entity, 33 | pub hooked_entity: Option, 34 | pub is_catchable: bool, 35 | } 36 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/shulker.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Default)] 4 | #[MinecraftEntity( 5 | ancestors { AbstractGolem, PathfinderMob, Mob, LivingEntity, Entity }, 6 | )] 7 | pub struct Shulker { 8 | pub abstract_golem: AbstractGolem, 9 | pub attach_face: u8, 10 | pub attach_position: Option, 11 | pub shield_height: u8, 12 | pub color: u8, 13 | } 14 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/tasks/mod.rs: -------------------------------------------------------------------------------- 1 | pub use super::*; 2 | 3 | mod newton; 4 | pub use newton::*; 5 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/tasks/newton.rs: -------------------------------------------------------------------------------- 1 | use crate::CollisionShape; 2 | 3 | use super::*; 4 | 5 | pub async fn newton_task(h: Handler, mut server_msg_rcvr: BroadcastReceiver) where AnyEntity: TryAsEntityRef { 6 | let Some(network_entity) = h.observe_any(|any_entity| any_entity.to_network()).await else { return; }; 7 | 8 | let (width, height) = match network_entity { 9 | Some(network_entity) => (network_entity.width() as f64, network_entity.height() as f64), 10 | None => { 11 | warn!("Entity {} has no network entity", h.eid); 12 | return; 13 | } 14 | }; 15 | 16 | loop { 17 | let Ok(msg) = server_msg_rcvr.recv().await else {continue}; 18 | 19 | if !matches!(&msg, &ServerMessage::Tick(_)) { 20 | continue; 21 | } 22 | 23 | // Get data from entity 24 | let Some((mut position, mut velocity)) = h.observe_any(|any_entity| { 25 | let entity = any_entity.as_entity(); 26 | (entity.position.clone(), entity.velocity.clone()) 27 | }).await else { return; }; 28 | 29 | // Apply velocity and collisions 30 | let mut changes = EntityChanges::nothing(); 31 | let mut new_velocity = velocity.clone(); 32 | new_velocity.y -= 9.81/20.0; 33 | let bounding_box = CollisionShape { 34 | x1: position.x - width/2.0, 35 | y1: position.y, 36 | z1: position.z - width/2.0, 37 | x2: position.x + width/2.0, 38 | y2: position.y + height, 39 | z2: position.z + width/2.0, 40 | }; 41 | let new_velocity = h.world.try_move(&bounding_box, &new_velocity).await; 42 | if velocity.x != new_velocity.x { 43 | velocity.x = 0.0; 44 | changes += EntityChanges::velocity(); 45 | } 46 | if velocity.y != new_velocity.y { 47 | velocity.y = 0.0; 48 | changes += EntityChanges::velocity(); 49 | } 50 | if velocity.z != new_velocity.z { 51 | velocity.z = 0.0; 52 | changes += EntityChanges::velocity(); 53 | } 54 | if !new_velocity.is_zero() { 55 | changes += EntityChanges::position(); 56 | position += new_velocity; 57 | } 58 | 59 | // TODO(feat): Apply air resistance to x and z velocity 60 | // Keep in mind that velocity shouldn't flicker when constantly kept up by another task but slowed down in this task 61 | 62 | // Mutate entity 63 | // TODO(correctness): Before modifying entity values, we should ensure the original values we based the changes on are still the same 64 | if changes.nothing_changed() { 65 | continue; 66 | } 67 | h.mutate(|entity| { 68 | let entity = entity.get_entity_mut(); 69 | entity.velocity = velocity; 70 | entity.position = position; 71 | ((), changes) 72 | }).await; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /minecraft-server/src/entities/thrown_item_projectile.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[MinecraftEntity( 4 | inheritable, 5 | ancestors { Entity }, 6 | descendants { ThrownEgg, ThrownEnderPearl, ThrownExperienceBottle, ThrownPotion, Snowball }, 7 | )] 8 | pub struct ThrownItemProjectile { 9 | pub entity: Entity, 10 | pub item: Slot, 11 | } 12 | 13 | impl Default for ThrownItemProjectile { 14 | fn default() -> Self { 15 | ThrownItemProjectile { 16 | entity: Entity::default(), 17 | item: Slot {item: None}, 18 | } 19 | } 20 | } 21 | 22 | #[MinecraftEntity( 23 | ancestors { ThrownItemProjectile, Entity }, 24 | )] 25 | pub struct ThrownEgg { 26 | pub thrown_item_projectile: ThrownItemProjectile, 27 | } 28 | 29 | impl Default for ThrownEgg { 30 | fn default() -> Self { 31 | ThrownEgg { 32 | thrown_item_projectile: ThrownItemProjectile { 33 | entity: Entity::default(), 34 | item: Slot {item: Some(SlotItem { 35 | item_id: Item::Egg, 36 | item_count: 1, 37 | nbt_data: NbtTag::Null 38 | })}, 39 | } 40 | } 41 | } 42 | } 43 | 44 | #[MinecraftEntity( 45 | ancestors { ThrownItemProjectile, Entity }, 46 | )] 47 | pub struct ThrownEnderPearl { 48 | pub thrown_item_projectile: ThrownItemProjectile, 49 | } 50 | 51 | impl Default for ThrownEnderPearl { 52 | fn default() -> Self { 53 | ThrownEnderPearl { 54 | thrown_item_projectile: ThrownItemProjectile { 55 | entity: Entity::default(), 56 | item: Slot {item: Some(SlotItem { 57 | item_id: Item::EnderPearl, 58 | item_count: 1, 59 | nbt_data: NbtTag::Null 60 | })}, 61 | } 62 | } 63 | } 64 | } 65 | 66 | #[MinecraftEntity( 67 | ancestors { ThrownItemProjectile, Entity }, 68 | )] 69 | pub struct ThrownExperienceBottle { 70 | pub thrown_item_projectile: ThrownItemProjectile, 71 | } 72 | 73 | impl Default for ThrownExperienceBottle { 74 | fn default() -> Self { 75 | ThrownExperienceBottle { 76 | thrown_item_projectile: ThrownItemProjectile { 77 | entity: Entity::default(), 78 | item: Slot {item: Some(SlotItem { 79 | item_id: Item::ExperienceBottle, 80 | item_count: 1, 81 | nbt_data: NbtTag::Null 82 | })}, 83 | } 84 | } 85 | } 86 | } 87 | 88 | #[MinecraftEntity( 89 | ancestors { ThrownItemProjectile, Entity }, 90 | )] 91 | pub struct ThrownPotion { 92 | pub thrown_item_projectile: ThrownItemProjectile, 93 | } 94 | 95 | impl Default for ThrownPotion { 96 | fn default() -> Self { 97 | ThrownPotion { 98 | thrown_item_projectile: ThrownItemProjectile { 99 | entity: Entity::default(), 100 | item: Slot {item: Some(SlotItem { 101 | item_id: Item::SplashPotion, 102 | item_count: 1, 103 | nbt_data: NbtTag::Null 104 | })}, 105 | } 106 | } 107 | } 108 | } 109 | 110 | #[MinecraftEntity( 111 | ancestors { ThrownItemProjectile, Entity }, 112 | )] 113 | pub struct Snowball { 114 | pub thrown_item_projectile: ThrownItemProjectile, 115 | } 116 | 117 | impl Default for Snowball { 118 | fn default() -> Self { 119 | Snowball { 120 | thrown_item_projectile: ThrownItemProjectile { 121 | entity: Entity::default(), 122 | item: Slot {item: Some(SlotItem { 123 | item_id: Item::Snowball, 124 | item_count: 1, 125 | nbt_data: NbtTag::Null 126 | })}, 127 | } 128 | } 129 | } 130 | } 131 | 132 | #[derive(Default)] 133 | #[MinecraftEntity( 134 | ancestors { Entity }, 135 | )] 136 | pub struct EyeOfEnder { 137 | pub entity: Entity, 138 | pub item: Slot, 139 | } 140 | 141 | -------------------------------------------------------------------------------- /minecraft-server/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::uninit_vec)] 2 | 3 | mod player_handler; 4 | mod server_behavior; 5 | mod prelude; 6 | mod world; 7 | mod entities; 8 | 9 | use crate::prelude::*; 10 | 11 | 12 | struct ServerFuture { 13 | server: ServerBehavior, 14 | } 15 | 16 | impl std::future::Future for ServerFuture { 17 | type Output = (); 18 | 19 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { 20 | self.server.poll(cx) 21 | } 22 | } 23 | 24 | #[tokio::main] 25 | async fn main() { 26 | env_logger::init(); 27 | 28 | let server = ServerBehavior::init().await; 29 | let fut = ServerFuture { server }; 30 | 31 | fut.await; 32 | } 33 | -------------------------------------------------------------------------------- /minecraft-server/src/player_handler/connect.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub async fn handle_connection( 4 | mut stream: TcpStream, 5 | addr: SocketAddr, 6 | server_msg_rcvr: BroadcastReceiver, 7 | world: &'static World, 8 | ) -> Result<(), ()> { 9 | // Receive handshake 10 | let packet = receive_packet(&mut stream).await?; 11 | let HandshakeServerbound::Hello { protocol_version, server_address, server_port, next_state } = HandshakeServerbound::deserialize_uncompressed_minecraft_packet(packet.as_slice()).unwrap(); 12 | match next_state { 13 | ConnectionState::Login => { 14 | let player_info = login(&mut stream, addr).await?; 15 | let (player_info, change_receiver) = handshake(&mut stream, player_info, world).await?; 16 | let uuid = player_info.uuid; 17 | let eid = Player::spawn_player(world, stream, player_info, server_msg_rcvr, change_receiver).await; 18 | Ok(()) 19 | }, 20 | ConnectionState::Status => { 21 | status(&mut stream).await; 22 | Ok(()) 23 | }, 24 | _ => { 25 | error!("Unexpected next state: {next_state:?}"); 26 | Err(()) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /minecraft-server/src/player_handler/login.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub struct LoggedInPlayerInfo { 4 | pub(super) addr: SocketAddr, 5 | pub(super) username: String, 6 | pub(super) uuid: u128, 7 | } 8 | 9 | pub async fn login(stream: &mut TcpStream, addr: SocketAddr) -> Result { 10 | // Receive login start 11 | let packet = receive_packet(stream).await?; 12 | let packet = LoginServerbound::deserialize_uncompressed_minecraft_packet(packet.as_slice()).unwrap(); 13 | let LoginServerbound::LoginStart{ username, player_uuid } = packet else { 14 | error!("Expected LoginStart packet, got: {packet:?}"); 15 | return Err(()); 16 | }; 17 | debug!("LoginStart: {username}"); 18 | 19 | // TODO encryption 20 | 21 | // TODO compression 22 | 23 | // Send login success 24 | let login_success = LoginClientbound::LoginSuccess { 25 | uuid: player_uuid, 26 | username, 27 | properties: Array::default(), 28 | }; 29 | send_packet(stream, login_success).await; 30 | debug!("LoginSuccess sent"); 31 | 32 | // Receive login acknowledged 33 | let packet = receive_packet(stream).await?; 34 | let packet = LoginServerbound::deserialize_uncompressed_minecraft_packet(packet.as_slice()).unwrap(); 35 | let LoginServerbound::LoginAcknowledged = packet else { 36 | error!("Expected LoginAcknowledged packet, got: {packet:?}"); 37 | return Err(()); 38 | }; 39 | debug!("LoginAcknowledged received"); 40 | 41 | // Ignore encryption response if any 42 | let packet = receive_packet(stream).await?; 43 | if let Ok(LoginServerbound::EncryptionResponse { .. }) = LoginServerbound::deserialize_uncompressed_minecraft_packet(packet.as_slice()) { 44 | // Ignore for now (TODO) 45 | //packet = receive_packet(stream).await?; 46 | } 47 | debug!("EncryptionResponse ignored"); 48 | 49 | Ok(LoggedInPlayerInfo { 50 | addr, 51 | username: username.to_owned(), 52 | uuid: player_uuid, 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /minecraft-server/src/player_handler/mod.rs: -------------------------------------------------------------------------------- 1 | pub use crate::prelude::*; 2 | 3 | mod connect; 4 | pub use connect::*; 5 | mod handshake; 6 | pub use handshake::*; 7 | mod login; 8 | pub use login::*; 9 | mod network; 10 | pub use network::*; 11 | mod status; 12 | pub use status::*; 13 | 14 | pub type Task = Pin> + Send + Sync + 'static>>; 15 | -------------------------------------------------------------------------------- /minecraft-server/src/player_handler/network.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub async fn receive_packet(stream: &mut TcpStream) -> Result, ()> { 4 | let mut length: Vec = Vec::with_capacity(2); 5 | 6 | loop { 7 | if length.len() >= 5 { 8 | //return Err("length too long".into()); 9 | } 10 | let mut byte = [0]; 11 | stream.read_exact(&mut byte).await.map_err(|_| ())?; 12 | length.push(byte[0]); 13 | if byte[0] < 0b1000_0000 { 14 | break; 15 | } 16 | } 17 | 18 | let length = VarInt::deserialize_uncompressed_minecraft_packet(length.as_mut_slice()).map_err(|_| ())?; 19 | 20 | let mut data = Vec::with_capacity(length.0 as usize); 21 | unsafe { data.set_len(length.0 as usize); } 22 | stream.read_exact(&mut data).await.map_err(|_| ())?; 23 | 24 | Ok(data) 25 | } 26 | 27 | pub async fn receive_packet_split(stream: &mut OwnedReadHalf) -> Result, ()> { 28 | let mut length: Vec = Vec::with_capacity(2); 29 | 30 | loop { 31 | if length.len() >= 5 { 32 | //return Err("length too long".into()); 33 | } 34 | let mut byte = [0]; 35 | stream.read_exact(&mut byte).await.map_err(|_| ())?; 36 | length.push(byte[0]); 37 | if byte[0] < 0b1000_0000 { 38 | break; 39 | } 40 | } 41 | 42 | let length = VarInt::deserialize_uncompressed_minecraft_packet(length.as_mut_slice()).map_err(|_| ())?; 43 | 44 | let mut data = Vec::with_capacity(length.0 as usize); 45 | unsafe { data.set_len(length.0 as usize); } 46 | stream.read_exact(&mut data).await.map_err(|_| ())?; 47 | 48 | Ok(data) 49 | } 50 | 51 | pub async fn send_packet_raw(stream: &mut TcpStream, packet: &[u8]) { 52 | let length = VarInt::from(packet.len()); 53 | stream.write_all(length.serialize_minecraft_packet().unwrap().as_slice()).await.unwrap(); 54 | stream.write_all(packet).await.unwrap(); 55 | stream.flush().await.unwrap(); 56 | } 57 | 58 | pub async fn send_packet_raw_split(stream: &mut OwnedWriteHalf, packet: &[u8]) { 59 | let length = VarInt::from(packet.len()); 60 | stream.write_all(length.serialize_minecraft_packet().unwrap().as_slice()).await.unwrap(); 61 | stream.write_all(packet).await.unwrap(); 62 | stream.flush().await.unwrap(); 63 | } 64 | 65 | pub async fn send_packet<'a, P: MinecraftPacketPart<'a>>(stream: &mut TcpStream, packet: P) { 66 | let packet = packet.serialize_minecraft_packet().unwrap(); 67 | send_packet_raw(stream, packet.as_slice()).await; 68 | } 69 | -------------------------------------------------------------------------------- /minecraft-server/src/player_handler/status.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub async fn status(stream: &mut TcpStream) -> Result<(), ()> { 4 | loop { 5 | let packet = receive_packet(stream).await?; 6 | match StatusServerbound::deserialize_uncompressed_minecraft_packet(packet.as_slice()).unwrap() { 7 | StatusServerbound::Request => { 8 | let response = StatusClientbound::Response { 9 | json_response: include_str!("../raw/status_response.json") 10 | }; 11 | send_packet(stream, response).await; 12 | debug!("StatusResponse sent"); 13 | }, 14 | StatusServerbound::Ping { payload } => { 15 | warn!("Ping received"); 16 | let pong = StatusClientbound::Pong { 17 | payload 18 | }; 19 | send_packet(stream, pong).await; 20 | debug!("Pong sent"); 21 | return Ok(()); 22 | }, 23 | _ => { 24 | debug!("Unexpected packet: {packet:?}"); 25 | } 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /minecraft-server/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::{entities::*, player_handler::*, server_behavior::*, world::*}; 2 | pub use futures::FutureExt; 3 | pub use log::{debug, error, info, trace, warn}; 4 | pub use minecraft_protocol::{ 5 | components::{ 6 | chat::ChatMode, 7 | chunk::{Chunk as NetworkChunk, ChunkData, PalettedData}, 8 | difficulty::Difficulty, 9 | entity::{EntityAttribute, EntityMetadata, EntityMetadataValue}, 10 | gamemode::{Gamemode, PreviousGamemode}, 11 | players::MainHand, 12 | slots::Slot, 13 | }, 14 | nbt::NbtTag, 15 | packets::{ 16 | config::{ClientboundPacket as ConfigClientbound, ServerboundPacket as ConfigServerbound}, 17 | handshake::ServerboundPacket as HandshakeServerbound, 18 | login::{ClientboundPacket as LoginClientbound, ServerboundPacket as LoginServerbound}, 19 | play_clientbound::ClientboundPacket as PlayClientbound, 20 | play_serverbound::ServerboundPacket as PlayServerbound, 21 | serializer::*, 22 | status::{ClientboundPacket as StatusClientbound, ServerboundPacket as StatusServerbound}, 23 | Array, ConnectionState, Map, RawBytes, VarInt, VarLong, UUID, Position as NetworkPosition 24 | }, 25 | ids::{ 26 | block_states::BlockWithState, 27 | entities::Entity as NetworkEntity, 28 | }, 29 | MinecraftPacketPart, 30 | }; 31 | pub use std::{ 32 | collections::{BTreeMap, HashMap, HashSet}, 33 | future::Future, 34 | net::SocketAddr, 35 | pin::Pin, 36 | sync::Arc, 37 | task::{ 38 | Context, 39 | Poll::{self, *}, 40 | Waker, 41 | }, 42 | time::Duration, 43 | }; 44 | pub use tokio::{ 45 | io::{AsyncReadExt, AsyncWriteExt}, 46 | net::{ 47 | tcp::{OwnedReadHalf, OwnedWriteHalf}, 48 | TcpStream, 49 | }, 50 | sync::{ 51 | broadcast::{ 52 | channel as broadcast_channel, error::RecvError as BroadcastRecvError, 53 | Receiver as BroadcastReceiver, Sender as BroadcastSender, 54 | }, 55 | mpsc::{channel as mpsc_channel, Receiver as MpscReceiver, Sender as MpscSender}, 56 | RwLock, 57 | }, 58 | }; 59 | pub use minecraft_positions::*; 60 | 61 | pub const MAX_PLAYERS: usize = 1001; 62 | -------------------------------------------------------------------------------- /minecraft-server/src/raw/registry_codec.mc_packet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mubelotix/minecraft-protocol/69c7bef61dfa06a58287cf979781c4a61098c094/minecraft-server/src/raw/registry_codec.mc_packet -------------------------------------------------------------------------------- /minecraft-server/src/raw/status_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": { 3 | "name": "1.20.2", 4 | "protocol": 764 5 | }, 6 | "players": { 7 | "max": 1000, 8 | "online": 0 9 | }, 10 | "description": { 11 | "text": "Minecraft rust server" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /minecraft-server/src/server_behavior.rs: -------------------------------------------------------------------------------- 1 | use tokio::net::TcpListener; 2 | 3 | use crate::prelude::*; 4 | 5 | /// Message sent from the server to all player handlers 6 | #[derive(Clone, Debug)] 7 | pub enum ServerMessage { 8 | /// Message indicating a new tick has started 9 | Tick(usize), 10 | } 11 | 12 | pub struct ServerBehavior { 13 | world: &'static World, 14 | message_receiver: BroadcastReceiver, 15 | } 16 | 17 | impl ServerBehavior { 18 | pub async fn init() -> ServerBehavior { 19 | let listener = TcpListener::bind("127.0.0.1:25567").await.expect("Failed to listen"); 20 | let (sender, receiver) = broadcast_channel(100); 21 | let world = Box::leak(Box::new(World::new(receiver.resubscribe()))); 22 | 23 | // Send ticks to player handlers 24 | tokio::spawn(async move { 25 | let mut tick_id = 0; 26 | let mut tick = tokio::time::interval(Duration::from_millis(50)); 27 | loop { 28 | tick.tick().await; 29 | let _ = sender.send(ServerMessage::Tick(tick_id)); 30 | tick_id += 1; 31 | } 32 | }); 33 | 34 | // Accept incoming connections 35 | let world2: &World = world; 36 | let receiver2 = receiver.resubscribe(); 37 | tokio::spawn(async move { 38 | while let Ok((stream, addr)) = listener.accept().await { 39 | // TODO(security): Limit player count 40 | let server_msg_rcvr = receiver2.resubscribe(); 41 | tokio::spawn(async move { 42 | handle_connection(stream, addr, server_msg_rcvr, world2).await; 43 | }); 44 | } 45 | error!("Listener couldn't listen anymore"); 46 | }); 47 | 48 | ServerBehavior { 49 | world, 50 | message_receiver: receiver, 51 | } 52 | } 53 | 54 | pub fn poll( 55 | &mut self, 56 | cx: &mut Context<'_> 57 | ) -> Poll<()> { 58 | Pending 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /minecraft-server/src/world/change.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | 3 | #[derive(Debug, Clone)] 4 | pub enum WorldChange { 5 | Block(BlockPosition, BlockWithState), 6 | EntitySpawned { 7 | eid: Eid, 8 | uuid: UUID, 9 | ty: NetworkEntity, 10 | position: Position, 11 | pitch: f32, 12 | yaw: f32, 13 | head_yaw: f32, 14 | data: u32, 15 | velocity: Translation, 16 | metadata: (), 17 | }, 18 | EntityDispawned { 19 | eid: Eid, 20 | }, 21 | EntityMetadata { 22 | eid: Eid, 23 | metadata: (), 24 | }, 25 | EntityPosition { 26 | eid: Eid, 27 | position: Position, 28 | }, 29 | EntityVelocity { 30 | eid: Eid, 31 | velocity: Translation, 32 | }, 33 | EntityPitch { 34 | eid: Eid, 35 | pitch: f32, 36 | yaw: f32, 37 | head_yaw: f32, 38 | }, 39 | } 40 | 41 | pub struct EntityChanges(u8); 42 | 43 | impl EntityChanges { 44 | pub const fn other() -> EntityChanges { 45 | EntityChanges(0) 46 | } 47 | 48 | pub const fn nothing() -> EntityChanges { 49 | EntityChanges(0) 50 | } 51 | 52 | pub const fn position() -> EntityChanges { 53 | EntityChanges(1) 54 | } 55 | 56 | pub const fn velocity() -> EntityChanges { 57 | EntityChanges(1 << 1) 58 | } 59 | 60 | pub const fn pitch() -> EntityChanges { 61 | EntityChanges(1 << 2) 62 | } 63 | 64 | pub const fn metadata() -> EntityChanges { 65 | EntityChanges(1 << 3) 66 | } 67 | 68 | pub const fn nothing_changed(&self) -> bool { 69 | self.0 == 0 70 | } 71 | 72 | pub const fn position_changed(&self) -> bool { 73 | self.0 & 1 != 0 74 | } 75 | 76 | pub const fn velocity_changed(&self) -> bool { 77 | self.0 & (1 << 1) != 0 78 | } 79 | 80 | pub const fn pitch_changed(&self) -> bool { 81 | self.0 & (1 << 2) != 0 82 | } 83 | 84 | pub const fn metadata_changed(&self) -> bool { 85 | self.0 & (1 << 3) != 0 86 | } 87 | } 88 | 89 | impl std::ops::Add for EntityChanges { 90 | type Output = EntityChanges; 91 | 92 | fn add(self, rhs: EntityChanges) -> EntityChanges { 93 | EntityChanges(self.0 | rhs.0) 94 | } 95 | } 96 | 97 | impl std::ops::AddAssign for EntityChanges { 98 | fn add_assign(&mut self, rhs: EntityChanges) { 99 | self.0 |= rhs.0; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /minecraft-server/src/world/loading_manager.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | 3 | #[derive(Default)] 4 | pub(super) struct WorldLoadingManager { 5 | loaded_chunks: HashMap>, 6 | loader_entities: HashMap>, 7 | } 8 | 9 | impl WorldLoadingManager { 10 | pub(super) fn update_loaded_chunks(&mut self, uuid: UUID, loaded_chunks: HashSet) { 11 | let loaded_before = self.loaded_chunks.entry(uuid).or_default(); 12 | for just_unloaded in loaded_before.difference(&loaded_chunks) { 13 | let mut can_be_removed = false; 14 | self.loader_entities.entry(just_unloaded.clone()).and_modify(|f| { 15 | f.remove(&uuid); 16 | if f.is_empty() { can_be_removed = true;} 17 | }); 18 | if can_be_removed { 19 | self.loader_entities.remove(just_unloaded); 20 | } 21 | } 22 | for newly_loaded in loaded_chunks.difference(loaded_before).cloned() { 23 | self.loader_entities.entry(newly_loaded).or_default().insert(uuid); 24 | } 25 | *loaded_before = loaded_chunks; 26 | if loaded_before.is_empty() { 27 | self.loaded_chunks.remove(&uuid); 28 | } 29 | } 30 | 31 | pub(super) fn get_loaders(&self, position: &ChunkColumnPosition) -> Option<&HashSet> { 32 | self.loader_entities.get(position) 33 | } 34 | 35 | pub(super) fn get_loaded_chunks(&self) -> HashSet { 36 | self.loader_entities.keys().cloned().collect() 37 | } 38 | } 39 | 40 | #[cfg(test)] 41 | mod tests { 42 | use super::*; 43 | 44 | #[test] 45 | fn test_world_loading_manager() { 46 | let mut manager = WorldLoadingManager::default(); 47 | 48 | let mut loaded_first = vec![ChunkColumnPosition{cx: 0, cz: 0}, ChunkColumnPosition{cx: 1, cz: 0}, ChunkColumnPosition{cx: 2, cz: 0}]; 49 | manager.update_loaded_chunks(0, loaded_first.clone().into_iter().collect()); 50 | assert!(manager.get_loaded_chunks().len() == 3); 51 | assert!(manager.get_loaders(&ChunkColumnPosition{cx: 2, cz: 0}).unwrap().len() == 1); 52 | 53 | let loaded_second = vec![ChunkColumnPosition{cx: 0, cz: 1}, ChunkColumnPosition{cx: 1, cz: 1}, ChunkColumnPosition{cx: 2, cz: 1}]; 54 | manager.update_loaded_chunks(1, loaded_second.clone().into_iter().collect()); 55 | assert!(manager.get_loaded_chunks().len() == 6); 56 | 57 | loaded_first = vec![ChunkColumnPosition{cx: 0, cz: 0}, ChunkColumnPosition{cx: 1, cz: 1}]; 58 | manager.update_loaded_chunks(0, loaded_first.clone().into_iter().collect()); 59 | assert!(manager.get_loaded_chunks().len() == 4); 60 | assert!(manager.get_loaders(&ChunkColumnPosition{cx: 1, cz: 1}).unwrap().len() == 2); 61 | assert!(manager.get_loaders(&ChunkColumnPosition{cx: 2, cz: 0}).is_none()); 62 | } 63 | } 64 | --------------------------------------------------------------------------------