do better caching

This commit is contained in:
Snowiiii 2024-08-01 23:48:45 +02:00
parent 4ee1c6190f
commit 5a1d333f02
9 changed files with 158 additions and 59 deletions

View File

@ -5,6 +5,7 @@ use crate::{
login::{CEncryptionRequest, CLoginSuccess}, login::{CEncryptionRequest, CLoginSuccess},
status::{CPingResponse, CStatusResponse}, status::{CPingResponse, CStatusResponse},
}, },
registry::{DimensionCodec, WolfCodec},
server::{ server::{
config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks}, config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks},
handshake::SHandShake, handshake::SHandShake,
@ -67,12 +68,8 @@ impl ClientPacketProcessor for Client {
fn handle_status_request(&mut self, server: &mut Server, _status_request: SStatusRequest) { fn handle_status_request(&mut self, server: &mut Server, _status_request: SStatusRequest) {
dbg!("sending status"); dbg!("sending status");
if let Ok(response) = serde_json::to_string(&server.status_response) { self.send_packet(CStatusResponse::new(&server.status_response_json))
self.send_packet(CStatusResponse::new(response))
.unwrap_or_else(|e| self.kick(&e.to_string())); .unwrap_or_else(|e| self.kick(&e.to_string()));
} else {
log::error!("Failed to parse Status response to JSON")
}
} }
fn handle_ping_request(&mut self, _server: &mut Server, ping_request: SPingRequest) { fn handle_ping_request(&mut self, _server: &mut Server, ping_request: SPingRequest) {
@ -129,11 +126,13 @@ impl ClientPacketProcessor for Client {
fn handle_login_acknowledged( fn handle_login_acknowledged(
&mut self, &mut self,
_server: &mut Server, server: &mut Server,
_login_acknowledged: SLoginAcknowledged, _login_acknowledged: SLoginAcknowledged,
) { ) {
self.connection_state = ConnectionState::Config; self.connection_state = ConnectionState::Config;
Server::send_brand(self).unwrap_or_else(|e| self.kick(&e.to_string())); server
.send_brand(self)
.unwrap_or_else(|e| self.kick(&e.to_string()));
// known data packs // known data packs
self.send_packet(CKnownPacks::new(&[KnownPack { self.send_packet(CKnownPacks::new(&[KnownPack {
namespace: "minecraft", namespace: "minecraft",
@ -167,6 +166,12 @@ impl ClientPacketProcessor for Client {
Entry { Entry {
entry_id: "minecraft:dimension_type", entry_id: "minecraft:dimension_type",
has_data: true, has_data: true,
data: &DimensionCodec::parse(),
},
Entry {
entry_id: "minecraft:wolf_variant",
has_data: true,
data: &WolfCodec::parse(),
}, },
/* Entry { /* Entry {
entry_id: "minecraft:worldgen/biome".into(), entry_id: "minecraft:worldgen/biome".into(),

View File

@ -83,10 +83,12 @@ impl Client {
} }
} }
/// adds a Incoming packet to the queue
pub fn add_packet(&mut self, packet: RawPacket) { pub fn add_packet(&mut self, packet: RawPacket) {
self.client_packets_queue.push_back(packet); self.client_packets_queue.push_back(packet);
} }
/// enables encryption
pub fn enable_encryption( pub fn enable_encryption(
&mut self, &mut self,
server: &mut Server, server: &mut Server,
@ -126,6 +128,7 @@ impl Client {
} }
} }
/// Handles an incoming decoded Packet
pub fn handle_packet(&mut self, server: &mut Server, packet: &mut RawPacket) { pub fn handle_packet(&mut self, server: &mut Server, packet: &mut RawPacket) {
dbg!("Handling packet"); dbg!("Handling packet");
let bytebuf = &mut packet.bytebuf; let bytebuf = &mut packet.bytebuf;
@ -186,6 +189,7 @@ impl Client {
} }
} }
// Reads the connection until our buffer of len 4096 is full, then decode
/// Returns `true` if the connection is closed. /// Returns `true` if the connection is closed.
pub fn poll(&mut self, server: &mut Server, event: &Event) -> Result<bool, io::Error> { pub fn poll(&mut self, server: &mut Server, event: &Event) -> Result<bool, io::Error> {
if event.is_readable() { if event.is_readable() {
@ -233,6 +237,7 @@ impl Client {
Ok(self.closed) Ok(self.closed)
} }
/// Kicks the Client with a reason depending on the connection state
pub fn kick(&mut self, reason: &str) { pub fn kick(&mut self, reason: &str) {
// Todo // Todo
match self.connection_state { match self.connection_state {
@ -251,7 +256,7 @@ impl Client {
self.close() self.close()
} }
// Kick before when needed /// You should prefer to use `kick` when you can
pub fn close(&mut self) { pub fn close(&mut self) {
self.closed = true; self.closed = true;
} }

View File

@ -1,4 +1,4 @@
use crate::protocol::{bytebuf::ByteBuffer, registry, ClientPacket, KnownPack, VarInt}; use crate::protocol::{bytebuf::ByteBuffer, ClientPacket, KnownPack, VarInt};
pub struct CCookieRequest { pub struct CCookieRequest {
// TODO // TODO
@ -11,12 +11,12 @@ impl ClientPacket for CCookieRequest {
} }
pub struct CPluginMessage<'a> { pub struct CPluginMessage<'a> {
channel: String, channel: &'a str,
data: &'a [u8], data: &'a [u8],
} }
impl<'a> CPluginMessage<'a> { impl<'a> CPluginMessage<'a> {
pub fn new(channel: String, data: &'a [u8]) -> Self { pub fn new(channel: &'a str, data: &'a [u8]) -> Self {
Self { channel, data } Self { channel, data }
} }
} }
@ -25,7 +25,7 @@ impl<'a> ClientPacket for CPluginMessage<'a> {
const PACKET_ID: VarInt = 0x01; const PACKET_ID: VarInt = 0x01;
fn write(&self, bytebuf: &mut ByteBuffer) { fn write(&self, bytebuf: &mut ByteBuffer) {
bytebuf.put_string(&self.channel); bytebuf.put_string(self.channel);
bytebuf.put_slice(self.data); bytebuf.put_slice(self.data);
} }
} }
@ -107,7 +107,7 @@ impl<'a> CRegistryData<'a> {
pub struct Entry<'a> { pub struct Entry<'a> {
pub entry_id: &'a str, pub entry_id: &'a str,
pub has_data: bool, pub has_data: bool,
// data provided by registry::write_codec pub data: &'a [u8],
} }
impl<'a> ClientPacket for CRegistryData<'a> { impl<'a> ClientPacket for CRegistryData<'a> {
@ -118,7 +118,7 @@ impl<'a> ClientPacket for CRegistryData<'a> {
bytebuf.put_list::<Entry>(self.entries, |p, v| { bytebuf.put_list::<Entry>(self.entries, |p, v| {
p.put_string(v.entry_id); p.put_string(v.entry_id);
p.put_bool(v.has_data); p.put_bool(v.has_data);
registry::write_codec(p, -64, 384); p.put_slice(v.data);
}); });
} }
} }

View File

@ -18,20 +18,20 @@ impl ClientPacket for CPingResponse {
} }
} }
pub struct CStatusResponse { pub struct CStatusResponse<'a> {
json_response: String, // 32767 json_response: &'a str, // 32767
} }
impl CStatusResponse { impl<'a> CStatusResponse<'a> {
pub fn new(json_response: String) -> Self { pub fn new(json_response: &'a str) -> Self {
Self { json_response } Self { json_response }
} }
} }
impl ClientPacket for CStatusResponse { impl<'a> ClientPacket for CStatusResponse<'a> {
const PACKET_ID: VarInt = 0x00; const PACKET_ID: VarInt = 0x00;
fn write(&self, bytebuf: &mut ByteBuffer) { fn write(&self, bytebuf: &mut ByteBuffer) {
bytebuf.put_string(self.json_response.as_str()); bytebuf.put_string(self.json_response);
} }
} }

View File

@ -6,7 +6,7 @@ use serde::Serialize;
use thiserror::Error; use thiserror::Error;
pub mod bytebuf; pub mod bytebuf;
mod registry; pub mod registry;
pub mod client; pub mod client;
pub mod server; pub mod server;

View File

@ -2,25 +2,25 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Dimension { pub struct Dimension {
pub ambient_light: f32, ambient_light: f32,
bed_works: i32, bed_works: u8,
coordinate_scale: f64, coordinate_scale: f64,
effects: DimensionEffects, effects: DimensionEffects,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
fixed_time: Option<i32>, fixed_time: Option<i64>,
has_ceiling: i32, has_ceiling: u8,
has_raids: i32, has_raids: u8,
has_skylight: i32, has_skylight: u8,
height: i32, height: i32,
infiniburn: String, infiniburn: String,
logical_height: i32, logical_height: i32,
min_y: i32, min_y: i32,
monster_spawn_block_light_limit: i32, monster_spawn_block_light_limit: i32,
monster_spawn_light_level: MonsterSpawnLightLevel, monster_spawn_light_level: MonsterSpawnLightLevel,
natural: i32, natural: u8,
piglin_safe: i32, piglin_safe: u8,
respawn_anchor_works: i32, respawn_anchor_works: u8,
ultrawarm: i32, ultrawarm: u8,
} }
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Debug)] #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Debug)]
@ -50,7 +50,12 @@ impl Default for Dimension {
logical_height: 384, logical_height: 384,
min_y: -64, min_y: -64,
monster_spawn_block_light_limit: 0, monster_spawn_block_light_limit: 0,
monster_spawn_light_level: MonsterSpawnLightLevel::Int(7), monster_spawn_light_level: MonsterSpawnLightLevel::Tagged(
MonsterSpawnLightLevelTagged::Uniform {
min_inclusive: 7,
max_inclusive: 0,
},
),
natural: 1, natural: 1,
piglin_safe: 0, piglin_safe: 0,
respawn_anchor_works: 0, respawn_anchor_works: 0,

View File

@ -1,10 +1,7 @@
use core::str; use dimensions::Dimension;
use std::{fs::File, io::Read}; use fastnbt::SerOpts;
use dimensions::{Dimension, DimensionEffects, MonsterSpawnLightLevel};
use fastnbt::{nbt, to_bytes_with_opts, SerOpts};
use flate2::read::GzDecoder;
use serde::Serialize; use serde::Serialize;
use wolf::WolfVariant;
use super::bytebuf::ByteBuffer; use super::bytebuf::ByteBuffer;
@ -12,11 +9,14 @@ mod biomes;
mod chat_type; mod chat_type;
mod damage_type; mod damage_type;
mod dimensions; mod dimensions;
mod wolf;
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
struct LoginInfo { struct LoginInfo {
#[serde(rename = "minecraft:dimension_type")] #[serde(rename = "minecraft:dimension_type")]
dimensions: Codec<dimensions::Dimension>, dimensions: Codec<dimensions::Dimension>,
// #[serde(rename = "minecraft:wolf_variant")]
// wolf: Codec<wolf::WolfVariant>,
/* #[serde(rename = "minecraft:worldgen/biome")] /* #[serde(rename = "minecraft:worldgen/biome")]
biomes: Codec<biomes::Biome>, biomes: Codec<biomes::Biome>,
#[serde(rename = "minecraft:chat_type")] #[serde(rename = "minecraft:chat_type")]
@ -25,6 +25,52 @@ struct LoginInfo {
damage: Codec<damage_type::DamageType>, */ damage: Codec<damage_type::DamageType>, */
} }
#[derive(Debug, Clone, Serialize)]
pub struct DimensionCodec {
#[serde(rename = "minecraft:dimension_type")]
dimensions: Codec<dimensions::Dimension>,
}
impl DimensionCodec {
pub fn parse() -> Vec<u8> {
let dimension = Dimension::default();
let codec = DimensionCodec {
dimensions: Codec {
ty: "minecraft:dimension_type".into(),
value: vec![RegistryValue {
name: "minecraft:overworld".into(),
id: 0,
element: dimension,
}],
},
};
fastnbt::to_bytes_with_opts(&codec, SerOpts::network_nbt()).unwrap()
}
}
#[derive(Debug, Clone, Serialize)]
pub struct WolfCodec {
#[serde(rename = "minecraft:wolf_variant")]
wolf: Codec<wolf::WolfVariant>,
}
impl WolfCodec {
pub fn parse() -> Vec<u8> {
let wolf = WolfVariant::default();
let codec = WolfCodec {
wolf: Codec {
ty: "minecraft:wolf_variant".into(),
value: vec![RegistryValue {
name: "minecraft:wolf_variant".into(),
id: 0,
element: wolf,
}],
},
};
fastnbt::to_bytes_with_opts(&codec, SerOpts::network_nbt()).unwrap()
}
}
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
struct Codec<T> { struct Codec<T> {
#[serde(rename = "type")] #[serde(rename = "type")]
@ -33,13 +79,14 @@ struct Codec<T> {
} }
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
struct RegistryValue<T> { struct RegistryValue<T> {
id: i32,
name: String, name: String,
id: i32,
element: T, element: T,
} }
pub fn write_codec(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) { pub fn write_codec(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) -> Vec<u8> {
let dimension = Dimension::default(); let dimension = Dimension::default();
let wolf = WolfVariant::default();
let info = LoginInfo { let info = LoginInfo {
dimensions: Codec { dimensions: Codec {
@ -50,7 +97,14 @@ pub fn write_codec(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) {
element: dimension, element: dimension,
}], }],
}, },
/* biomes: Codec { /* wolf: Codec {
ty: "minecraft:wolf_variant".into(),
value: vec![RegistryValue {
name: "minecraft:wolf_variant".into(),
id: 0,
element: wolf,
}],
}, */ /* biomes: Codec {
ty: "minecraft:worldgen/biome".into(), ty: "minecraft:worldgen/biome".into(),
value: biomes::all(), value: biomes::all(),
}, },
@ -64,8 +118,5 @@ pub fn write_codec(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) {
}, */ }, */
}; };
let val = &fastnbt::to_bytes_with_opts(&info, SerOpts::network_nbt()).unwrap(); fastnbt::to_bytes_with_opts(&info, SerOpts::network_nbt()).unwrap()
dbg!(val);
// Dimension codec
out.put_slice(val);
} }

View File

@ -0,0 +1,20 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WolfVariant {
wild_texture: String,
tame_texture: String,
angry_texture: String,
biomes: String,
}
impl Default for WolfVariant {
fn default() -> Self {
Self {
wild_texture: "minecraft:entity/wolf/wolf_ashen".to_string(),
tame_texture: "minecraft:entity/wolf/wolf_ashen_tame".to_string(),
angry_texture: "minecraft:entity/wolf/wolf_ashen_angry".to_string(),
biomes: "minecraft:snowy_taiga".to_string(),
}
}
}

View File

@ -31,11 +31,18 @@ pub struct Server {
pub private_key: RsaPrivateKey, pub private_key: RsaPrivateKey,
pub public_key_der: Box<[u8]>, pub public_key_der: Box<[u8]>,
/// the maximum amount of players that can join the Server
pub max_players: u32, pub max_players: u32,
pub world: World, pub world: World,
pub status_response: StatusResponse, pub status_response: StatusResponse,
// We cache the json response here so we don't parse it every time someone makes a Status request.
// Keep in mind that we must parse this again, when the StatusResponse changes which usally happen when a player joins or leaves
pub status_response_json: String,
/// Cache the Server brand buffer so we don't have to rebuild them every time a player joins
pub cached_server_brand: Vec<u8>,
// todo replace with HashMap <World, Player> // todo replace with HashMap <World, Player>
entity_id: AtomicI32, // todo: place this into every world entity_id: AtomicI32, // todo: place this into every world
@ -44,9 +51,11 @@ pub struct Server {
impl Server { impl Server {
pub fn new(config: (BasicConfiguration, AdvancedConfiguration)) -> Self { pub fn new(config: (BasicConfiguration, AdvancedConfiguration)) -> Self {
let max_players = 20; let status_response = Self::build_response(&config.0);
let config_clone = &config; let status_response_json = serde_json::to_string(&status_response)
let status_response = Self::default_response(config_clone); .expect("Failed to parse Status response into JSON");
let cached_server_brand = Self::build_brand();
// todo, only create when needed // todo, only create when needed
let (public_key, private_key) = Self::generate_keys(); let (public_key, private_key) = Self::generate_keys();
@ -65,9 +74,11 @@ impl Server {
encryption: config.1.encryption, encryption: config.1.encryption,
compression_threshold: None, // 256 compression_threshold: None, // 256
public_key, public_key,
cached_server_brand,
private_key, private_key,
max_players, max_players: config.0.max_plyers,
status_response, status_response,
status_response_json,
public_key_der, public_key_der,
difficulty: config.0.default_difficulty, difficulty: config.0.default_difficulty,
} }
@ -127,21 +138,23 @@ impl Server {
self.entity_id.fetch_add(1, Ordering::SeqCst) self.entity_id.fetch_add(1, Ordering::SeqCst)
} }
pub fn send_brand(client: &mut Client) -> Result<(), PacketError> { pub fn build_brand() -> Vec<u8> {
// send server brand
let brand = "pumpkin"; let brand = "pumpkin";
let mut buf = vec![]; let mut buf = vec![];
let _ = VarInt32(brand.len() as i32).encode(&mut buf); let _ = VarInt32(brand.len() as i32).encode(&mut buf);
buf.extend_from_slice(brand.as_bytes()); buf.extend_from_slice(brand.as_bytes());
buf
}
pub fn send_brand(&self, client: &mut Client) -> Result<(), PacketError> {
// send server brand
client.send_packet(CPluginMessage::new( client.send_packet(CPluginMessage::new(
"minecraft:brand".to_string(), "minecraft:brand",
buf.as_slice(), &self.cached_server_brand,
)) ))
} }
pub fn default_response( pub fn build_response(config: &BasicConfiguration) -> StatusResponse {
config: &(BasicConfiguration, AdvancedConfiguration),
) -> StatusResponse {
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/icon.png"); let path = concat!(env!("CARGO_MANIFEST_DIR"), "/icon.png");
StatusResponse { StatusResponse {
@ -150,14 +163,14 @@ impl Server {
protocol: 767, protocol: 767,
}, },
players: Players { players: Players {
max: config.0.max_plyers, max: config.max_plyers,
online: 0, online: 0,
sample: vec![Sample { sample: vec![Sample {
name: "".into(), name: "".into(),
id: "".into(), id: "".into(),
}], }],
}, },
description: config.0.motd.clone(), description: config.motd.clone(),
favicon: Self::load_icon(path), favicon: Self::load_icon(path),
} }
} }