Merge branch 'master' into master

This commit is contained in:
Edvin Bryntesson 2024-09-06 21:54:23 +02:00
commit 72d95cd416
No known key found for this signature in database
52 changed files with 653 additions and 469 deletions

538
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,17 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = [ "pumpkin-config", "pumpkin-core", "pumpkin-entity", "pumpkin-inventory", "pumpkin-macros/", "pumpkin-plugin", "pumpkin-protocol/", "pumpkin-registry/", "pumpkin-world", "pumpkin/"] members = [
"pumpkin-config",
"pumpkin-core",
"pumpkin-entity",
"pumpkin-inventory",
"pumpkin-macros/",
"pumpkin-plugin",
"pumpkin-protocol/",
"pumpkin-registry/",
"pumpkin-world",
"pumpkin/",
]
[workspace.package] [workspace.package]
version = "0.1.0" version = "0.1.0"
@ -13,7 +24,14 @@ codegen-units = 1
[workspace.dependencies] [workspace.dependencies]
log = "0.4" log = "0.4"
tokio = { version = "1.39.2", features = ["net", "macros", "rt-multi-thread", "fs", "io-util", "sync"] } tokio = { version = "1.40", features = [
"net",
"macros",
"rt-multi-thread",
"fs",
"io-util",
"sync",
] }
rayon = "1.10.0" rayon = "1.10.0"
uuid = { version = "1.10.0", features = ["serde", "v3"] } uuid = { version = "1.10.0", features = ["serde", "v3"] }
derive_more = { version = "1.0.0", features = ["full"] } derive_more = { version = "1.0.0", features = ["full"] }

View File

@ -5,8 +5,8 @@ edition.workspace = true
[dependencies] [dependencies]
pumpkin-core = { path = "../pumpkin-core" } pumpkin-core = { path = "../pumpkin-core" }
serde.workspace = true
log.workspace = true
serde = "1.0"
toml = "0.8" toml = "0.8"
log.workspace = true

View File

@ -5,8 +5,8 @@ edition.workspace = true
[dependencies] [dependencies]
serde.workspace = true serde.workspace = true
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
uuid.workspace = true uuid.workspace = true
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
colored = "2" colored = "2"
md5 = "0.7.0" md5 = "0.7.0"

View File

@ -6,7 +6,6 @@ use super::Text;
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "action", content = "contents", rename_all = "snake_case")] #[serde(tag = "action", content = "contents", rename_all = "snake_case")]
#[allow(clippy::enum_variant_names)]
pub enum HoverEvent<'a> { pub enum HoverEvent<'a> {
/// Displays a tooltip with the given text. /// Displays a tooltip with the given text.
ShowText(Text<'a>), ShowText(Text<'a>),

View File

@ -5,6 +5,3 @@ edition.workspace = true
[dependencies] [dependencies]
pumpkin-core = { path = "../pumpkin-core"} pumpkin-core = { path = "../pumpkin-core"}
num-traits = "0.2"
num-derive = "0.4"

View File

@ -2,8 +2,6 @@ use crate::container_click::MouseClick;
use crate::{handle_item_change, Container, InventoryError, WindowType}; use crate::{handle_item_change, Container, InventoryError, WindowType};
use pumpkin_world::item::ItemStack; use pumpkin_world::item::ItemStack;
#[allow(dead_code)]
#[derive(Debug)]
pub struct PlayerInventory { pub struct PlayerInventory {
// Main Inventory + Hotbar // Main Inventory + Hotbar
crafting: [Option<ItemStack>; 4], crafting: [Option<ItemStack>; 4],

View File

@ -7,6 +7,6 @@ edition.workspace = true
proc-macro = true proc-macro = true
[dependencies] [dependencies]
proc-macro2 = "1.0.86" proc-macro2 = "1.0"
quote = "1.0.37" quote = "1.0"
syn = "2.0.76" syn = "2.0"

View File

@ -4,6 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
[dependencies] [dependencies]
extism = "1.5.0"
log.workspace = true
serde.workspace = true serde.workspace = true
log.workspace = true
extism = "1.6.0"

View File

@ -13,12 +13,10 @@ bytes = "1.7"
uuid.workspace = true uuid.workspace = true
serde.workspace = true serde.workspace = true
# to parse strings to json responses
serde_json = "1.0"
flate2 = "1.0.33" flate2 = "1.0.33"
thiserror = "1.0.63" thiserror = "1.0"
log.workspace = true log.workspace = true
num-traits = "0.2" num-traits = "0.2"
num-derive = "0.4" num-derive = "0.4"
@ -26,6 +24,6 @@ num-derive = "0.4"
# encryption # encryption
aes = "0.8.4" aes = "0.8.4"
cfb8 = "0.8.1" cfb8 = "0.8.1"
take_mut = "0.2.2"
itertools = "0.13.0" itertools = "0.13.0"
fastnbt = { git = "https://github.com/owengage/fastnbt.git" } fastnbt = { git = "https://github.com/owengage/fastnbt.git" }

View File

@ -45,77 +45,77 @@ impl<'a, 'de> de::Deserializer<'de> for Deserializer<'a> {
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
visitor.visit_bool(self.inner.get_bool()) visitor.visit_bool(self.inner.get_bool()?)
} }
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error> fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
visitor.visit_i8(self.inner.get_i8()) visitor.visit_i8(self.inner.get_i8()?)
} }
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error> fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
visitor.visit_i16(self.inner.get_i16()) visitor.visit_i16(self.inner.get_i16()?)
} }
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error> fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
visitor.visit_i32(self.inner.get_i32()) visitor.visit_i32(self.inner.get_i32()?)
} }
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error> fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
visitor.visit_i64(self.inner.get_i64()) visitor.visit_i64(self.inner.get_i64()?)
} }
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error> fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
visitor.visit_u8(self.inner.get_u8()) visitor.visit_u8(self.inner.get_u8()?)
} }
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error> fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
visitor.visit_u16(self.inner.get_u16()) visitor.visit_u16(self.inner.get_u16()?)
} }
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error> fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
visitor.visit_u32(self.inner.get_u32()) visitor.visit_u32(self.inner.get_u32()?)
} }
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error> fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
visitor.visit_u64(self.inner.get_u64()) visitor.visit_u64(self.inner.get_u64()?)
} }
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error> fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
visitor.visit_f32(self.inner.get_f32()) visitor.visit_f32(self.inner.get_f32()?)
} }
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error> fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
visitor.visit_f64(self.inner.get_f64()) visitor.visit_f64(self.inner.get_f64()?)
} }
fn deserialize_char<V>(self, _visitor: V) -> Result<V::Value, Self::Error> fn deserialize_char<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
@ -129,7 +129,7 @@ impl<'a, 'de> de::Deserializer<'de> for Deserializer<'a> {
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
let string = self.inner.get_string().map_err(DeserializerError::Stdio)?; let string = self.inner.get_string()?;
visitor.visit_str(&string) visitor.visit_str(&string)
} }
@ -137,7 +137,7 @@ impl<'a, 'de> de::Deserializer<'de> for Deserializer<'a> {
where where
V: de::Visitor<'de>, V: de::Visitor<'de>,
{ {
let string = self.inner.get_string().map_err(DeserializerError::Stdio)?; let string = self.inner.get_string()?;
visitor.visit_str(&string) visitor.visit_str(&string)
} }

View File

@ -1,7 +1,6 @@
use crate::{BitSet, FixedBitSet, VarInt, VarLongType}; use crate::{BitSet, FixedBitSet, VarInt, VarLongType};
use bytes::{Buf, BufMut, BytesMut}; use bytes::{Buf, BufMut, BytesMut};
use core::str; use core::str;
use std::io::{self, Error, ErrorKind};
mod deserializer; mod deserializer;
pub use deserializer::DeserializerError; pub use deserializer::DeserializerError;
@ -26,12 +25,12 @@ impl ByteBuffer {
Self { buffer } Self { buffer }
} }
pub fn get_var_int(&mut self) -> VarInt { pub fn get_var_int(&mut self) -> Result<VarInt, DeserializerError> {
let mut value: i32 = 0; let mut value: i32 = 0;
let mut position: i32 = 0; let mut position: i32 = 0;
loop { loop {
let read = self.buffer.get_u8(); let read = self.get_u8()?;
value |= ((read & SEGMENT_BITS) as i32) << position; value |= ((read & SEGMENT_BITS) as i32) << position;
@ -42,19 +41,19 @@ impl ByteBuffer {
position += 7; position += 7;
if position >= 32 { if position >= 32 {
panic!("VarInt is too big"); return Err(DeserializerError::Message("VarInt is too big".to_string()));
} }
} }
VarInt(value) Ok(VarInt(value))
} }
pub fn get_var_long(&mut self) -> VarLongType { pub fn get_var_long(&mut self) -> Result<VarLongType, DeserializerError> {
let mut value: i64 = 0; let mut value: i64 = 0;
let mut position: i64 = 0; let mut position: i64 = 0;
loop { loop {
let read = self.buffer.get_u8(); let read = self.get_u8()?;
value |= ((read & SEGMENT_BITS) as i64) << position; value |= ((read & SEGMENT_BITS) as i64) << position;
@ -65,49 +64,48 @@ impl ByteBuffer {
position += 7; position += 7;
if position >= 64 { if position >= 64 {
panic!("VarInt is too big"); return Err(DeserializerError::Message("VarLong is too big".to_string()));
} }
} }
value Ok(value)
} }
pub fn get_string(&mut self) -> Result<String, io::Error> { pub fn get_string(&mut self) -> Result<String, DeserializerError> {
self.get_string_len(32767) self.get_string_len(32767)
} }
pub fn get_string_len(&mut self, max_size: usize) -> Result<String, io::Error> { pub fn get_string_len(&mut self, max_size: usize) -> Result<String, DeserializerError> {
let size = self.get_var_int().0; let size = self.get_var_int()?.0;
if size as usize > max_size { if size as usize > max_size {
return Err(Error::new( return Err(DeserializerError::Message(
ErrorKind::InvalidData, "String length is bigger than max size".to_string(),
"String length is bigger than max size",
)); ));
} }
let data = self.buffer.copy_to_bytes(size as usize);
let data = self.copy_to_bytes(size as usize)?;
if data.len() > max_size { if data.len() > max_size {
return Err(Error::new( return Err(DeserializerError::Message(
ErrorKind::InvalidData, "String is bigger than max size".to_string(),
"String is bigger than max size",
)); ));
} }
match str::from_utf8(&data) { match str::from_utf8(&data) {
Ok(string_result) => Ok(string_result.to_string()), Ok(string_result) => Ok(string_result.to_string()),
Err(e) => Err(Error::new(ErrorKind::InvalidData, e)), Err(e) => Err(DeserializerError::Message(e.to_string())),
} }
} }
pub fn get_bool(&mut self) -> bool { pub fn get_bool(&mut self) -> Result<bool, DeserializerError> {
self.buffer.get_u8() != 0 Ok(self.get_u8()? != 0)
} }
pub fn get_uuid(&mut self) -> uuid::Uuid { pub fn get_uuid(&mut self) -> Result<uuid::Uuid, DeserializerError> {
let mut bytes = [0u8; 16]; let mut bytes = [0u8; 16];
self.buffer.copy_to_slice(&mut bytes); self.copy_to_slice(&mut bytes)?;
uuid::Uuid::from_slice(&bytes).expect("Failed to parse UUID") Ok(uuid::Uuid::from_slice(&bytes).expect("Failed to parse UUID"))
} }
pub fn get_fixed_bitset(&mut self, bits: usize) -> FixedBitSet { pub fn get_fixed_bitset(&mut self, bits: usize) -> Result<FixedBitSet, DeserializerError> {
self.copy_to_bytes(bits.div_ceil(8)) self.copy_to_bytes(bits.div_ceil(8))
} }
@ -161,11 +159,14 @@ impl ByteBuffer {
/// Reads a boolean. If true, the closure is called, and the returned value is /// Reads a boolean. If true, the closure is called, and the returned value is
/// wrapped in Some. Otherwise, this returns None. /// wrapped in Some. Otherwise, this returns None.
pub fn get_option<T>(&mut self, val: impl FnOnce(&mut Self) -> T) -> Option<T> { pub fn get_option<T>(
if self.get_bool() { &mut self,
Some(val(self)) val: impl FnOnce(&mut Self) -> Result<T, DeserializerError>,
) -> Result<Option<T>, DeserializerError> {
if self.get_bool()? {
Ok(Some(val(self)?))
} else { } else {
None Ok(None)
} }
} }
/// Writes `true` if the option is Some, or `false` if None. If the option is /// Writes `true` if the option is Some, or `false` if None. If the option is
@ -177,13 +178,16 @@ impl ByteBuffer {
} }
} }
pub fn get_list<T>(&mut self, val: impl Fn(&mut Self) -> T) -> Vec<T> { pub fn get_list<T>(
let len = self.get_var_int().0 as usize; &mut self,
val: impl Fn(&mut Self) -> Result<T, DeserializerError>,
) -> Result<Vec<T>, DeserializerError> {
let len = self.get_var_int()?.0 as usize;
let mut list = Vec::with_capacity(len); let mut list = Vec::with_capacity(len);
for _ in 0..len { for _ in 0..len {
list.push(val(self)); list.push(val(self)?);
} }
list Ok(list)
} }
/// Writes a list to the buffer. /// Writes a list to the buffer.
pub fn put_list<T>(&mut self, list: &[T], write: impl Fn(&mut Self, &T)) { pub fn put_list<T>(&mut self, list: &[T], write: impl Fn(&mut Self, &T)) {
@ -211,50 +215,109 @@ impl ByteBuffer {
pub fn buf(&mut self) -> &mut BytesMut { pub fn buf(&mut self) -> &mut BytesMut {
&mut self.buffer &mut self.buffer
} }
}
// trait // Trait equivalents
impl ByteBuffer { pub fn get_u8(&mut self) -> Result<u8, DeserializerError> {
pub fn get_u8(&mut self) -> u8 { if self.buffer.has_remaining() {
self.buffer.get_u8() Ok(self.buffer.get_u8())
} else {
Err(DeserializerError::Message(
"No bytes left to consume".to_string(),
))
}
} }
pub fn get_i8(&mut self) -> i8 { pub fn get_i8(&mut self) -> Result<i8, DeserializerError> {
self.buffer.get_i8() if self.buffer.has_remaining() {
Ok(self.buffer.get_i8())
} else {
Err(DeserializerError::Message(
"No bytes left to consume".to_string(),
))
}
} }
pub fn get_u16(&mut self) -> u16 { pub fn get_u16(&mut self) -> Result<u16, DeserializerError> {
self.buffer.get_u16() if self.buffer.remaining() >= 2 {
Ok(self.buffer.get_u16())
} else {
Err(DeserializerError::Message(
"Less than 2 bytes left to consume".to_string(),
))
}
} }
pub fn get_i16(&mut self) -> i16 { pub fn get_i16(&mut self) -> Result<i16, DeserializerError> {
self.buffer.get_i16() if self.buffer.remaining() >= 2 {
Ok(self.buffer.get_i16())
} else {
Err(DeserializerError::Message(
"Less than 2 bytes left to consume".to_string(),
))
}
} }
pub fn get_u32(&mut self) -> u32 { pub fn get_u32(&mut self) -> Result<u32, DeserializerError> {
self.buffer.get_u32() if self.buffer.remaining() >= 4 {
Ok(self.buffer.get_u32())
} else {
Err(DeserializerError::Message(
"Less than 4 bytes left to consume".to_string(),
))
}
} }
pub fn get_i32(&mut self) -> i32 { pub fn get_i32(&mut self) -> Result<i32, DeserializerError> {
self.buffer.get_i32() if self.buffer.remaining() >= 4 {
Ok(self.buffer.get_i32())
} else {
Err(DeserializerError::Message(
"Less than 4 bytes left to consume".to_string(),
))
}
} }
pub fn get_u64(&mut self) -> u64 { pub fn get_u64(&mut self) -> Result<u64, DeserializerError> {
self.buffer.get_u64() if self.buffer.remaining() >= 8 {
Ok(self.buffer.get_u64())
} else {
Err(DeserializerError::Message(
"Less than 8 bytes left to consume".to_string(),
))
}
} }
pub fn get_i64(&mut self) -> i64 { pub fn get_i64(&mut self) -> Result<i64, DeserializerError> {
self.buffer.get_i64() if self.buffer.remaining() >= 8 {
Ok(self.buffer.get_i64())
} else {
Err(DeserializerError::Message(
"Less than 8 bytes left to consume".to_string(),
))
}
} }
pub fn get_f32(&mut self) -> f32 { pub fn get_f32(&mut self) -> Result<f32, DeserializerError> {
self.buffer.get_f32() if self.buffer.remaining() >= 4 {
Ok(self.buffer.get_f32())
} else {
Err(DeserializerError::Message(
"Less than 4 bytes left to consume".to_string(),
))
}
} }
pub fn get_f64(&mut self) -> f64 { pub fn get_f64(&mut self) -> Result<f64, DeserializerError> {
self.buffer.get_f64() if self.buffer.remaining() >= 8 {
Ok(self.buffer.get_f64())
} else {
Err(DeserializerError::Message(
"Less than 8 bytes left to consume".to_string(),
))
}
} }
// TODO: SerializerError?
pub fn put_u8(&mut self, n: u8) { pub fn put_u8(&mut self, n: u8) {
self.buffer.put_u8(n) self.buffer.put_u8(n)
} }
@ -295,12 +358,25 @@ impl ByteBuffer {
self.buffer.put_f64(n) self.buffer.put_f64(n)
} }
pub fn copy_to_bytes(&mut self, len: usize) -> bytes::Bytes { pub fn copy_to_bytes(&mut self, len: usize) -> Result<bytes::Bytes, DeserializerError> {
self.buffer.copy_to_bytes(len) if self.buffer.len() >= len {
Ok(self.buffer.copy_to_bytes(len))
} else {
Err(DeserializerError::Message(
"Unable to copy bytes".to_string(),
))
}
} }
pub fn copy_to_slice(&mut self, dst: &mut [u8]) { pub fn copy_to_slice(&mut self, dst: &mut [u8]) -> Result<(), DeserializerError> {
self.buffer.copy_to_slice(dst) if self.buffer.remaining() >= dst.len() {
self.buffer.copy_to_slice(dst);
Ok(())
} else {
Err(DeserializerError::Message(
"Unable to copy slice".to_string(),
))
}
} }
pub fn put_slice(&mut self, src: &[u8]) { pub fn put_slice(&mut self, src: &[u8]) {

View File

@ -83,12 +83,13 @@ where
P: Packet + Serialize, P: Packet + Serialize,
{ {
fn write(&self, bytebuf: &mut ByteBuffer) { fn write(&self, bytebuf: &mut ByteBuffer) {
take_mut::take(bytebuf, |bytebuf| { let mut serializer = serializer::Serializer::new(ByteBuffer::empty());
let mut serializer = serializer::Serializer::new(bytebuf);
self.serialize(&mut serializer) self.serialize(&mut serializer)
.expect("Could not serialize packet"); .expect("Could not serialize packet");
serializer.into() // We write the packet in an empty bytebuffer and then put it into our current one.
}); // In the future we may do packet batching thats the reason i don't let every packet create a new bytebuffer and use
// an existing instead
bytebuf.put(serializer.output.buf());
} }
} }

View File

@ -9,7 +9,7 @@ use thiserror::Error;
use super::ByteBuffer; use super::ByteBuffer;
pub struct Serializer { pub struct Serializer {
output: ByteBuffer, pub output: ByteBuffer,
} }
impl Serializer { impl Serializer {

View File

@ -30,7 +30,7 @@ pub struct CLogin<'a> {
} }
impl<'a> CLogin<'a> { impl<'a> CLogin<'a> {
#[allow(clippy::too_many_arguments)] #[expect(clippy::too_many_arguments)]
pub fn new( pub fn new(
entity_id: i32, entity_id: i32,
is_hardcore: bool, is_hardcore: bool,

View File

@ -21,7 +21,7 @@ pub struct CParticle<'a> {
} }
impl<'a> CParticle<'a> { impl<'a> CParticle<'a> {
#[allow(clippy::too_many_arguments)] #[expect(clippy::too_many_arguments)]
pub fn new( pub fn new(
long_distance: bool, long_distance: bool,
x: f64, x: f64,

View File

@ -22,7 +22,7 @@ pub struct CPlayerChatMessage<'a> {
} }
impl<'a> CPlayerChatMessage<'a> { impl<'a> CPlayerChatMessage<'a> {
#[allow(clippy::too_many_arguments)] #[expect(clippy::too_many_arguments)]
pub fn new( pub fn new(
sender: UUID, sender: UUID,
index: VarInt, index: VarInt,

View File

@ -22,7 +22,7 @@ pub struct CSpawnEntity {
} }
impl CSpawnEntity { impl CSpawnEntity {
#[allow(clippy::too_many_arguments)] #[expect(clippy::too_many_arguments)]
pub fn new( pub fn new(
entity_id: VarInt, entity_id: VarInt,
entity_uuid: UUID, entity_uuid: UUID,

View File

@ -22,8 +22,6 @@ pub struct PacketEncoder {
} }
impl PacketEncoder { impl PacketEncoder {
// i think clippy is broken
#[allow(clippy::needless_borrows_for_generic_args)]
pub fn append_packet<P: ClientPacket>(&mut self, packet: &P) -> Result<(), PacketError> { pub fn append_packet<P: ClientPacket>(&mut self, packet: &P) -> Result<(), PacketError> {
let start_len = self.buf.len(); let start_len = self.buf.len();
@ -87,6 +85,7 @@ impl PacketEncoder {
let mut front = &mut self.buf[start_len..]; let mut front = &mut self.buf[start_len..];
#[allow(clippy::needless_borrows_for_generic_args)]
VarInt(packet_len as i32) VarInt(packet_len as i32)
.encode(&mut front) .encode(&mut front)
.map_err(|_| PacketError::EncodeLength)?; .map_err(|_| PacketError::EncodeLength)?;

View File

@ -16,10 +16,10 @@ pub struct SHandShake {
impl ServerPacket for SHandShake { impl ServerPacket for SHandShake {
fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> { fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> {
Ok(Self { Ok(Self {
protocol_version: bytebuf.get_var_int(), protocol_version: bytebuf.get_var_int()?,
server_address: bytebuf.get_string_len(255).unwrap(), server_address: bytebuf.get_string_len(255)?,
server_port: bytebuf.get_u16(), server_port: bytebuf.get_u16()?,
next_state: bytebuf.get_var_int().into(), next_state: bytebuf.get_var_int()?.into(),
}) })
} }
} }

View File

@ -15,10 +15,10 @@ pub struct SEncryptionResponse {
impl ServerPacket for SEncryptionResponse { impl ServerPacket for SEncryptionResponse {
fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> { fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> {
let shared_secret_length = bytebuf.get_var_int(); let shared_secret_length = bytebuf.get_var_int()?;
let shared_secret = bytebuf.copy_to_bytes(shared_secret_length.0 as usize); let shared_secret = bytebuf.copy_to_bytes(shared_secret_length.0 as usize)?;
let verify_token_length = bytebuf.get_var_int(); let verify_token_length = bytebuf.get_var_int()?;
let verify_token = bytebuf.copy_to_bytes(shared_secret_length.0 as usize); let verify_token = bytebuf.copy_to_bytes(shared_secret_length.0 as usize)?;
Ok(Self { Ok(Self {
shared_secret_length, shared_secret_length,
shared_secret: shared_secret.to_vec(), shared_secret: shared_secret.to_vec(),

View File

@ -14,8 +14,8 @@ pub struct SLoginStart {
impl ServerPacket for SLoginStart { impl ServerPacket for SLoginStart {
fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> { fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> {
Ok(Self { Ok(Self {
name: bytebuf.get_string_len(16).unwrap(), name: bytebuf.get_string_len(16)?,
uuid: bytebuf.get_uuid(), uuid: bytebuf.get_uuid()?,
}) })
} }
} }

View File

@ -16,9 +16,9 @@ pub struct SLoginPluginResponse {
impl ServerPacket for SLoginPluginResponse { impl ServerPacket for SLoginPluginResponse {
fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> { fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> {
Ok(Self { Ok(Self {
message_id: bytebuf.get_var_int(), message_id: bytebuf.get_var_int()?,
successful: bytebuf.get_bool(), successful: bytebuf.get_bool()?,
data: bytebuf.get_option(|v| v.get_slice()), data: bytebuf.get_option(|v| Ok(v.get_slice()))?,
}) })
} }
} }

View File

@ -21,12 +21,12 @@ pub struct SChatMessage {
impl ServerPacket for SChatMessage { impl ServerPacket for SChatMessage {
fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> { fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> {
Ok(Self { Ok(Self {
message: bytebuf.get_string().unwrap(), message: bytebuf.get_string()?,
timestamp: bytebuf.get_i64(), timestamp: bytebuf.get_i64()?,
salt: bytebuf.get_i64(), salt: bytebuf.get_i64()?,
signature: bytebuf.get_option(|v| v.copy_to_bytes(256)), signature: bytebuf.get_option(|v| v.copy_to_bytes(256))?,
message_count: bytebuf.get_var_int(), message_count: bytebuf.get_var_int()?,
acknowledged: bytebuf.get_fixed_bitset(20), acknowledged: bytebuf.get_fixed_bitset(20)?,
}) })
} }
} }

View File

@ -18,8 +18,8 @@ impl ServerPacket for SInteract {
fn read( fn read(
bytebuf: &mut crate::bytebuf::ByteBuffer, bytebuf: &mut crate::bytebuf::ByteBuffer,
) -> Result<Self, crate::bytebuf::DeserializerError> { ) -> Result<Self, crate::bytebuf::DeserializerError> {
let entity_id = bytebuf.get_var_int(); let entity_id = bytebuf.get_var_int()?;
let typ = bytebuf.get_var_int(); let typ = bytebuf.get_var_int()?;
let action = ActionType::from_i32(typ.0).ok_or(DeserializerError::Message( let action = ActionType::from_i32(typ.0).ok_or(DeserializerError::Message(
"invalid action type".to_string(), "invalid action type".to_string(),
))?; ))?;
@ -27,13 +27,13 @@ impl ServerPacket for SInteract {
ActionType::Interact => None, ActionType::Interact => None,
ActionType::Attack => None, ActionType::Attack => None,
ActionType::InteractAt => { ActionType::InteractAt => {
Some((bytebuf.get_f32(), bytebuf.get_f32(), bytebuf.get_f32())) Some((bytebuf.get_f32()?, bytebuf.get_f32()?, bytebuf.get_f32()?))
} }
}; };
let hand = match action { let hand = match action {
ActionType::Interact => Some(bytebuf.get_var_int()), ActionType::Interact => Some(bytebuf.get_var_int()?),
ActionType::Attack => None, ActionType::Attack => None,
ActionType::InteractAt => Some(bytebuf.get_var_int()), ActionType::InteractAt => Some(bytebuf.get_var_int()?),
}; };
Ok(Self { Ok(Self {
@ -41,7 +41,7 @@ impl ServerPacket for SInteract {
typ, typ,
target_position, target_position,
hand, hand,
sneaking: bytebuf.get_bool(), sneaking: bytebuf.get_bool()?,
}) })
} }
} }

View File

@ -25,9 +25,9 @@ pub enum Action {
impl ServerPacket for SPlayerCommand { impl ServerPacket for SPlayerCommand {
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Result<Self, DeserializerError> { fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Result<Self, DeserializerError> {
Ok(Self { Ok(Self {
entity_id: bytebuf.get_var_int(), entity_id: bytebuf.get_var_int()?,
action: bytebuf.get_var_int(), action: bytebuf.get_var_int()?,
jump_boost: bytebuf.get_var_int(), jump_boost: bytebuf.get_var_int()?,
}) })
} }
} }

View File

@ -7,7 +7,6 @@ use serde::{
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct Slot { pub struct Slot {
item_count: VarInt, item_count: VarInt,
item_id: Option<VarInt>, item_id: Option<VarInt>,

View File

@ -9,6 +9,5 @@ pumpkin-core = { path = "../pumpkin-core"}
# nbt # nbt
fastnbt = { git = "https://github.com/owengage/fastnbt.git" } fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
fastsnbt = "0.2"
serde.workspace = true serde.workspace = true

View File

@ -33,12 +33,12 @@ impl Registry {
registry_id: "minecraft:worldgen/biome".to_string(), registry_id: "minecraft:worldgen/biome".to_string(),
registry_entries: vec![ registry_entries: vec![
RegistryEntry { RegistryEntry {
entry_id: "minecraft:snowy_taiga", entry_id: "minecraft:plains",
data: fastnbt::to_bytes_with_opts(&Biome::default(), SerOpts::network_nbt()) data: fastnbt::to_bytes_with_opts(&Biome::default(), SerOpts::network_nbt())
.unwrap(), .unwrap(),
}, },
RegistryEntry { RegistryEntry {
entry_id: "minecraft:plains", entry_id: "minecraft:snowy_taiga",
data: fastnbt::to_bytes_with_opts(&Biome::default(), SerOpts::network_nbt()) data: fastnbt::to_bytes_with_opts(&Biome::default(), SerOpts::network_nbt())
.unwrap(), .unwrap(),
}, },

View File

@ -6,20 +6,22 @@ edition.workspace = true
[dependencies] [dependencies]
pumpkin-core = { path = "../pumpkin-core"} pumpkin-core = { path = "../pumpkin-core"}
fastnbt = { git = "https://github.com/owengage/fastnbt.git" } fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
fastsnbt = "0.2"
tokio.workspace = true tokio.workspace = true
rayon.workspace = true rayon.workspace = true
derive_more.workspace = true derive_more.workspace = true
itertools = "0.13.0" itertools = "0.13.0"
thiserror = "1.0.63" thiserror = "1.0"
futures = "0.3.30" futures = "0.3"
flate2 = "1.0.33" flate2 = "1.0"
serde.workspace = true serde.workspace = true
serde_json = "1.0" serde_json = "1.0"
static_assertions = "1.1.0" static_assertions = "1.1.0"
log.workspace = true log.workspace = true
noise = "0.9.0"
rand = "0.8.5"
num-traits = "0.2" num-traits = "0.2"
num-derive = "0.4" num-derive = "0.4"

View File

@ -1,9 +1,11 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
// TODO make this work with the protocol // TODO make this work with the protocol
// Send by the registry
#[derive(Serialize, Deserialize, Clone, Copy)] #[derive(Serialize, Deserialize, Clone, Copy)]
#[non_exhaustive] #[non_exhaustive]
pub enum Biome { pub enum Biome {
Plains, Plains,
SnowyTiga,
// TODO list all Biomes // TODO list all Biomes
} }

View File

@ -54,7 +54,7 @@ pub struct ChunkHeightmaps {
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[allow(dead_code)] #[expect(dead_code)]
struct ChunkSection { struct ChunkSection {
#[serde(rename = "Y")] #[serde(rename = "Y")]
y: i32, y: i32,
@ -64,7 +64,7 @@ struct ChunkSection {
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]
struct ChunkNbt { struct ChunkNbt {
#[allow(dead_code)] #[expect(dead_code)]
data_version: usize, data_version: usize,
#[serde(rename = "sections")] #[serde(rename = "sections")]
@ -171,7 +171,7 @@ impl ChunkBlocks {
index.y.get_absolute() as usize * CHUNK_AREA + *index.z as usize * 16 + *index.x as usize index.y.get_absolute() as usize * CHUNK_AREA + *index.z as usize * 16 + *index.x as usize
} }
#[allow(dead_code)] #[expect(dead_code)]
fn calculate_heightmap(&self) -> ChunkHeightmaps { fn calculate_heightmap(&self) -> ChunkHeightmaps {
// figure out how LongArray is formatted // figure out how LongArray is formatted
// figure out how to find out if block is motion blocking // figure out how to find out if block is motion blocking

View File

@ -24,7 +24,6 @@ pub fn get_protocol_id(category: &str, entry: &str) -> u32 {
.expect("No Entry found") .expect("No Entry found")
} }
#[allow(dead_code)]
pub fn get_default<'a>(category: &str) -> Option<&'a str> { pub fn get_default<'a>(category: &str) -> Option<&'a str> {
REGISTRY REGISTRY
.get(category) .get(category)

View File

@ -28,12 +28,12 @@ pub struct ItemElement {
components: ItemComponents, components: ItemComponents,
} }
#[allow(dead_code)] #[expect(dead_code)]
pub fn get_item_element(item_id: &str) -> &ItemComponents { pub fn get_item_element(item_id: &str) -> &ItemComponents {
&ITEMS.get(item_id).expect("Item not found").components &ITEMS.get(item_id).expect("Item not found").components
} }
#[allow(dead_code)] #[expect(dead_code)]
pub fn get_item_protocol_id(item_id: &str) -> u32 { pub fn get_item_protocol_id(item_id: &str) -> u32 {
global_registry::get_protocol_id(ITEM_REGISTRY, item_id) global_registry::get_protocol_id(ITEM_REGISTRY, item_id)
} }

View File

@ -26,7 +26,7 @@ pub struct Level {
} }
struct SaveFile { struct SaveFile {
#[allow(dead_code)] #[expect(dead_code)]
root_folder: PathBuf, root_folder: PathBuf,
region_folder: PathBuf, region_folder: PathBuf,
} }
@ -277,7 +277,7 @@ impl Level {
match compression { match compression {
Compression::Gzip => { Compression::Gzip => {
let mut z = GzDecoder::new(&compressed_data[..]); let mut z = GzDecoder::new(&compressed_data[..]);
let mut chunk_data = Vec::new(); let mut chunk_data = Vec::with_capacity(compressed_data.len());
match z.read_to_end(&mut chunk_data) { match z.read_to_end(&mut chunk_data) {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
@ -288,7 +288,7 @@ impl Level {
} }
Compression::Zlib => { Compression::Zlib => {
let mut z = ZlibDecoder::new(&compressed_data[..]); let mut z = ZlibDecoder::new(&compressed_data[..]);
let mut chunk_data = Vec::new(); let mut chunk_data = Vec::with_capacity(compressed_data.len());
match z.read_to_end(&mut chunk_data) { match z.read_to_end(&mut chunk_data) {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {

View File

@ -1,3 +1,4 @@
use noise::Perlin;
use pumpkin_core::math::vector2::Vector2; use pumpkin_core::math::vector2::Vector2;
use static_assertions::assert_obj_safe; use static_assertions::assert_obj_safe;
@ -12,7 +13,6 @@ pub trait GeneratorInit {
} }
pub trait WorldGenerator: Sync + Send { pub trait WorldGenerator: Sync + Send {
#[allow(dead_code)]
fn generate_chunk(&self, at: Vector2<i32>) -> ChunkData; fn generate_chunk(&self, at: Vector2<i32>) -> ChunkData;
} }
assert_obj_safe! {WorldGenerator} assert_obj_safe! {WorldGenerator}
@ -21,6 +21,17 @@ pub(crate) trait BiomeGenerator: Sync + Send {
fn generate_biome(&self, at: XZBlockCoordinates) -> Biome; fn generate_biome(&self, at: XZBlockCoordinates) -> Biome;
} }
#[expect(dead_code)]
pub(crate) trait TerrainGenerator: Sync + Send { pub(crate) trait TerrainGenerator: Sync + Send {
fn prepare_chunk(&self, at: &Vector2<i32>);
/// Is static
fn generate_block(&self, at: BlockCoordinates, biome: Biome) -> BlockId; fn generate_block(&self, at: BlockCoordinates, biome: Biome) -> BlockId;
} }
pub(crate) trait PerlinTerrainGenerator: Sync + Send {
fn prepare_chunk(&self, at: &Vector2<i32>, perlin: &Perlin);
/// Dependens on the perlin noise height
fn generate_block(&self, at: BlockCoordinates, chunk_height: i16, biome: Biome) -> BlockId;
}

View File

@ -1,36 +1,89 @@
use noise::{NoiseFn, Perlin};
use pumpkin_core::math::vector2::Vector2; use pumpkin_core::math::vector2::Vector2;
use crate::{ use crate::{
chunk::{ChunkBlocks, ChunkData}, chunk::{ChunkBlocks, ChunkData},
coordinates::{ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates}, coordinates::{ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates},
WORLD_LOWEST_Y, WORLD_MAX_Y, WORLD_LOWEST_Y,
}; };
use super::{ use super::{
generator::{BiomeGenerator, GeneratorInit, TerrainGenerator, WorldGenerator}, generator::{BiomeGenerator, GeneratorInit, PerlinTerrainGenerator, WorldGenerator},
Seed, Seed,
}; };
pub struct GenericGenerator<B: BiomeGenerator, T: TerrainGenerator> { pub struct GenericGenerator<B: BiomeGenerator, T: PerlinTerrainGenerator> {
biome_generator: B, biome_generator: B,
terrain_generator: T, terrain_generator: T,
// TODO: May make this optional?. But would be pain to use in most biomes then. Maybe make a new trait like
// PerlinTerrainGenerator
perlin: Perlin,
} }
impl<B: BiomeGenerator + GeneratorInit, T: TerrainGenerator + GeneratorInit> GeneratorInit impl<B: BiomeGenerator + GeneratorInit, T: PerlinTerrainGenerator + GeneratorInit> GeneratorInit
for GenericGenerator<B, T> for GenericGenerator<B, T>
{ {
fn new(seed: Seed) -> Self { fn new(seed: Seed) -> Self {
Self { Self {
biome_generator: B::new(seed), biome_generator: B::new(seed),
terrain_generator: T::new(seed), terrain_generator: T::new(seed),
perlin: Perlin::new(seed.0 as u32),
} }
} }
} }
impl<B: BiomeGenerator, T: TerrainGenerator> WorldGenerator for GenericGenerator<B, T> { impl<B: BiomeGenerator, T: PerlinTerrainGenerator> WorldGenerator for GenericGenerator<B, T> {
fn generate_chunk(&self, at: Vector2<i32>) -> ChunkData { fn generate_chunk(&self, at: Vector2<i32>) -> ChunkData {
let mut blocks = ChunkBlocks::default(); let mut blocks = ChunkBlocks::default();
self.terrain_generator.prepare_chunk(&at, &self.perlin);
let noise_value = self.perlin.get([at.x as f64 / 16.0, at.z as f64 / 16.0]);
let base_height = 64.0;
let height_variation = 16.0;
let chunk_height = (noise_value * height_variation + base_height) as i32;
for x in 0..16u8 {
for z in 0..16u8 {
let biome = self.biome_generator.generate_biome(
ChunkRelativeXZBlockCoordinates {
x: x.into(),
z: z.into(),
}
.with_chunk_coordinates(at),
);
// Iterate from the highest block to the lowest, in order to minimize the heightmap updates
for y in (WORLD_LOWEST_Y..chunk_height as i16).rev() {
let coordinates = ChunkRelativeBlockCoordinates {
x: x.into(),
y: y.into(),
z: z.into(),
};
blocks.set_block(
coordinates,
self.terrain_generator.generate_block(
coordinates.with_chunk_coordinates(at),
chunk_height as i16,
biome,
),
);
}
}
}
ChunkData {
blocks,
position: at,
}
}
}
// TODO: implement static terrain generator
/*
fn generate_chunk(&mut self, at: Vector2<i32>) -> ChunkData {
let mut blocks = ChunkBlocks::default();
self.terrain_generator.prepare_chunk(&at, &self.perlin);
for x in 0..16u8 { for x in 0..16u8 {
for z in 0..16u8 { for z in 0..16u8 {
let biome = self.biome_generator.generate_biome( let biome = self.biome_generator.generate_biome(
@ -62,5 +115,5 @@ impl<B: BiomeGenerator, T: TerrainGenerator> WorldGenerator for GenericGenerator
blocks, blocks,
position: at, position: at,
} }
}
} }
*/

View File

@ -1 +1,2 @@
pub mod overworld;
pub mod superflat; pub mod superflat;

View File

@ -0,0 +1 @@
pub mod plains;

View File

@ -0,0 +1,60 @@
use noise::Perlin;
use pumpkin_core::math::vector2::Vector2;
use crate::{
biome::Biome,
block::BlockId,
coordinates::{BlockCoordinates, XZBlockCoordinates},
world_gen::{
generator::{BiomeGenerator, GeneratorInit, PerlinTerrainGenerator},
generic_generator::GenericGenerator,
Seed,
},
};
pub type PlainsGenerator = GenericGenerator<PlainsBiomeGenerator, PlainsTerrainGenerator>;
pub(crate) struct PlainsBiomeGenerator {}
impl GeneratorInit for PlainsBiomeGenerator {
fn new(_: Seed) -> Self {
Self {}
}
}
impl BiomeGenerator for PlainsBiomeGenerator {
// TODO make generic over Biome and allow changing the Biome in the config.
fn generate_biome(&self, _: XZBlockCoordinates) -> Biome {
Biome::Plains
}
}
pub(crate) struct PlainsTerrainGenerator {}
impl GeneratorInit for PlainsTerrainGenerator {
fn new(_: Seed) -> Self {
Self {}
}
}
impl PerlinTerrainGenerator for PlainsTerrainGenerator {
fn prepare_chunk(&self, _at: &Vector2<i32>, _perlin: &Perlin) {}
// TODO allow specifying which blocks should be at which height in the config.
fn generate_block(&self, at: BlockCoordinates, chunk_height: i16, _: Biome) -> BlockId {
let begin_stone_height = chunk_height - 5;
let begin_dirt_height = chunk_height - 1;
let y = *at.y;
if y == -64 {
BlockId::from_id(79) // BEDROCK
} else if y >= -63 && y <= begin_stone_height {
return BlockId::from_id(1); // STONE
} else if y >= begin_stone_height && y < begin_dirt_height {
return BlockId::from_id(10); // DIRT;
} else if y == chunk_height - 1 {
return BlockId::from_id(9); // GRASS BLOCK
} else {
BlockId::AIR
}
}
}

View File

@ -0,0 +1 @@
pub mod biome;

View File

@ -1,3 +1,5 @@
use pumpkin_core::math::vector2::Vector2;
use crate::{ use crate::{
biome::Biome, biome::Biome,
block::BlockId, block::BlockId,
@ -9,7 +11,7 @@ use crate::{
}, },
}; };
#[allow(dead_code)] #[expect(dead_code)]
pub type SuperflatGenerator = GenericGenerator<SuperflatBiomeGenerator, SuperflatTerrainGenerator>; pub type SuperflatGenerator = GenericGenerator<SuperflatBiomeGenerator, SuperflatTerrainGenerator>;
pub(crate) struct SuperflatBiomeGenerator {} pub(crate) struct SuperflatBiomeGenerator {}
@ -36,6 +38,7 @@ impl GeneratorInit for SuperflatTerrainGenerator {
} }
impl TerrainGenerator for SuperflatTerrainGenerator { impl TerrainGenerator for SuperflatTerrainGenerator {
fn prepare_chunk(&self, _at: &Vector2<i32>) {}
// TODO allow specifying which blocks should be at which height in the config. // TODO allow specifying which blocks should be at which height in the config.
fn generate_block(&self, at: BlockCoordinates, _: Biome) -> BlockId { fn generate_block(&self, at: BlockCoordinates, _: Biome) -> BlockId {
match *at.y { match *at.y {

View File

@ -1,16 +1,15 @@
mod generator; mod generator;
mod generic_generator; mod generic_generator;
mod implementations; mod implementation;
mod seed; mod seed;
pub use generator::WorldGenerator; pub use generator::WorldGenerator;
use implementation::overworld::biome::plains::PlainsGenerator;
pub use seed::Seed; pub use seed::Seed;
use generator::GeneratorInit; use generator::GeneratorInit;
use implementations::superflat::SuperflatGenerator;
#[allow(dead_code)]
pub fn get_world_gen(seed: Seed) -> Box<dyn WorldGenerator> { pub fn get_world_gen(seed: Seed) -> Box<dyn WorldGenerator> {
// TODO decide which WorldGenerator to pick based on config. // TODO decide which WorldGenerator to pick based on config.
Box::new(SuperflatGenerator::new(seed)) Box::new(PlainsGenerator::new(seed))
} }

View File

@ -1,6 +1,5 @@
use std::hash::{DefaultHasher, Hash, Hasher}; use std::hash::{DefaultHasher, Hash, Hasher};
#[allow(dead_code)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Seed(pub i64); pub struct Seed(pub i64);

View File

@ -21,7 +21,6 @@ pumpkin-registry = { path = "../pumpkin-registry"}
# config # config
serde.workspace = true serde.workspace = true
serde_json = "1.0" serde_json = "1.0"
toml = "0.8.19"
bytes = "1.7" bytes = "1.7"
@ -29,7 +28,7 @@ rand = "0.8.5"
num-traits = "0.2" num-traits = "0.2"
num-derive = "0.4" num-derive = "0.4"
num-bigint = "0.4.6" num-bigint = "0.4"
ctrlc = "3.4" ctrlc = "3.4"
@ -47,7 +46,7 @@ digest = "=0.11.0-pre.9"
hmac = "0.12.1" hmac = "0.12.1"
sha2 = "0.10.8" sha2 = "0.10.8"
thiserror = "1.0.63" thiserror = "1.0"
# icon loading # icon loading
base64 = "0.22.1" base64 = "0.22.1"
@ -59,7 +58,6 @@ log.workspace = true
# networking # networking
mio = { version = "1.0.2", features = ["os-poll", "net"]} mio = { version = "1.0.2", features = ["os-poll", "net"]}
crossbeam-channel = "0.5.13"
uuid.workspace = true uuid.workspace = true
tokio.workspace = true tokio.workspace = true

View File

@ -13,8 +13,8 @@ use uuid::Uuid;
use crate::server::Server; use crate::server::Server;
#[derive(Deserialize, Clone, Debug)] #[derive(Deserialize, Clone, Debug)]
#[allow(non_snake_case)] #[expect(non_snake_case)]
#[allow(dead_code)] #[expect(dead_code)]
pub struct ProfileTextures { pub struct ProfileTextures {
timestamp: i64, timestamp: i64,
profileId: Uuid, profileId: Uuid,
@ -24,8 +24,7 @@ pub struct ProfileTextures {
} }
#[derive(Deserialize, Clone, Debug)] #[derive(Deserialize, Clone, Debug)]
#[allow(non_snake_case)] #[expect(dead_code)]
#[allow(dead_code)]
pub struct Texture { pub struct Texture {
url: String, url: String,
metadata: Option<HashMap<String, String>>, metadata: Option<HashMap<String, String>>,

View File

@ -72,7 +72,7 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
))); )));
} else { } else {
// TODO // TODO
#[allow(clippy::let_underscore_future)] #[expect(clippy::let_underscore_future)]
let _ = target.set_gamemode(gamemode); let _ = target.set_gamemode(gamemode);
target.send_system_message(TextComponent::text(&format!( target.send_system_message(TextComponent::text(&format!(
"Game mode was set to {:?}", "Game mode was set to {:?}",
@ -97,7 +97,7 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
))); )));
} else { } else {
// TODO // TODO
#[allow(clippy::let_underscore_future)] #[expect(clippy::let_underscore_future)]
let _ = target.set_gamemode(gamemode); let _ = target.set_gamemode(gamemode);
target.send_system_message(TextComponent::text(&format!( target.send_system_message(TextComponent::text(&format!(
"Game mode was set to {:?}", "Game mode was set to {:?}",

View File

@ -23,8 +23,9 @@ pub(crate) enum NodeType<'a> {
run: &'a (dyn Fn(&mut CommandSender, &mut Server, &ConsumedArgs) -> Result<(), InvalidTreeError> run: &'a (dyn Fn(&mut CommandSender, &mut Server, &ConsumedArgs) -> Result<(), InvalidTreeError>
+ Sync), + Sync),
}, },
#[allow(dead_code)] // todo: remove (so far no commands requiring this are implemented) Literal {
Literal { string: &'a str }, string: &'a str,
},
Argument { Argument {
name: &'a str, name: &'a str,
consumer: ArgumentConsumer<'a>, consumer: ArgumentConsumer<'a>,

View File

@ -131,7 +131,7 @@ impl<'a> NonLeafNodeBuilder<'a> {
} }
/// Matches a sting literal. /// Matches a sting literal.
#[allow(dead_code)] // todo: remove (so far no commands requiring this are implemented) #[expect(dead_code)] // todo: remove (so far no commands requiring this are implemented)
pub fn literal(string: &str) -> NonLeafNodeBuilder { pub fn literal(string: &str) -> NonLeafNodeBuilder {
NonLeafNodeBuilder { NonLeafNodeBuilder {
node_type: NodeType::Literal { string }, node_type: NodeType::Literal { string },

View File

@ -1,5 +1,4 @@
#![allow(clippy::await_holding_refcell_ref)] #![expect(clippy::await_holding_lock)]
#![allow(clippy::await_holding_lock)]
#[cfg(target_os = "wasi")] #[cfg(target_os = "wasi")]
compile_error!("Compiling for WASI targets is not supported!"); compile_error!("Compiling for WASI targets is not supported!");

View File

@ -52,7 +52,7 @@ pub fn receive_plugin_response(
buf.put_slice(data_without_signature); buf.put_slice(data_without_signature);
// check velocity version // check velocity version
let version = buf.get_var_int(); let version = buf.get_var_int().unwrap();
let version = version.0; let version = version.0;
if version > MAX_SUPPORTED_FORWARDING_VERSION { if version > MAX_SUPPORTED_FORWARDING_VERSION {
client.kick(&format!( client.kick(&format!(