mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
reading nbt in the protocol works
This commit is contained in:
parent
1cdd061a99
commit
af28b0e57a
9 changed files with 211 additions and 56 deletions
|
@ -57,23 +57,26 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// game
|
// game
|
||||||
// loop {
|
loop {
|
||||||
// let packet_result = conn.read().await;
|
let packet_result = conn.read().await;
|
||||||
// match packet_result {
|
match packet_result {
|
||||||
// Ok(packet) => match packet {
|
Ok(packet) => match packet {
|
||||||
// GamePacket::ClientboundKeepAlivePacket(p) => {
|
GamePacket::ClientboundLoginPacket(p) => {
|
||||||
// println!("Got keep alive packet {:?}", p.keep_alive_id);
|
println!("Got login packet {:?}", p);
|
||||||
// }
|
}
|
||||||
// GamePacket::ClientboundChatMessagePacket(p) => {
|
// GamePacket::ClientboundKeepAlivePacket(p) => {
|
||||||
// println!("Got chat message packet {:?}", p.message);
|
// println!("Got keep alive packet {:?}", p.keep_alive_id);
|
||||||
// }
|
// }
|
||||||
// _ => panic!("unhandled packet"),
|
// GamePacket::ClientboundChatMessagePacket(p) => {
|
||||||
// },
|
// println!("Got chat message packet {:?}", p.message);
|
||||||
// Err(e) => {
|
// }
|
||||||
// println!("Error: {:?}", e);
|
_ => panic!("unhandled packet"),
|
||||||
// }
|
},
|
||||||
// }
|
Err(e) => {
|
||||||
// }
|
println!("Error: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,14 +26,21 @@ impl GameType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_id(id: u8) -> GameType {
|
pub fn from_id(id: u8) -> Result<GameType, String> {
|
||||||
match id {
|
Ok(match id {
|
||||||
0 => GameType::SURVIVAL,
|
0 => GameType::SURVIVAL,
|
||||||
1 => GameType::CREATIVE,
|
1 => GameType::CREATIVE,
|
||||||
2 => GameType::ADVENTURE,
|
2 => GameType::ADVENTURE,
|
||||||
3 => GameType::SPECTATOR,
|
3 => GameType::SPECTATOR,
|
||||||
_ => panic!("Unknown game type id: {}", id),
|
_ => return Err(format!("Unknown game type id: {}", id)),
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_optional_id(id: i8) -> Result<Option<GameType>, String> {
|
||||||
|
Ok(match id {
|
||||||
|
-1 => None,
|
||||||
|
id => Some(GameType::from_id(id as u8)?),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn short_name(&self) -> &'static str {
|
pub fn short_name(&self) -> &'static str {
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
struct RegistryHolder {}
|
|
||||||
|
|
||||||
fn make_network_codec() -> NetworkCodec {
|
|
||||||
// Codec codec = ResourceLocation.CODEC.xmap(ResourceKey::createRegistryKey, ResourceKey::location);
|
|
||||||
// Codec codec2 = codec.partialDispatch("type", mappedRegistry -> DataResult.success(mappedRegistry.key()), resourceKey -> RegistryHolder.getNetworkCodec(resourceKey).map(codec -> MappedRegistry.networkCodec(resourceKey, Lifecycle.experimental(), codec)));
|
|
||||||
// UnboundedMapCodec unboundedMapCodec = Codec.unboundedMap((Codec)codec, (Codec)codec2);
|
|
||||||
// return RegistryHolder.captureMap(unboundedMapCodec);
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! A resource, like minecraft:stone
|
//! A resource, like minecraft:stone
|
||||||
|
|
||||||
#[derive(Hash, Clone, Debug)]
|
#[derive(Hash, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct ResourceLocation {
|
pub struct ResourceLocation {
|
||||||
pub namespace: String,
|
pub namespace: String,
|
||||||
pub path: String,
|
pub path: String,
|
||||||
|
|
|
@ -121,7 +121,18 @@ impl Tag {
|
||||||
R: AsyncRead + std::marker::Unpin + std::marker::Send,
|
R: AsyncRead + std::marker::Unpin + std::marker::Send,
|
||||||
{
|
{
|
||||||
// default to compound tag
|
// default to compound tag
|
||||||
Tag::read_known(stream, 10).await
|
|
||||||
|
// the parent compound only ever has one item
|
||||||
|
let tag_id = stream.read_u8().await.unwrap_or(0);
|
||||||
|
if tag_id == 0 {
|
||||||
|
return Ok(Tag::End);
|
||||||
|
}
|
||||||
|
let name = read_string(stream).await?;
|
||||||
|
let tag = Tag::read_known(stream, tag_id).await?;
|
||||||
|
let mut map = HashMap::with_capacity(1);
|
||||||
|
map.insert(name, tag);
|
||||||
|
|
||||||
|
Ok(Tag::Compound(map))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_zlib<R>(stream: &mut R) -> Result<Tag, Error>
|
pub async fn read_zlib<R>(stream: &mut R) -> Result<Tag, Error>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use azalea_core::resource_location::ResourceLocation;
|
||||||
use byteorder::{BigEndian, WriteBytesExt};
|
use byteorder::{BigEndian, WriteBytesExt};
|
||||||
use tokio::io::{AsyncRead, AsyncReadExt};
|
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||||
|
|
||||||
|
@ -39,6 +40,11 @@ pub trait Writable {
|
||||||
fn write_int(&mut self, n: i32) -> Result<(), std::io::Error>;
|
fn write_int(&mut self, n: i32) -> Result<(), std::io::Error>;
|
||||||
fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error>;
|
fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error>;
|
||||||
fn write_nbt(&mut self, nbt: &azalea_nbt::Tag) -> Result<(), std::io::Error>;
|
fn write_nbt(&mut self, nbt: &azalea_nbt::Tag) -> Result<(), std::io::Error>;
|
||||||
|
fn write_long(&mut self, n: i64) -> Result<(), std::io::Error>;
|
||||||
|
fn write_resource_location(
|
||||||
|
&mut self,
|
||||||
|
location: &ResourceLocation,
|
||||||
|
) -> Result<(), std::io::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -140,6 +146,17 @@ impl Writable for Vec<u8> {
|
||||||
nbt.write(self)
|
nbt.write(self)
|
||||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_long(&mut self, n: i64) -> Result<(), std::io::Error> {
|
||||||
|
WriteBytesExt::write_i64::<BigEndian>(self, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_resource_location(
|
||||||
|
&mut self,
|
||||||
|
location: &ResourceLocation,
|
||||||
|
) -> Result<(), std::io::Error> {
|
||||||
|
self.write_utf(&location.to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -156,6 +173,8 @@ pub trait Readable {
|
||||||
async fn read_int(&mut self) -> Result<i32, String>;
|
async fn read_int(&mut self) -> Result<i32, String>;
|
||||||
async fn read_boolean(&mut self) -> Result<bool, String>;
|
async fn read_boolean(&mut self) -> Result<bool, String>;
|
||||||
async fn read_nbt(&mut self) -> Result<azalea_nbt::Tag, String>;
|
async fn read_nbt(&mut self) -> Result<azalea_nbt::Tag, String>;
|
||||||
|
async fn read_long(&mut self) -> Result<i64, String>;
|
||||||
|
async fn read_resource_location(&mut self) -> Result<ResourceLocation, String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -285,14 +304,32 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_nbt(&mut self) -> Result<azalea_nbt::Tag, String> {
|
async fn read_nbt(&mut self) -> Result<azalea_nbt::Tag, String> {
|
||||||
Ok(azalea_nbt::Tag::read(self).await.unwrap())
|
match azalea_nbt::Tag::read(self).await {
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
// Err(e) => Err(e.to_string()),
|
||||||
|
Err(e) => Err(e.to_string()).unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_long(&mut self) -> Result<i64, String> {
|
||||||
|
match AsyncReadExt::read_i64(self).await {
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
Err(_) => Err("Error reading long".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_resource_location(&mut self) -> Result<ResourceLocation, String> {
|
||||||
|
// get the resource location from the string
|
||||||
|
let location_string = self.read_utf().await?;
|
||||||
|
let location = ResourceLocation::new(&location_string)?;
|
||||||
|
Ok(location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::io::Cursor;
|
use std::{collections::HashMap, io::Cursor};
|
||||||
use tokio::io::BufReader;
|
use tokio::io::BufReader;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -387,4 +424,55 @@ mod tests {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_nbt() {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
buf.write_nbt(&azalea_nbt::Tag::Compound(HashMap::from_iter(vec![(
|
||||||
|
"hello world".to_string(),
|
||||||
|
azalea_nbt::Tag::Compound(HashMap::from_iter(vec![(
|
||||||
|
"name".to_string(),
|
||||||
|
azalea_nbt::Tag::String("Bananrama".to_string()),
|
||||||
|
)])),
|
||||||
|
)])))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut buf = BufReader::new(Cursor::new(buf));
|
||||||
|
|
||||||
|
let result = buf.read_nbt().await.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
azalea_nbt::Tag::Compound(HashMap::from_iter(vec![(
|
||||||
|
"hello world".to_string(),
|
||||||
|
azalea_nbt::Tag::Compound(HashMap::from_iter(vec![(
|
||||||
|
"name".to_string(),
|
||||||
|
azalea_nbt::Tag::String("Bananrama".to_string()),
|
||||||
|
)])),
|
||||||
|
)]))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_long() {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
buf.write_long(123456).unwrap();
|
||||||
|
|
||||||
|
let mut buf = BufReader::new(Cursor::new(buf));
|
||||||
|
|
||||||
|
assert_eq!(buf.read_long().await.unwrap(), 123456);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_resource_location() {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
buf.write_resource_location(&ResourceLocation::new("minecraft:dirt").unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut buf = BufReader::new(Cursor::new(buf));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
buf.read_resource_location().await.unwrap(),
|
||||||
|
ResourceLocation::new("minecraft:dirt").unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use super::GamePacket;
|
use super::GamePacket;
|
||||||
use crate::mc_buf::{Readable, Writable};
|
use crate::mc_buf::{Readable, Writable};
|
||||||
use azalea_core::{game_type::GameType, resource_location::ResourceLocation};
|
use azalea_core::{game_type::GameType, resource_location::ResourceLocation};
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ClientboundLoginPacket {
|
pub struct ClientboundLoginPacket {
|
||||||
|
@ -50,27 +51,73 @@ impl ClientboundLoginPacket {
|
||||||
buf.write_byte(self.game_type.to_id())?;
|
buf.write_byte(self.game_type.to_id())?;
|
||||||
buf.write_byte(GameType::to_optional_id(&self.previous_game_type) as u8)?;
|
buf.write_byte(GameType::to_optional_id(&self.previous_game_type) as u8)?;
|
||||||
buf.write_list(&self.levels, |buf, resource_location| {
|
buf.write_list(&self.levels, |buf, resource_location| {
|
||||||
buf.write_utf(&resource_location.to_string())
|
buf.write_resource_location(resource_location)
|
||||||
})?;
|
})?;
|
||||||
self.registry_holder
|
buf.write_nbt(&self.registry_holder)?;
|
||||||
.write(buf)
|
buf.write_nbt(&self.dimension_type)?;
|
||||||
.map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "write registry holder"))?;
|
buf.write_resource_location(&self.dimension)?;
|
||||||
|
buf.write_long(self.seed)?;
|
||||||
|
buf.write_varint(self.max_players)?;
|
||||||
|
buf.write_varint(self.chunk_radius)?;
|
||||||
|
buf.write_varint(self.simulation_distance)?;
|
||||||
|
buf.write_boolean(self.reduced_debug_info)?;
|
||||||
|
buf.write_boolean(self.show_death_screen)?;
|
||||||
|
buf.write_boolean(self.is_debug)?;
|
||||||
|
buf.write_boolean(self.is_flat)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
|
pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
|
||||||
buf: &mut T,
|
buf: &mut T,
|
||||||
) -> Result<GamePacket, String> {
|
) -> Result<GamePacket, String> {
|
||||||
let transaction_id = buf.read_varint().await? as u32;
|
let player_id = buf.read_int().await?;
|
||||||
let identifier = ResourceLocation::new(&buf.read_utf().await?)?;
|
let hardcore = buf.read_boolean().await?;
|
||||||
let data = buf.read_bytes(1048576).await?;
|
let game_type = GameType::from_id(buf.read_byte().await?)?;
|
||||||
panic!("not implemented");
|
let previous_game_type = GameType::from_optional_id(buf.read_byte().await? as i8)?;
|
||||||
// Ok(ClientboundLoginPacket {
|
|
||||||
// transaction_id,
|
let mut levels = Vec::new();
|
||||||
// identifier,
|
let length = buf.read_varint().await?;
|
||||||
// data,
|
for _ in 0..length {
|
||||||
// }
|
levels.push(buf.read_resource_location().await?);
|
||||||
// .get())
|
}
|
||||||
|
|
||||||
|
// println!("about to read nbt");
|
||||||
|
// // read all the bytes into a buffer, print it, and panic
|
||||||
|
// let mut registry_holder_buf = Vec::new();
|
||||||
|
// buf.read_to_end(&mut registry_holder_buf).await.unwrap();
|
||||||
|
// println!("{:?}", String::from_utf8_lossy(®istry_holder_buf));
|
||||||
|
// panic!("");
|
||||||
|
|
||||||
|
let registry_holder = buf.read_nbt().await?;
|
||||||
|
let dimension_type = buf.read_nbt().await?;
|
||||||
|
let dimension = buf.read_resource_location().await?;
|
||||||
|
let seed = buf.read_long().await?;
|
||||||
|
let max_players = buf.read_varint().await?;
|
||||||
|
let chunk_radius = buf.read_varint().await?;
|
||||||
|
let simulation_distance = buf.read_varint().await?;
|
||||||
|
let reduced_debug_info = buf.read_boolean().await?;
|
||||||
|
let show_death_screen = buf.read_boolean().await?;
|
||||||
|
let is_debug = buf.read_boolean().await?;
|
||||||
|
let is_flat = buf.read_boolean().await?;
|
||||||
|
|
||||||
|
Ok(ClientboundLoginPacket {
|
||||||
|
player_id,
|
||||||
|
hardcore,
|
||||||
|
game_type,
|
||||||
|
previous_game_type,
|
||||||
|
levels,
|
||||||
|
registry_holder,
|
||||||
|
dimension_type,
|
||||||
|
dimension,
|
||||||
|
seed,
|
||||||
|
max_players,
|
||||||
|
chunk_radius,
|
||||||
|
simulation_distance,
|
||||||
|
reduced_debug_info,
|
||||||
|
show_death_screen,
|
||||||
|
is_debug,
|
||||||
|
is_flat,
|
||||||
|
}
|
||||||
|
.get())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,16 +25,23 @@ impl ProtocolPacket for GamePacket {
|
||||||
|
|
||||||
/// Read a packet by its id, ConnectionProtocol, and flow
|
/// Read a packet by its id, ConnectionProtocol, and flow
|
||||||
async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
|
async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
|
||||||
_id: u32,
|
id: u32,
|
||||||
flow: &PacketFlow,
|
flow: &PacketFlow,
|
||||||
_buf: &mut T,
|
buf: &mut T,
|
||||||
) -> Result<GamePacket, String>
|
) -> Result<GamePacket, String>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
match flow {
|
Ok(match flow {
|
||||||
PacketFlow::ServerToClient => Err("HandshakePacket::read not implemented".to_string()),
|
PacketFlow::ServerToClient => match id {
|
||||||
PacketFlow::ClientToServer => Err("HandshakePacket::read not implemented".to_string()),
|
0x26 => clientbound_login_packet::ClientboundLoginPacket::read(buf).await?,
|
||||||
}
|
|
||||||
|
_ => return Err(format!("Unknown ServerToClient game packet id: {}", id)),
|
||||||
|
},
|
||||||
|
PacketFlow::ClientToServer => match id {
|
||||||
|
// 0x00 => serverbound_hello_packet::ServerboundHelloPacket::read(buf).await?,
|
||||||
|
_ => return Err(format!("Unknown ClientToServer game packet id: {}", id)),
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,11 +68,11 @@ impl ProtocolPacket for LoginPacket {
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
_ => return Err(format!("Unknown ServerToClient status packet id: {}", id)),
|
_ => return Err(format!("Unknown ServerToClient login packet id: {}", id)),
|
||||||
},
|
},
|
||||||
PacketFlow::ClientToServer => match id {
|
PacketFlow::ClientToServer => match id {
|
||||||
0x00 => serverbound_hello_packet::ServerboundHelloPacket::read(buf).await?,
|
0x00 => serverbound_hello_packet::ServerboundHelloPacket::read(buf).await?,
|
||||||
_ => return Err(format!("Unknown ClientToServer status packet id: {}", id)),
|
_ => return Err(format!("Unknown ClientToServer login packet id: {}", id)),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue