a bare-bones limbo server in rust (mirror of https://github.com/xoogware/crawlspace)

feat: basic Decode derive

brwr.dev aab2a99b 089d9707

verified
+272 -141
+121 -2
crawlspace-macro/src/lib.rs
··· 1 1 use proc_macro::{Span, TokenStream}; 2 - use quote::{quote, TokenStreamExt}; 3 - use syn::{parse_macro_input, DeriveInput, Fields, Ident, Index, Lit}; 2 + use quote::quote; 3 + use syn::{parse_macro_input, parse_quote, DeriveInput, Fields, Ident, Index, Lit}; 4 4 5 5 #[proc_macro_derive(Packet, attributes(packet))] 6 6 pub fn derive_packet(input: TokenStream) -> TokenStream { ··· 182 182 } 183 183 .into() 184 184 } 185 + 186 + /// Automatically implements "straight-across" decoding for the given struct, i.e. fields are 187 + /// deserialized in order as is. Supports #[varint] and #[varlong] attributes on integer types to 188 + /// deserialize as those formats instead. 189 + #[proc_macro_derive(Decode, attributes(varint, varlong))] 190 + pub fn derive_decode(input: TokenStream) -> TokenStream { 191 + let input = parse_macro_input!(input as DeriveInput); 192 + 193 + let syn::Data::Struct(data) = input.data else { 194 + panic!("Can only derive Decode on a struct"); 195 + }; 196 + 197 + let name = input.ident; 198 + 199 + let struct_tokens = match data.fields { 200 + Fields::Named(fields) => { 201 + let mut field_tokens = proc_macro2::TokenStream::new(); 202 + 203 + for field in fields.named { 204 + let field_name = field.ident.expect("couldn't get ident for named field"); 205 + let ty = field.ty; 206 + 207 + let wrapped = format!("for field {field_name} in {name}"); 208 + 209 + if field 210 + .attrs 211 + .iter() 212 + .any(|attr| attr.meta.path().is_ident("varint")) 213 + { 214 + field_tokens.extend(quote! { 215 + #field_name: VarInt::decode(r) 216 + .wrap_err(#wrapped)? 217 + .try_into()?, 218 + }); 219 + } else if field 220 + .attrs 221 + .iter() 222 + .any(|attr| attr.meta.path().is_ident("varlong")) 223 + { 224 + field_tokens.extend(quote! { 225 + #field_name: VarLong::decode(r) 226 + .wrap_err(#wrapped)? 227 + .try_into()?, 228 + }); 229 + } else { 230 + field_tokens.extend(quote! { 231 + #field_name: <#ty as Decode>::decode(r) 232 + .wrap_err(#wrapped)?, 233 + }); 234 + } 235 + } 236 + quote! { 237 + Self { 238 + #field_tokens 239 + } 240 + } 241 + } 242 + Fields::Unnamed(fields) => { 243 + let mut field_tokens = proc_macro2::TokenStream::new(); 244 + for (i, field) in fields.unnamed.into_iter().enumerate() { 245 + let ty = field.ty; 246 + 247 + let wrapped = format!("for field {i} in {name}"); 248 + 249 + if field 250 + .attrs 251 + .iter() 252 + .any(|attr| attr.meta.path().is_ident("varint")) 253 + { 254 + field_tokens.extend(quote! { 255 + VarInt::decode(r) 256 + .wrap_err(#wrapped)? 257 + .try_into()?, 258 + }); 259 + } else if field 260 + .attrs 261 + .iter() 262 + .any(|attr| attr.meta.path().is_ident("varlong")) 263 + { 264 + field_tokens.extend(quote! { 265 + VarLong::decode(r) 266 + .wrap_err(#wrapped)? 267 + .try_into()?, 268 + }); 269 + } else { 270 + field_tokens.extend(quote! { 271 + <#ty as Decode>::decode(r) 272 + .wrap_err(#wrapped)?, 273 + }); 274 + } 275 + } 276 + quote! { 277 + Self(#field_tokens) 278 + } 279 + } 280 + Fields::Unit => quote! { Self }, 281 + }; 282 + 283 + let struct_generics = input.generics; 284 + let where_clause = struct_generics.where_clause.clone(); 285 + 286 + let mut impl_generics = struct_generics.clone(); 287 + if impl_generics.lifetimes().count() == 0 { 288 + impl_generics.params.push(parse_quote!('a)); 289 + } 290 + 291 + quote! { 292 + impl #impl_generics Decode #impl_generics for #name #struct_generics #where_clause { 293 + fn decode(r: &mut &'a [u8]) -> color_eyre::Result<Self> 294 + where 295 + Self: Sized, 296 + { 297 + use color_eyre::eyre::WrapErr; 298 + Ok(#struct_tokens) 299 + } 300 + } 301 + } 302 + .into() 303 + }
+85 -1
crawlspace/src/protocol/datatypes/impls.rs
··· 46 46 } 47 47 } 48 48 49 + impl<'a> Decode<'a> for i8 { 50 + fn decode(r: &mut &'a [u8]) -> Result<Self> 51 + where 52 + Self: Sized, 53 + { 54 + Ok(r.read_i8()?) 55 + } 56 + } 57 + 49 58 impl Encode for i8 { 50 59 fn encode(&self, mut w: impl std::io::Write) -> Result<()> { 51 60 Ok(w.write_i8(*self)?) 52 61 } 53 62 } 54 63 64 + impl<'a> Decode<'a> for u8 { 65 + fn decode(r: &mut &'a [u8]) -> Result<Self> 66 + where 67 + Self: Sized, 68 + { 69 + Ok(r.read_u8()?) 70 + } 71 + } 72 + 55 73 impl Encode for u8 { 56 74 fn encode(&self, mut w: impl std::io::Write) -> Result<()> { 57 75 Ok(w.write_u8(*self)?) 58 76 } 59 77 } 60 78 79 + impl<'a> Decode<'a> for i16 { 80 + fn decode(r: &mut &'a [u8]) -> Result<Self> 81 + where 82 + Self: Sized, 83 + { 84 + Ok(r.read_i16::<BigEndian>()?) 85 + } 86 + } 87 + 61 88 impl Encode for i16 { 62 89 fn encode(&self, mut w: impl std::io::Write) -> Result<()> { 63 90 Ok(w.write_i16::<BigEndian>(*self)?) 64 91 } 65 92 } 66 93 94 + impl<'a> Decode<'a> for u16 { 95 + fn decode(r: &mut &'a [u8]) -> Result<Self> 96 + where 97 + Self: Sized, 98 + { 99 + Ok(r.read_u16::<BigEndian>()?) 100 + } 101 + } 102 + 103 + impl<'a> Decode<'a> for i32 { 104 + fn decode(r: &mut &'a [u8]) -> Result<Self> 105 + where 106 + Self: Sized, 107 + { 108 + Ok(r.read_i32::<BigEndian>()?) 109 + } 110 + } 111 + 67 112 impl Encode for i32 { 68 113 fn encode(&self, mut w: impl std::io::Write) -> Result<()> { 69 114 Ok(w.write_i32::<BigEndian>(*self)?) ··· 71 116 } 72 117 73 118 impl<'a> Decode<'a> for i64 { 74 - fn decode(r: &mut &'a [u8]) -> Result<Self> { 119 + fn decode(r: &mut &'a [u8]) -> Result<Self> 120 + where 121 + Self: Sized, 122 + { 75 123 Ok(r.read_i64::<BigEndian>()?) 76 124 } 77 125 } ··· 82 130 } 83 131 } 84 132 133 + impl<'a> Decode<'a> for u64 { 134 + fn decode(r: &mut &'a [u8]) -> Result<Self> 135 + where 136 + Self: Sized, 137 + { 138 + Ok(r.read_u64::<BigEndian>()?) 139 + } 140 + } 141 + 85 142 impl Encode for u64 { 86 143 fn encode(&self, mut w: impl std::io::Write) -> Result<()> { 87 144 Ok(w.write_u64::<BigEndian>(*self)?) 88 145 } 89 146 } 90 147 148 + impl<'a> Decode<'a> for u128 { 149 + fn decode(r: &mut &'a [u8]) -> Result<Self> 150 + where 151 + Self: Sized, 152 + { 153 + Ok(r.read_u128::<BigEndian>()?) 154 + } 155 + } 156 + 91 157 impl Encode for u128 { 92 158 fn encode(&self, mut w: impl std::io::Write) -> Result<()> { 93 159 Ok(w.write_u128::<BigEndian>(*self)?) 94 160 } 95 161 } 96 162 163 + impl<'a> Decode<'a> for f32 { 164 + fn decode(r: &mut &'a [u8]) -> Result<Self> 165 + where 166 + Self: Sized, 167 + { 168 + Ok(r.read_f32::<BigEndian>()?) 169 + } 170 + } 171 + 97 172 impl Encode for f32 { 98 173 fn encode(&self, mut w: impl std::io::Write) -> Result<()> { 99 174 Ok(w.write_f32::<BigEndian>(*self)?) 175 + } 176 + } 177 + 178 + impl<'a> Decode<'a> for f64 { 179 + fn decode(r: &mut &'a [u8]) -> Result<Self> 180 + where 181 + Self: Sized, 182 + { 183 + Ok(r.read_f64::<BigEndian>()?) 100 184 } 101 185 } 102 186
+6
crawlspace/src/protocol/datatypes/variable.rs
··· 296 296 } 297 297 } 298 298 } 299 + 300 + impl From<VarInt> for i32 { 301 + fn from(value: VarInt) -> Self { 302 + value.0 303 + } 304 + }
+17 -1
crawlspace/src/protocol/mod.rs
··· 78 78 mod encoder; 79 79 80 80 use bit_vec::BitVec; 81 - use color_eyre::eyre::{Context, Result}; 81 + use color_eyre::eyre::{bail, Context, Result}; 82 82 use datatypes::{Bounded, VarInt}; 83 83 use serde::{Deserialize, Serialize}; 84 84 use std::collections::HashMap; ··· 214 214 Status, 215 215 Login, 216 216 Transfer, 217 + } 218 + 219 + impl Decode<'_> for ProtocolState { 220 + fn decode(r: &mut &'_ [u8]) -> Result<Self> 221 + where 222 + Self: Sized, 223 + { 224 + let state = VarInt::decode(r)?; 225 + 226 + match state.0 { 227 + 1 => Ok(ProtocolState::Status), 228 + 2 => Ok(ProtocolState::Login), 229 + 3 => Ok(ProtocolState::Transfer), 230 + e => bail!("Invalid next state requested: {e}"), 231 + } 232 + } 217 233 } 218 234 219 235 #[derive(Error, Debug)]
+3 -19
crawlspace/src/protocol/packets/login/config.rs
··· 18 18 */ 19 19 20 20 use color_eyre::eyre::{ensure, Result}; 21 - use crawlspace_macro::{Encode, Packet}; 21 + use crawlspace_macro::{Decode, Encode, Packet}; 22 22 23 23 use crate::protocol::{ 24 24 datatypes::{Bounded, VarInt}, ··· 36 36 known_packs: Vec<KnownPack<'a>>, 37 37 } 38 38 39 - #[derive(Debug, Encode)] 39 + #[derive(Debug, Encode, Decode)] 40 40 pub struct KnownPack<'a> { 41 41 namespace: Bounded<&'a str>, 42 42 id: Bounded<&'a str>, ··· 86 86 } 87 87 } 88 88 89 - impl<'a> Decode<'a> for KnownPack<'a> { 90 - fn decode(r: &mut &'a [u8]) -> Result<Self> { 91 - Ok(Self { 92 - namespace: Bounded::<&'a str>::decode(r)?, 93 - id: Bounded::<&'a str>::decode(r)?, 94 - version: Bounded::<&'a str>::decode(r)?, 95 - }) 96 - } 97 - } 98 - 99 89 #[derive(Debug, Packet, Encode)] 100 90 #[packet( 101 91 id = "minecraft:finish_configuration", ··· 104 94 )] 105 95 pub struct FinishConfigurationC; 106 96 107 - #[derive(Debug, Packet)] 97 + #[derive(Debug, Packet, Decode)] 108 98 #[packet( 109 99 id = "minecraft:finish_configuration", 110 100 serverbound, 111 101 state = "PacketState::Configuration" 112 102 )] 113 103 pub struct FinishConfigurationAckS; 114 - 115 - impl<'a> Decode<'a> for FinishConfigurationAckS { 116 - fn decode(_r: &mut &'a [u8]) -> Result<Self> { 117 - Ok(Self) 118 - } 119 - }
+2 -15
crawlspace/src/protocol/packets/login/handshake.rs
··· 17 17 * <https://www.gnu.org/licenses/>. 18 18 */ 19 19 20 - use byteorder::{BigEndian, ReadBytesExt}; 21 - use color_eyre::eyre::Result; 22 - use crawlspace_macro::Packet; 20 + use crawlspace_macro::{Decode, Packet}; 23 21 24 22 use crate::protocol::{ 25 23 datatypes::{Bounded, VarInt}, 26 24 Decode, Packet, PacketDirection, PacketState, ProtocolState, 27 25 }; 28 26 29 - #[derive(Debug, Packet)] 27 + #[derive(Debug, Packet, Decode)] 30 28 #[packet( 31 29 id = "minecraft:intention", 32 30 serverbound, ··· 38 36 _server_port: u16, 39 37 pub next_state: ProtocolState, 40 38 } 41 - 42 - impl<'a> Decode<'a> for HandshakeS<'a> { 43 - fn decode(buf: &mut &'a [u8]) -> Result<Self> { 44 - Ok(Self { 45 - protocol_version: VarInt::decode(buf)?, 46 - _server_address: Bounded::<&'a str, 255>::decode(buf)?, 47 - _server_port: buf.read_u16::<BigEndian>()?, 48 - next_state: ProtocolState::try_from(VarInt::decode(buf)?.0)?, 49 - }) 50 - } 51 - }
+3 -18
crawlspace/src/protocol/packets/login/login.rs
··· 18 18 */ 19 19 20 20 use color_eyre::eyre::Result; 21 - use crawlspace_macro::Packet; 21 + use crawlspace_macro::{Decode, Packet}; 22 22 use uuid::Uuid; 23 23 24 24 use crate::protocol::{ ··· 26 26 Decode, Encode, Packet, PacketDirection, PacketState, Property, 27 27 }; 28 28 29 - #[derive(Debug, Packet)] 29 + #[derive(Debug, Packet, Decode)] 30 30 #[packet(id = "minecraft:hello", serverbound, state = "PacketState::Login")] 31 31 pub struct LoginStartS<'a> { 32 32 pub name: Bounded<&'a str, 16>, 33 33 pub player_uuid: Uuid, 34 - } 35 - 36 - impl<'a> Decode<'a> for LoginStartS<'a> { 37 - fn decode(buf: &mut &'a [u8]) -> Result<Self> { 38 - Ok(Self { 39 - name: Bounded::<&'a str, 16>::decode(buf)?, 40 - player_uuid: Uuid::decode(buf)?, 41 - }) 42 - } 43 34 } 44 35 45 36 #[derive(Debug, Packet)] ··· 113 104 } 114 105 } 115 106 116 - #[derive(Debug, Packet)] 107 + #[derive(Debug, Packet, Decode)] 117 108 #[packet( 118 109 id = "minecraft:login_acknowledged", 119 110 serverbound, 120 111 state = "PacketState::Login" 121 112 )] 122 113 pub struct LoginAckS; 123 - 124 - impl Decode<'_> for LoginAckS { 125 - fn decode(_r: &mut &'_ [u8]) -> Result<Self> { 126 - Ok(Self) 127 - } 128 - }
+4 -18
crawlspace/src/protocol/packets/login/status.rs
··· 19 19 20 20 use std::io::Write; 21 21 22 - use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 22 + use byteorder::{BigEndian, WriteBytesExt}; 23 23 use color_eyre::eyre::Result; 24 - use crawlspace_macro::Packet; 24 + use crawlspace_macro::{Decode, Packet}; 25 25 26 26 use crate::protocol::{Decode, Encode, Packet, PacketDirection, PacketState}; 27 27 28 - #[derive(Debug, Packet)] 28 + #[derive(Debug, Packet, Decode)] 29 29 #[packet( 30 30 id = "minecraft:status_request", 31 31 serverbound, ··· 33 33 )] 34 34 pub struct StatusRequestS; 35 35 36 - impl<'a> Decode<'a> for StatusRequestS { 37 - fn decode(_buf: &mut &'a [u8]) -> Result<Self> { 38 - Ok(Self) 39 - } 40 - } 41 - 42 36 #[derive(Debug, Packet)] 43 37 #[packet( 44 38 id = "minecraft:status_response", ··· 61 55 pub payload: i64, 62 56 } 63 57 64 - #[derive(Debug, Packet)] 58 + #[derive(Debug, Packet, Decode)] 65 59 #[packet(id = "minecraft:pong", clientbound, state = "PacketState::Status")] 66 60 pub struct PingS { 67 61 pub payload: i64, ··· 72 66 Ok(w.write_i64::<BigEndian>(self.payload)?) 73 67 } 74 68 } 75 - 76 - impl<'a> Decode<'a> for PingS { 77 - fn decode(buf: &mut &'a [u8]) -> Result<Self> { 78 - Ok(Self { 79 - payload: buf.read_i64::<BigEndian>()?, 80 - }) 81 - } 82 - }
+4 -21
crawlspace/src/protocol/packets/play/interactions.rs
··· 18 18 */ 19 19 20 20 use byteorder::{BigEndian, ReadBytesExt}; 21 - use crawlspace_macro::Packet; 21 + use crawlspace_macro::{Decode, Packet}; 22 22 23 23 use crate::protocol::{ 24 24 datatypes::{Position, VarInt}, 25 25 Decode, Packet, PacketDirection, PacketState, 26 26 }; 27 27 28 - #[derive(Debug, Packet)] 28 + #[derive(Debug, Packet, Decode)] 29 29 #[packet(id = "minecraft:use_item_on", serverbound, state = "PacketState::Play")] 30 30 pub struct UseItemOnS { 31 + #[varint] 31 32 pub hand: Hand, 32 33 pub location: Position, 34 + #[varint] 33 35 pub face: Face, 34 36 pub cursor_x: f32, 35 37 pub cursor_y: f32, ··· 94 96 } 95 97 } 96 98 } 97 - 98 - impl Decode<'_> for UseItemOnS { 99 - fn decode(r: &mut &'_ [u8]) -> color_eyre::eyre::Result<Self> 100 - where 101 - Self: Sized, 102 - { 103 - Ok(Self { 104 - hand: VarInt::decode(r)?.try_into()?, 105 - location: Position::decode(r)?, 106 - face: VarInt::decode(r)?.try_into()?, 107 - cursor_x: r.read_f32::<BigEndian>()?, 108 - cursor_y: r.read_f32::<BigEndian>()?, 109 - cursor_z: r.read_f32::<BigEndian>()?, 110 - inside_block: r.read_u8()? == 1, 111 - world_border_hit: r.read_u8()? == 1, 112 - sequence: VarInt::decode(r)?, 113 - }) 114 - } 115 - }
+2 -8
crawlspace/src/protocol/packets/play/keepalive.rs
··· 17 17 * <https://www.gnu.org/licenses/>. 18 18 */ 19 19 20 - use crawlspace_macro::{Encode, Packet}; 20 + use crawlspace_macro::{Decode, Encode, Packet}; 21 21 22 22 use crate::protocol::{Decode, Encode, Packet, PacketDirection, PacketState}; 23 23 ··· 25 25 #[packet(id = "minecraft:keep_alive", clientbound, state = "PacketState::Play")] 26 26 pub struct KeepAliveC(pub i64); 27 27 28 - #[derive(Debug, Packet)] 28 + #[derive(Debug, Packet, Decode)] 29 29 #[packet(id = "minecraft:keep_alive", serverbound, state = "PacketState::Play")] 30 30 #[expect(unused)] 31 31 pub struct KeepAliveS(i64); 32 - 33 - impl<'a> Decode<'a> for KeepAliveS { 34 - fn decode(r: &mut &'a [u8]) -> color_eyre::eyre::Result<Self> { 35 - Ok(Self(i64::decode(r)?)) 36 - } 37 - }
+17 -27
crawlspace/src/protocol/packets/play/position.rs
··· 18 18 */ 19 19 20 20 use byteorder::{BigEndian, ReadBytesExt}; 21 - use crawlspace_macro::Packet; 21 + use crawlspace_macro::{Decode, Packet}; 22 22 23 23 use crate::protocol::{Decode, Packet, PacketDirection, PacketState}; 24 24 25 - #[derive(Debug, Packet)] 25 + #[derive(Debug, Packet, Decode)] 26 26 #[packet( 27 27 id = "minecraft:move_player_pos", 28 28 serverbound, ··· 32 32 pub x: f64, 33 33 pub feet_y: f64, 34 34 pub z: f64, 35 - pub on_ground: bool, 36 - } 37 - 38 - impl Decode<'_> for SetPlayerPositionS { 39 - fn decode(r: &mut &'_ [u8]) -> color_eyre::eyre::Result<Self> 40 - where 41 - Self: Sized, 42 - { 43 - Ok(Self { 44 - x: r.read_f64::<BigEndian>()?, 45 - feet_y: r.read_f64::<BigEndian>()?, 46 - z: r.read_f64::<BigEndian>()?, 47 - on_ground: r.read_u8()? == 1, 48 - }) 49 - } 35 + pub on_ground: PosRotFlags, 50 36 } 51 37 52 - #[derive(Debug, Packet)] 38 + #[derive(Debug, Packet, Decode)] 53 39 #[packet( 54 40 id = "minecraft:move_player_pos_rot", 55 41 serverbound, ··· 61 47 pub z: f64, 62 48 pub yaw: f32, 63 49 pub pitch: f32, 64 - pub on_ground: bool, 50 + pub flags: PosRotFlags, 51 + } 52 + 53 + #[derive(Clone, Debug)] 54 + pub struct PosRotFlags { 55 + on_ground: bool, 56 + against_wall: bool, 65 57 } 66 58 67 - impl Decode<'_> for SetPlayerPositionAndRotationS { 59 + impl Decode<'_> for PosRotFlags { 68 60 fn decode(r: &mut &'_ [u8]) -> color_eyre::eyre::Result<Self> 69 61 where 70 62 Self: Sized, 71 63 { 72 - Ok(Self { 73 - x: r.read_f64::<BigEndian>()?, 74 - feet_y: r.read_f64::<BigEndian>()?, 75 - z: r.read_f64::<BigEndian>()?, 76 - yaw: r.read_f32::<BigEndian>()?, 77 - pitch: r.read_f32::<BigEndian>()?, 78 - on_ground: r.read_u8()? == 1, 64 + let field = u8::decode(r)?; 65 + 66 + Ok(PosRotFlags { 67 + on_ground: field & 0b00000001 != 0, 68 + against_wall: field & 0b00000010 != 0, 79 69 }) 80 70 } 81 71 }
+3 -10
crawlspace/src/protocol/packets/play/teleport.rs
··· 19 19 20 20 use std::sync::atomic::{AtomicI32, Ordering}; 21 21 22 - use crawlspace_macro::{Encode, Packet}; 22 + use crawlspace_macro::{Decode, Encode, Packet}; 23 23 24 24 use crate::protocol::{datatypes::VarInt, Decode, Encode, Packet, PacketDirection, PacketState}; 25 25 ··· 131 131 } 132 132 } 133 133 134 - #[derive(Debug, Packet)] 134 + #[derive(Debug, Packet, Decode)] 135 135 #[packet( 136 136 id = "minecraft:accept_teleportation", 137 137 serverbound, 138 138 state = "PacketState::Play" 139 139 )] 140 140 pub struct ConfirmTeleportS { 141 + #[varint] 141 142 pub id: i32, 142 143 } 143 - 144 - impl Decode<'_> for ConfirmTeleportS { 145 - fn decode(r: &mut &'_ [u8]) -> color_eyre::eyre::Result<Self> { 146 - Ok(Self { 147 - id: VarInt::decode(r)?.0, 148 - }) 149 - } 150 - }
+5 -1
crawlspace/src/server/mod.rs
··· 94 94 match player.handle_all_packets().await { 95 95 Ok(()) => (), 96 96 Err(why) => { 97 - error!("error handling packets for player {}: {why}", player.id()); 97 + error!( 98 + "error handling packets for player {}: {:?}", 99 + player.id(), 100 + why 101 + ); 98 102 invalid_players.insert(*id); 99 103 continue; 100 104 }