Add Serverbound Player Command

This commit is contained in:
Snowiiii 2024-08-06 12:47:37 +02:00
parent 81f82a41b7
commit d1c3b2f10c
11 changed files with 138 additions and 88 deletions

15
Cargo.lock generated
View File

@ -888,6 +888,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
]
[[package]]
name = "num-integer"
version = "0.1.46"
@ -1151,6 +1162,8 @@ dependencies = [
"log",
"mio",
"num-bigint",
"num-derive",
"num-traits",
"pumpkin-protocol",
"pumpkin-registry",
"pumpkin-world",
@ -1185,6 +1198,8 @@ dependencies = [
"cfb8",
"fastnbt 2.5.0 (git+https://github.com/owengage/fastnbt.git)",
"log",
"num-derive",
"num-traits",
"pumpkin-macros",
"serde",
"serde_json",

View File

@ -17,6 +17,8 @@ serde_json = "1.0"
thiserror = "1.0.63"
log = "0.4"
num-traits = "0.2"
num-derive = "0.4"
# for text component
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }

View File

@ -1,3 +1,6 @@
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use crate::{ServerPacket, VarInt};
pub struct SConfirmTeleport {
@ -48,6 +51,35 @@ impl ServerPacket for SPlayerPosition {
}
}
pub struct SPlayerCommand {
pub entitiy_id: VarInt,
pub action: Action,
pub jump_boost: VarInt,
}
#[derive(FromPrimitive)]
pub enum Action {
StartSneaking = 0,
StopSneaking,
LeaveBed,
StartSprinting,
StopSprinting,
StartHourseJump,
OpenVehicleInventory,
StartFlyingElytra,
}
impl ServerPacket for SPlayerCommand {
const PACKET_ID: VarInt = 0x25;
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
Self {
entitiy_id: bytebuf.get_var_int(),
action: Action::from_i32(bytebuf.get_var_int()).unwrap(),
jump_boost: bytebuf.get_var_int(),
}
}
}
pub struct SPlayerPositionRotation {
pub x: f64,
pub feet_y: f64,

View File

@ -16,6 +16,8 @@ toml = "0.8.19"
rand = "0.8.5"
num-traits = "0.2"
num-derive = "0.4"
num-bigint = "0.4.6"
# encryption

View File

@ -1,3 +1,4 @@
use num_traits::FromPrimitive;
use pumpkin_protocol::{
client::{
config::{CFinishConfig, CKnownPacks, CRegistryData},
@ -140,10 +141,10 @@ impl Client {
self.config = Some(PlayerConfig {
locale: client_information.locale,
view_distance: client_information.view_distance,
chat_mode: ChatMode::from(client_information.chat_mode),
chat_mode: ChatMode::from_i32(client_information.chat_mode).unwrap(),
chat_colors: client_information.chat_colors,
skin_parts: client_information.skin_parts,
main_hand: Hand::from(client_information.main_hand),
main_hand: Hand::from_i32(client_information.main_hand).unwrap(),
text_filtering: client_information.text_filtering,
server_listing: client_information.server_listing,
});

View File

@ -12,6 +12,7 @@ use crate::{
use authentication::GameProfile;
use mio::{event::Event, net::TcpStream, Token};
use num_traits::ToPrimitive;
use pumpkin_protocol::{
client::{
config::CConfigDisconnect,
@ -25,8 +26,8 @@ use pumpkin_protocol::{
handshake::SHandShake,
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
play::{
SChatCommand, SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation,
SPlayerRotation,
SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition,
SPlayerPositionRotation, SPlayerRotation,
},
status::{SPingRequest, SStatusRequest},
},
@ -139,7 +140,7 @@ impl Client {
}
pub fn set_gamemode(&mut self, gamemode: GameMode) {
self.send_packet(CGameEvent::new(3, gamemode.to_byte() as f32))
self.send_packet(CGameEvent::new(3, gamemode.to_f32().unwrap()))
.unwrap_or_else(|e| self.kick(&e.to_string()));
}
@ -241,6 +242,9 @@ impl Client {
SPlayerRotation::PACKET_ID => {
self.handle_rotation(server, SPlayerRotation::read(bytebuf))
}
SPlayerCommand::PACKET_ID => {
self.handle_player_command(server, SPlayerCommand::read(bytebuf))
}
_ => log::error!("Failed to handle player packet id {}", packet.id),
}
}

View File

@ -1,5 +1,6 @@
use pumpkin_protocol::server::play::{
SChatCommand, SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation,
SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation,
SPlayerRotation,
};
use crate::{
@ -72,4 +73,23 @@ impl Client {
pub fn handle_chat_command(&mut self, server: &mut Server, command: SChatCommand) {
handle_command(&mut CommandSender::Player(self), command.command, server);
}
pub fn handle_player_command(&mut self, _server: &mut Server, command: SPlayerCommand) {
let player = self.player.as_mut().unwrap();
if command.entitiy_id != player.entity.entity_id {
return;
}
match command.action {
pumpkin_protocol::server::play::Action::StartSneaking => player.sneaking = true,
pumpkin_protocol::server::play::Action::StopSneaking => player.sneaking = false,
pumpkin_protocol::server::play::Action::LeaveBed => todo!(),
pumpkin_protocol::server::play::Action::StartSprinting => player.sprinting = true,
pumpkin_protocol::server::play::Action::StopSprinting => player.sprinting = false,
pumpkin_protocol::server::play::Action::StartHourseJump => todo!(),
pumpkin_protocol::server::play::Action::OpenVehicleInventory => todo!(),
pumpkin_protocol::server::play::Action::StartFlyingElytra => todo!(),
}
}
}

View File

@ -14,9 +14,23 @@ const CURRENT_BASE_VERSION: &str = "1.0.0";
pub struct AdvancedConfiguration {
/// Requires Online mode
/// Should player have skins
use_skins: bool,
pub use_skins: bool,
/// Should chat be enabled
enable_chat: bool,
pub enable_chat: bool,
pub commands: Commands,
}
#[derive(Deserialize, Serialize)]
pub struct Commands {
// Are commands from the Console accepted ?
pub use_console: bool,
}
impl Default for Commands {
fn default() -> Self {
Self { use_console: true }
}
}
/// Important: The Configuration should match Vanilla by default
@ -25,6 +39,7 @@ impl Default for AdvancedConfiguration {
Self {
use_skins: true,
enable_chat: true,
commands: Commands::default(),
}
}
}
@ -67,7 +82,6 @@ pub struct BasicConfiguration {
pub default_gamemode: GameMode,
}
impl Default for BasicConfiguration {
fn default() -> Self {
Self {
@ -96,7 +110,7 @@ impl AdvancedConfiguration {
pub fn load<P: AsRef<Path>>(path: P) -> AdvancedConfiguration {
if path.as_ref().exists() {
let toml = std::fs::read_to_string(path).expect("Couldn't read configuration");
toml::from_str(toml.as_str()).expect("Couldn't parse")
toml::from_str(toml.as_str()).expect("Couldn't parse, Proberbly old config")
} else {
let config = AdvancedConfiguration::default();
let toml = toml::to_string(&config).expect("Couldn't create toml!");

View File

@ -1,5 +1,6 @@
use std::str::FromStr;
use num_derive::{FromPrimitive, ToPrimitive};
use pumpkin_protocol::VarInt;
use serde::{Deserialize, Serialize};
@ -16,6 +17,9 @@ pub struct Player {
// Client side value, Should be not trusted
pub on_ground: bool,
pub sneaking: bool,
pub sprinting: bool,
// Current awaiting teleport id, None if did not teleport
pub awaiting_teleport: Option<VarInt>,
}
@ -31,6 +35,8 @@ impl Player {
pitch: 0.0,
on_ground: false,
awaiting_teleport: None,
sneaking: false,
sprinting: false,
}
}
@ -39,47 +45,22 @@ impl Player {
}
}
#[derive(FromPrimitive)]
pub enum Hand {
Main,
Off,
}
impl From<VarInt> for Hand {
fn from(value: VarInt) -> Self {
match value {
0 => Self::Off,
1 => Self::Main,
_ => {
log::info!("Unexpected Hand {}", value);
Self::Main
}
}
}
}
#[derive(FromPrimitive)]
pub enum ChatMode {
Enabled,
CommandsOnly,
Hidden,
}
impl From<VarInt> for ChatMode {
fn from(value: VarInt) -> Self {
match value {
0 => Self::Enabled,
1 => Self::CommandsOnly,
2 => Self::Hidden,
_ => {
log::info!("Unexpected ChatMode {}", value);
Self::Enabled
}
}
}
}
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize, FromPrimitive, ToPrimitive)]
pub enum GameMode {
Undefined,
Undefined = -1,
Survival,
Creative,
Adventure,
@ -102,31 +83,3 @@ impl FromStr for GameMode {
}
}
}
impl From<i8> for GameMode {
fn from(value: i8) -> Self {
match value {
-1 => GameMode::Undefined,
0 => GameMode::Survival,
1 => GameMode::Creative,
2 => GameMode::Adventure,
3 => GameMode::Spectator,
_ => {
log::info!("Unexpected GameMode {}", value);
Self::Survival
}
}
}
}
impl GameMode {
pub fn to_byte(self) -> i8 {
match self {
Self::Undefined => -1,
Self::Survival => 0,
Self::Creative => 1,
Self::Adventure => 2,
Self::Spectator => 3,
}
}
}

View File

@ -29,6 +29,9 @@ pub mod util;
#[cfg(not(target_os = "wasi"))]
fn main() -> io::Result<()> {
use std::time::Instant;
let time = Instant::now();
let basic_config = BasicConfiguration::load("configuration.toml");
let advanced_configuration = AdvancedConfiguration::load("features.toml");
@ -58,31 +61,34 @@ fn main() -> io::Result<()> {
// Unique token for each incoming connection.
let mut unique_token = Token(SERVER.0 + 1);
let mut connections: HashMap<Token, Client> = HashMap::new();
let use_console = advanced_configuration.commands.use_console;
log::info!("You now can connect to the server");
let mut connections: HashMap<Token, Client> = HashMap::new();
let server = Arc::new(Mutex::new(Server::new((
basic_config,
advanced_configuration,
))));
let server_clone = server.clone();
thread::spawn(move || {
let stdin = std::io::stdin();
loop {
let mut out = String::new();
stdin
.read_line(&mut out)
.expect("Failed to read console line");
handle_command(
&mut commands::CommandSender::Console,
out,
&mut server_clone.lock().unwrap(),
);
}
});
log::info!("Started Server took {}ms", time.elapsed().as_millis());
log::info!("You now can connect to the server");
if use_console {
let server_clone = server.clone();
thread::spawn(move || {
let stdin = std::io::stdin();
loop {
let mut out = String::new();
stdin
.read_line(&mut out)
.expect("Failed to read console line");
handle_command(
&mut commands::CommandSender::Console,
out,
&mut server_clone.lock().unwrap(),
);
}
});
}
loop {
if let Err(err) = poll.poll(&mut events, None) {
if interrupted(&err) {

View File

@ -7,6 +7,7 @@ use std::{
use base64::{engine::general_purpose, Engine};
use mio::{event::Event, Poll, Token};
use num_traits::ToPrimitive;
use pumpkin_protocol::{
client::{
config::CPluginMessage,
@ -62,7 +63,6 @@ impl Server {
let status_response = Self::build_response(&config.0);
let status_response_json = serde_json::to_string(&status_response)
.expect("Failed to parse Status response into JSON");
let cached_server_brand = Self::build_brand();
// todo, only create when needed
@ -137,8 +137,9 @@ impl Server {
GameMode::Undefined => GameMode::Survival,
game_mode => game_mode,
}
.to_byte() as u8,
self.base_config.default_gamemode.to_byte(),
.to_u8()
.unwrap(),
self.base_config.default_gamemode.to_i8().unwrap(),
false,
false,
false, // deth loc