Network Serialization Quality of Life and Safety Improvements (#701)

This commit is contained in:
kralverde
2025-04-07 07:59:00 -10:00
committed by GitHub
parent 67f26cf9bd
commit 91ef79325a
46 changed files with 596 additions and 430 deletions

View File

@@ -7,7 +7,7 @@ use syn::{Ident, LitInt};
#[derive(Deserialize)]
struct DamageTypeEntry {
id: u32,
id: u8,
components: DamageTypeData,
}
@@ -111,7 +111,7 @@ pub(crate) fn build() -> TokenStream {
pub effects: Option<DamageEffects>,
pub message_id: &'static str,
pub scaling: DamageScaling,
pub id: u32,
pub id: u8,
}
#[derive(Clone, Copy, Debug, PartialEq)]

View File

@@ -34,16 +34,16 @@ pub(crate) fn build() -> TokenStream {
let mut variants = TokenStream::new();
for (name, typee) in json.iter() {
let i = typee.id;
let i = typee.id as u8;
let name = format_ident!("{}", name.to_uppercase());
variants.extend([quote! {
pub const #name: u32 = #i;
pub const #name: u8 = #i;
}]);
}
let raw_id = json.len() as u32;
let raw_id = json.len() as u8;
variants.extend([quote! {
pub const RAW: u32 = #raw_id; // One higher than highest vanilla id
pub const RAW: u8 = #raw_id; // One higher than highest vanilla id
}]);
quote! {

View File

@@ -187,6 +187,7 @@ pub fn get_nbt_string<R: Read>(bytes: &mut NbtReadHelper<R>) -> Result<String, E
Ok(string.to_string())
}
// TODO: This is a bit hacky
pub(crate) const NBT_ARRAY_TAG: &str = "__nbt_array";
pub(crate) const NBT_INT_ARRAY_TAG: &str = "__nbt_int_array";
pub(crate) const NBT_LONG_ARRAY_TAG: &str = "__nbt_long_array";

View File

@@ -1,13 +1,10 @@
use std::io::Write;
use pumpkin_data::packet::clientbound::CONFIG_SELECT_KNOWN_PACKS;
use pumpkin_macros::packet;
use serde::Serialize;
use crate::{
ClientPacket, KnownPack,
ser::{NetworkWriteExt, WritingError},
};
use crate::KnownPack;
#[derive(Serialize)]
#[packet(CONFIG_SELECT_KNOWN_PACKS)]
pub struct CKnownPacks<'a> {
pub known_packs: &'a [KnownPack<'a>],
@@ -18,14 +15,3 @@ impl<'a> CKnownPacks<'a> {
Self { known_packs }
}
}
impl ClientPacket for CKnownPacks<'_> {
fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> {
let mut write = write;
write.write_list::<KnownPack>(self.known_packs, |p, v| {
p.write_string(v.namespace)?;
p.write_string(v.id)?;
p.write_string(v.version)
})
}
}

View File

@@ -1,15 +1,10 @@
use std::io::Write;
use pumpkin_data::packet::clientbound::CONFIG_REGISTRY_DATA;
use pumpkin_macros::packet;
use serde::Serialize;
use crate::{
ClientPacket,
codec::identifier::Identifier,
ser::{NetworkWriteExt, WritingError},
};
use crate::{codec::identifier::Identifier, ser::network_serialize_no_prefix};
#[derive(Serialize)]
#[packet(CONFIG_REGISTRY_DATA)]
pub struct CRegistryData<'a> {
pub registry_id: &'a Identifier,
@@ -25,11 +20,14 @@ impl<'a> CRegistryData<'a> {
}
}
#[derive(Serialize)]
pub struct RegistryEntry {
pub entry_id: Identifier,
#[serde(serialize_with = "network_serialize_no_prefix")]
pub data: Option<Box<[u8]>>,
}
// TODO: No unwraps
impl RegistryEntry {
pub fn from_nbt(name: &str, nbt: &impl Serialize) -> Self {
let mut data_buf = Vec::new();
@@ -48,14 +46,3 @@ impl RegistryEntry {
}
}
}
impl ClientPacket for CRegistryData<'_> {
fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> {
let mut write = write;
write.write_identifier(self.registry_id)?;
write.write_list::<RegistryEntry>(self.entries, |p, v| {
p.write_identifier(&v.entry_id)?;
p.write_option(&v.data, |p, v| p.write_slice(v))
})
}
}

View File

@@ -1,4 +1,4 @@
use crate::{Link, VarInt};
use crate::Link;
use pumpkin_data::packet::clientbound::CONFIG_SERVER_LINKS;
use pumpkin_macros::packet;
use serde::Serialize;
@@ -6,12 +6,11 @@ use serde::Serialize;
#[derive(Serialize)]
#[packet(CONFIG_SERVER_LINKS)]
pub struct CConfigServerLinks<'a> {
links_count: &'a VarInt,
links: &'a [Link<'a>],
}
impl<'a> CConfigServerLinks<'a> {
pub fn new(links_count: &'a VarInt, links: &'a [Link<'a>]) -> Self {
Self { links_count, links }
pub fn new(links: &'a [Link<'a>]) -> Self {
Self { links }
}
}

View File

@@ -1,4 +1,4 @@
use crate::{VarInt, codec::identifier::Identifier};
use crate::codec::identifier::Identifier;
use pumpkin_data::packet::clientbound::CONFIG_STORE_COOKIE;
use pumpkin_macros::packet;
@@ -8,16 +8,11 @@ use pumpkin_macros::packet;
/// The Notchian (vanilla) client only accepts cookies of up to 5 KiB in size.
pub struct CStoreCookie<'a> {
key: &'a Identifier,
payload_length: VarInt,
payload: &'a [u8], // 5120,
}
impl<'a> CStoreCookie<'a> {
pub fn new(key: &'a Identifier, payload: &'a [u8]) -> Self {
Self {
key,
payload_length: VarInt(payload.len() as i32),
payload,
}
Self { key, payload }
}
}

View File

@@ -10,7 +10,7 @@ use pumpkin_world::block::registry;
use crate::{
ClientPacket,
codec::{identifier::Identifier, var_int::VarInt},
codec::identifier::Identifier,
ser::{NetworkWriteExt, WritingError},
};
@@ -32,7 +32,10 @@ impl ClientPacket for CUpdateTags<'_> {
p.write_identifier(&Identifier::vanilla(registry_key.identifier_string()))?;
let values = get_registry_key_tags(registry_key);
p.write_var_int(&VarInt::from(values.len()))?;
p.write_var_int(&values.len().try_into().map_err(|_| {
WritingError::Message(format!("{} isn't representable as a VarInt", values.len()))
})?)?;
for (key, values) in values.iter() {
// This is technically an `Identifier` but same thing
p.write_string_bounded(key, u16::MAX as usize)?;
@@ -43,7 +46,7 @@ impl ClientPacket for CUpdateTags<'_> {
_ => unimplemented!(),
};
p.write_var_int(&VarInt::from(id))
p.write_var_int(&id.into())
})?;
}

View File

@@ -2,15 +2,11 @@ use pumpkin_data::packet::clientbound::LOGIN_HELLO;
use pumpkin_macros::packet;
use serde::{Deserialize, Serialize};
use crate::VarInt;
#[derive(Serialize, Deserialize)]
#[packet(LOGIN_HELLO)]
pub struct CEncryptionRequest<'a> {
pub server_id: &'a str, // 20
pub public_key_length: VarInt,
pub public_key: &'a [u8],
pub verify_token_length: VarInt,
pub verify_token: &'a [u8],
pub should_authenticate: bool,
}
@@ -24,9 +20,7 @@ impl<'a> CEncryptionRequest<'a> {
) -> Self {
Self {
server_id,
public_key_length: public_key.len().into(),
public_key,
verify_token_length: verify_token.len().into(),
verify_token,
should_authenticate,
}

View File

@@ -1,13 +1,10 @@
use std::io::Write;
use pumpkin_data::packet::clientbound::LOGIN_LOGIN_FINISHED;
use pumpkin_macros::packet;
use serde::Serialize;
use crate::{
ClientPacket, Property,
ser::{NetworkWriteExt, WritingError},
};
use crate::Property;
#[derive(Serialize)]
#[packet(LOGIN_LOGIN_FINISHED)]
pub struct CLoginSuccess<'a> {
pub uuid: &'a uuid::Uuid,
@@ -24,16 +21,3 @@ impl<'a> CLoginSuccess<'a> {
}
}
}
impl ClientPacket for CLoginSuccess<'_> {
fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> {
let mut write = write;
write.write_uuid(self.uuid)?;
write.write_string(self.username)?;
write.write_list::<Property>(self.properties, |p, v| {
p.write_string(&v.name)?;
p.write_string(&v.value)?;
p.write_option(&v.signature, |p, v| p.write_string(v))
})
}
}

View File

@@ -1,8 +1,8 @@
use std::io::Write;
use crate::ClientPacket;
use crate::client::play::bossevent_action::BosseventAction;
use crate::ser::{NetworkWriteExt, WritingError};
use crate::{ClientPacket, VarInt};
use pumpkin_data::packet::clientbound::PLAY_BOSS_EVENT;
use pumpkin_macros::packet;
@@ -32,29 +32,29 @@ impl ClientPacket for CBossEvent<'_> {
division,
flags,
} => {
write.write_var_int(&VarInt::from(0u8))?;
write.write_var_int(&0.into())?;
write.write_slice(&title.encode())?;
write.write_f32_be(*health)?;
write.write_var_int(color)?;
write.write_var_int(division)?;
write.write_u8_be(*flags)
}
BosseventAction::Remove => write.write_var_int(&VarInt::from(1u8)),
BosseventAction::Remove => write.write_var_int(&1.into()),
BosseventAction::UpdateHealth(health) => {
write.write_var_int(&VarInt::from(2u8))?;
write.write_var_int(&2.into())?;
write.write_f32_be(*health)
}
BosseventAction::UpdateTile(title) => {
write.write_var_int(&VarInt::from(3u8))?;
write.write_var_int(&3.into())?;
write.write_slice(&title.encode())
}
BosseventAction::UpdateStyle { color, dividers } => {
write.write_var_int(&VarInt::from(4u8))?;
write.write_var_int(&4.into())?;
write.write_var_int(color)?;
write.write_var_int(dividers)
}
BosseventAction::UpdateFlags(flags) => {
write.write_var_int(&VarInt::from(5u8))?;
write.write_var_int(&5.into())?;
write.write_u8_be(*flags)
}
}

View File

@@ -11,7 +11,7 @@ pub struct CChunkBatchEnd {
}
impl CChunkBatchEnd {
pub fn new(count: usize) -> Self {
pub fn new(count: u16) -> Self {
Self {
batch_size: count.into(),
}

View File

@@ -53,7 +53,12 @@ impl ClientPacket for CChunkData<'_> {
// TODO: Implement, currently default to full bright
let chunk_light = vec![0xFFu8; chunk_light_len];
light_buf.write_var_int(&chunk_light_len.into())?;
light_buf.write_var_int(&chunk_light_len.try_into().map_err(|_| {
WritingError::Message(format!(
"{} is not representable as a VarInt!",
chunk_light_len
))
})?)?;
light_buf.write_slice(&chunk_light)?;
// Block count
@@ -68,7 +73,12 @@ impl ClientPacket for CChunkData<'_> {
data_buf.write_var_int(&registry_id.into())?;
}
NetworkPalette::Indirect(palette) => {
data_buf.write_var_int(&palette.len().into())?;
data_buf.write_var_int(&palette.len().try_into().map_err(|_| {
WritingError::Message(format!(
"{} is not representable as a VarInt!",
palette.len()
))
})?)?;
for registry_id in palette {
data_buf.write_var_int(&registry_id.into())?;
}
@@ -89,7 +99,12 @@ impl ClientPacket for CChunkData<'_> {
data_buf.write_var_int(&registry_id.into())?;
}
NetworkPalette::Indirect(palette) => {
data_buf.write_var_int(&palette.len().into())?;
data_buf.write_var_int(&palette.len().try_into().map_err(|_| {
WritingError::Message(format!(
"{} is not representable as a VarInt!",
palette.len()
))
})?)?;
for registry_id in palette {
data_buf.write_var_int(&registry_id.into())?;
}
@@ -105,7 +120,12 @@ impl ClientPacket for CChunkData<'_> {
}
// Chunk data
write.write_var_int(&data_buf.len().into())?;
write.write_var_int(&data_buf.len().try_into().map_err(|_| {
WritingError::Message(format!(
"{} is not representable as a VarInt!",
data_buf.len()
))
})?)?;
write.write_slice(&data_buf)?;
// TODO: block entities
@@ -123,7 +143,12 @@ impl ClientPacket for CChunkData<'_> {
write.write_bitset(&BitSet(Box::new([0])))?;
// Sky light
write.write_var_int(&self.0.section.sections.len().into())?;
write.write_var_int(&self.0.section.sections.len().try_into().map_err(|_| {
WritingError::Message(format!(
"{} is not representable as a VarInt!",
self.0.section.sections.len()
))
})?)?;
write.write_slice(&light_buf)?;
// Block Lighting

View File

@@ -1,24 +1,26 @@
use std::io::Write;
use pumpkin_data::packet::clientbound::PLAY_COMMAND_SUGGESTIONS;
use pumpkin_macros::packet;
use pumpkin_util::text::TextComponent;
use serde::Serialize;
use crate::{
ClientPacket, VarInt,
ser::{NetworkWriteExt, WritingError},
};
use crate::VarInt;
#[derive(Serialize)]
#[packet(PLAY_COMMAND_SUGGESTIONS)]
pub struct CCommandSuggestions {
id: VarInt,
start: VarInt,
length: VarInt,
matches: Vec<CommandSuggestion>,
matches: Box<[CommandSuggestion]>,
}
impl CCommandSuggestions {
pub fn new(id: VarInt, start: VarInt, length: VarInt, matches: Vec<CommandSuggestion>) -> Self {
pub fn new(
id: VarInt,
start: VarInt,
length: VarInt,
matches: Box<[CommandSuggestion]>,
) -> Self {
Self {
id,
start,
@@ -28,28 +30,7 @@ impl CCommandSuggestions {
}
}
impl ClientPacket for CCommandSuggestions {
fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> {
let mut write = write;
write.write_var_int(&self.id)?;
write.write_var_int(&self.start)?;
write.write_var_int(&self.length)?;
write.write_list(&self.matches, |write, suggestion| {
write.write_string(&suggestion.suggestion)?;
write.write_bool(suggestion.tooltip.is_some())?;
if let Some(tooltip) = &suggestion.tooltip {
write.write_slice(&tooltip.encode())?;
}
Ok(())
})?;
Ok(())
}
}
#[derive(PartialEq, Eq, Hash, Debug)]
#[derive(PartialEq, Eq, Hash, Debug, Serialize)]
pub struct CommandSuggestion {
pub suggestion: String,
pub tooltip: Option<TextComponent>,

View File

@@ -10,12 +10,12 @@ use crate::{
#[packet(PLAY_COMMANDS)]
pub struct CCommands<'a> {
pub nodes: Vec<ProtoNode<'a>>,
pub nodes: Box<[ProtoNode<'a>]>,
pub root_node_index: VarInt,
}
impl<'a> CCommands<'a> {
pub fn new(nodes: Vec<ProtoNode<'a>>, root_node_index: VarInt) -> Self {
pub fn new(nodes: Box<[ProtoNode<'a>]>, root_node_index: VarInt) -> Self {
Self {
nodes,
root_node_index,
@@ -34,7 +34,7 @@ impl ClientPacket for CCommands<'_> {
}
pub struct ProtoNode<'a> {
pub children: Vec<VarInt>,
pub children: Box<[VarInt]>,
pub node_type: ProtoNodeType<'a>,
}

View File

@@ -2,17 +2,19 @@ use pumpkin_data::packet::clientbound::PLAY_SET_ENTITY_DATA;
use pumpkin_macros::packet;
use serde::Serialize;
use crate::VarInt;
use crate::{VarInt, ser::network_serialize_no_prefix};
#[derive(Serialize)]
#[packet(PLAY_SET_ENTITY_DATA)]
pub struct CSetEntityMetadata {
entity_id: VarInt,
metadata: Vec<u8>,
// TODO: We should migrate the serialization of this into this file
#[serde(serialize_with = "network_serialize_no_prefix")]
metadata: Box<[u8]>,
}
impl CSetEntityMetadata {
pub fn new(entity_id: VarInt, metadata: Vec<u8>) -> Self {
pub fn new(entity_id: VarInt, metadata: Box<[u8]>) -> Self {
Self {
entity_id,
metadata,

View File

@@ -11,7 +11,6 @@ use crate::{VarInt, codec::identifier::Identifier};
pub struct CLogin<'a> {
entity_id: i32,
is_hardcore: bool,
dimension_count: VarInt,
dimension_names: &'a [Identifier],
max_players: VarInt,
view_distance: VarInt,
@@ -61,7 +60,6 @@ impl<'a> CLogin<'a> {
Self {
entity_id,
is_hardcore,
dimension_count: VarInt(dimension_names.len() as i32),
dimension_names,
max_players,
view_distance,

View File

@@ -36,7 +36,14 @@ impl Serialize for CMultiBlockUpdate {
let mut tuple = serializer.serialize_tuple(2 + self.positions_to_state_ids.len())?;
tuple.serialize_element(&vector3::packed_chunk_pos(&self.chunk_section))?;
tuple.serialize_element(&VarInt::from(self.positions_to_state_ids.len() as i32))?;
tuple.serialize_element(&VarInt(
self.positions_to_state_ids.len().try_into().map_err(|_| {
serde::ser::Error::custom(format!(
"{} is not representable as a VarInt!",
self.positions_to_state_ids.len()
))
})?,
))?;
for (position, state_id) in &self.positions_to_state_ids {
let long = ((*state_id as u64) << 12) | (*position as u64);

View File

@@ -65,6 +65,7 @@ impl CPlayerChatMessage {
}
}
//TODO: Check if we need this custom impl
impl ClientPacket for CPlayerChatMessage {
fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> {
let mut write = write;

View File

@@ -42,6 +42,7 @@ impl<'a> CPlayerInfoUpdate<'a> {
}
}
// TODO: Check if we need this custom impl
impl ClientPacket for CPlayerInfoUpdate<'_> {
fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> {
let mut write = write;
@@ -63,9 +64,19 @@ impl ClientPacket for CPlayerInfoUpdate<'_> {
p.write_option(init_chat, |p, v| {
p.write_uuid(&v.session_id)?;
p.write_i64_be(v.expires_at)?;
p.write_var_int(&v.public_key.len().into())?;
p.write_var_int(&v.public_key.len().try_into().map_err(|_| {
WritingError::Message(format!(
"{} isn't representable as a VarInt",
v.public_key.len()
))
})?)?;
p.write_slice(&v.public_key)?;
p.write_var_int(&v.signature.len().into())?;
p.write_var_int(&v.signature.len().try_into().map_err(|_| {
WritingError::Message(format!(
"{} isn't representable as a VarInt",
v.signature.len()
))
})?)?;
p.write_slice(&v.signature)
})?;
}

View File

@@ -39,6 +39,7 @@ impl<'a> CPlayerPosition<'a> {
}
}
// TODO: Do we need a custom impl?
impl ClientPacket for CPlayerPosition<'_> {
fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> {
let mut write = write;

View File

@@ -2,22 +2,16 @@ use pumpkin_data::packet::clientbound::PLAY_PLAYER_INFO_REMOVE;
use pumpkin_macros::packet;
use serde::{Serialize, ser::SerializeSeq};
use crate::VarInt;
#[derive(Serialize)]
#[packet(PLAY_PLAYER_INFO_REMOVE)]
pub struct CRemovePlayerInfo<'a> {
players_count: VarInt,
#[serde(serialize_with = "serialize_slice_uuids")]
players: &'a [uuid::Uuid],
}
impl<'a> CRemovePlayerInfo<'a> {
pub fn new(players_count: VarInt, players: &'a [uuid::Uuid]) -> Self {
Self {
players_count,
players,
}
pub fn new(players: &'a [uuid::Uuid]) -> Self {
Self { players }
}
}

View File

@@ -7,15 +7,11 @@ use crate::VarInt;
#[derive(Serialize)]
#[packet(PLAY_REMOVE_ENTITIES)]
pub struct CRemoveEntities<'a> {
entity_count: VarInt,
entity_ids: &'a [VarInt],
}
impl<'a> CRemoveEntities<'a> {
pub fn new(entity_ids: &'a [VarInt]) -> Self {
Self {
entity_count: entity_ids.len().into(),
entity_ids,
}
Self { entity_ids }
}
}

View File

@@ -1,4 +1,4 @@
use crate::{Link, VarInt};
use crate::Link;
use pumpkin_data::packet::clientbound::PLAY_SERVER_LINKS;
use pumpkin_macros::packet;
use serde::Serialize;
@@ -6,12 +6,11 @@ use serde::Serialize;
#[derive(Serialize)]
#[packet(PLAY_SERVER_LINKS)]
pub struct CPlayServerLinks<'a> {
links_count: &'a VarInt,
links: &'a [Link<'a>],
}
impl<'a> CPlayServerLinks<'a> {
pub fn new(links_count: &'a VarInt, links: &'a [Link<'a>]) -> Self {
Self { links_count, links }
pub fn new(links: &'a [Link<'a>]) -> Self {
Self { links }
}
}

View File

@@ -10,7 +10,6 @@ use serde::Serialize;
pub struct CSetContainerContent<'a> {
window_id: VarInt,
state_id: VarInt,
count: VarInt,
slot_data: &'a [Slot],
carried_item: &'a Slot,
}
@@ -25,7 +24,6 @@ impl<'a> CSetContainerContent<'a> {
Self {
window_id,
state_id,
count: slots.len().into(),
slot_data: slots,
carried_item,
}

View File

@@ -1,4 +1,4 @@
use crate::{VarInt, codec::identifier::Identifier};
use crate::codec::identifier::Identifier;
use pumpkin_data::packet::clientbound::PLAY_STORE_COOKIE;
use pumpkin_macros::packet;
use serde::Serialize;
@@ -9,16 +9,11 @@ use serde::Serialize;
#[packet(PLAY_STORE_COOKIE)]
pub struct CStoreCookie<'a> {
key: &'a Identifier,
payload_length: VarInt,
payload: &'a [u8], // 5120,
}
impl<'a> CStoreCookie<'a> {
pub fn new(key: &'a Identifier, payload: &'a [u8]) -> Self {
Self {
key,
payload_length: VarInt(payload.len() as i32),
payload,
}
Self { key, payload }
}
}

View File

@@ -42,6 +42,7 @@ impl<'a> CTeleportEntity<'a> {
}
}
// TODO: Do we need a custom impl?
impl ClientPacket for CTeleportEntity<'_> {
fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> {
let mut write = write;

View File

@@ -12,7 +12,10 @@ pub struct BitSet(pub Box<[i64]>);
impl BitSet {
pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> {
write.write_var_int(&self.0.len().into())?;
write.write_var_int(&self.0.len().try_into().map_err(|_| {
WritingError::Message(format!("{} isn't representable as a VarInt", self.0.len()))
})?)?;
for b in &self.0 {
write.write_i64_be(*b)?;
}

View File

@@ -1,20 +1,21 @@
use crate::VarInt;
use pumpkin_data::item::Item;
use pumpkin_world::item::ItemStack;
use serde::ser::SerializeSeq;
use serde::{
Deserialize, Serialize, Serializer,
de::{self, SeqAccess},
ser,
};
#[derive(Debug, Clone)]
pub struct Slot {
pub item_count: VarInt,
item_id: Option<VarInt>,
num_components_to_add: Option<VarInt>,
num_components_to_remove: Option<VarInt>,
components_to_add: Option<Vec<(VarInt, ())>>, // The second type depends on the varint
components_to_remove: Option<Vec<VarInt>>,
pub enum Slot {
NoItem,
Item {
// This also handles items on the ground which can have >64 items
item_count: u32,
item_id: u16,
// TODO: Implement item components
},
}
impl<'de> Deserialize<'de> for Slot {
@@ -27,7 +28,7 @@ impl<'de> Deserialize<'de> for Slot {
type Value = Slot;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a valid VarInt encoded in a byte sequence")
formatter.write_str("a valid Slot encoded in a byte sequence")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
@@ -37,39 +38,39 @@ impl<'de> Deserialize<'de> for Slot {
let item_count = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("Failed to decode VarInt"))?;
if item_count.0 == 0 {
return Ok(Slot {
item_count: 0.into(),
item_id: None,
num_components_to_add: None,
num_components_to_remove: None,
components_to_add: None,
components_to_remove: None,
});
}
let item_id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("Failed to decode VarInt"))?;
let num_components_to_add = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("Failed to decode VarInt"))?;
let num_components_to_remove = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("Failed to decode VarInt"))?;
if num_components_to_add.0 != 0 || num_components_to_remove.0 != 0 {
return Err(de::Error::custom(
"Slot components are currently unsupported",
));
}
Ok(Slot {
item_count,
item_id: Some(item_id),
num_components_to_add: Some(num_components_to_add),
num_components_to_remove: Some(num_components_to_remove),
components_to_add: None,
components_to_remove: None,
})
let slot = if item_count.0 == 0 {
Slot::NoItem
} else {
let item_id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No item id VarInt!"))?;
let num_components_to_add = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No component add length VarInt!"))?;
let num_components_to_remove = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No component remove length VarInt!"))?;
if num_components_to_add.0 != 0 || num_components_to_remove.0 != 0 {
return Err(de::Error::custom(
"Slot components are currently unsupported",
));
}
let item_id: u16 = item_id
.0
.try_into()
.map_err(|_| de::Error::custom("Invalid item id!"))?;
Slot::Item {
// i32 can always be u32
item_count: item_count.0 as u32,
item_id,
}
};
Ok(slot)
}
}
@@ -82,48 +83,33 @@ impl Serialize for Slot {
where
S: Serializer,
{
if self.item_count == 0.into() {
let mut s = serializer.serialize_seq(Some(1))?;
s.serialize_element(&self.item_count)?;
s.end()
} else {
match (&self.num_components_to_add, &self.num_components_to_remove) {
(Some(to_add), Some(to_remove)) => {
let mut s = serializer.serialize_seq(Some(6))?;
s.serialize_element(&self.item_count)?;
s.serialize_element(self.item_id.as_ref().unwrap())?;
s.serialize_element(to_add)?;
s.serialize_element(to_remove)?;
s.serialize_element(self.components_to_add.as_ref().unwrap())?;
s.serialize_element(self.components_to_remove.as_ref().unwrap())?;
s.end()
match self {
Self::NoItem => VarInt(0).serialize(serializer),
Self::Item {
item_count,
item_id,
} => {
// TODO: Components
#[derive(Serialize)]
struct NetworkRepr {
item_count: VarInt,
item_id: VarInt,
components_to_add: VarInt,
components_to_remove: VarInt,
}
(None, Some(to_remove)) => {
let mut s = serializer.serialize_seq(Some(5))?;
s.serialize_element(&self.item_count)?;
s.serialize_element(self.item_id.as_ref().unwrap())?;
s.serialize_element(&VarInt(0))?;
s.serialize_element(to_remove)?;
s.serialize_element(self.components_to_remove.as_ref().unwrap())?;
s.end()
}
(Some(to_add), None) => {
let mut s = serializer.serialize_seq(Some(5))?;
s.serialize_element(&self.item_count)?;
s.serialize_element(self.item_id.as_ref().unwrap())?;
s.serialize_element(to_add)?;
s.serialize_element(&VarInt(0))?;
s.serialize_element(self.components_to_add.as_ref().unwrap())?;
s.end()
}
(None, None) => {
let mut s = serializer.serialize_seq(Some(4))?;
s.serialize_element(&self.item_count)?;
s.serialize_element(&self.item_id.as_ref().unwrap())?;
s.serialize_element(&VarInt(0))?;
s.serialize_element(&VarInt(0))?;
s.end()
let item_count: i32 = (*item_count)
.try_into()
.map_err(|_| ser::Error::custom("Item count overflows an i32!"))?;
NetworkRepr {
item_count: item_count.into(),
item_id: (*item_id).into(),
components_to_add: 0.into(),
components_to_remove: 0.into(),
}
.serialize(serializer)
}
}
}
@@ -131,48 +117,36 @@ impl Serialize for Slot {
impl Slot {
pub fn new(item_id: u16, count: u32) -> Self {
Slot {
item_count: count.into(),
item_id: Some((item_id as i32).into()),
// TODO: add these
num_components_to_add: None,
num_components_to_remove: None,
components_to_add: None,
components_to_remove: None,
Self::Item {
item_count: count,
item_id,
}
}
pub fn to_stack(self) -> Result<Option<ItemStack>, &'static str> {
let item_id = self.item_id;
let Some(item_id) = item_id else {
return Ok(None);
};
let item_id = item_id.0.try_into().map_err(|_| "Item id too large")?;
let item = Item::from_id(item_id).ok_or("Item id invalid")?;
if self.item_count.0 > item.components.max_stack_size as i32 {
Err("Oversized stack")
} else {
let stack = ItemStack {
item,
item_count: self
.item_count
.0
.try_into()
.map_err(|_| "Stack count too large")?,
};
Ok(Some(stack))
match self {
Self::NoItem => Ok(None),
Self::Item {
item_count,
item_id,
} => {
let item = Item::from_id(item_id).ok_or("Item id invalid")?;
if item_count > item.components.max_stack_size as u32 {
Err("Stack item count greater than allowed")
} else {
let stack = ItemStack {
item,
// This is checked above
item_count: item_count as u8,
};
Ok(Some(stack))
}
}
}
}
pub const fn empty() -> Self {
Slot {
item_count: VarInt(0),
item_id: None,
num_components_to_add: None,
num_components_to_remove: None,
components_to_add: None,
components_to_remove: None,
}
Self::NoItem
}
}

View File

@@ -47,6 +47,7 @@ impl VarInt {
Ok(())
}
// TODO: Validate that the first byte will not overflow a i32
pub fn decode(read: &mut impl Read) -> Result<Self, ReadingError> {
let mut val = 0;
for i in 0..Self::MAX_SIZE.get() {
@@ -99,41 +100,40 @@ impl VarInt {
}
}
impl From<i32> for VarInt {
fn from(value: i32) -> Self {
VarInt(value)
}
// Macros are needed because traits over generics succccccccccck
macro_rules! gen_from {
($ty: ty) => {
impl From<$ty> for VarInt {
fn from(value: $ty) -> Self {
VarInt(value.into())
}
}
};
}
impl From<u32> for VarInt {
fn from(value: u32) -> Self {
VarInt(value as i32)
}
gen_from!(i8);
gen_from!(u8);
gen_from!(i16);
gen_from!(u16);
gen_from!(i32);
macro_rules! gen_try_from {
($ty: ty) => {
impl TryFrom<$ty> for VarInt {
type Error = <i32 as TryFrom<$ty>>::Error;
fn try_from(value: $ty) -> Result<Self, Self::Error> {
Ok(VarInt(value.try_into()?))
}
}
};
}
impl From<u8> for VarInt {
fn from(value: u8) -> Self {
VarInt(value as i32)
}
}
impl From<u16> for VarInt {
fn from(value: u16) -> Self {
VarInt(value as i32)
}
}
impl From<usize> for VarInt {
fn from(value: usize) -> Self {
VarInt(value as i32)
}
}
impl From<VarInt> for i32 {
fn from(value: VarInt) -> Self {
value.0
}
}
gen_try_from!(u32);
gen_try_from!(i64);
gen_try_from!(u64);
gen_try_from!(isize);
gen_try_from!(usize);
impl AsRef<i32> for VarInt {
fn as_ref(&self) -> &i32 {

View File

@@ -47,6 +47,7 @@ impl VarLong {
Ok(())
}
// TODO: Validate that the first byte will not overflow a i64
pub fn decode(read: &mut impl Read) -> Result<Self, ReadingError> {
let mut val = 0;
for i in 0..Self::MAX_SIZE.get() {

View File

@@ -11,7 +11,6 @@ use ser::{NetworkWriteExt, ReadingError, WritingError, packet::Packet};
use serde::{
Deserialize, Serialize, Serializer,
de::{DeserializeSeed, Visitor},
ser::SerializeSeq,
};
use tokio::io::{AsyncRead, AsyncWrite};
@@ -86,7 +85,7 @@ where
{
enum IdOrStateDeserializer<T> {
Init,
Id(u32),
Id(u16),
Value(T),
}
@@ -104,11 +103,15 @@ where
IdOrStateDeserializer::Init => {
// Get the VarInt
let id = VarInt::deserialize(deserializer)?;
assert!(id.0 >= 0);
*self = IdOrStateDeserializer::<T>::Id(id.0 as u32);
*self = IdOrStateDeserializer::<T>::Id(id.0.try_into().map_err(|_| {
serde::de::Error::custom(format!(
"{} cannot be mapped to a registry id",
id.0
))
})?);
}
IdOrStateDeserializer::Id(id) => {
assert!(*id == 0);
debug_assert!(*id == 0);
// Get the data
let value = T::deserialize(deserializer)?;
*self = IdOrStateDeserializer::Value(value);
@@ -144,7 +147,7 @@ where
#[derive(PartialEq, Clone)]
pub enum IdOr<T> {
Id(u32),
Id(u16),
Value(T),
}
@@ -168,10 +171,16 @@ impl<T: Serialize> Serialize for IdOr<T> {
match self {
IdOr::Id(id) => VarInt::from(*id + 1).serialize(serializer),
IdOr::Value(value) => {
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element(&VarInt::from(0))?;
seq.serialize_element(value)?;
seq.end()
#[derive(Serialize)]
struct NetworkRepr<T: Serialize> {
zero_id: VarInt,
value: T,
}
NetworkRepr {
zero_id: 0.into(),
value,
}
.serialize(serializer)
}
}
}
@@ -383,6 +392,7 @@ pub struct Property {
pub signature: Option<String>,
}
#[derive(Serialize)]
pub struct KnownPack<'a> {
pub namespace: &'a str,
pub id: &'a str,

View File

@@ -144,7 +144,12 @@ impl<W: AsyncWrite + Unpin> NetworkEncoder<W> {
if data_len > MAX_PACKET_DATA_SIZE {
return Err(PacketEncodeError::TooLong(data_len));
}
let data_len_var_int: VarInt = data_len.into();
let data_len_var_int: VarInt = data_len.try_into().map_err(|_| {
PacketEncodeError::Message(format!(
"Packet data length is too large to fit in VarInt! ({})",
data_len
))
})?;
if let Some((compression_threshold, compression_level)) = self.compression {
if data_len >= compression_threshold {
@@ -170,8 +175,15 @@ impl<W: AsyncWrite + Unpin> NetworkEncoder<W> {
.map_err(|err| PacketEncodeError::Message(err.to_string()))?;
debug_assert!(!compressed_buf.is_empty());
let full_packet_len_var_int: VarInt =
(data_len_var_int.written_size() + compressed_buf.len()).into();
let full_packet_len_var_int: VarInt = (data_len_var_int.written_size()
+ compressed_buf.len())
.try_into()
.map_err(|_| {
PacketEncodeError::Message(format!(
"Full packet length is too large to fit in VarInt! ({})",
data_len
))
})?;
let complete_serialization_length =
full_packet_len_var_int.written_size() + full_packet_len_var_int.0 as usize;
@@ -197,8 +209,14 @@ impl<W: AsyncWrite + Unpin> NetworkEncoder<W> {
// 0 to indicate uncompressed
let data_len_var_int: VarInt = 0.into();
let full_packet_len_var_int: VarInt =
(data_len_var_int.written_size() + data_len).into();
let full_packet_len_var_int: VarInt = (data_len_var_int.written_size() + data_len)
.try_into()
.map_err(|_| {
PacketEncodeError::Message(format!(
"Full packet length is too large to fit in VarInt! ({})",
data_len
))
})?;
let complete_serialization_length =
full_packet_len_var_int.written_size() + full_packet_len_var_int.0 as usize;

View File

@@ -11,6 +11,17 @@ use thiserror::Error;
pub mod packet;
pub mod serializer;
// TODO: This is a bit hacky
const NO_PREFIX_MARKER: &str = "__network_no_prefix";
pub fn network_serialize_no_prefix<T, S>(input: T, serializer: S) -> Result<S::Ok, S::Error>
where
T: serde::Serialize,
S: serde::Serializer,
{
serializer.serialize_newtype_struct(NO_PREFIX_MARKER, &input)
}
#[derive(Debug, Error)]
pub enum ReadingError {
#[error("EOF, Tried to read {0} but No bytes left to consume")]
@@ -262,26 +273,55 @@ pub trait NetworkWriteExt {
fn write_f64_be(&mut self, data: f64) -> Result<(), WritingError>;
fn write_slice(&mut self, data: &[u8]) -> Result<(), WritingError>;
fn write_bool(&mut self, data: bool) -> Result<(), WritingError>;
fn write_bool(&mut self, data: bool) -> Result<(), WritingError> {
if data {
self.write_u8_be(1)
} else {
self.write_u8_be(0)
}
}
fn write_var_int(&mut self, data: &VarInt) -> Result<(), WritingError>;
fn write_var_long(&mut self, data: &VarLong) -> Result<(), WritingError>;
fn write_string_bounded(&mut self, data: &str, bound: usize) -> Result<(), WritingError>;
fn write_string(&mut self, data: &str) -> Result<(), WritingError>;
fn write_identifier(&mut self, data: &Identifier) -> Result<(), WritingError>;
fn write_uuid(&mut self, data: &uuid::Uuid) -> Result<(), WritingError>;
fn write_uuid(&mut self, data: &uuid::Uuid) -> Result<(), WritingError> {
let (first, second) = data.as_u64_pair();
self.write_u64_be(first)?;
self.write_u64_be(second)
}
fn write_bitset(&mut self, bitset: &BitSet) -> Result<(), WritingError>;
fn write_option<G>(
&mut self,
data: &Option<G>,
write: impl FnOnce(&mut Self, &G) -> Result<(), WritingError>,
) -> Result<(), WritingError>;
writer: impl FnOnce(&mut Self, &G) -> Result<(), WritingError>,
) -> Result<(), WritingError> {
if let Some(data) = data {
self.write_bool(true)?;
writer(self, data)
} else {
self.write_bool(false)
}
}
fn write_list<G>(
&mut self,
data: &[G],
write: impl Fn(&mut Self, &G) -> Result<(), WritingError>,
) -> Result<(), WritingError>;
list: &[G],
writer: impl Fn(&mut Self, &G) -> Result<(), WritingError>,
) -> Result<(), WritingError> {
self.write_var_int(&list.len().try_into().map_err(|_| {
WritingError::Message(format!("{} isn't representable as a VarInt", list.len()))
})?)?;
for data in list {
writer(self, data)?;
}
Ok(())
}
}
impl<W: Write> NetworkWriteExt for W {
@@ -339,14 +379,6 @@ impl<W: Write> NetworkWriteExt for W {
self.write_all(data).map_err(WritingError::IoError)
}
fn write_bool(&mut self, data: bool) -> Result<(), WritingError> {
if data {
self.write_u8_be(1)
} else {
self.write_u8_be(0)
}
}
fn write_var_int(&mut self, data: &VarInt) -> Result<(), WritingError> {
data.encode(self)
}
@@ -357,7 +389,10 @@ impl<W: Write> NetworkWriteExt for W {
fn write_string_bounded(&mut self, data: &str, bound: usize) -> Result<(), WritingError> {
assert!(data.len() <= bound);
self.write_var_int(&data.len().into())?;
self.write_var_int(&data.len().try_into().map_err(|_| {
WritingError::Message(format!("{} isn't representable as a VarInt", data.len()))
})?)?;
self.write_all(data.as_bytes())
.map_err(WritingError::IoError)
}
@@ -370,41 +405,9 @@ impl<W: Write> NetworkWriteExt for W {
data.encode(self)
}
fn write_uuid(&mut self, data: &uuid::Uuid) -> Result<(), WritingError> {
let (first, second) = data.as_u64_pair();
self.write_u64_be(first)?;
self.write_u64_be(second)
}
fn write_bitset(&mut self, data: &BitSet) -> Result<(), WritingError> {
data.encode(self)
}
fn write_option<G>(
&mut self,
data: &Option<G>,
writer: impl FnOnce(&mut Self, &G) -> Result<(), WritingError>,
) -> Result<(), WritingError> {
if let Some(data) = data {
self.write_bool(true)?;
writer(self, data)
} else {
self.write_bool(false)
}
}
fn write_list<G>(
&mut self,
list: &[G],
writer: impl Fn(&mut Self, &G) -> Result<(), WritingError>,
) -> Result<(), WritingError> {
self.write_var_int(&list.len().into())?;
for data in list {
writer(self, data)?;
}
Ok(())
}
}
#[cfg(test)]

View File

@@ -2,10 +2,10 @@ use std::fmt::Display;
use serde::{
Serialize,
ser::{self},
ser::{self, Impossible},
};
use super::{NetworkWriteExt, Write, WritingError};
use super::{NO_PREFIX_MARKER, NetworkWriteExt, Write, WritingError};
pub struct Serializer<W: Write> {
pub write: W,
@@ -23,6 +23,198 @@ impl ser::Error for WritingError {
}
}
/// This serializer just writes a sequence without a varint prefix and defers the rest of the
/// serialization to the wrapped serializer
struct NonPrefixedSeqSerializer<'a, W: Write> {
wrapped: &'a mut Serializer<W>,
}
macro_rules! create_fail_method {
($method: ident, $ty: ty) => {
fn $method(self, _v: $ty) -> Result<Self::Ok, Self::Error> {
Err(WritingError::Serde(format!(
"Expected a sequence, but found {}!",
stringify!($ty)
)))
}
};
}
impl<W: Write> ser::SerializeSeq for NonPrefixedSeqSerializer<'_, W> {
type Ok = ();
type Error = WritingError;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(&mut *self.wrapped).map(|_| ())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<W: Write> ser::Serializer for NonPrefixedSeqSerializer<'_, W> {
type Ok = ();
type Error = WritingError;
type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
type SerializeStruct = Impossible<Self::Ok, Self::Error>;
type SerializeMap = Impossible<Self::Ok, Self::Error>;
type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
type SerializeTuple = Impossible<Self::Ok, Self::Error>;
type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
type SerializeSeq = Self;
create_fail_method!(serialize_bool, bool);
create_fail_method!(serialize_bytes, &[u8]);
create_fail_method!(serialize_char, char);
create_fail_method!(serialize_f32, f32);
create_fail_method!(serialize_f64, f64);
create_fail_method!(serialize_i8, i8);
create_fail_method!(serialize_i16, i16);
create_fail_method!(serialize_i32, i32);
create_fail_method!(serialize_i64, i64);
create_fail_method!(serialize_u8, u8);
create_fail_method!(serialize_u16, u16);
create_fail_method!(serialize_u32, u32);
create_fail_method!(serialize_u64, u64);
create_fail_method!(serialize_str, &str);
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Err(WritingError::Serde(
"Expected a sequence but found a map!".into(),
))
}
fn serialize_newtype_struct<T>(
self,
name: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
Err(WritingError::Serde(format!(
"Expected a sequence but found a newtype struct {}!",
name
)))
}
fn serialize_newtype_variant<T>(
self,
name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
Err(WritingError::Serde(format!(
"Expected a sequence but found a newtype variant {}!",
name
)))
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
self.wrapped.serialize_none()
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(self)
}
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
self.wrapped.serialize_bool(true)?;
value.serialize(self)
}
fn serialize_struct(
self,
name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Err(WritingError::Serde(format!(
"Expected a sequence but found a struct {}!",
name
)))
}
fn serialize_struct_variant(
self,
name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Err(WritingError::Serde(format!(
"Expected a sequence but found a struct variant {}!",
name
)))
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Err(WritingError::Serde(
"Expected a sequence but found a tuple!".into(),
))
}
fn serialize_tuple_struct(
self,
name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Err(WritingError::Serde(format!(
"Expected a sequence but found a tuple struct {}!",
name
)))
}
fn serialize_tuple_variant(
self,
name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
Err(WritingError::Serde(format!(
"Expected a sequence but found a tuple variant {}!",
name
)))
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Err(WritingError::Serde(
"Expected a sequence but found a unit!".into(),
))
}
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
Err(WritingError::Serde(format!(
"Expected a sequence but found a unit struct {}!",
name
)))
}
fn serialize_unit_variant(
self,
name: &'static str,
_variant_index: u32,
_variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
Err(WritingError::Serde(format!(
"Expected a sequence but found a unit variant {}!",
name
)))
}
}
// General notes on the serializer:
//
// Primitives are written as-is
@@ -87,13 +279,13 @@ impl<W: Write> ser::Serializer for &mut Serializer<W> {
// TODO: This is super sketchy... is there a way to do it better? Can we choose what
// serializer to use on a struct somehow from within the struct?
if name == "TextComponent" {
let mut buf = Vec::new();
let mut nbt_serializer = pumpkin_nbt::serializer::Serializer::new(&mut buf, None);
value
.serialize(&mut nbt_serializer)
.expect("Failed to serialize NBT for TextComponent within the network serializer");
self.serialize_bytes(&buf)
let mut nbt_serializer =
pumpkin_nbt::serializer::Serializer::new(&mut self.write, None);
value.serialize(&mut nbt_serializer).map_err(|err| {
WritingError::Serde(format!("Failed to serialize TextComponent NBT: {}", err))
})
} else if name == NO_PREFIX_MARKER {
value.serialize(NonPrefixedSeqSerializer { wrapped: self })
} else {
value.serialize(self)
}
@@ -108,15 +300,26 @@ impl<W: Write> ser::Serializer for &mut Serializer<W> {
where
T: ?Sized + Serialize,
{
self.write.write_var_int(&variant_index.into())?;
self.write
.write_var_int(&variant_index.try_into().map_err(|_| {
WritingError::Message(format!("{} isn't representable as a VarInt", variant_index))
})?)?;
value.serialize(self)
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
self.write.write_bool(false)
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
// here is where all arrays/list getting written, usually we prefix the length of every length with an var int. The problem is
// that byte arrays also getting thrown in here, and we don't want to prefix them
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
let Some(len) = len else {
return Err(WritingError::Serde(
"Sequences must have a known length".into(),
));
};
self.write.write_var_int(&len.try_into().map_err(|_| {
WritingError::Message(format!("{} isn't representable as a VarInt", len))
})?)?;
Ok(self)
}
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
@@ -163,7 +366,10 @@ impl<W: Write> ser::Serializer for &mut Serializer<W> {
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
// Serialize ENUM index as varint
self.write.write_var_int(&variant_index.into())?;
self.write
.write_var_int(&variant_index.try_into().map_err(|_| {
WritingError::Message(format!("{} isn't representable as a VarInt", variant_index))
})?)?;
Ok(self)
}
fn serialize_u128(self, _v: u128) -> Result<Self::Ok, Self::Error> {
@@ -194,7 +400,10 @@ impl<W: Write> ser::Serializer for &mut Serializer<W> {
_variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
// For ENUMs, only write enum index as varint
self.write.write_var_int(&variant_index.into())
self.write
.write_var_int(&variant_index.try_into().map_err(|_| {
WritingError::Message(format!("{} isn't representable as a VarInt", variant_index))
})?)
}
fn is_human_readable(&self) -> bool {
false

View File

@@ -48,7 +48,7 @@ pub async fn send_c_commands_packet(player: &Arc<Player>, dispatcher: &CommandDi
let mut proto_nodes = Vec::new();
let root_node_index = root.build(&mut proto_nodes);
let packet = CCommands::new(proto_nodes, root_node_index.into());
let packet = CCommands::new(proto_nodes.into(), root_node_index.try_into().unwrap());
player.client.enqueue_packet(&packet).await;
}
@@ -63,12 +63,12 @@ impl<'a> ProtoNodeBuilder<'a> {
let mut children = Vec::new();
for node in self.child_nodes {
let i = node.build(buffer);
children.push(i.into());
children.push(i.try_into().unwrap());
}
let i = buffer.len();
buffer.push(ProtoNode {
children,
children: children.into(),
node_type: self.node_type,
});
i

View File

@@ -4,9 +4,10 @@ use crossbeam::atomic::AtomicCell;
use pumpkin_data::damage::DamageType;
use pumpkin_nbt::compound::NbtCompound;
// TODO: This entire thing should be atomic, not individual fields
pub struct HungerManager {
/// The current hunger level.
pub level: AtomicCell<u32>,
pub level: AtomicCell<u8>,
/// The food saturation level.
pub saturation: AtomicCell<f32>,
pub exhaustion: AtomicCell<f32>,
@@ -67,8 +68,10 @@ impl HungerManager {
#[async_trait]
impl NBTStorage for HungerManager {
// TODO: Proper value checks
async fn write_nbt(&self, nbt: &mut NbtCompound) {
nbt.put_int("foodLevel", self.level.load() as i32);
nbt.put_int("foodLevel", self.level.load().into());
nbt.put_float("foodSaturationLevel", self.saturation.load());
nbt.put_float("foodExhaustionLevel", self.exhaustion.load());
nbt.put_int("foodTickTimer", self.tick_timer.load() as i32);
@@ -76,7 +79,7 @@ impl NBTStorage for HungerManager {
async fn read_nbt(&mut self, nbt: &mut NbtCompound) {
self.level
.store(nbt.get_int("foodLevel").unwrap_or(20) as u32);
.store(nbt.get_int("foodLevel").unwrap_or(20) as u8);
self.saturation
.store(nbt.get_float("foodSaturationLevel").unwrap_or(5.0));
self.exhaustion

View File

@@ -144,7 +144,7 @@ impl EntityBase for ItemEntity {
.enqueue_packet(&CTakeItemEntity::new(
self.entity.entity_id.into(),
player.entity_id().into(),
total_pick_up.into(),
total_pick_up.try_into().unwrap(),
))
.await;
}

View File

@@ -80,7 +80,7 @@ impl LivingEntity {
.broadcast_packet_all(&CTakeItemEntity::new(
item.entity_id.into(),
self.entity.entity_id.into(),
stack_amount.into(),
stack_amount.try_into().unwrap(),
))
.await;
}

View File

@@ -411,7 +411,7 @@ impl Entity {
self.world
.read()
.await
.broadcast_packet_all(&CSetEntityMetadata::new(self.entity_id.into(), buf))
.broadcast_packet_all(&CSetEntityMetadata::new(self.entity_id.into(), buf.into()))
.await;
}

View File

@@ -4,7 +4,7 @@ use std::{
ops::AddAssign,
sync::{
Arc,
atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32, Ordering},
atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU8, AtomicU32, Ordering},
},
time::{Duration, Instant},
};
@@ -220,7 +220,7 @@ pub struct Player {
/// The player's last known experience level.
pub last_sent_xp: AtomicI32,
pub last_sent_health: AtomicI32,
pub last_sent_food: AtomicU32,
pub last_sent_food: AtomicU8,
pub last_food_saturation: AtomicBool,
/// The player's permission level.
pub permission_lvl: AtomicCell<PermissionLvl>,
@@ -319,7 +319,7 @@ impl Player {
chunk_manager: Mutex::new(ChunkManager::new(16)),
last_sent_xp: AtomicI32::new(-1),
last_sent_health: AtomicI32::new(-1),
last_sent_food: AtomicU32::new(0),
last_sent_food: AtomicU8::new(0),
last_food_saturation: AtomicBool::new(true),
has_played_before: AtomicBool::new(false),
chat_session: Arc::new(Mutex::new(ChatSession::default())), // Placeholder value until the player actually sets their session id
@@ -520,7 +520,7 @@ impl Player {
) {
self.client
.enqueue_packet(&CSoundEffect::new(
IdOr::Id(u32::from(sound_id)),
IdOr::Id(sound_id),
category,
position,
volume,
@@ -581,7 +581,7 @@ impl Player {
self.client.send_packet_now(&CChunkData(&chunk)).await;
}
self.client
.send_packet_now(&CChunkBatchEnd::new(chunk_count))
.send_packet_now(&CChunkBatchEnd::new(chunk_count as u16))
.await;
}
@@ -1232,7 +1232,7 @@ impl Player {
pub async fn send_message(
&self,
message: &TextComponent,
chat_type: u32,
chat_type: u8,
sender_name: &TextComponent,
target_name: Option<&TextComponent>,
) {

View File

@@ -75,7 +75,7 @@ impl Player {
inventory.increment_state_id();
let packet = CSetContainerContent::new(
id.into(),
(inventory.state_id).into(),
(inventory.state_id).try_into().unwrap(),
&slots,
&carried_item,
);

View File

@@ -7,7 +7,6 @@ use pumpkin_protocol::{
config::{CConfigAddResourcePack, CConfigServerLinks, CKnownPacks, CUpdateTags},
login::{CLoginSuccess, CSetCompression},
},
codec::var_int::VarInt,
server::login::{SEncryptionResponse, SLoginCookieResponse, SLoginPluginResponse, SLoginStart},
};
use pumpkin_util::text::TextComponent;
@@ -247,8 +246,10 @@ impl Client {
async fn enable_compression(&self) {
let compression = advanced_config().networking.packet_compression.info.clone();
// We want to wait until we have sent the compression packet to the client
self.send_packet_now(&CSetCompression::new(compression.threshold.into()))
.await;
self.send_packet_now(&CSetCompression::new(
compression.threshold.try_into().unwrap(),
))
.await;
self.set_compression(compression).await;
}
@@ -340,15 +341,11 @@ impl Client {
self.send_packet_now(&server.get_branding()).await;
if advanced_config().server_links.enabled {
self.send_packet_now(&CConfigServerLinks::new(
&VarInt(LINKS.len() as i32),
&LINKS,
))
.await;
self.send_packet_now(&CConfigServerLinks::new(&LINKS)).await;
}
// TODO: Is this the right place to send them?
// Send tags.
// TODO: Is this the right place to send them?
self.send_packet_now(&CUpdateTags::new(&[
pumpkin_data::tag::RegistryKey::Block,

View File

@@ -1593,9 +1593,9 @@ impl Player {
let response = CCommandSuggestions::new(
packet.id,
(last_word_start + 2).into(),
(cmd.len() - last_word_start - 1).into(),
suggestions,
(last_word_start + 2).try_into().unwrap(),
(cmd.len() - last_word_start - 1).try_into().unwrap(),
suggestions.into(),
);
self.client.enqueue_packet(&response).await;

View File

@@ -344,7 +344,7 @@ impl Server {
&self,
message: &TextComponent,
sender_name: &TextComponent,
chat_type: u32,
chat_type: u8,
target_name: Option<&TextComponent>,
) {
send_cancellable! {{

View File

@@ -207,7 +207,7 @@ impl World {
&self,
message: &TextComponent,
sender_name: &TextComponent,
chat_type: u32,
chat_type: u8,
target_name: Option<&TextComponent>,
) {
self.broadcast_packet_all(&CDisguisedChatMessage::new(
@@ -320,14 +320,7 @@ impl World {
pitch: f32,
) {
let seed = thread_rng().r#gen::<f64>();
let packet = CSoundEffect::new(
IdOr::Id(u32::from(sound_id)),
category,
position,
volume,
pitch,
seed,
);
let packet = CSoundEffect::new(IdOr::Id(sound_id), category, position, volume, pitch, seed);
self.broadcast_packet_all(&packet).await;
}
@@ -518,7 +511,7 @@ impl World {
entity_id,
base_config.hardcore,
&dimensions,
base_config.max_players.into(),
base_config.max_players.try_into().unwrap(),
base_config.view_distance.get().into(), // TODO: view distance
base_config.simulation_distance.get().into(), // TODO: sim view dinstance
false,
@@ -811,7 +804,7 @@ impl World {
} else {
Particle::ExplosionEmitter
};
let sound = IdOr::<SoundEvent>::Id(Sound::EntityGenericExplode as u32);
let sound = IdOr::<SoundEvent>::Id(Sound::EntityGenericExplode as u16);
for (_, player) in self.players.read().await.iter() {
if player.position().squared_distance_to_vec(position) > 4096.0 {
continue;
@@ -1200,11 +1193,8 @@ impl World {
.remove(&player.gameprofile.id)
.unwrap();
let uuid = player.gameprofile.id;
self.broadcast_packet_except(
&[player.gameprofile.id],
&CRemovePlayerInfo::new(1.into(), &[uuid]),
)
.await;
self.broadcast_packet_except(&[player.gameprofile.id], &CRemovePlayerInfo::new(&[uuid]))
.await;
self.broadcast_packet_all(&CRemoveEntities::new(&[player.entity_id().into()]))
.await;