Some refactors
This commit is contained in:
parent
fd667901cd
commit
3dae300f81
@ -3,6 +3,13 @@ name = "pumpkin-protocol"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["packets", "query"]
|
||||
packets = ["serverbound", "clientbound"]
|
||||
serverbound = []
|
||||
clientbound = []
|
||||
query = []
|
||||
|
||||
[dependencies]
|
||||
pumpkin-nbt = { path = "../pumpkin-nbt" }
|
||||
pumpkin-config = { path = "../pumpkin-config" }
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::slot::Slot;
|
||||
use crate::codec::slot::Slot;
|
||||
use crate::VarInt;
|
||||
|
||||
use pumpkin_macros::client_packet;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::slot::Slot;
|
||||
use crate::codec::slot::Slot;
|
||||
use crate::VarInt;
|
||||
|
||||
use pumpkin_macros::client_packet;
|
||||
|
||||
@ -5,6 +5,7 @@ use thiserror::Error;
|
||||
|
||||
pub mod bit_set;
|
||||
pub mod identifier;
|
||||
pub mod slot;
|
||||
pub mod var_int;
|
||||
pub mod var_long;
|
||||
|
||||
|
||||
@ -7,13 +7,15 @@ use pumpkin_core::text::{style::Style, TextComponent};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod bytebuf;
|
||||
#[cfg(feature = "clientbound")]
|
||||
pub mod client;
|
||||
pub mod codec;
|
||||
pub mod packet_decoder;
|
||||
pub mod packet_encoder;
|
||||
#[cfg(feature = "query")]
|
||||
pub mod query;
|
||||
#[cfg(feature = "serverbound")]
|
||||
pub mod server;
|
||||
pub mod slot;
|
||||
|
||||
/// To current Minecraft protocol
|
||||
/// Don't forget to change this when porting
|
||||
|
||||
@ -16,8 +16,8 @@ type Cipher = cfb8::Decryptor<aes::Aes128>;
|
||||
pub struct PacketDecoder {
|
||||
buf: BytesMut,
|
||||
decompress_buf: BytesMut,
|
||||
compression: bool,
|
||||
cipher: Option<Cipher>,
|
||||
compression: bool,
|
||||
decompressor: Decompressor,
|
||||
}
|
||||
|
||||
@ -28,8 +28,8 @@ impl Default for PacketDecoder {
|
||||
Self {
|
||||
buf: BytesMut::new(),
|
||||
decompress_buf: BytesMut::new(),
|
||||
compression: false,
|
||||
cipher: None,
|
||||
compression: false,
|
||||
decompressor: Decompressor::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,8 +15,8 @@ type Cipher = cfb8::Encryptor<aes::Aes128>;
|
||||
pub struct PacketEncoder {
|
||||
buf: BytesMut,
|
||||
compress_buf: Vec<u8>,
|
||||
compression_threshold: Option<u32>,
|
||||
cipher: Option<Cipher>,
|
||||
compression_threshold: Option<u32>,
|
||||
compressor: Compressor, // Reuse the compressor for all packets
|
||||
}
|
||||
|
||||
@ -27,8 +27,8 @@ impl Default for PacketEncoder {
|
||||
Self {
|
||||
buf: BytesMut::with_capacity(1024),
|
||||
compress_buf: Vec::with_capacity(1024),
|
||||
compression_threshold: None,
|
||||
cipher: None,
|
||||
compression_threshold: None,
|
||||
compressor: Compressor::new(CompressionLvl::fastest()), // init compressor with fastest compression level
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ use crate::{
|
||||
#[server_packet("config:cookie_response")]
|
||||
/// Response to a Cookie Request (configuration) from the server.
|
||||
/// The Notchian (vanilla) server only accepts responses of up to 5 kiB in size.
|
||||
pub struct SCookieResponse {
|
||||
pub struct SConfigCookieResponse {
|
||||
pub key: Identifier,
|
||||
pub has_payload: bool,
|
||||
pub payload_length: Option<VarInt>,
|
||||
@ -20,7 +20,7 @@ pub struct SCookieResponse {
|
||||
|
||||
const MAX_PAYLOAD_SIZE: i32 = 5120;
|
||||
|
||||
impl ServerPacket for SCookieResponse {
|
||||
impl ServerPacket for SConfigCookieResponse {
|
||||
fn read(bytebuf: &mut impl Buf) -> Result<Self, ReadingError> {
|
||||
let key = bytebuf.try_get_identifer()?;
|
||||
let has_payload = bytebuf.try_get_bool()?;
|
||||
|
||||
@ -10,7 +10,7 @@ use serde::de;
|
||||
#[server_packet("login:cookie_response")]
|
||||
/// Response to a Cookie Request (login) from the server.
|
||||
/// The Notchian server only accepts responses of up to 5 kiB in size.
|
||||
pub struct SCookieResponse {
|
||||
pub struct SLoginCookieResponse {
|
||||
pub key: Identifier,
|
||||
pub has_payload: bool,
|
||||
pub payload_length: Option<VarInt>,
|
||||
@ -19,7 +19,7 @@ pub struct SCookieResponse {
|
||||
|
||||
const MAX_PAYLOAD_SIZE: i32 = 5120;
|
||||
|
||||
impl ServerPacket for SCookieResponse {
|
||||
impl ServerPacket for SLoginCookieResponse {
|
||||
fn read(bytebuf: &mut impl Buf) -> Result<Self, ReadingError> {
|
||||
let key = bytebuf.try_get_identifer()?;
|
||||
let has_payload = bytebuf.try_get_bool()?;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::slot::Slot;
|
||||
use crate::codec::slot::Slot;
|
||||
use crate::VarInt;
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use pumpkin_macros::server_packet;
|
||||
|
||||
use crate::slot::Slot;
|
||||
use crate::codec::slot::Slot;
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[server_packet("play:set_creative_mode_slot")]
|
||||
|
||||
@ -661,7 +661,7 @@ mod test {
|
||||
|
||||
use crate::{
|
||||
block::BlockState,
|
||||
world_gen::{
|
||||
generation::{
|
||||
chunk_noise::{
|
||||
BlockStateSampler, ChunkNoiseDensityFunctions, ChunkNoiseGenerator,
|
||||
ChunkNoiseState, LAVA_BLOCK, WATER_BLOCK,
|
||||
@ -6,11 +6,11 @@ use pumpkin_macros::block_state;
|
||||
|
||||
use crate::{
|
||||
block::BlockState,
|
||||
match_ref_implementations,
|
||||
world_gen::{
|
||||
generation::{
|
||||
noise::{density::basic::WrapperType, lerp3},
|
||||
section_coords,
|
||||
},
|
||||
match_ref_implementations,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -1379,7 +1379,7 @@ impl ChunkNoiseGenerator {
|
||||
mod test {
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
|
||||
use crate::world_gen::{
|
||||
use crate::generation::{
|
||||
aquifer_sampler::{FluidLevel, FluidLevelSampler},
|
||||
generation_shapes::GenerationShape,
|
||||
noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER},
|
||||
@ -6,7 +6,7 @@ use crate::biome::Biome;
|
||||
use crate::block::block_state::BlockState;
|
||||
use crate::chunk::{ChunkBlocks, ChunkData};
|
||||
use crate::coordinates::{BlockCoordinates, ChunkRelativeBlockCoordinates, XZBlockCoordinates};
|
||||
use crate::world_gen::Seed;
|
||||
use crate::generation::Seed;
|
||||
|
||||
pub trait GeneratorInit {
|
||||
fn new(seed: Seed) -> Self;
|
||||
@ -7,7 +7,7 @@ use crate::{
|
||||
biome::Biome,
|
||||
chunk::ChunkBlocks,
|
||||
coordinates::{BlockCoordinates, ChunkRelativeBlockCoordinates, XZBlockCoordinates},
|
||||
world_gen::{
|
||||
generation::{
|
||||
generator::{BiomeGenerator, GeneratorInit, PerlinTerrainGenerator},
|
||||
generic_generator::GenericGenerator,
|
||||
Seed,
|
||||
@ -4,7 +4,7 @@ use crate::{
|
||||
biome::Biome,
|
||||
block::block_state::BlockState,
|
||||
coordinates::XZBlockCoordinates,
|
||||
world_gen::{
|
||||
generation::{
|
||||
generator::{BiomeGenerator, GeneratorInit, TerrainGenerator},
|
||||
generic_generator::GenericGenerator,
|
||||
Seed,
|
||||
@ -14,7 +14,7 @@ use crate::{
|
||||
coordinates::{
|
||||
ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates, XZBlockCoordinates,
|
||||
},
|
||||
world_gen::{
|
||||
generation::{
|
||||
generator::{BiomeGenerator, GeneratorInit, TerrainGenerator},
|
||||
proto_chunk::ProtoChunk,
|
||||
Seed, WorldGenerator,
|
||||
@ -151,8 +151,7 @@ mod test {
|
||||
};
|
||||
|
||||
use crate::{
|
||||
read_data_from_file,
|
||||
world_gen::noise::{
|
||||
generation::noise::{
|
||||
config::NoiseConfig,
|
||||
density::{
|
||||
built_in_density_function::{
|
||||
@ -165,6 +164,7 @@ mod test {
|
||||
},
|
||||
router::OVERWORLD_NOISE_ROUTER,
|
||||
},
|
||||
read_data_from_file,
|
||||
};
|
||||
|
||||
use super::LegacyChunkNoiseVisitor;
|
||||
@ -1,6 +1,6 @@
|
||||
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::{
|
||||
component_functions::{
|
||||
@ -419,10 +419,10 @@ mod test {
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use crate::{
|
||||
read_data_from_file,
|
||||
world_gen::noise::density::{
|
||||
generation::noise::density::{
|
||||
component_functions::ImmutableComponentFunctionImpl, NoisePos, UnblendedNoisePos,
|
||||
},
|
||||
read_data_from_file,
|
||||
};
|
||||
|
||||
use super::YClampedFunction;
|
||||
@ -1,6 +1,6 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::world_gen::blender::BlenderImpl;
|
||||
use crate::generation::blender::BlenderImpl;
|
||||
|
||||
use super::{
|
||||
component_functions::{
|
||||
@ -953,7 +953,7 @@ mod test {
|
||||
|
||||
use pumpkin_core::random::{legacy_rand::LegacyRand, RandomDeriver, RandomImpl};
|
||||
|
||||
use crate::world_gen::noise::{
|
||||
use crate::generation::noise::{
|
||||
built_in_noise_params,
|
||||
density::{
|
||||
noise::{InternalNoise, NoiseFunction},
|
||||
@ -1,6 +1,6 @@
|
||||
use pumpkin_core::random::{legacy_rand::LegacyRand, RandomImpl};
|
||||
|
||||
use crate::world_gen::noise::simplex::SimplexNoiseSampler;
|
||||
use crate::generation::noise::simplex::SimplexNoiseSampler;
|
||||
|
||||
use super::{
|
||||
component_functions::{
|
||||
@ -10,7 +10,7 @@ use component_functions::{
|
||||
use enum_dispatch::enum_dispatch;
|
||||
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;
|
||||
|
||||
@ -76,8 +76,8 @@ pub trait NoisePosImpl {
|
||||
pub mod built_in_density_function {
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
use crate::world_gen::noise::built_in_noise_params::{self};
|
||||
use crate::world_gen::positions::{MAX_COLUMN_HEIGHT, MIN_HEIGHT};
|
||||
use crate::generation::noise::built_in_noise_params::{self};
|
||||
use crate::generation::positions::{MAX_COLUMN_HEIGHT, MIN_HEIGHT};
|
||||
|
||||
use pumpkin_core::math::floor_div;
|
||||
|
||||
@ -879,7 +879,7 @@ mod test {
|
||||
legacy_rand::LegacyRand, RandomDeriver, RandomDeriverImpl, RandomImpl,
|
||||
};
|
||||
|
||||
use crate::world_gen::noise::{
|
||||
use crate::generation::noise::{
|
||||
built_in_noise_params,
|
||||
density::{built_in_density_function::*, NoisePos, UnblendedNoisePos},
|
||||
perlin::DoublePerlinNoiseSampler,
|
||||
@ -3,11 +3,11 @@ use std::{marker::PhantomData, sync::Arc};
|
||||
use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomGenerator, RandomImpl};
|
||||
|
||||
use crate::{
|
||||
match_ref_implementations,
|
||||
world_gen::noise::{
|
||||
generation::noise::{
|
||||
clamped_lerp,
|
||||
perlin::{DoublePerlinNoiseParameters, DoublePerlinNoiseSampler, OctavePerlinNoiseSampler},
|
||||
},
|
||||
match_ref_implementations,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -2,7 +2,7 @@ use std::{marker::PhantomData, sync::Arc};
|
||||
|
||||
use enum_dispatch::enum_dispatch;
|
||||
|
||||
use crate::world_gen::noise::lerp;
|
||||
use crate::generation::noise::lerp;
|
||||
|
||||
use super::{
|
||||
component_functions::{
|
||||
@ -744,7 +744,7 @@ mod test {
|
||||
|
||||
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,
|
||||
component_functions::{ComponentReference, NoEnvironment, SharedComponentReference},
|
||||
test::{FakeEnvironment, OwnedConverter, TestConverter},
|
||||
@ -1,7 +1,7 @@
|
||||
// From da java
|
||||
|
||||
use crate::world_gen::noise::density::peaks_valleys_noise;
|
||||
use crate::world_gen::noise::lerp;
|
||||
use crate::generation::noise::density::peaks_valleys_noise;
|
||||
use crate::generation::noise::lerp;
|
||||
|
||||
use super::component_functions::SharedComponentReference;
|
||||
use super::spline::{FloatAmplifier, ImmutableSpline, ImmutableSplineRef, SplineBuilder};
|
||||
@ -523,7 +523,7 @@ pub fn create_jaggedness_spline(
|
||||
mod test {
|
||||
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, EROSION_OVERWORLD, RIDGES_FOLDED_OVERWORLD, RIDGES_OVERWORLD,
|
||||
},
|
||||
@ -381,7 +381,7 @@ mod double_perlin_noise_sampler_test {
|
||||
legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, RandomGenerator, RandomImpl,
|
||||
};
|
||||
|
||||
use crate::world_gen::noise::perlin::{DoublePerlinNoiseParameters, DoublePerlinNoiseSampler};
|
||||
use crate::generation::noise::perlin::{DoublePerlinNoiseParameters, DoublePerlinNoiseSampler};
|
||||
|
||||
#[test]
|
||||
fn sample_legacy() {
|
||||
@ -760,7 +760,7 @@ mod perlin_noise_sampler_test {
|
||||
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;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
use crate::world_gen::{
|
||||
use crate::generation::{
|
||||
noise::density::{
|
||||
apply_blend_density,
|
||||
basic::RangeFunction,
|
||||
@ -476,8 +476,7 @@ mod test {
|
||||
};
|
||||
|
||||
use crate::{
|
||||
read_data_from_file,
|
||||
world_gen::noise::{
|
||||
generation::noise::{
|
||||
config::LegacyChunkNoiseVisitor,
|
||||
density::{
|
||||
built_in_density_function::{EROSION_OVERWORLD, SLOPED_CHEESE_OVERWORLD},
|
||||
@ -491,6 +490,7 @@ mod test {
|
||||
perlin::DoublePerlinNoiseSampler,
|
||||
router::OVERWORLD_NOISE_ROUTER,
|
||||
},
|
||||
read_data_from_file,
|
||||
};
|
||||
|
||||
use super::{apply_surface_slides, create_caves};
|
||||
@ -276,7 +276,7 @@ impl OctaveSimplexNoiseSampler {
|
||||
mod octave_simplex_noise_sampler_test {
|
||||
use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomImpl};
|
||||
|
||||
use crate::world_gen::noise::simplex::OctaveSimplexNoiseSampler;
|
||||
use crate::generation::noise::simplex::OctaveSimplexNoiseSampler;
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
@ -413,7 +413,7 @@ mod simplex_noise_sampler_test {
|
||||
|
||||
use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomImpl};
|
||||
|
||||
use crate::world_gen::noise::simplex::SimplexNoiseSampler;
|
||||
use crate::generation::noise::simplex::SimplexNoiseSampler;
|
||||
|
||||
#[test]
|
||||
fn test_create() {
|
||||
@ -2,7 +2,7 @@ use pumpkin_core::math::{vector2::Vector2, vector3::Vector3};
|
||||
|
||||
use crate::{
|
||||
block::BlockState,
|
||||
world_gen::{
|
||||
generation::{
|
||||
chunk_noise::CHUNK_DIM,
|
||||
generation_shapes::GenerationShape,
|
||||
noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER},
|
||||
@ -14,7 +14,7 @@ use crate::{
|
||||
chunk::{
|
||||
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.
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
use world_gen::{
|
||||
use generation::{
|
||||
aquifer_sampler::{FluidLevel, FluidLevelSampler},
|
||||
chunk_noise::{ChunkNoiseGenerator, LAVA_BLOCK, WATER_BLOCK},
|
||||
generation_shapes::GenerationShape,
|
||||
noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER},
|
||||
proto_chunk::{ProtoChunk, StandardChunkFluidLevelSampler},
|
||||
};
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
|
||||
pub mod biome;
|
||||
pub mod block;
|
||||
@ -13,9 +13,9 @@ pub mod chunk;
|
||||
pub mod coordinates;
|
||||
pub mod cylindrical_chunk_iterator;
|
||||
pub mod dimension;
|
||||
mod generation;
|
||||
pub mod item;
|
||||
pub mod level;
|
||||
mod world_gen;
|
||||
|
||||
pub const WORLD_HEIGHT: usize = 384;
|
||||
pub const WORLD_LOWEST_Y: i16 = -64;
|
||||
|
||||
@ -57,10 +57,9 @@ use pumpkin_world::{
|
||||
use tokio::sync::{Mutex, Notify};
|
||||
|
||||
use super::Entity;
|
||||
use crate::error::PumpkinError;
|
||||
use crate::{error::PumpkinError, net::GameProfile};
|
||||
use crate::{
|
||||
client::{
|
||||
authentication::GameProfile,
|
||||
net::{
|
||||
combat::{self, player_attack_sound, AttackType},
|
||||
Client, PlayerConfig,
|
||||
},
|
||||
|
||||
@ -35,7 +35,7 @@ compile_error!("Compiling for WASI targets is not supported!");
|
||||
|
||||
use log::LevelFilter;
|
||||
|
||||
use client::Client;
|
||||
use net::{lan_broadcast, query, rcon::RCONServer, Client};
|
||||
use server::{ticker::Ticker, Server};
|
||||
use std::io::{self};
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
@ -50,19 +50,14 @@ use crate::server::CURRENT_MC_VERSION;
|
||||
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||
use pumpkin_core::text::{color::NamedColor, TextComponent};
|
||||
use pumpkin_protocol::CURRENT_MC_PROTOCOL;
|
||||
use rcon::RCONServer;
|
||||
use std::time::Instant;
|
||||
// Setup some tokens to allow us to identify which event is for which socket.
|
||||
|
||||
pub mod block;
|
||||
pub mod client;
|
||||
pub mod command;
|
||||
pub mod entity;
|
||||
pub mod error;
|
||||
pub mod lan_broadcast;
|
||||
pub mod proxy;
|
||||
pub mod query;
|
||||
pub mod rcon;
|
||||
pub mod net;
|
||||
pub mod server;
|
||||
pub mod world;
|
||||
|
||||
|
||||
@ -2,15 +2,14 @@ use std::{collections::HashMap, net::IpAddr};
|
||||
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use pumpkin_config::{auth::TextureConfig, ADVANCED_CONFIG};
|
||||
use pumpkin_core::ProfileAction;
|
||||
use pumpkin_protocol::Property;
|
||||
use reqwest::{StatusCode, Url};
|
||||
use serde::Deserialize;
|
||||
use sha1::Digest;
|
||||
use sha2::Sha256;
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::GameProfile;
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[expect(dead_code)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -29,14 +28,8 @@ pub struct Texture {
|
||||
metadata: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
#[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>>,
|
||||
}
|
||||
const MOJANG_AUTHENTICATION_URL: &str = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}";
|
||||
const MOJANG_PREVENT_PROXY_AUTHENTICATION_URL: &str = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}";
|
||||
|
||||
/// 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
|
||||
/// 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(
|
||||
username: &str,
|
||||
server_hash: &str,
|
||||
ip: &IpAddr,
|
||||
auth_client: &reqwest::Client,
|
||||
) -> Result<GameProfile, AuthError> {
|
||||
assert!(ADVANCED_CONFIG.authentication.enabled);
|
||||
let address = if ADVANCED_CONFIG.authentication.prevent_proxy_connections {
|
||||
let auth_url = ADVANCED_CONFIG
|
||||
.authentication
|
||||
.prevent_proxy_connection_auth_url
|
||||
.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
|
||||
.replace("{username}", username)
|
||||
@ -74,7 +66,7 @@ pub async fn authenticate(
|
||||
.authentication
|
||||
.auth_url
|
||||
.as_deref()
|
||||
.unwrap_or("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}");
|
||||
.unwrap_or(MOJANG_AUTHENTICATION_URL);
|
||||
|
||||
auth_url
|
||||
.replace("{username}", username)
|
||||
@ -129,10 +121,6 @@ pub fn is_texture_url_valid(url: &Url, config: &TextureConfig) -> Result<(), Tex
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn offline_uuid(username: &str) -> Result<Uuid, uuid::Error> {
|
||||
Uuid::from_slice(&Sha256::digest(username)[..16])
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AuthError {
|
||||
#[error("Missing auth client")]
|
||||
@ -12,9 +12,9 @@ use pumpkin_inventory::{Container, WindowType};
|
||||
use pumpkin_protocol::client::play::{
|
||||
CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot,
|
||||
};
|
||||
use pumpkin_protocol::codec::slot::Slot;
|
||||
use pumpkin_protocol::codec::var_int::VarInt;
|
||||
use pumpkin_protocol::server::play::SClickContainer;
|
||||
use pumpkin_protocol::slot::Slot;
|
||||
use pumpkin_world::item::item_registry::Item;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use std::sync::Arc;
|
||||
@ -13,34 +13,57 @@ use crate::{
|
||||
server::Server,
|
||||
};
|
||||
|
||||
use authentication::GameProfile;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use pumpkin_config::compression::CompressionInfo;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
use pumpkin_core::{text::TextComponent, ProfileAction};
|
||||
use pumpkin_protocol::{
|
||||
bytebuf::{packet_id::Packet, ReadingError},
|
||||
client::{config::CConfigDisconnect, login::CLoginDisconnect, play::CPlayDisconnect},
|
||||
packet_decoder::PacketDecoder,
|
||||
packet_encoder::{PacketEncodeError, PacketEncoder},
|
||||
server::{
|
||||
config::{SAcknowledgeFinishConfig, SClientInformationConfig, SKnownPacks, SPluginMessage},
|
||||
config::{
|
||||
SAcknowledgeFinishConfig, SClientInformationConfig, SConfigCookieResponse, SKnownPacks,
|
||||
SPluginMessage,
|
||||
},
|
||||
handshake::SHandShake,
|
||||
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
||||
login::{
|
||||
SEncryptionResponse, SLoginAcknowledged, SLoginCookieResponse, SLoginPluginResponse,
|
||||
SLoginStart,
|
||||
},
|
||||
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::sync::Mutex;
|
||||
|
||||
use pumpkin_protocol::server::config::SCookieResponse as SCCookieResponse;
|
||||
use pumpkin_protocol::server::login::SCookieResponse as SLCookieResponse;
|
||||
use thiserror::Error;
|
||||
pub mod authentication;
|
||||
mod client_packet;
|
||||
use uuid::Uuid;
|
||||
mod authentication;
|
||||
pub mod combat;
|
||||
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.
|
||||
///
|
||||
@ -102,8 +125,6 @@ pub struct Client {
|
||||
pub server_address: Mutex<String>,
|
||||
/// The current connection state of the client (e.g., Handshaking, Status, Play).
|
||||
pub connection_state: AtomicCell<ConnectionState>,
|
||||
/// Whether encryption is enabled for the connection.
|
||||
pub encryption: AtomicBool,
|
||||
/// Indicates if the client connection is closed.
|
||||
pub closed: AtomicBool,
|
||||
/// The underlying TCP connection to the client.
|
||||
@ -138,7 +159,6 @@ impl Client {
|
||||
connection_writer: Arc::new(Mutex::new(connection_writer)),
|
||||
enc: Arc::new(Mutex::new(PacketEncoder::default())),
|
||||
dec: Arc::new(Mutex::new(PacketDecoder::default())),
|
||||
encryption: AtomicBool::new(false),
|
||||
closed: AtomicBool::new(false),
|
||||
client_packets_queue: Arc::new(Mutex::new(VecDeque::new())),
|
||||
make_player: AtomicBool::new(false),
|
||||
@ -182,8 +202,6 @@ impl Client {
|
||||
shared_secret: Option<&[u8]>, // decrypted
|
||||
) -> Result<(), EncryptionError> {
|
||||
if let Some(shared_secret) = shared_secret {
|
||||
self.encryption
|
||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
let crypt_key: [u8; 16] = shared_secret
|
||||
.try_into()
|
||||
.map_err(|_| EncryptionError::SharedWrongLength)?;
|
||||
@ -433,8 +451,8 @@ impl Client {
|
||||
SLoginAcknowledged::PACKET_ID => {
|
||||
self.handle_login_acknowledged(server).await;
|
||||
}
|
||||
SLCookieResponse::PACKET_ID => {
|
||||
self.handle_login_cookie_response(SLCookieResponse::read(bytebuf)?);
|
||||
SLoginCookieResponse::PACKET_ID => {
|
||||
self.handle_login_cookie_response(SLoginCookieResponse::read(bytebuf)?);
|
||||
}
|
||||
_ => {
|
||||
log::error!(
|
||||
@ -469,8 +487,8 @@ impl Client {
|
||||
self.handle_known_packs(server, SKnownPacks::read(bytebuf)?)
|
||||
.await;
|
||||
}
|
||||
SCCookieResponse::PACKET_ID => {
|
||||
self.handle_config_cookie_response(SCCookieResponse::read(bytebuf)?);
|
||||
SConfigCookieResponse::PACKET_ID => {
|
||||
self.handle_config_cookie_response(SConfigCookieResponse::read(bytebuf)?);
|
||||
}
|
||||
_ => {
|
||||
log::error!(
|
||||
98
pumpkin/src/net/packet/config.rs
Normal file
98
pumpkin/src/net/packet/config.rs
Normal 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(
|
||||
®istry.registry_id,
|
||||
®istry.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);
|
||||
}
|
||||
}
|
||||
29
pumpkin/src/net/packet/handshake.rs
Normal file
29
pumpkin/src/net/packet/handshake.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,43 +1,30 @@
|
||||
use super::{authentication::AuthError, Client, PlayerConfig};
|
||||
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 std::sync::LazyLock;
|
||||
|
||||
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||
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::{
|
||||
client::{
|
||||
config::{CConfigAddResourcePack, CFinishConfig, CKnownPacks, CRegistryData},
|
||||
config::{CConfigAddResourcePack, CKnownPacks},
|
||||
login::{CLoginSuccess, CSetCompression},
|
||||
status::CPingResponse,
|
||||
play::{CServerLinks, Label, Link, LinkType},
|
||||
},
|
||||
server::{
|
||||
config::{SClientInformationConfig, SKnownPacks, SPluginMessage},
|
||||
handshake::SHandShake,
|
||||
login::{SEncryptionResponse, SLoginPluginResponse, SLoginStart},
|
||||
status::SStatusPingRequest,
|
||||
},
|
||||
ConnectionState, KnownPack, CURRENT_MC_PROTOCOL,
|
||||
};
|
||||
use std::{
|
||||
num::{NonZeroI32, NonZeroU8},
|
||||
sync::LazyLock,
|
||||
codec::var_int::VarInt,
|
||||
server::login::{SEncryptionResponse, SLoginCookieResponse, SLoginPluginResponse, SLoginStart},
|
||||
ConnectionState, KnownPack,
|
||||
};
|
||||
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(|| {
|
||||
let mut links: Vec<Link> = Vec::new();
|
||||
|
||||
@ -98,49 +85,7 @@ static LINKS: LazyLock<Vec<Link>> = LazyLock::new(|| {
|
||||
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 {
|
||||
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) {
|
||||
log::debug!("login start");
|
||||
|
||||
@ -154,7 +99,7 @@ impl Client {
|
||||
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;
|
||||
return;
|
||||
}
|
||||
@ -164,9 +109,15 @@ impl Client {
|
||||
let proxy = &ADVANCED_CONFIG.proxy;
|
||||
if proxy.enabled {
|
||||
if proxy.velocity.enabled {
|
||||
velocity_login(self).await;
|
||||
velocity::velocity_login(self).await;
|
||||
} 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)) => {
|
||||
// self.address.lock() = ip;
|
||||
self.finish_login(&profile).await;
|
||||
@ -309,7 +260,10 @@ impl Client {
|
||||
}
|
||||
// validate textures
|
||||
for property in &profile.properties {
|
||||
validate_textures(property, &ADVANCED_CONFIG.authentication.textures)
|
||||
authentication::validate_textures(
|
||||
property,
|
||||
&ADVANCED_CONFIG.authentication.textures,
|
||||
)
|
||||
.map_err(AuthError::TextureError)?;
|
||||
}
|
||||
return Ok(profile);
|
||||
@ -317,7 +271,7 @@ impl Client {
|
||||
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
|
||||
log::debug!(
|
||||
"Received cookie_response[login]: key: \"{}\", has_payload: \"{}\", payload_length: \"{}\"",
|
||||
@ -326,7 +280,6 @@ impl Client {
|
||||
packet.payload_length.unwrap_or(VarInt::from(0)).0
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn handle_plugin_response(&self, plugin_response: SLoginPluginResponse) {
|
||||
log::debug!("Handling plugin");
|
||||
let velocity_config = &ADVANCED_CONFIG.proxy.velocity;
|
||||
@ -386,82 +339,4 @@ impl Client {
|
||||
.await;
|
||||
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(
|
||||
®istry.registry_id,
|
||||
®istry.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);
|
||||
}
|
||||
}
|
||||
9
pumpkin/src/net/packet/mod.rs
Normal file
9
pumpkin/src/net/packet/mod.rs
Normal 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)
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
use std::num::NonZeroU8;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::PlayerConfig;
|
||||
use crate::block::block_manager::BlockActionResult;
|
||||
use crate::net::PlayerConfig;
|
||||
use crate::{
|
||||
command::CommandSender,
|
||||
entity::player::{ChatMode, Hand, Player},
|
||||
18
pumpkin/src/net/packet/status.rs
Normal file
18
pumpkin/src/net/packet/status.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,10 @@
|
||||
use std::net::IpAddr;
|
||||
use std::{net::IpAddr, net::SocketAddr};
|
||||
|
||||
use pumpkin_protocol::Property;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::{
|
||||
client::authentication::{offline_uuid, GameProfile},
|
||||
Client,
|
||||
};
|
||||
use crate::net::{offline_uuid, GameProfile};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum BungeeCordError {
|
||||
@ -20,11 +18,23 @@ pub enum BungeeCordError {
|
||||
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(
|
||||
client: &Client,
|
||||
username: String,
|
||||
client_address: &Mutex<SocketAddr>,
|
||||
server_address: &str,
|
||||
name: String,
|
||||
) -> Result<(IpAddr, GameProfile), BungeeCordError> {
|
||||
let server_address = client.server_address.lock().await;
|
||||
let data = server_address.split('\0').take(4).collect::<Vec<_>>();
|
||||
|
||||
// Ip of player, only given if ip_forward on bungee is true
|
||||
@ -32,15 +42,13 @@ pub async fn bungeecord_login(
|
||||
Some(ip) => ip
|
||||
.parse()
|
||||
.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
|
||||
let id = match data.get(2) {
|
||||
Some(uuid) => uuid.parse().map_err(|_| BungeeCordError::FailedParseUUID)?,
|
||||
None => {
|
||||
offline_uuid(username.as_str()).map_err(|_| BungeeCordError::FailedMakeOfflineUUID)?
|
||||
}
|
||||
None => offline_uuid(name.as_str()).map_err(|_| BungeeCordError::FailedMakeOfflineUUID)?,
|
||||
};
|
||||
|
||||
// Read properties and get textures
|
||||
@ -57,7 +65,7 @@ pub async fn bungeecord_login(
|
||||
ip,
|
||||
GameProfile {
|
||||
id,
|
||||
name: username,
|
||||
name,
|
||||
properties,
|
||||
profile_actions: None,
|
||||
},
|
||||
@ -14,7 +14,7 @@ use rand::Rng;
|
||||
use sha2::Sha256;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::client::{authentication::GameProfile, Client};
|
||||
use crate::net::{Client, GameProfile};
|
||||
|
||||
type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
@ -13,7 +13,6 @@ pub struct RCONServer;
|
||||
|
||||
impl RCONServer {
|
||||
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 password = Arc::new(config.password.clone());
|
||||
@ -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>> {
|
||||
assert!(!png_data.is_empty(), "PNG data is empty");
|
||||
let icon = png::Decoder::new(Cursor::new(&png_data));
|
||||
let reader = icon.read_info()?;
|
||||
let info = reader.info();
|
||||
@ -70,6 +71,7 @@ impl CachedBranding {
|
||||
}
|
||||
|
||||
impl CachedStatus {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
let status_response = Self::build_response(&BASIC_CONFIG);
|
||||
let status_response_json = serde_json::to_string(&status_response)
|
||||
|
||||
@ -5,7 +5,7 @@ use rsa::{traits::PublicKeyParts as _, Pkcs1v15Encrypt, RsaPrivateKey};
|
||||
use sha1::Sha1;
|
||||
use sha2::Digest;
|
||||
|
||||
use crate::client::EncryptionError;
|
||||
use crate::net::EncryptionError;
|
||||
|
||||
pub struct KeyStore {
|
||||
pub private_key: RsaPrivateKey,
|
||||
@ -13,6 +13,7 @@ pub struct KeyStore {
|
||||
}
|
||||
|
||||
impl KeyStore {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
log::debug!("Creating encryption keys...");
|
||||
let private_key = Self::generate_private_key();
|
||||
|
||||
@ -23,12 +23,12 @@ use tokio::sync::{Mutex, RwLock};
|
||||
|
||||
use crate::block::block_manager::BlockManager;
|
||||
use crate::block::default_block_manager;
|
||||
use crate::client::EncryptionError;
|
||||
use crate::net::EncryptionError;
|
||||
use crate::world::custom_bossbar::CustomBossbars;
|
||||
use crate::{
|
||||
client::Client,
|
||||
command::{default_dispatcher, dispatcher::CommandDispatcher},
|
||||
entity::player::Player,
|
||||
net::Client,
|
||||
world::World,
|
||||
};
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ use pumpkin_protocol::client::play::{
|
||||
CSetBorderWarningDelay, CSetBorderWarningDistance,
|
||||
};
|
||||
|
||||
use crate::client::Client;
|
||||
use crate::net::Client;
|
||||
|
||||
use super::World;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user