diff --git a/pumpkin/src/commands/cmd_gamemode.rs b/pumpkin/src/commands/cmd_gamemode.rs index 17f5de91a..a55743889 100644 --- a/pumpkin/src/commands/cmd_gamemode.rs +++ b/pumpkin/src/commands/cmd_gamemode.rs @@ -65,12 +65,19 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { let gamemode = parse_arg_gamemode(args)?; return if let Player(target) = sender { - target.set_gamemode(server, gamemode); - target.send_system_message(TextComponent::text(&format!( - "Game mode was set to {:?}", - gamemode - ))); - + dbg!("1"); + if target.gamemode == gamemode { + target.send_system_message(TextComponent::text(&format!( + "You already in {:?} gamemode", + gamemode + ))); + } else { + target.set_gamemode(server, gamemode); + target.send_system_message(TextComponent::text(&format!( + "Game mode was set to {:?}", + gamemode + ))); + } Ok(()) } else { Err(InvalidRequirementError) @@ -82,11 +89,18 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { let gamemode = parse_arg_gamemode(args)?; let target = parse_arg_player(sender, ARG_TARGET, args)?; - target.set_gamemode(server, gamemode); - target.send_system_message(TextComponent::text(&format!( - "Set own game mode to {:?}", - gamemode - ))); + if target.gamemode == gamemode { + target.send_system_message(TextComponent::text(&format!( + "You already in {:?} gamemode", + gamemode + ))); + } else { + target.set_gamemode(server, gamemode); + target.send_system_message(TextComponent::text(&format!( + "Game mode was set to {:?}", + gamemode + ))); + } Ok(()) }, diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index ee806f1c6..bf2d1f6ba 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -8,8 +8,8 @@ use pumpkin_inventory::player::PlayerInventory; use pumpkin_protocol::{ bytebuf::{packet_id::Packet, DeserializerError}, client::play::{ - CGameEvent, CPlayDisconnect, CPlayerInfoUpdate, CSyncPlayerPosition, CSystemChatMessage, - PlayerAction, + CGameEvent, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CSyncPlayerPosition, + CSystemChatMessage, PlayerAction, }, position::WorldPosition, server::play::{ @@ -25,6 +25,28 @@ use serde::{Deserialize, Serialize}; use crate::{client::Client, server::Server, util::boundingbox::BoundingBox}; +pub struct PlayerAbilities { + pub invulnerable: bool, + pub flying: bool, + pub allow_flying: bool, + pub creative: bool, + pub fly_speed: f32, + pub walk_speed_fov: f32, +} + +impl Default for PlayerAbilities { + fn default() -> Self { + Self { + invulnerable: false, + flying: false, + allow_flying: false, + creative: false, + fly_speed: 0.5, + walk_speed_fov: 0.1, + } + } +} + pub struct Player { pub client: Client, pub entity: Entity, @@ -35,6 +57,8 @@ pub struct Player { pub food: i32, pub food_saturation: f32, pub inventory: PlayerInventory, + /// send `send_abilties_update` when changed + pub abilities: PlayerAbilities, // Client side value, Should be not trusted pub on_ground: bool, @@ -70,6 +94,7 @@ impl Player { velocity: Vector3::new(0.0, 0.0, 0.0), inventory: PlayerInventory::new(), teleport_id_count: 0, + abilities: PlayerAbilities::default(), gamemode, } } @@ -100,6 +125,29 @@ impl Player { ); } + pub fn send_abilties_update(&mut self) { + let mut b = 0i8; + let abilities = &self.abilities; + + if abilities.invulnerable { + b |= 1; + } + if abilities.flying { + b |= 2; + } + if abilities.allow_flying { + b |= 4; + } + if abilities.creative { + b |= 8; + } + self.client.send_packet(&CPlayerAbilities::new( + b, + abilities.fly_speed, + abilities.walk_speed_fov, + )); + } + pub fn teleport(&mut self, x: f64, y: f64, z: f64, yaw: f32, pitch: f32) { // this is the ultra special magic code used to create the teleport id self.teleport_id_count += 1; @@ -163,7 +211,14 @@ impl Player { } pub fn set_gamemode(&mut self, server: &mut Server, gamemode: GameMode) { + // We could send the same gamemode without problems. But why waste bandwidth ? + assert!( + self.gamemode != gamemode, + "Setting the same gamemode as already is" + ); self.gamemode = gamemode; + // So a little story time. I actually made an abitlties_from_gamemode function. I looked at vanilla and they always send the abilties from the gamemode. But the funny thing actually is. That the client + // does actually use the same method and set the abilties when receiving the CGameEvent gamemode packet. Just Mojang nonsense server.broadcast_packet( self, &CPlayerInfoUpdate::new(