Add Commands

This commit is contained in:
Snowiiii 2024-08-05 10:56:57 +02:00
parent 5714c1b786
commit c54ab006ae
14 changed files with 209 additions and 18 deletions

2
Cargo.lock generated
View File

@ -1141,7 +1141,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin" name = "pumpkin"
version = "0.1.0" version = "0.1.0-dev"
dependencies = [ dependencies = [
"base64", "base64",
"crossbeam-channel", "crossbeam-channel",

View File

@ -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);
}

View File

@ -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 { pub struct SetHeldItem {
slot: i8, slot: i8,
@ -106,11 +106,11 @@ impl ClientPacket for CPlayerAbilities {
} }
pub struct CPlayDisconnect { pub struct CPlayDisconnect {
reason: Text, reason: TextComponent,
} }
impl CPlayDisconnect { impl CPlayDisconnect {
pub fn new(reason: Text) -> Self { pub fn new(reason: TextComponent) -> Self {
Self { reason } 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 { pub struct CChangeDifficulty {
difficulty: u8, difficulty: u8,
locked: bool, locked: bool,

View File

@ -13,6 +13,8 @@ pub mod text;
pub mod packet_decoder; pub mod packet_decoder;
pub mod packet_encoder; pub mod packet_encoder;
pub const CURRENT_MC_PROTOCOL: u32 = 767;
pub const MAX_PACKET_SIZE: i32 = 2097152; pub const MAX_PACKET_SIZE: i32 = 2097152;
/// usally uses a namespace like "minecraft:thing" /// usally uses a namespace like "minecraft:thing"

View File

@ -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 struct SPlayerPosition {
pub x: f64, pub x: f64,
pub feet_y: f64, pub feet_y: f64,

View File

@ -6,12 +6,59 @@ use serde::{Deserialize, Serialize};
// represents a text component // represents a text component
// Reference: https://wiki.vg/Text_formatting#Text_components // Reference: https://wiki.vg/Text_formatting#Text_components
#[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)]
pub struct Text { pub struct TextComponent {
pub text: String, pub text: String,
} }
impl Text { impl TextComponent {
pub fn encode(&self) -> Vec<u8> { pub fn encode(&self) -> Vec<u8> {
fastnbt::to_bytes_with_opts(&self, SerOpts::network_nbt()).unwrap() fastnbt::to_bytes_with_opts(&self, SerOpts::network_nbt()).unwrap()
} }
} }
impl From<String> 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,
}

View File

@ -1,6 +1,7 @@
[package] [package]
name = "pumpkin" name = "pumpkin"
version = "0.1.0" version = "0.1.0-dev"
description = "Empowering everyone to host fast and efficient Minecraft servers."
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@ -17,7 +17,7 @@ use pumpkin_protocol::{
client::{ client::{
config::CConfigDisconnect, config::CConfigDisconnect,
login::CLoginDisconnect, login::CLoginDisconnect,
play::{CPlayDisconnect, CSyncPlayerPostion}, play::{CPlayDisconnect, CSyncPlayerPostion, CSystemChatMessge},
}, },
packet_decoder::PacketDecoder, packet_decoder::PacketDecoder,
packet_encoder::PacketEncoder, packet_encoder::PacketEncoder,
@ -25,10 +25,13 @@ use pumpkin_protocol::{
config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks, SPluginMessage}, config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks, SPluginMessage},
handshake::SHandShake, handshake::SHandShake,
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
play::{SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation}, play::{
SChatCommand, SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation,
SPlayerRotation,
},
status::{SPingRequest, SStatusRequest}, status::{SPingRequest, SStatusRequest},
}, },
text::Text, text::TextComponent,
ClientPacket, ConnectionState, PacketError, RawPacket, ServerPacket, ClientPacket, ConnectionState, PacketError, RawPacket, ServerPacket,
}; };
@ -224,6 +227,9 @@ impl Client {
SConfirmTeleport::PACKET_ID => { SConfirmTeleport::PACKET_ID => {
self.handle_confirm_teleport(server, SConfirmTeleport::read(bytebuf)) self.handle_confirm_teleport(server, SConfirmTeleport::read(bytebuf))
} }
SChatCommand::PACKET_ID => {
self.handle_chat_command(server, SChatCommand::read(bytebuf))
}
SPlayerPosition::PACKET_ID => { SPlayerPosition::PACKET_ID => {
self.handle_position(server, SPlayerPosition::read(bytebuf)) 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 /// 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) {
dbg!(reason); dbg!(reason);
@ -297,7 +308,7 @@ impl Client {
.unwrap_or_else(|_| self.close()); .unwrap_or_else(|_| self.close());
} }
ConnectionState::Play => { ConnectionState::Play => {
self.send_packet(CPlayDisconnect::new(Text { self.send_packet(CPlayDisconnect::new(TextComponent {
text: reason.to_string(), text: reason.to_string(),
})) }))
.unwrap_or_else(|_| self.close()); .unwrap_or_else(|_| self.close());

View File

@ -1,8 +1,11 @@
use pumpkin_protocol::server::play::{ 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; use super::Client;
@ -10,6 +13,8 @@ use super::Client;
pub trait PlayerPacketProcessor { pub trait PlayerPacketProcessor {
fn handle_confirm_teleport(&mut self, server: &mut Server, confirm_teleport: SConfirmTeleport); 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(&mut self, server: &mut Server, position: SPlayerPosition);
fn handle_position_rotation( fn handle_position_rotation(
@ -78,4 +83,8 @@ impl PlayerPacketProcessor for Client {
player.yaw = rotation.yaw; player.yaw = rotation.yaw;
player.pitch = rotation.pitch; player.pitch = rotation.pitch;
} }
fn handle_chat_command(&mut self, server: &mut Server, command: SChatCommand) {
handle_command(&mut CommandSender::Player(self), command.command, server);
}
} }

View File

@ -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)
}
}

View File

@ -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})")))
}
}

View File

@ -8,14 +8,23 @@ use crate::{entity::player::GameMode, server::Difficulty};
const CURRENT_BASE_VERSION: &str = "1.0.0"; const CURRENT_BASE_VERSION: &str = "1.0.0";
#[derive(Deserialize, Serialize)] #[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 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 { impl Default for AdvancedConfiguration {
fn default() -> Self { fn default() -> Self {
Self { Self {
liquid_physics: true, use_skins: true,
enable_chat: true,
} }
} }
} }

View File

@ -10,6 +10,7 @@ use server::Server;
const SERVER: Token = Token(0); const SERVER: Token = Token(0);
pub mod client; pub mod client;
pub mod commands;
pub mod configuration; pub mod configuration;
pub mod entity; pub mod entity;
pub mod server; pub mod server;

View File

@ -14,6 +14,7 @@ use pumpkin_protocol::{
play::{CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerInfoUpdate, PlayerAction}, play::{CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerInfoUpdate, PlayerAction},
}, },
BitSet, ClientPacket, PacketError, Players, Sample, StatusResponse, VarInt, VarInt32, Version, BitSet, ClientPacket, PacketError, Players, Sample, StatusResponse, VarInt, VarInt32, Version,
CURRENT_MC_PROTOCOL,
}; };
use pumpkin_world::chunk::TestChunk; use pumpkin_world::chunk::TestChunk;
use rsa::{rand_core::OsRng, traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; 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 struct Server {
pub compression_threshold: Option<u8>, pub compression_threshold: Option<u8>,
@ -174,7 +177,7 @@ impl Server {
} }
/// Sends a Packet to all Players /// Sends a Packet to all Players
fn broadcast_packet<P>(&mut self, packet: P) -> Result<(), PacketError> fn _broadcast_packet<P>(&mut self, packet: P) -> Result<(), PacketError>
where where
P: ClientPacket, P: ClientPacket,
P: Clone, P: Clone,
@ -234,8 +237,8 @@ impl Server {
StatusResponse { StatusResponse {
version: Version { version: Version {
name: "1.21".into(), name: CURRENT_MC_VERSION.into(),
protocol: 767, protocol: CURRENT_MC_PROTOCOL,
}, },
players: Players { players: Players {
max: config.max_players, max: config.max_players,