use std::io::{Write, Read}; use uuid::Uuid; use crate::{ser::{Serializable, Deserializable}, varint::VarInt}; use super::{ServerPacket, ClientPacket, Protocol, ProtocolState, common::COMMON}; pub const VERSION: i32 = 763; pub const PROTOCOL: Protocol = Protocol { name: "1.20.1", version: VERSION, encode, decode, }; fn encode(mut w: Box<&mut dyn Write>, state: ProtocolState, ev: ServerPacket) -> anyhow::Result<()> { match ev { ServerPacket::LoginSuccess { name, uuid } => { VarInt(0x02).serialize(&mut w)?; uuid.serialize(&mut w)?; name.serialize(&mut w)?; // number of properties (0) VarInt(0x00).serialize(&mut w)?; }, ServerPacket::JoinGame { eid, gamemode, hardcode } => { VarInt(0x28).serialize(&mut w)?; eid.serialize(&mut w)?; hardcode.serialize(&mut w)?; gamemode.serialize(&mut w)?; (-1i8).serialize(&mut w)?; // prev gamemode undefined VarInt(1).serialize(&mut w)?; // dimension count "qc:world".serialize(&mut w)?; // register one dimension include_bytes!("registry.nbt").serialize(&mut w)?; // registry codec "minecraft:the_end".serialize(&mut w)?; // dimension type "qc:world".serialize(&mut w)?; // dimension name 0i64.serialize(&mut w)?; // seed VarInt(65535).serialize(&mut w)?; // max players VarInt(8).serialize(&mut w)?; // view dist VarInt(0).serialize(&mut w)?; // sim dist false.serialize(&mut w)?; // reduce debug info false.serialize(&mut w)?; // respawn screen false.serialize(&mut w)?; // is debug false.serialize(&mut w)?; // is flat false.serialize(&mut w)?; // has death location VarInt(1).serialize(&mut w)?; // portal cooldown }, ServerPacket::KeepAlive(data) => { VarInt(0x23).serialize(&mut w)?; data.serialize(&mut w)?; }, ServerPacket::PlayDisconnect(msg) => { VarInt(0x1A).serialize(&mut w)?; msg.serialize(&mut w)?; }, ServerPacket::PluginMessage { channel, mut data } => { VarInt(0x17).serialize(&mut w)?; channel.serialize(&mut w)?; w.write_all(&mut data)?; }, ServerPacket::ChunkData { x, z } => { VarInt(0x24).serialize(&mut w)?; // chunk x x.serialize(&mut w)?; // chunk z z.serialize(&mut w)?; // heightmap let hmdata = [0i64; 37]; let mut heightmap = nbt::Blob::new(); heightmap.insert("MOTION_BLOCKING", hmdata.as_ref()).unwrap(); heightmap.serialize(&mut w)?; // chunk data let mut chunk_data: Vec = Vec::new(); for _ in 0..(384 / 16) { // number of non-air blocks (0i16).serialize(&mut chunk_data)?; // block states (0u8).serialize(&mut chunk_data)?; VarInt(0).serialize(&mut chunk_data)?; VarInt(0).serialize(&mut chunk_data)?; // biomes (0u8).serialize(&mut chunk_data)?; VarInt(0).serialize(&mut chunk_data)?; VarInt(0).serialize(&mut chunk_data)?; } VarInt(chunk_data.len() as i32).serialize(&mut w)?; w.write_all(&chunk_data)?; // block entities VarInt(0).serialize(&mut w)?; // light masks VarInt(0).serialize(&mut w)?; VarInt(0).serialize(&mut w)?; VarInt(0).serialize(&mut w)?; VarInt(0).serialize(&mut w)?; // sky light array len VarInt(0).serialize(&mut w)?; // block light array len VarInt(0).serialize(&mut w)?; }, ServerPacket::SetDefaultSpawn { pos, angle } => { VarInt(0x50).serialize(&mut w)?; pos.serialize(&mut w)?; angle.serialize(&mut w)?; }, _ => { (COMMON.encode)(w, state, ev)?; } } Ok(()) } fn decode(mut r: Box<&mut dyn Read>, state: ProtocolState, len: i32, id: i32) -> anyhow::Result> { type Ps = ProtocolState; match (state, id) { (Ps::Login, 0x00) => { let name = String::deserialize(&mut r)?; let has_uuid = bool::deserialize(&mut r)?; let uuid = if has_uuid { Some(Uuid::deserialize(&mut r)?) } else { None }; Ok(Some(ClientPacket::LoginStart { name, uuid })) } (Ps::Login, 0x01 | 0x02) => Ok(None), // unsupported (Ps::Play, 0x0D) => { let channel = String::deserialize(&mut r)?; let mut data = Vec::new(); r.read_to_end(&mut data)?; Ok(Some(ClientPacket::PluginMessage { channel, data })) } (Ps::Play, 0x12) => { let data = i64::deserialize(&mut r)?; Ok(Some(ClientPacket::KeepAlive(data))) } (Ps::Play, 0x14 | 0x15 | 0x16) => Ok(None), // position & rotation _ => (COMMON.decode)(r, state, len, id) } }