aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <github@matdoes.dev>2021-12-26 14:15:06 -0600
committermat <github@matdoes.dev>2021-12-26 14:15:06 -0600
commitaf28b0e57aeeca8790e3014f3e568c60ae892e39 (patch)
tree497fa95e62393111f0b87d31926066153679bb52
parent1cdd061a999bfa16907ebcc5ab38b1863839b5f1 (diff)
downloadazalea-drasl-af28b0e57aeeca8790e3014f3e568c60ae892e39.tar.xz
reading nbt in the protocol works
-rw-r--r--azalea-client/src/connect.rs37
-rw-r--r--azalea-core/src/game_type.rs15
-rw-r--r--azalea-core/src/registries.rs8
-rw-r--r--azalea-core/src/resource_location.rs2
-rw-r--r--azalea-nbt/src/decode.rs13
-rw-r--r--azalea-protocol/src/mc_buf.rs92
-rw-r--r--azalea-protocol/src/packets/game/clientbound_login_packet.rs77
-rw-r--r--azalea-protocol/src/packets/game/mod.rs19
-rw-r--r--azalea-protocol/src/packets/login/mod.rs4
9 files changed, 211 insertions, 56 deletions
diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs
index d3702874..90cb3ad7 100644
--- a/azalea-client/src/connect.rs
+++ b/azalea-client/src/connect.rs
@@ -57,23 +57,26 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> {
};
// game
- // loop {
- // let packet_result = conn.read().await;
- // match packet_result {
- // Ok(packet) => match packet {
- // GamePacket::ClientboundKeepAlivePacket(p) => {
- // println!("Got keep alive packet {:?}", p.keep_alive_id);
- // }
- // GamePacket::ClientboundChatMessagePacket(p) => {
- // println!("Got chat message packet {:?}", p.message);
- // }
- // _ => panic!("unhandled packet"),
- // },
- // Err(e) => {
- // println!("Error: {:?}", e);
- // }
- // }
- // }
+ loop {
+ let packet_result = conn.read().await;
+ match packet_result {
+ Ok(packet) => match packet {
+ GamePacket::ClientboundLoginPacket(p) => {
+ println!("Got login packet {:?}", p);
+ }
+ // GamePacket::ClientboundKeepAlivePacket(p) => {
+ // println!("Got keep alive packet {:?}", p.keep_alive_id);
+ // }
+ // GamePacket::ClientboundChatMessagePacket(p) => {
+ // println!("Got chat message packet {:?}", p.message);
+ // }
+ _ => panic!("unhandled packet"),
+ },
+ Err(e) => {
+ println!("Error: {:?}", e);
+ }
+ }
+ }
Ok(())
}
diff --git a/azalea-core/src/game_type.rs b/azalea-core/src/game_type.rs
index 61abf898..b6ff479d 100644
--- a/azalea-core/src/game_type.rs
+++ b/azalea-core/src/game_type.rs
@@ -26,14 +26,21 @@ impl GameType {
}
}
- pub fn from_id(id: u8) -> GameType {
- match id {
+ pub fn from_id(id: u8) -> Result<GameType, String> {
+ Ok(match id {
0 => GameType::SURVIVAL,
1 => GameType::CREATIVE,
2 => GameType::ADVENTURE,
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 {
diff --git a/azalea-core/src/registries.rs b/azalea-core/src/registries.rs
deleted file mode 100644
index 605d7770..00000000
--- a/azalea-core/src/registries.rs
+++ /dev/null
@@ -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);
-}
diff --git a/azalea-core/src/resource_location.rs b/azalea-core/src/resource_location.rs
index c1fed451..7e28a2a2 100644
--- a/azalea-core/src/resource_location.rs
+++ b/azalea-core/src/resource_location.rs
@@ -1,6 +1,6 @@
//! A resource, like minecraft:stone
-#[derive(Hash, Clone, Debug)]
+#[derive(Hash, Clone, Debug, PartialEq, Eq)]
pub struct ResourceLocation {
pub namespace: String,
pub path: String,
diff --git a/azalea-nbt/src/decode.rs b/azalea-nbt/src/decode.rs
index b21bd9a5..41689a46 100644
--- a/azalea-nbt/src/decode.rs
+++ b/azalea-nbt/src/decode.rs
@@ -121,7 +121,18 @@ impl Tag {
R: AsyncRead + std::marker::Unpin + std::marker::Send,
{
// 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>
diff --git a/azalea-protocol/src/mc_buf.rs b/azalea-protocol/src/mc_buf.rs
index cf98aca3..04857235 100644
--- a/azalea-protocol/src/mc_buf.rs
+++ b/azalea-protocol/src/mc_buf.rs
@@ -3,6 +3,7 @@
use std::io::Write;
use async_trait::async_trait;
+use azalea_core::resource_location::ResourceLocation;
use byteorder::{BigEndian, WriteBytesExt};
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_boolean(&mut self, b: bool) -> 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]
@@ -140,6 +146,17 @@ impl Writable for Vec<u8> {
nbt.write(self)
.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]
@@ -156,6 +173,8 @@ pub trait Readable {
async fn read_int(&mut self) -> Result<i32, String>;
async fn read_boolean(&mut self) -> Result<bool, 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]
@@ -285,14 +304,32 @@ where
}
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)]
mod tests {
use super::*;
- use std::io::Cursor;
+ use std::{collections::HashMap, io::Cursor};
use tokio::io::BufReader;
#[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()
+ );
+ }
}
diff --git a/azalea-protocol/src/packets/game/clientbound_login_packet.rs b/azalea-protocol/src/packets/game/clientbound_login_packet.rs
index 54205c48..1b90ce14 100644
--- a/azalea-protocol/src/packets/game/clientbound_login_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_login_packet.rs
@@ -1,6 +1,7 @@
use super::GamePacket;
use crate::mc_buf::{Readable, Writable};
use azalea_core::{game_type::GameType, resource_location::ResourceLocation};
+use tokio::io::AsyncReadExt;
#[derive(Clone, Debug)]
pub struct ClientboundLoginPacket {
@@ -50,27 +51,73 @@ impl ClientboundLoginPacket {
buf.write_byte(self.game_type.to_id())?;
buf.write_byte(GameType::to_optional_id(&self.previous_game_type) as u8)?;
buf.write_list(&self.levels, |buf, resource_location| {
- buf.write_utf(&resource_location.to_string())
+ buf.write_resource_location(resource_location)
})?;
- self.registry_holder
- .write(buf)
- .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "write registry holder"))?;
-
+ buf.write_nbt(&self.registry_holder)?;
+ buf.write_nbt(&self.dimension_type)?;
+ 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(())
}
pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
buf: &mut T,
) -> Result<GamePacket, String> {
- let transaction_id = buf.read_varint().await? as u32;
- let identifier = ResourceLocation::new(&buf.read_utf().await?)?;
- let data = buf.read_bytes(1048576).await?;
- panic!("not implemented");
- // Ok(ClientboundLoginPacket {
- // transaction_id,
- // identifier,
- // data,
- // }
- // .get())
+ let player_id = buf.read_int().await?;
+ let hardcore = buf.read_boolean().await?;
+ let game_type = GameType::from_id(buf.read_byte().await?)?;
+ let previous_game_type = GameType::from_optional_id(buf.read_byte().await? as i8)?;
+
+ let mut levels = Vec::new();
+ let length = buf.read_varint().await?;
+ for _ in 0..length {
+ levels.push(buf.read_resource_location().await?);
+ }
+
+ // 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(&registry_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())
}
}
diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs
index 932435d9..5697a0ad 100644
--- a/azalea-protocol/src/packets/game/mod.rs
+++ b/azalea-protocol/src/packets/game/mod.rs
@@ -25,16 +25,23 @@ impl ProtocolPacket for GamePacket {
/// Read a packet by its id, ConnectionProtocol, and flow
async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
- _id: u32,
+ id: u32,
flow: &PacketFlow,
- _buf: &mut T,
+ buf: &mut T,
) -> Result<GamePacket, String>
where
Self: Sized,
{
- match flow {
- PacketFlow::ServerToClient => Err("HandshakePacket::read not implemented".to_string()),
- PacketFlow::ClientToServer => Err("HandshakePacket::read not implemented".to_string()),
- }
+ Ok(match flow {
+ PacketFlow::ServerToClient => match id {
+ 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)),
+ },
+ })
}
}
diff --git a/azalea-protocol/src/packets/login/mod.rs b/azalea-protocol/src/packets/login/mod.rs
index 65d94bed..4d490d08 100644
--- a/azalea-protocol/src/packets/login/mod.rs
+++ b/azalea-protocol/src/packets/login/mod.rs
@@ -68,11 +68,11 @@ impl ProtocolPacket for LoginPacket {
)
.await?
}
- _ => return Err(format!("Unknown ServerToClient status packet id: {}", id)),
+ _ => return Err(format!("Unknown ServerToClient login packet id: {}", id)),
},
PacketFlow::ClientToServer => match id {
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)),
},
})
}