From c54ab006ae63edf527af39ebf090d9c32195bc3f Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Mon, 5 Aug 2024 10:56:57 +0200 Subject: [PATCH] Add Commands --- Cargo.lock | 2 +- pumpkin-command/src/lib.rs | 7 ++++ pumpkin-protocol/src/client/play/mod.rs | 26 +++++++++++-- pumpkin-protocol/src/lib.rs | 2 + pumpkin-protocol/src/server/play/mod.rs | 14 +++++++ pumpkin-protocol/src/text.rs | 51 ++++++++++++++++++++++++- pumpkin/Cargo.toml | 3 +- pumpkin/src/client/mod.rs | 19 +++++++-- pumpkin/src/client/player_packet.rs | 13 ++++++- pumpkin/src/commands/mod.rs | 38 ++++++++++++++++++ pumpkin/src/commands/pumpkin.rs | 29 ++++++++++++++ pumpkin/src/configuration.rs | 13 ++++++- pumpkin/src/main.rs | 1 + pumpkin/src/server.rs | 9 +++-- 14 files changed, 209 insertions(+), 18 deletions(-) create mode 100644 pumpkin-command/src/lib.rs create mode 100644 pumpkin/src/commands/mod.rs create mode 100644 pumpkin/src/commands/pumpkin.rs diff --git a/Cargo.lock b/Cargo.lock index c9eac86dc..ccade3a28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1141,7 +1141,7 @@ dependencies = [ [[package]] name = "pumpkin" -version = "0.1.0" +version = "0.1.0-dev" dependencies = [ "base64", "crossbeam-channel", diff --git a/pumpkin-command/src/lib.rs b/pumpkin-command/src/lib.rs new file mode 100644 index 000000000..5ca92f1e0 --- /dev/null +++ b/pumpkin-command/src/lib.rs @@ -0,0 +1,7 @@ +pub trait Command<'a> { + const NAME: &'a str; + const ALIEASES: [&'a str]; + const DESCRIPTION: &'a str; + + fn on_execute(client: &mut Cli); +} \ No newline at end of file diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index d36b56f10..a2ffb5760 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -1,4 +1,4 @@ -use crate::{bytebuf::ByteBuffer, text::Text, BitSet, ClientPacket, Property, VarInt}; +use crate::{bytebuf::ByteBuffer, text::TextComponent, BitSet, ClientPacket, Property, VarInt}; pub struct SetHeldItem { slot: i8, @@ -106,11 +106,11 @@ impl ClientPacket for CPlayerAbilities { } pub struct CPlayDisconnect { - reason: Text, + reason: TextComponent, } impl CPlayDisconnect { - pub fn new(reason: Text) -> Self { + pub fn new(reason: TextComponent) -> Self { Self { reason } } } @@ -123,6 +123,26 @@ impl ClientPacket for CPlayDisconnect { } } +pub struct CSystemChatMessge { + content: TextComponent, + overlay: bool, +} + +impl CSystemChatMessge { + pub fn new(content: TextComponent, overlay: bool) -> Self { + Self { content, overlay } + } +} + +impl ClientPacket for CSystemChatMessge { + const PACKET_ID: VarInt = 0x6C; + + fn write(&self, bytebuf: &mut ByteBuffer) { + bytebuf.put_slice(&self.content.encode()); + bytebuf.put_bool(self.overlay); + } +} + pub struct CChangeDifficulty { difficulty: u8, locked: bool, diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index 5b88e1a5d..32b973cd3 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -13,6 +13,8 @@ pub mod text; pub mod packet_decoder; pub mod packet_encoder; +pub const CURRENT_MC_PROTOCOL: u32 = 767; + pub const MAX_PACKET_SIZE: i32 = 2097152; /// usally uses a namespace like "minecraft:thing" diff --git a/pumpkin-protocol/src/server/play/mod.rs b/pumpkin-protocol/src/server/play/mod.rs index f17b2ad22..209f46251 100644 --- a/pumpkin-protocol/src/server/play/mod.rs +++ b/pumpkin-protocol/src/server/play/mod.rs @@ -14,6 +14,20 @@ impl ServerPacket for SConfirmTeleport { } } +pub struct SChatCommand { + pub command: String, +} + +impl ServerPacket for SChatCommand { + const PACKET_ID: VarInt = 0x04; + + fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self { + Self { + command: bytebuf.get_string().unwrap(), + } + } +} + pub struct SPlayerPosition { pub x: f64, pub feet_y: f64, diff --git a/pumpkin-protocol/src/text.rs b/pumpkin-protocol/src/text.rs index bb56d9226..8e9767df2 100644 --- a/pumpkin-protocol/src/text.rs +++ b/pumpkin-protocol/src/text.rs @@ -6,12 +6,59 @@ use serde::{Deserialize, Serialize}; // represents a text component // Reference: https://wiki.vg/Text_formatting#Text_components #[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)] -pub struct Text { +pub struct TextComponent { pub text: String, } -impl Text { +impl TextComponent { pub fn encode(&self) -> Vec { fastnbt::to_bytes_with_opts(&self, SerOpts::network_nbt()).unwrap() } } + +impl From for TextComponent { + fn from(value: String) -> Self { + Self { text: value } + } +} + +impl From<&str> for TextComponent { + fn from(value: &str) -> Self { + Self { + text: value.to_string(), + } + } +} + +/// Text color +#[derive(Default, Debug, Clone, Copy)] +pub enum Color { + /// The default color for the text will be used, which varies by context + /// (in some cases, it's white; in others, it's black; in still others, it + /// is a shade of gray that isn't normally used on text). + #[default] + Reset, + /// One of the 16 named Minecraft colors + Named(NamedColor), +} + +/// Named Minecraft color +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum NamedColor { + Black = 0, + DarkBlue, + DarkGreen, + DarkCyan, + DarkRed, + Purple, + Gold, + Gray, + DarkGray, + Blue, + BrightGreen, + Cyan, + Red, + Pink, + Yellow, + White, +} diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index c4835a583..69aaf790e 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "pumpkin" -version = "0.1.0" +version = "0.1.0-dev" +description = "Empowering everyone to host fast and efficient Minecraft servers." edition = "2021" [dependencies] diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index a639c83d8..5f67af936 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -17,7 +17,7 @@ use pumpkin_protocol::{ client::{ config::CConfigDisconnect, login::CLoginDisconnect, - play::{CPlayDisconnect, CSyncPlayerPostion}, + play::{CPlayDisconnect, CSyncPlayerPostion, CSystemChatMessge}, }, packet_decoder::PacketDecoder, packet_encoder::PacketEncoder, @@ -25,10 +25,13 @@ use pumpkin_protocol::{ config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks, SPluginMessage}, handshake::SHandShake, login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, - play::{SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation}, + play::{ + SChatCommand, SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation, + SPlayerRotation, + }, status::{SPingRequest, SStatusRequest}, }, - text::Text, + text::TextComponent, ClientPacket, ConnectionState, PacketError, RawPacket, ServerPacket, }; @@ -224,6 +227,9 @@ impl Client { SConfirmTeleport::PACKET_ID => { self.handle_confirm_teleport(server, SConfirmTeleport::read(bytebuf)) } + SChatCommand::PACKET_ID => { + self.handle_chat_command(server, SChatCommand::read(bytebuf)) + } SPlayerPosition::PACKET_ID => { self.handle_position(server, SPlayerPosition::read(bytebuf)) } @@ -284,6 +290,11 @@ impl Client { } } + pub fn send_message(&mut self, text: TextComponent) { + self.send_packet(CSystemChatMessge::new(text, false)) + .unwrap_or_else(|e| self.kick(&e.to_string())); + } + /// Kicks the Client with a reason depending on the connection state pub fn kick(&mut self, reason: &str) { dbg!(reason); @@ -297,7 +308,7 @@ impl Client { .unwrap_or_else(|_| self.close()); } ConnectionState::Play => { - self.send_packet(CPlayDisconnect::new(Text { + self.send_packet(CPlayDisconnect::new(TextComponent { text: reason.to_string(), })) .unwrap_or_else(|_| self.close()); diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 4fa4da4bc..7e2c20df7 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -1,8 +1,11 @@ use pumpkin_protocol::server::play::{ - SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, + SChatCommand, SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, }; -use crate::server::Server; +use crate::{ + commands::{handle_command, CommandSender}, + server::Server, +}; use super::Client; @@ -10,6 +13,8 @@ use super::Client; pub trait PlayerPacketProcessor { fn handle_confirm_teleport(&mut self, server: &mut Server, confirm_teleport: SConfirmTeleport); + fn handle_chat_command(&mut self, server: &mut Server, command: SChatCommand); + fn handle_position(&mut self, server: &mut Server, position: SPlayerPosition); fn handle_position_rotation( @@ -78,4 +83,8 @@ impl PlayerPacketProcessor for Client { player.yaw = rotation.yaw; player.pitch = rotation.pitch; } + + fn handle_chat_command(&mut self, server: &mut Server, command: SChatCommand) { + handle_command(&mut CommandSender::Player(self), command.command, server); + } } diff --git a/pumpkin/src/commands/mod.rs b/pumpkin/src/commands/mod.rs new file mode 100644 index 000000000..5578a3d17 --- /dev/null +++ b/pumpkin/src/commands/mod.rs @@ -0,0 +1,38 @@ +use pumpkin::PumpkinCommand; +use pumpkin_protocol::text::TextComponent; + +use crate::{client::Client, server::Server}; + +mod pumpkin; + +/// I think it would be great to split this up into a seperate crate, But idk how i should do that, Because we have to rely on Client and Server +pub trait Command<'a> { + // Name of the Plugin, Use lower case + const NAME: &'a str; + const DESCRIPTION: &'a str; + + fn on_execute(sender: &mut CommandSender<'a>, command: String, server: &mut Server); +} + +pub enum CommandSender<'a> { + Console, + Player(&'a mut Client), +} + +impl<'a> CommandSender<'a> { + pub fn send_message(&mut self, text: TextComponent) { + match self { + // todo: add color and stuff to console + CommandSender::Console => log::info!("{}", text.text), + CommandSender::Player(c) => c.send_message(text), + } + } +} +pub fn handle_command(sender: &mut CommandSender, command: String, server: &mut Server) { + let command = command.to_lowercase(); + dbg!("handling command"); + dbg!(&command); + if command.starts_with(PumpkinCommand::NAME) { + PumpkinCommand::on_execute(sender, command, server) + } +} diff --git a/pumpkin/src/commands/pumpkin.rs b/pumpkin/src/commands/pumpkin.rs new file mode 100644 index 000000000..d743c4c83 --- /dev/null +++ b/pumpkin/src/commands/pumpkin.rs @@ -0,0 +1,29 @@ +use pumpkin_protocol::{text::TextComponent, CURRENT_MC_PROTOCOL}; + +use crate::server::CURRENT_MC_VERSION; + +use super::Command; + +pub struct PumpkinCommand {} + +impl PumpkinCommand { + pub fn new() -> Self { + Self {} + } +} + +impl<'a> Command<'a> for PumpkinCommand { + const NAME: &'a str = "pumpkin"; + + const DESCRIPTION: &'a str = "Displays information about Pumpkin"; + + fn on_execute( + sender: &mut super::CommandSender<'a>, + _command: String, + _server: &mut crate::server::Server, + ) { + let version = env!("CARGO_PKG_VERSION"); + let description = env!("CARGO_PKG_DESCRIPTION"); + sender.send_message(TextComponent::from(format!("Pumpkin {version}, {description} (Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL})"))) + } +} diff --git a/pumpkin/src/configuration.rs b/pumpkin/src/configuration.rs index f7166b875..608ac53bf 100644 --- a/pumpkin/src/configuration.rs +++ b/pumpkin/src/configuration.rs @@ -8,14 +8,23 @@ use crate::{entity::player::GameMode, server::Difficulty}; const CURRENT_BASE_VERSION: &str = "1.0.0"; #[derive(Deserialize, Serialize)] +/// The idea is that Pumpkin should very customizable, You can Enable or Disable Features depning on your needs. +/// This also allows you get some Performance or Resource boosts. +/// Important: The Configuration should match Vanilla by default pub struct AdvancedConfiguration { - pub liquid_physics: bool, + /// Requires Online mode + /// Should player have skins + use_skins: bool, + /// Should chat be enabled + enable_chat: bool, } +/// Important: The Configuration should match Vanilla by default impl Default for AdvancedConfiguration { fn default() -> Self { Self { - liquid_physics: true, + use_skins: true, + enable_chat: true, } } } diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index e74b7d322..d1ad1f961 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -10,6 +10,7 @@ use server::Server; const SERVER: Token = Token(0); pub mod client; +pub mod commands; pub mod configuration; pub mod entity; pub mod server; diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index 4fc9358ec..d32c3a69c 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -14,6 +14,7 @@ use pumpkin_protocol::{ play::{CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerInfoUpdate, PlayerAction}, }, BitSet, ClientPacket, PacketError, Players, Sample, StatusResponse, VarInt, VarInt32, Version, + CURRENT_MC_PROTOCOL, }; use pumpkin_world::chunk::TestChunk; use rsa::{rand_core::OsRng, traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; @@ -28,6 +29,8 @@ use crate::{ }, }; +pub const CURRENT_MC_VERSION: &str = "1.21"; + pub struct Server { pub compression_threshold: Option, @@ -174,7 +177,7 @@ impl Server { } /// Sends a Packet to all Players - fn broadcast_packet

(&mut self, packet: P) -> Result<(), PacketError> + fn _broadcast_packet

(&mut self, packet: P) -> Result<(), PacketError> where P: ClientPacket, P: Clone, @@ -234,8 +237,8 @@ impl Server { StatusResponse { version: Version { - name: "1.21".into(), - protocol: 767, + name: CURRENT_MC_VERSION.into(), + protocol: CURRENT_MC_PROTOCOL, }, players: Players { max: config.max_players,