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]]
name = "pumpkin"
version = "0.1.0"
version = "0.1.0-dev"
dependencies = [
"base64",
"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 {
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,

View File

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

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 x: f64,
pub feet_y: f64,

View File

@ -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<u8> {
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]
name = "pumpkin"
version = "0.1.0"
version = "0.1.0-dev"
description = "Empowering everyone to host fast and efficient Minecraft servers."
edition = "2021"
[dependencies]

View File

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

View File

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

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";
#[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,
}
}
}

View File

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

View File

@ -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<u8>,
@ -174,7 +177,7 @@ impl Server {
}
/// 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
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,