Some refactors

This commit is contained in:
Alexander Medvedev 2024-12-24 00:56:19 +01:00
parent fd667901cd
commit 3dae300f81
72 changed files with 333 additions and 284 deletions

View File

@ -3,6 +3,13 @@ name = "pumpkin-protocol"
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
[features]
default = ["packets", "query"]
packets = ["serverbound", "clientbound"]
serverbound = []
clientbound = []
query = []
[dependencies] [dependencies]
pumpkin-nbt = { path = "../pumpkin-nbt" } pumpkin-nbt = { path = "../pumpkin-nbt" }
pumpkin-config = { path = "../pumpkin-config" } pumpkin-config = { path = "../pumpkin-config" }

View File

@ -1,4 +1,4 @@
use crate::slot::Slot; use crate::codec::slot::Slot;
use crate::VarInt; use crate::VarInt;
use pumpkin_macros::client_packet; use pumpkin_macros::client_packet;

View File

@ -1,4 +1,4 @@
use crate::slot::Slot; use crate::codec::slot::Slot;
use crate::VarInt; use crate::VarInt;
use pumpkin_macros::client_packet; use pumpkin_macros::client_packet;

View File

@ -5,6 +5,7 @@ use thiserror::Error;
pub mod bit_set; pub mod bit_set;
pub mod identifier; pub mod identifier;
pub mod slot;
pub mod var_int; pub mod var_int;
pub mod var_long; pub mod var_long;

View File

@ -7,13 +7,15 @@ use pumpkin_core::text::{style::Style, TextComponent};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub mod bytebuf; pub mod bytebuf;
#[cfg(feature = "clientbound")]
pub mod client; pub mod client;
pub mod codec; pub mod codec;
pub mod packet_decoder; pub mod packet_decoder;
pub mod packet_encoder; pub mod packet_encoder;
#[cfg(feature = "query")]
pub mod query; pub mod query;
#[cfg(feature = "serverbound")]
pub mod server; pub mod server;
pub mod slot;
/// To current Minecraft protocol /// To current Minecraft protocol
/// Don't forget to change this when porting /// Don't forget to change this when porting

View File

@ -16,8 +16,8 @@ type Cipher = cfb8::Decryptor<aes::Aes128>;
pub struct PacketDecoder { pub struct PacketDecoder {
buf: BytesMut, buf: BytesMut,
decompress_buf: BytesMut, decompress_buf: BytesMut,
compression: bool,
cipher: Option<Cipher>, cipher: Option<Cipher>,
compression: bool,
decompressor: Decompressor, decompressor: Decompressor,
} }
@ -28,8 +28,8 @@ impl Default for PacketDecoder {
Self { Self {
buf: BytesMut::new(), buf: BytesMut::new(),
decompress_buf: BytesMut::new(), decompress_buf: BytesMut::new(),
compression: false,
cipher: None, cipher: None,
compression: false,
decompressor: Decompressor::new(), decompressor: Decompressor::new(),
} }
} }

View File

@ -15,8 +15,8 @@ type Cipher = cfb8::Encryptor<aes::Aes128>;
pub struct PacketEncoder { pub struct PacketEncoder {
buf: BytesMut, buf: BytesMut,
compress_buf: Vec<u8>, compress_buf: Vec<u8>,
compression_threshold: Option<u32>,
cipher: Option<Cipher>, cipher: Option<Cipher>,
compression_threshold: Option<u32>,
compressor: Compressor, // Reuse the compressor for all packets compressor: Compressor, // Reuse the compressor for all packets
} }
@ -27,8 +27,8 @@ impl Default for PacketEncoder {
Self { Self {
buf: BytesMut::with_capacity(1024), buf: BytesMut::with_capacity(1024),
compress_buf: Vec::with_capacity(1024), compress_buf: Vec::with_capacity(1024),
compression_threshold: None,
cipher: None, cipher: None,
compression_threshold: None,
compressor: Compressor::new(CompressionLvl::fastest()), // init compressor with fastest compression level compressor: Compressor::new(CompressionLvl::fastest()), // init compressor with fastest compression level
} }
} }

View File

@ -11,7 +11,7 @@ use crate::{
#[server_packet("config:cookie_response")] #[server_packet("config:cookie_response")]
/// Response to a Cookie Request (configuration) from the server. /// Response to a Cookie Request (configuration) from the server.
/// The Notchian (vanilla) server only accepts responses of up to 5 kiB in size. /// The Notchian (vanilla) server only accepts responses of up to 5 kiB in size.
pub struct SCookieResponse { pub struct SConfigCookieResponse {
pub key: Identifier, pub key: Identifier,
pub has_payload: bool, pub has_payload: bool,
pub payload_length: Option<VarInt>, pub payload_length: Option<VarInt>,
@ -20,7 +20,7 @@ pub struct SCookieResponse {
const MAX_PAYLOAD_SIZE: i32 = 5120; const MAX_PAYLOAD_SIZE: i32 = 5120;
impl ServerPacket for SCookieResponse { impl ServerPacket for SConfigCookieResponse {
fn read(bytebuf: &mut impl Buf) -> Result<Self, ReadingError> { fn read(bytebuf: &mut impl Buf) -> Result<Self, ReadingError> {
let key = bytebuf.try_get_identifer()?; let key = bytebuf.try_get_identifer()?;
let has_payload = bytebuf.try_get_bool()?; let has_payload = bytebuf.try_get_bool()?;

View File

@ -10,7 +10,7 @@ use serde::de;
#[server_packet("login:cookie_response")] #[server_packet("login:cookie_response")]
/// Response to a Cookie Request (login) from the server. /// Response to a Cookie Request (login) from the server.
/// The Notchian server only accepts responses of up to 5 kiB in size. /// The Notchian server only accepts responses of up to 5 kiB in size.
pub struct SCookieResponse { pub struct SLoginCookieResponse {
pub key: Identifier, pub key: Identifier,
pub has_payload: bool, pub has_payload: bool,
pub payload_length: Option<VarInt>, pub payload_length: Option<VarInt>,
@ -19,7 +19,7 @@ pub struct SCookieResponse {
const MAX_PAYLOAD_SIZE: i32 = 5120; const MAX_PAYLOAD_SIZE: i32 = 5120;
impl ServerPacket for SCookieResponse { impl ServerPacket for SLoginCookieResponse {
fn read(bytebuf: &mut impl Buf) -> Result<Self, ReadingError> { fn read(bytebuf: &mut impl Buf) -> Result<Self, ReadingError> {
let key = bytebuf.try_get_identifer()?; let key = bytebuf.try_get_identifer()?;
let has_payload = bytebuf.try_get_bool()?; let has_payload = bytebuf.try_get_bool()?;

View File

@ -1,4 +1,4 @@
use crate::slot::Slot; use crate::codec::slot::Slot;
use crate::VarInt; use crate::VarInt;
use num_derive::FromPrimitive; use num_derive::FromPrimitive;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;

View File

@ -1,6 +1,6 @@
use pumpkin_macros::server_packet; use pumpkin_macros::server_packet;
use crate::slot::Slot; use crate::codec::slot::Slot;
#[derive(serde::Deserialize, Debug)] #[derive(serde::Deserialize, Debug)]
#[server_packet("play:set_creative_mode_slot")] #[server_packet("play:set_creative_mode_slot")]

View File

@ -661,7 +661,7 @@ mod test {
use crate::{ use crate::{
block::BlockState, block::BlockState,
world_gen::{ generation::{
chunk_noise::{ chunk_noise::{
BlockStateSampler, ChunkNoiseDensityFunctions, ChunkNoiseGenerator, BlockStateSampler, ChunkNoiseDensityFunctions, ChunkNoiseGenerator,
ChunkNoiseState, LAVA_BLOCK, WATER_BLOCK, ChunkNoiseState, LAVA_BLOCK, WATER_BLOCK,

View File

@ -6,11 +6,11 @@ use pumpkin_macros::block_state;
use crate::{ use crate::{
block::BlockState, block::BlockState,
match_ref_implementations, generation::{
world_gen::{
noise::{density::basic::WrapperType, lerp3}, noise::{density::basic::WrapperType, lerp3},
section_coords, section_coords,
}, },
match_ref_implementations,
}; };
use super::{ use super::{
@ -1379,7 +1379,7 @@ impl ChunkNoiseGenerator {
mod test { mod test {
use pumpkin_core::math::vector2::Vector2; use pumpkin_core::math::vector2::Vector2;
use crate::world_gen::{ use crate::generation::{
aquifer_sampler::{FluidLevel, FluidLevelSampler}, aquifer_sampler::{FluidLevel, FluidLevelSampler},
generation_shapes::GenerationShape, generation_shapes::GenerationShape,
noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER}, noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER},

View File

@ -6,7 +6,7 @@ use crate::biome::Biome;
use crate::block::block_state::BlockState; use crate::block::block_state::BlockState;
use crate::chunk::{ChunkBlocks, ChunkData}; use crate::chunk::{ChunkBlocks, ChunkData};
use crate::coordinates::{BlockCoordinates, ChunkRelativeBlockCoordinates, XZBlockCoordinates}; use crate::coordinates::{BlockCoordinates, ChunkRelativeBlockCoordinates, XZBlockCoordinates};
use crate::world_gen::Seed; use crate::generation::Seed;
pub trait GeneratorInit { pub trait GeneratorInit {
fn new(seed: Seed) -> Self; fn new(seed: Seed) -> Self;

View File

@ -7,7 +7,7 @@ use crate::{
biome::Biome, biome::Biome,
chunk::ChunkBlocks, chunk::ChunkBlocks,
coordinates::{BlockCoordinates, ChunkRelativeBlockCoordinates, XZBlockCoordinates}, coordinates::{BlockCoordinates, ChunkRelativeBlockCoordinates, XZBlockCoordinates},
world_gen::{ generation::{
generator::{BiomeGenerator, GeneratorInit, PerlinTerrainGenerator}, generator::{BiomeGenerator, GeneratorInit, PerlinTerrainGenerator},
generic_generator::GenericGenerator, generic_generator::GenericGenerator,
Seed, Seed,

View File

@ -4,7 +4,7 @@ use crate::{
biome::Biome, biome::Biome,
block::block_state::BlockState, block::block_state::BlockState,
coordinates::XZBlockCoordinates, coordinates::XZBlockCoordinates,
world_gen::{ generation::{
generator::{BiomeGenerator, GeneratorInit, TerrainGenerator}, generator::{BiomeGenerator, GeneratorInit, TerrainGenerator},
generic_generator::GenericGenerator, generic_generator::GenericGenerator,
Seed, Seed,

View File

@ -14,7 +14,7 @@ use crate::{
coordinates::{ coordinates::{
ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates, XZBlockCoordinates, ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates, XZBlockCoordinates,
}, },
world_gen::{ generation::{
generator::{BiomeGenerator, GeneratorInit, TerrainGenerator}, generator::{BiomeGenerator, GeneratorInit, TerrainGenerator},
proto_chunk::ProtoChunk, proto_chunk::ProtoChunk,
Seed, WorldGenerator, Seed, WorldGenerator,

View File

@ -151,8 +151,7 @@ mod test {
}; };
use crate::{ use crate::{
read_data_from_file, generation::noise::{
world_gen::noise::{
config::NoiseConfig, config::NoiseConfig,
density::{ density::{
built_in_density_function::{ built_in_density_function::{
@ -165,6 +164,7 @@ mod test {
}, },
router::OVERWORLD_NOISE_ROUTER, router::OVERWORLD_NOISE_ROUTER,
}, },
read_data_from_file,
}; };
use super::LegacyChunkNoiseVisitor; use super::LegacyChunkNoiseVisitor;

View File

@ -1,6 +1,6 @@
use std::{hash::Hash, marker::PhantomData}; use std::{hash::Hash, marker::PhantomData};
use crate::{match_ref_implementations, world_gen::noise::clamped_map}; use crate::{generation::noise::clamped_map, match_ref_implementations};
use super::{ use super::{
component_functions::{ component_functions::{
@ -419,10 +419,10 @@ mod test {
use std::{fs, path::Path}; use std::{fs, path::Path};
use crate::{ use crate::{
read_data_from_file, generation::noise::density::{
world_gen::noise::density::{
component_functions::ImmutableComponentFunctionImpl, NoisePos, UnblendedNoisePos, component_functions::ImmutableComponentFunctionImpl, NoisePos, UnblendedNoisePos,
}, },
read_data_from_file,
}; };
use super::YClampedFunction; use super::YClampedFunction;

View File

@ -1,6 +1,6 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use crate::world_gen::blender::BlenderImpl; use crate::generation::blender::BlenderImpl;
use super::{ use super::{
component_functions::{ component_functions::{

View File

@ -953,7 +953,7 @@ mod test {
use pumpkin_core::random::{legacy_rand::LegacyRand, RandomDeriver, RandomImpl}; use pumpkin_core::random::{legacy_rand::LegacyRand, RandomDeriver, RandomImpl};
use crate::world_gen::noise::{ use crate::generation::noise::{
built_in_noise_params, built_in_noise_params,
density::{ density::{
noise::{InternalNoise, NoiseFunction}, noise::{InternalNoise, NoiseFunction},

View File

@ -1,6 +1,6 @@
use pumpkin_core::random::{legacy_rand::LegacyRand, RandomImpl}; use pumpkin_core::random::{legacy_rand::LegacyRand, RandomImpl};
use crate::world_gen::noise::simplex::SimplexNoiseSampler; use crate::generation::noise::simplex::SimplexNoiseSampler;
use super::{ use super::{
component_functions::{ component_functions::{

View File

@ -10,7 +10,7 @@ use component_functions::{
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use noise::{InternalNoise, NoiseFunction}; use noise::{InternalNoise, NoiseFunction};
use crate::world_gen::{blender::Blender, chunk_noise::ChunkNoisePos}; use crate::generation::{blender::Blender, chunk_noise::ChunkNoisePos};
use super::perlin::DoublePerlinNoiseParameters; use super::perlin::DoublePerlinNoiseParameters;
@ -76,8 +76,8 @@ pub trait NoisePosImpl {
pub mod built_in_density_function { pub mod built_in_density_function {
use std::sync::{Arc, LazyLock}; use std::sync::{Arc, LazyLock};
use crate::world_gen::noise::built_in_noise_params::{self}; use crate::generation::noise::built_in_noise_params::{self};
use crate::world_gen::positions::{MAX_COLUMN_HEIGHT, MIN_HEIGHT}; use crate::generation::positions::{MAX_COLUMN_HEIGHT, MIN_HEIGHT};
use pumpkin_core::math::floor_div; use pumpkin_core::math::floor_div;
@ -879,7 +879,7 @@ mod test {
legacy_rand::LegacyRand, RandomDeriver, RandomDeriverImpl, RandomImpl, legacy_rand::LegacyRand, RandomDeriver, RandomDeriverImpl, RandomImpl,
}; };
use crate::world_gen::noise::{ use crate::generation::noise::{
built_in_noise_params, built_in_noise_params,
density::{built_in_density_function::*, NoisePos, UnblendedNoisePos}, density::{built_in_density_function::*, NoisePos, UnblendedNoisePos},
perlin::DoublePerlinNoiseSampler, perlin::DoublePerlinNoiseSampler,

View File

@ -3,11 +3,11 @@ use std::{marker::PhantomData, sync::Arc};
use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomGenerator, RandomImpl}; use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomGenerator, RandomImpl};
use crate::{ use crate::{
match_ref_implementations, generation::noise::{
world_gen::noise::{
clamped_lerp, clamped_lerp,
perlin::{DoublePerlinNoiseParameters, DoublePerlinNoiseSampler, OctavePerlinNoiseSampler}, perlin::{DoublePerlinNoiseParameters, DoublePerlinNoiseSampler, OctavePerlinNoiseSampler},
}, },
match_ref_implementations,
}; };
use super::{ use super::{

View File

@ -2,7 +2,7 @@ use std::{marker::PhantomData, sync::Arc};
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use crate::world_gen::noise::lerp; use crate::generation::noise::lerp;
use super::{ use super::{
component_functions::{ component_functions::{
@ -744,7 +744,7 @@ mod test {
use pumpkin_core::random::{legacy_rand::LegacyRand, RandomDeriver, RandomImpl}; use pumpkin_core::random::{legacy_rand::LegacyRand, RandomDeriver, RandomImpl};
use crate::world_gen::noise::density::{ use crate::generation::noise::density::{
built_in_density_function::CONTINENTS_OVERWORLD, built_in_density_function::CONTINENTS_OVERWORLD,
component_functions::{ComponentReference, NoEnvironment, SharedComponentReference}, component_functions::{ComponentReference, NoEnvironment, SharedComponentReference},
test::{FakeEnvironment, OwnedConverter, TestConverter}, test::{FakeEnvironment, OwnedConverter, TestConverter},

View File

@ -1,7 +1,7 @@
// From da java // From da java
use crate::world_gen::noise::density::peaks_valleys_noise; use crate::generation::noise::density::peaks_valleys_noise;
use crate::world_gen::noise::lerp; use crate::generation::noise::lerp;
use super::component_functions::SharedComponentReference; use super::component_functions::SharedComponentReference;
use super::spline::{FloatAmplifier, ImmutableSpline, ImmutableSplineRef, SplineBuilder}; use super::spline::{FloatAmplifier, ImmutableSpline, ImmutableSplineRef, SplineBuilder};
@ -523,7 +523,7 @@ pub fn create_jaggedness_spline(
mod test { mod test {
use pumpkin_core::random::{legacy_rand::LegacyRand, RandomDeriver, RandomImpl}; use pumpkin_core::random::{legacy_rand::LegacyRand, RandomDeriver, RandomImpl};
use crate::world_gen::noise::density::{ use crate::generation::noise::density::{
built_in_density_function::{ built_in_density_function::{
CONTINENTS_OVERWORLD, EROSION_OVERWORLD, RIDGES_FOLDED_OVERWORLD, RIDGES_OVERWORLD, CONTINENTS_OVERWORLD, EROSION_OVERWORLD, RIDGES_FOLDED_OVERWORLD, RIDGES_OVERWORLD,
}, },

View File

@ -381,7 +381,7 @@ mod double_perlin_noise_sampler_test {
legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, RandomGenerator, RandomImpl, legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, RandomGenerator, RandomImpl,
}; };
use crate::world_gen::noise::perlin::{DoublePerlinNoiseParameters, DoublePerlinNoiseSampler}; use crate::generation::noise::perlin::{DoublePerlinNoiseParameters, DoublePerlinNoiseSampler};
#[test] #[test]
fn sample_legacy() { fn sample_legacy() {
@ -760,7 +760,7 @@ mod perlin_noise_sampler_test {
random::{xoroshiro128::Xoroshiro, RandomDeriverImpl, RandomGenerator, RandomImpl}, random::{xoroshiro128::Xoroshiro, RandomDeriverImpl, RandomGenerator, RandomImpl},
}; };
use crate::{read_data_from_file, world_gen::noise::perlin::PerlinNoiseSampler}; use crate::{generation::noise::perlin::PerlinNoiseSampler, read_data_from_file};
use super::OctavePerlinNoiseSampler; use super::OctavePerlinNoiseSampler;

View File

@ -1,6 +1,6 @@
use std::sync::{Arc, LazyLock}; use std::sync::{Arc, LazyLock};
use crate::world_gen::{ use crate::generation::{
noise::density::{ noise::density::{
apply_blend_density, apply_blend_density,
basic::RangeFunction, basic::RangeFunction,
@ -476,8 +476,7 @@ mod test {
}; };
use crate::{ use crate::{
read_data_from_file, generation::noise::{
world_gen::noise::{
config::LegacyChunkNoiseVisitor, config::LegacyChunkNoiseVisitor,
density::{ density::{
built_in_density_function::{EROSION_OVERWORLD, SLOPED_CHEESE_OVERWORLD}, built_in_density_function::{EROSION_OVERWORLD, SLOPED_CHEESE_OVERWORLD},
@ -491,6 +490,7 @@ mod test {
perlin::DoublePerlinNoiseSampler, perlin::DoublePerlinNoiseSampler,
router::OVERWORLD_NOISE_ROUTER, router::OVERWORLD_NOISE_ROUTER,
}, },
read_data_from_file,
}; };
use super::{apply_surface_slides, create_caves}; use super::{apply_surface_slides, create_caves};

View File

@ -276,7 +276,7 @@ impl OctaveSimplexNoiseSampler {
mod octave_simplex_noise_sampler_test { mod octave_simplex_noise_sampler_test {
use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomImpl}; use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomImpl};
use crate::world_gen::noise::simplex::OctaveSimplexNoiseSampler; use crate::generation::noise::simplex::OctaveSimplexNoiseSampler;
#[test] #[test]
fn test_new() { fn test_new() {
@ -413,7 +413,7 @@ mod simplex_noise_sampler_test {
use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomImpl}; use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomImpl};
use crate::world_gen::noise::simplex::SimplexNoiseSampler; use crate::generation::noise::simplex::SimplexNoiseSampler;
#[test] #[test]
fn test_create() { fn test_create() {

View File

@ -2,7 +2,7 @@ use pumpkin_core::math::{vector2::Vector2, vector3::Vector3};
use crate::{ use crate::{
block::BlockState, block::BlockState,
world_gen::{ generation::{
chunk_noise::CHUNK_DIM, chunk_noise::CHUNK_DIM,
generation_shapes::GenerationShape, generation_shapes::GenerationShape,
noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER}, noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER},

View File

@ -14,7 +14,7 @@ use crate::{
chunk::{ chunk::{
anvil::AnvilChunkReader, ChunkData, ChunkParsingError, ChunkReader, ChunkReadingError, anvil::AnvilChunkReader, ChunkData, ChunkParsingError, ChunkReader, ChunkReadingError,
}, },
world_gen::{get_world_gen, Seed, WorldGenerator}, generation::{get_world_gen, Seed, WorldGenerator},
}; };
/// The `Level` module provides functionality for working with chunks within or outside a Minecraft world. /// The `Level` module provides functionality for working with chunks within or outside a Minecraft world.

View File

@ -1,11 +1,11 @@
use pumpkin_core::math::vector2::Vector2; use generation::{
use world_gen::{
aquifer_sampler::{FluidLevel, FluidLevelSampler}, aquifer_sampler::{FluidLevel, FluidLevelSampler},
chunk_noise::{ChunkNoiseGenerator, LAVA_BLOCK, WATER_BLOCK}, chunk_noise::{ChunkNoiseGenerator, LAVA_BLOCK, WATER_BLOCK},
generation_shapes::GenerationShape, generation_shapes::GenerationShape,
noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER}, noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER},
proto_chunk::{ProtoChunk, StandardChunkFluidLevelSampler}, proto_chunk::{ProtoChunk, StandardChunkFluidLevelSampler},
}; };
use pumpkin_core::math::vector2::Vector2;
pub mod biome; pub mod biome;
pub mod block; pub mod block;
@ -13,9 +13,9 @@ pub mod chunk;
pub mod coordinates; pub mod coordinates;
pub mod cylindrical_chunk_iterator; pub mod cylindrical_chunk_iterator;
pub mod dimension; pub mod dimension;
mod generation;
pub mod item; pub mod item;
pub mod level; pub mod level;
mod world_gen;
pub const WORLD_HEIGHT: usize = 384; pub const WORLD_HEIGHT: usize = 384;
pub const WORLD_LOWEST_Y: i16 = -64; pub const WORLD_LOWEST_Y: i16 = -64;

View File

@ -57,10 +57,9 @@ use pumpkin_world::{
use tokio::sync::{Mutex, Notify}; use tokio::sync::{Mutex, Notify};
use super::Entity; use super::Entity;
use crate::error::PumpkinError; use crate::{error::PumpkinError, net::GameProfile};
use crate::{ use crate::{
client::{ net::{
authentication::GameProfile,
combat::{self, player_attack_sound, AttackType}, combat::{self, player_attack_sound, AttackType},
Client, PlayerConfig, Client, PlayerConfig,
}, },

View File

@ -35,7 +35,7 @@ compile_error!("Compiling for WASI targets is not supported!");
use log::LevelFilter; use log::LevelFilter;
use client::Client; use net::{lan_broadcast, query, rcon::RCONServer, Client};
use server::{ticker::Ticker, Server}; use server::{ticker::Ticker, Server};
use std::io::{self}; use std::io::{self};
use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::io::{AsyncBufReadExt, BufReader};
@ -50,19 +50,14 @@ use crate::server::CURRENT_MC_VERSION;
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
use pumpkin_core::text::{color::NamedColor, TextComponent}; use pumpkin_core::text::{color::NamedColor, TextComponent};
use pumpkin_protocol::CURRENT_MC_PROTOCOL; use pumpkin_protocol::CURRENT_MC_PROTOCOL;
use rcon::RCONServer;
use std::time::Instant; use std::time::Instant;
// Setup some tokens to allow us to identify which event is for which socket. // Setup some tokens to allow us to identify which event is for which socket.
pub mod block; pub mod block;
pub mod client;
pub mod command; pub mod command;
pub mod entity; pub mod entity;
pub mod error; pub mod error;
pub mod lan_broadcast; pub mod net;
pub mod proxy;
pub mod query;
pub mod rcon;
pub mod server; pub mod server;
pub mod world; pub mod world;

View File

@ -2,15 +2,14 @@ use std::{collections::HashMap, net::IpAddr};
use base64::{engine::general_purpose, Engine}; use base64::{engine::general_purpose, Engine};
use pumpkin_config::{auth::TextureConfig, ADVANCED_CONFIG}; use pumpkin_config::{auth::TextureConfig, ADVANCED_CONFIG};
use pumpkin_core::ProfileAction;
use pumpkin_protocol::Property; use pumpkin_protocol::Property;
use reqwest::{StatusCode, Url}; use reqwest::{StatusCode, Url};
use serde::Deserialize; use serde::Deserialize;
use sha1::Digest;
use sha2::Sha256;
use thiserror::Error; use thiserror::Error;
use uuid::Uuid; use uuid::Uuid;
use super::GameProfile;
#[derive(Deserialize, Clone, Debug)] #[derive(Deserialize, Clone, Debug)]
#[expect(dead_code)] #[expect(dead_code)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -29,14 +28,8 @@ pub struct Texture {
metadata: Option<HashMap<String, String>>, metadata: Option<HashMap<String, String>>,
} }
#[derive(Deserialize, Clone, Debug)] const MOJANG_AUTHENTICATION_URL: &str = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}";
pub struct GameProfile { const MOJANG_PREVENT_PROXY_AUTHENTICATION_URL: &str = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}";
pub id: Uuid,
pub name: String,
pub properties: Vec<Property>,
#[serde(rename = "profileActions")]
pub profile_actions: Option<Vec<ProfileAction>>,
}
/// Sends a GET request to Mojang's authentication servers to verify a client's Minecraft account. /// Sends a GET request to Mojang's authentication servers to verify a client's Minecraft account.
/// ///
@ -50,20 +43,19 @@ pub struct GameProfile {
/// 2. Mojang's servers verify the client's credentials and add the player to the their Servers /// 2. Mojang's servers verify the client's credentials and add the player to the their Servers
/// 3. Now our server will send a Request to the Session servers and check if the Player has joined the Session Server . /// 3. Now our server will send a Request to the Session servers and check if the Player has joined the Session Server .
/// ///
/// See <https://snowiiii.github.io/Pumpkin/developer/authentication.html> /// See <https://snowiiii.github.io/Pumpkin-Website/developer/authentication.html>
pub async fn authenticate( pub async fn authenticate(
username: &str, username: &str,
server_hash: &str, server_hash: &str,
ip: &IpAddr, ip: &IpAddr,
auth_client: &reqwest::Client, auth_client: &reqwest::Client,
) -> Result<GameProfile, AuthError> { ) -> Result<GameProfile, AuthError> {
assert!(ADVANCED_CONFIG.authentication.enabled);
let address = if ADVANCED_CONFIG.authentication.prevent_proxy_connections { let address = if ADVANCED_CONFIG.authentication.prevent_proxy_connections {
let auth_url = ADVANCED_CONFIG let auth_url = ADVANCED_CONFIG
.authentication .authentication
.prevent_proxy_connection_auth_url .prevent_proxy_connection_auth_url
.as_deref() .as_deref()
.unwrap_or("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}&ip={ip}"); .unwrap_or(MOJANG_PREVENT_PROXY_AUTHENTICATION_URL);
auth_url auth_url
.replace("{username}", username) .replace("{username}", username)
@ -74,7 +66,7 @@ pub async fn authenticate(
.authentication .authentication
.auth_url .auth_url
.as_deref() .as_deref()
.unwrap_or("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}"); .unwrap_or(MOJANG_AUTHENTICATION_URL);
auth_url auth_url
.replace("{username}", username) .replace("{username}", username)
@ -129,10 +121,6 @@ pub fn is_texture_url_valid(url: &Url, config: &TextureConfig) -> Result<(), Tex
Ok(()) Ok(())
} }
pub fn offline_uuid(username: &str) -> Result<Uuid, uuid::Error> {
Uuid::from_slice(&Sha256::digest(username)[..16])
}
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum AuthError { pub enum AuthError {
#[error("Missing auth client")] #[error("Missing auth client")]

View File

@ -12,9 +12,9 @@ use pumpkin_inventory::{Container, WindowType};
use pumpkin_protocol::client::play::{ use pumpkin_protocol::client::play::{
CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot, CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot,
}; };
use pumpkin_protocol::codec::slot::Slot;
use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::codec::var_int::VarInt;
use pumpkin_protocol::server::play::SClickContainer; use pumpkin_protocol::server::play::SClickContainer;
use pumpkin_protocol::slot::Slot;
use pumpkin_world::item::item_registry::Item; use pumpkin_world::item::item_registry::Item;
use pumpkin_world::item::ItemStack; use pumpkin_world::item::ItemStack;
use std::sync::Arc; use std::sync::Arc;

View File

@ -13,34 +13,57 @@ use crate::{
server::Server, server::Server,
}; };
use authentication::GameProfile;
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use pumpkin_config::compression::CompressionInfo; use pumpkin_config::compression::CompressionInfo;
use pumpkin_core::text::TextComponent; use pumpkin_core::{text::TextComponent, ProfileAction};
use pumpkin_protocol::{ use pumpkin_protocol::{
bytebuf::{packet_id::Packet, ReadingError}, bytebuf::{packet_id::Packet, ReadingError},
client::{config::CConfigDisconnect, login::CLoginDisconnect, play::CPlayDisconnect}, client::{config::CConfigDisconnect, login::CLoginDisconnect, play::CPlayDisconnect},
packet_decoder::PacketDecoder, packet_decoder::PacketDecoder,
packet_encoder::{PacketEncodeError, PacketEncoder}, packet_encoder::{PacketEncodeError, PacketEncoder},
server::{ server::{
config::{SAcknowledgeFinishConfig, SClientInformationConfig, SKnownPacks, SPluginMessage}, config::{
SAcknowledgeFinishConfig, SClientInformationConfig, SConfigCookieResponse, SKnownPacks,
SPluginMessage,
},
handshake::SHandShake, handshake::SHandShake,
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, login::{
SEncryptionResponse, SLoginAcknowledged, SLoginCookieResponse, SLoginPluginResponse,
SLoginStart,
},
status::{SStatusPingRequest, SStatusRequest}, status::{SStatusPingRequest, SStatusRequest},
}, },
ClientPacket, ConnectionState, RawPacket, ServerPacket, ClientPacket, ConnectionState, Property, RawPacket, ServerPacket,
}; };
use serde::Deserialize;
use sha1::Digest;
use sha2::Sha256;
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use pumpkin_protocol::server::config::SCookieResponse as SCCookieResponse;
use pumpkin_protocol::server::login::SCookieResponse as SLCookieResponse;
use thiserror::Error; use thiserror::Error;
pub mod authentication; use uuid::Uuid;
mod client_packet; mod authentication;
pub mod combat; pub mod combat;
mod container; mod container;
pub mod player_packet; pub mod lan_broadcast;
mod packet;
mod proxy;
pub mod query;
pub mod rcon;
#[derive(Deserialize, Clone, Debug)]
pub struct GameProfile {
pub id: Uuid,
pub name: String,
pub properties: Vec<Property>,
#[serde(rename = "profileActions")]
pub profile_actions: Option<Vec<ProfileAction>>,
}
pub fn offline_uuid(username: &str) -> Result<Uuid, uuid::Error> {
Uuid::from_slice(&Sha256::digest(username)[..16])
}
/// Represents a player's configuration settings. /// Represents a player's configuration settings.
/// ///
@ -102,8 +125,6 @@ pub struct Client {
pub server_address: Mutex<String>, pub server_address: Mutex<String>,
/// The current connection state of the client (e.g., Handshaking, Status, Play). /// The current connection state of the client (e.g., Handshaking, Status, Play).
pub connection_state: AtomicCell<ConnectionState>, pub connection_state: AtomicCell<ConnectionState>,
/// Whether encryption is enabled for the connection.
pub encryption: AtomicBool,
/// Indicates if the client connection is closed. /// Indicates if the client connection is closed.
pub closed: AtomicBool, pub closed: AtomicBool,
/// The underlying TCP connection to the client. /// The underlying TCP connection to the client.
@ -138,7 +159,6 @@ impl Client {
connection_writer: Arc::new(Mutex::new(connection_writer)), connection_writer: Arc::new(Mutex::new(connection_writer)),
enc: Arc::new(Mutex::new(PacketEncoder::default())), enc: Arc::new(Mutex::new(PacketEncoder::default())),
dec: Arc::new(Mutex::new(PacketDecoder::default())), dec: Arc::new(Mutex::new(PacketDecoder::default())),
encryption: AtomicBool::new(false),
closed: AtomicBool::new(false), closed: AtomicBool::new(false),
client_packets_queue: Arc::new(Mutex::new(VecDeque::new())), client_packets_queue: Arc::new(Mutex::new(VecDeque::new())),
make_player: AtomicBool::new(false), make_player: AtomicBool::new(false),
@ -182,8 +202,6 @@ impl Client {
shared_secret: Option<&[u8]>, // decrypted shared_secret: Option<&[u8]>, // decrypted
) -> Result<(), EncryptionError> { ) -> Result<(), EncryptionError> {
if let Some(shared_secret) = shared_secret { if let Some(shared_secret) = shared_secret {
self.encryption
.store(true, std::sync::atomic::Ordering::Relaxed);
let crypt_key: [u8; 16] = shared_secret let crypt_key: [u8; 16] = shared_secret
.try_into() .try_into()
.map_err(|_| EncryptionError::SharedWrongLength)?; .map_err(|_| EncryptionError::SharedWrongLength)?;
@ -433,8 +451,8 @@ impl Client {
SLoginAcknowledged::PACKET_ID => { SLoginAcknowledged::PACKET_ID => {
self.handle_login_acknowledged(server).await; self.handle_login_acknowledged(server).await;
} }
SLCookieResponse::PACKET_ID => { SLoginCookieResponse::PACKET_ID => {
self.handle_login_cookie_response(SLCookieResponse::read(bytebuf)?); self.handle_login_cookie_response(SLoginCookieResponse::read(bytebuf)?);
} }
_ => { _ => {
log::error!( log::error!(
@ -469,8 +487,8 @@ impl Client {
self.handle_known_packs(server, SKnownPacks::read(bytebuf)?) self.handle_known_packs(server, SKnownPacks::read(bytebuf)?)
.await; .await;
} }
SCCookieResponse::PACKET_ID => { SConfigCookieResponse::PACKET_ID => {
self.handle_config_cookie_response(SCCookieResponse::read(bytebuf)?); self.handle_config_cookie_response(SConfigCookieResponse::read(bytebuf)?);
} }
_ => { _ => {
log::error!( log::error!(

View File

@ -0,0 +1,98 @@
use std::num::NonZeroU8;
use crate::{
entity::player::{ChatMode, Hand},
net::{Client, PlayerConfig},
server::Server,
};
use core::str;
use num_traits::FromPrimitive;
use pumpkin_protocol::{
client::config::{CFinishConfig, CRegistryData},
codec::var_int::VarInt,
server::config::{
SClientInformationConfig, SConfigCookieResponse, SKnownPacks, SPluginMessage,
},
ConnectionState,
};
impl Client {
pub async fn handle_client_information_config(
&self,
client_information: SClientInformationConfig,
) {
log::debug!("Handling client settings");
if client_information.view_distance <= 0 {
self.kick("Cannot have zero or negative view distance!")
.await;
return;
}
if let (Some(main_hand), Some(chat_mode)) = (
Hand::from_i32(client_information.main_hand.into()),
ChatMode::from_i32(client_information.chat_mode.into()),
) {
*self.config.lock().await = Some(PlayerConfig {
locale: client_information.locale,
view_distance: unsafe {
NonZeroU8::new_unchecked(client_information.view_distance as u8)
},
chat_mode,
chat_colors: client_information.chat_colors,
skin_parts: client_information.skin_parts,
main_hand,
text_filtering: client_information.text_filtering,
server_listing: client_information.server_listing,
});
} else {
self.kick("Invalid hand or chat type").await;
}
}
pub async fn handle_plugin_message(&self, plugin_message: SPluginMessage) {
log::debug!("Handling plugin message");
if plugin_message
.channel
.to_string()
.starts_with("minecraft:brand")
{
log::debug!("got a client brand");
match str::from_utf8(&plugin_message.data) {
Ok(brand) => *self.brand.lock().await = Some(brand.to_string()),
Err(e) => self.kick(&e.to_string()).await,
}
}
}
pub fn handle_config_cookie_response(&self, packet: SConfigCookieResponse) {
// TODO: allow plugins to access this
log::debug!(
"Received cookie_response[config]: key: \"{}\", has_payload: \"{}\", payload_length: \"{}\"",
packet.key.to_string(),
packet.has_payload,
packet.payload_length.unwrap_or(VarInt::from(0)).0
);
}
pub async fn handle_known_packs(&self, server: &Server, _config_acknowledged: SKnownPacks) {
log::debug!("Handling known packs");
for registry in &server.cached_registry {
self.send_packet(&CRegistryData::new(
&registry.registry_id,
&registry.registry_entries,
))
.await;
}
// We are done with configuring
log::debug!("finished config");
self.send_packet(&CFinishConfig::new()).await;
}
pub fn handle_config_acknowledged(&self) {
log::debug!("Handling config acknowledge");
self.connection_state.store(ConnectionState::Play);
self.make_player
.store(true, std::sync::atomic::Ordering::Relaxed);
}
}

View File

@ -0,0 +1,29 @@
use std::num::NonZeroI32;
use pumpkin_protocol::{server::handshake::SHandShake, ConnectionState, CURRENT_MC_PROTOCOL};
use crate::{net::Client, server::CURRENT_MC_VERSION};
impl Client {
pub async fn handle_handshake(&self, handshake: SHandShake) {
let version = handshake.protocol_version.0;
self.protocol_version
.store(version, std::sync::atomic::Ordering::Relaxed);
*self.server_address.lock().await = handshake.server_address;
log::debug!("Handshake: next state {:?}", &handshake.next_state);
self.connection_state.store(handshake.next_state);
if self.connection_state.load() != ConnectionState::Status {
let protocol = version;
match protocol.cmp(&NonZeroI32::from(CURRENT_MC_PROTOCOL).get()) {
std::cmp::Ordering::Less => {
self.kick(&format!("Client outdated ({protocol}), Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}")).await;
}
std::cmp::Ordering::Equal => {}
std::cmp::Ordering::Greater => {
self.kick(&format!("Server outdated, Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}")).await;
}
}
}
}
}

View File

@ -1,43 +1,30 @@
use super::{authentication::AuthError, Client, PlayerConfig}; use std::sync::LazyLock;
use crate::{
client::authentication::{self, offline_uuid, validate_textures, GameProfile},
entity::player::{ChatMode, Hand},
proxy::{
bungeecord,
velocity::{self, velocity_login},
},
server::{Server, CURRENT_MC_VERSION},
};
use core::str;
use num_traits::FromPrimitive;
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
use pumpkin_core::text::TextComponent; use pumpkin_core::text::TextComponent;
use pumpkin_protocol::server::config::SCookieResponse as SCCookieResponse;
use pumpkin_protocol::server::login::SCookieResponse as SLCookieResponse;
use pumpkin_protocol::{
client::config::{CServerLinks, Label, Link, LinkType},
codec::var_int::VarInt,
};
use pumpkin_protocol::{ use pumpkin_protocol::{
client::{ client::{
config::{CConfigAddResourcePack, CFinishConfig, CKnownPacks, CRegistryData}, config::{CConfigAddResourcePack, CKnownPacks},
login::{CLoginSuccess, CSetCompression}, login::{CLoginSuccess, CSetCompression},
status::CPingResponse, play::{CServerLinks, Label, Link, LinkType},
}, },
server::{ codec::var_int::VarInt,
config::{SClientInformationConfig, SKnownPacks, SPluginMessage}, server::login::{SEncryptionResponse, SLoginCookieResponse, SLoginPluginResponse, SLoginStart},
handshake::SHandShake, ConnectionState, KnownPack,
login::{SEncryptionResponse, SLoginPluginResponse, SLoginStart},
status::SStatusPingRequest,
},
ConnectionState, KnownPack, CURRENT_MC_PROTOCOL,
};
use std::{
num::{NonZeroI32, NonZeroU8},
sync::LazyLock,
}; };
use uuid::Uuid; use uuid::Uuid;
use crate::{
net::{
authentication::{self, AuthError},
offline_uuid,
packet::is_valid_player_name,
proxy::{bungeecord, velocity},
Client, GameProfile,
},
server::Server,
};
static LINKS: LazyLock<Vec<Link>> = LazyLock::new(|| { static LINKS: LazyLock<Vec<Link>> = LazyLock::new(|| {
let mut links: Vec<Link> = Vec::new(); let mut links: Vec<Link> = Vec::new();
@ -98,49 +85,7 @@ static LINKS: LazyLock<Vec<Link>> = LazyLock::new(|| {
links links
}); });
/// Processes incoming Packets from the Client to the Server
/// Implements the `Client` Packets
/// NEVER TRUST THE CLIENT. HANDLE EVERY ERROR, UNWRAP/EXPECT
impl Client { impl Client {
pub async fn handle_handshake(&self, handshake: SHandShake) {
let version = handshake.protocol_version.0;
self.protocol_version
.store(version, std::sync::atomic::Ordering::Relaxed);
*self.server_address.lock().await = handshake.server_address;
log::debug!("Handshake: next state {:?}", &handshake.next_state);
self.connection_state.store(handshake.next_state);
if self.connection_state.load() != ConnectionState::Status {
let protocol = version;
match protocol.cmp(&NonZeroI32::from(CURRENT_MC_PROTOCOL).get()) {
std::cmp::Ordering::Less => {
self.kick(&format!("Client outdated ({protocol}), Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}")).await;
}
std::cmp::Ordering::Equal => {}
std::cmp::Ordering::Greater => {
self.kick(&format!("Server outdated, Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}")).await;
}
}
}
}
pub async fn handle_status_request(&self, server: &Server) {
log::debug!("Handling status request");
let status = server.get_status();
self.send_packet(&status.lock().await.get_status()).await;
}
pub async fn handle_ping_request(&self, ping_request: SStatusPingRequest) {
log::debug!("Handling ping request");
self.send_packet(&CPingResponse::new(ping_request.payload))
.await;
self.close();
}
fn is_valid_player_name(name: &str) -> bool {
name.len() <= 16 && name.chars().all(|c| c > 32u8 as char && c < 127u8 as char)
}
pub async fn handle_login_start(&self, server: &Server, login_start: SLoginStart) { pub async fn handle_login_start(&self, server: &Server, login_start: SLoginStart) {
log::debug!("login start"); log::debug!("login start");
@ -154,7 +99,7 @@ impl Client {
return; return;
} }
if !Self::is_valid_player_name(&login_start.name) { if !is_valid_player_name(&login_start.name) {
self.kick("Invalid characters in username").await; self.kick("Invalid characters in username").await;
return; return;
} }
@ -164,9 +109,15 @@ impl Client {
let proxy = &ADVANCED_CONFIG.proxy; let proxy = &ADVANCED_CONFIG.proxy;
if proxy.enabled { if proxy.enabled {
if proxy.velocity.enabled { if proxy.velocity.enabled {
velocity_login(self).await; velocity::velocity_login(self).await;
} else if proxy.bungeecord.enabled { } else if proxy.bungeecord.enabled {
match bungeecord::bungeecord_login(self, login_start.name).await { match bungeecord::bungeecord_login(
&self.address,
&self.server_address.lock().await,
login_start.name,
)
.await
{
Ok((_ip, profile)) => { Ok((_ip, profile)) => {
// self.address.lock() = ip; // self.address.lock() = ip;
self.finish_login(&profile).await; self.finish_login(&profile).await;
@ -309,7 +260,10 @@ impl Client {
} }
// validate textures // validate textures
for property in &profile.properties { for property in &profile.properties {
validate_textures(property, &ADVANCED_CONFIG.authentication.textures) authentication::validate_textures(
property,
&ADVANCED_CONFIG.authentication.textures,
)
.map_err(AuthError::TextureError)?; .map_err(AuthError::TextureError)?;
} }
return Ok(profile); return Ok(profile);
@ -317,7 +271,7 @@ impl Client {
Err(AuthError::MissingAuthClient) Err(AuthError::MissingAuthClient)
} }
pub fn handle_login_cookie_response(&self, packet: SLCookieResponse) { pub fn handle_login_cookie_response(&self, packet: SLoginCookieResponse) {
// TODO: allow plugins to access this // TODO: allow plugins to access this
log::debug!( log::debug!(
"Received cookie_response[login]: key: \"{}\", has_payload: \"{}\", payload_length: \"{}\"", "Received cookie_response[login]: key: \"{}\", has_payload: \"{}\", payload_length: \"{}\"",
@ -326,7 +280,6 @@ impl Client {
packet.payload_length.unwrap_or(VarInt::from(0)).0 packet.payload_length.unwrap_or(VarInt::from(0)).0
); );
} }
pub async fn handle_plugin_response(&self, plugin_response: SLoginPluginResponse) { pub async fn handle_plugin_response(&self, plugin_response: SLoginPluginResponse) {
log::debug!("Handling plugin"); log::debug!("Handling plugin");
let velocity_config = &ADVANCED_CONFIG.proxy.velocity; let velocity_config = &ADVANCED_CONFIG.proxy.velocity;
@ -386,82 +339,4 @@ impl Client {
.await; .await;
log::debug!("login acknowledged"); log::debug!("login acknowledged");
} }
pub async fn handle_client_information_config(
&self,
client_information: SClientInformationConfig,
) {
log::debug!("Handling client settings");
if client_information.view_distance <= 0 {
self.kick("Cannot have zero or negative view distance!")
.await;
return;
}
if let (Some(main_hand), Some(chat_mode)) = (
Hand::from_i32(client_information.main_hand.into()),
ChatMode::from_i32(client_information.chat_mode.into()),
) {
*self.config.lock().await = Some(PlayerConfig {
locale: client_information.locale,
view_distance: unsafe {
NonZeroU8::new_unchecked(client_information.view_distance as u8)
},
chat_mode,
chat_colors: client_information.chat_colors,
skin_parts: client_information.skin_parts,
main_hand,
text_filtering: client_information.text_filtering,
server_listing: client_information.server_listing,
});
} else {
self.kick("Invalid hand or chat type").await;
}
}
pub async fn handle_plugin_message(&self, plugin_message: SPluginMessage) {
log::debug!("Handling plugin message");
if plugin_message
.channel
.to_string()
.starts_with("minecraft:brand")
{
log::debug!("got a client brand");
match str::from_utf8(&plugin_message.data) {
Ok(brand) => *self.brand.lock().await = Some(brand.to_string()),
Err(e) => self.kick(&e.to_string()).await,
}
}
}
pub fn handle_config_cookie_response(&self, packet: SCCookieResponse) {
// TODO: allow plugins to access this
log::debug!(
"Received cookie_response[config]: key: \"{}\", has_payload: \"{}\", payload_length: \"{}\"",
packet.key.to_string(),
packet.has_payload,
packet.payload_length.unwrap_or(VarInt::from(0)).0
);
}
pub async fn handle_known_packs(&self, server: &Server, _config_acknowledged: SKnownPacks) {
log::debug!("Handling known packs");
for registry in &server.cached_registry {
self.send_packet(&CRegistryData::new(
&registry.registry_id,
&registry.registry_entries,
))
.await;
}
// We are done with configuring
log::debug!("finished config");
self.send_packet(&CFinishConfig::new()).await;
}
pub fn handle_config_acknowledged(&self) {
log::debug!("Handling config acknowledge");
self.connection_state.store(ConnectionState::Play);
self.make_player
.store(true, std::sync::atomic::Ordering::Relaxed);
}
} }

View File

@ -0,0 +1,9 @@
mod config;
mod handshake;
mod login;
mod play;
mod status;
fn is_valid_player_name(name: &str) -> bool {
name.len() <= 16 && name.chars().all(|c| c > 32u8 as char && c < 127u8 as char)
}

View File

@ -1,8 +1,8 @@
use std::num::NonZeroU8; use std::num::NonZeroU8;
use std::sync::Arc; use std::sync::Arc;
use super::PlayerConfig;
use crate::block::block_manager::BlockActionResult; use crate::block::block_manager::BlockActionResult;
use crate::net::PlayerConfig;
use crate::{ use crate::{
command::CommandSender, command::CommandSender,
entity::player::{ChatMode, Hand, Player}, entity::player::{ChatMode, Hand, Player},

View File

@ -0,0 +1,18 @@
use pumpkin_protocol::{client::status::CPingResponse, server::status::SStatusPingRequest};
use crate::{net::Client, server::Server};
impl Client {
pub async fn handle_status_request(&self, server: &Server) {
log::debug!("Handling status request");
let status = server.get_status();
self.send_packet(&status.lock().await.get_status()).await;
}
pub async fn handle_ping_request(&self, ping_request: SStatusPingRequest) {
log::debug!("Handling ping request");
self.send_packet(&CPingResponse::new(ping_request.payload))
.await;
self.close();
}
}

View File

@ -1,12 +1,10 @@
use std::net::IpAddr; use std::{net::IpAddr, net::SocketAddr};
use pumpkin_protocol::Property; use pumpkin_protocol::Property;
use thiserror::Error; use thiserror::Error;
use tokio::sync::Mutex;
use crate::{ use crate::net::{offline_uuid, GameProfile};
client::authentication::{offline_uuid, GameProfile},
Client,
};
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum BungeeCordError { pub enum BungeeCordError {
@ -20,11 +18,23 @@ pub enum BungeeCordError {
FailedMakeOfflineUUID, FailedMakeOfflineUUID,
} }
/// Attempts to login a player via `BungeeCord`.
///
/// This function should be called when receiving the `SLoginStart` packet.
/// It utilizes the `server_address` received in the `SHandShake` packet,
/// which may contain optional data about the client:
///
/// 1. IP address (if `ip_forward` is enabled on the `BungeeCord` server)
/// 2. UUID (if `ip_forward` is enabled on the `BungeeCord` server)
/// 3. Game profile properties (if `ip_forward` and `online_mode` are enabled on the `BungeeCord` server)
///
/// If any of the optional data is missing, the function will attempt to
/// determine the player's information locally.
pub async fn bungeecord_login( pub async fn bungeecord_login(
client: &Client, client_address: &Mutex<SocketAddr>,
username: String, server_address: &str,
name: String,
) -> Result<(IpAddr, GameProfile), BungeeCordError> { ) -> Result<(IpAddr, GameProfile), BungeeCordError> {
let server_address = client.server_address.lock().await;
let data = server_address.split('\0').take(4).collect::<Vec<_>>(); let data = server_address.split('\0').take(4).collect::<Vec<_>>();
// Ip of player, only given if ip_forward on bungee is true // Ip of player, only given if ip_forward on bungee is true
@ -32,15 +42,13 @@ pub async fn bungeecord_login(
Some(ip) => ip Some(ip) => ip
.parse() .parse()
.map_err(|_| BungeeCordError::FailedParseAddress)?, .map_err(|_| BungeeCordError::FailedParseAddress)?,
None => client.address.lock().await.ip(), None => client_address.lock().await.ip(),
}; };
// Uuid of player, only given if ip_forward on bungee is true // Uuid of player, only given if ip_forward on bungee is true
let id = match data.get(2) { let id = match data.get(2) {
Some(uuid) => uuid.parse().map_err(|_| BungeeCordError::FailedParseUUID)?, Some(uuid) => uuid.parse().map_err(|_| BungeeCordError::FailedParseUUID)?,
None => { None => offline_uuid(name.as_str()).map_err(|_| BungeeCordError::FailedMakeOfflineUUID)?,
offline_uuid(username.as_str()).map_err(|_| BungeeCordError::FailedMakeOfflineUUID)?
}
}; };
// Read properties and get textures // Read properties and get textures
@ -57,7 +65,7 @@ pub async fn bungeecord_login(
ip, ip,
GameProfile { GameProfile {
id, id,
name: username, name,
properties, properties,
profile_actions: None, profile_actions: None,
}, },

View File

@ -14,7 +14,7 @@ use rand::Rng;
use sha2::Sha256; use sha2::Sha256;
use thiserror::Error; use thiserror::Error;
use crate::client::{authentication::GameProfile, Client}; use crate::net::{Client, GameProfile};
type HmacSha256 = Hmac<Sha256>; type HmacSha256 = Hmac<Sha256>;

View File

@ -13,7 +13,6 @@ pub struct RCONServer;
impl RCONServer { impl RCONServer {
pub async fn new(config: &RCONConfig, server: Arc<Server>) -> Result<Self, std::io::Error> { pub async fn new(config: &RCONConfig, server: Arc<Server>) -> Result<Self, std::io::Error> {
assert!(config.enabled, "RCON is not enabled");
let listener = tokio::net::TcpListener::bind(config.address).await.unwrap(); let listener = tokio::net::TcpListener::bind(config.address).await.unwrap();
let password = Arc::new(config.password.clone()); let password = Arc::new(config.password.clone());

View File

@ -26,6 +26,7 @@ fn load_icon_from_file<P: AsRef<Path>>(path: P) -> Result<String, Box<dyn error:
} }
fn load_icon_from_bytes(png_data: &[u8]) -> Result<String, Box<dyn error::Error>> { fn load_icon_from_bytes(png_data: &[u8]) -> Result<String, Box<dyn error::Error>> {
assert!(!png_data.is_empty(), "PNG data is empty");
let icon = png::Decoder::new(Cursor::new(&png_data)); let icon = png::Decoder::new(Cursor::new(&png_data));
let reader = icon.read_info()?; let reader = icon.read_info()?;
let info = reader.info(); let info = reader.info();
@ -70,6 +71,7 @@ impl CachedBranding {
} }
impl CachedStatus { impl CachedStatus {
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
let status_response = Self::build_response(&BASIC_CONFIG); let status_response = Self::build_response(&BASIC_CONFIG);
let status_response_json = serde_json::to_string(&status_response) let status_response_json = serde_json::to_string(&status_response)

View File

@ -5,7 +5,7 @@ use rsa::{traits::PublicKeyParts as _, Pkcs1v15Encrypt, RsaPrivateKey};
use sha1::Sha1; use sha1::Sha1;
use sha2::Digest; use sha2::Digest;
use crate::client::EncryptionError; use crate::net::EncryptionError;
pub struct KeyStore { pub struct KeyStore {
pub private_key: RsaPrivateKey, pub private_key: RsaPrivateKey,
@ -13,6 +13,7 @@ pub struct KeyStore {
} }
impl KeyStore { impl KeyStore {
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
log::debug!("Creating encryption keys..."); log::debug!("Creating encryption keys...");
let private_key = Self::generate_private_key(); let private_key = Self::generate_private_key();

View File

@ -23,12 +23,12 @@ use tokio::sync::{Mutex, RwLock};
use crate::block::block_manager::BlockManager; use crate::block::block_manager::BlockManager;
use crate::block::default_block_manager; use crate::block::default_block_manager;
use crate::client::EncryptionError; use crate::net::EncryptionError;
use crate::world::custom_bossbar::CustomBossbars; use crate::world::custom_bossbar::CustomBossbars;
use crate::{ use crate::{
client::Client,
command::{default_dispatcher, dispatcher::CommandDispatcher}, command::{default_dispatcher, dispatcher::CommandDispatcher},
entity::player::Player, entity::player::Player,
net::Client,
world::World, world::World,
}; };

View File

@ -3,7 +3,7 @@ use pumpkin_protocol::client::play::{
CSetBorderWarningDelay, CSetBorderWarningDistance, CSetBorderWarningDelay, CSetBorderWarningDistance,
}; };
use crate::client::Client; use crate::net::Client;
use super::World; use super::World;