Add: Basic Combat

This commit is contained in:
Snowiiii 2024-08-13 09:16:00 +02:00
parent c2bde2f73e
commit 9fc86e260b
15 changed files with 163 additions and 55 deletions

View File

@ -0,0 +1,17 @@
use pumpkin_macros::packet;
use serde::Serialize;
use crate::VarInt;
#[derive(Serialize)]
#[packet(0x24)]
pub struct CHurtAnimation {
entitiy_id: VarInt,
yaw: f32,
}
impl CHurtAnimation {
pub fn new(entitiy_id: VarInt, yaw: f32) -> Self {
Self { entitiy_id, yaw }
}
}

View File

@ -7,6 +7,7 @@ mod c_entity_animation;
mod c_entity_metadata;
mod c_game_event;
mod c_head_rot;
mod c_hurt_animation;
mod c_login;
mod c_open_screen;
mod c_play_disconnect;
@ -35,6 +36,7 @@ pub use c_entity_animation::*;
pub use c_entity_metadata::*;
pub use c_game_event::*;
pub use c_head_rot::*;
pub use c_hurt_animation::*;
pub use c_login::*;
pub use c_open_screen::*;
pub use c_play_disconnect::*;

View File

@ -0,0 +1,25 @@
use pumpkin_macros::packet;
use serde::Deserialize;
use crate::{ServerPacket, VarInt};
#[packet(0x16)]
pub struct SInteract {
pub entity_id: VarInt,
pub typ: VarInt,
pub target_x: Option<f32>,
// don't ask me why, adding more values does not work :c
}
// TODO
impl ServerPacket for SInteract {
fn read(
bytebuf: &mut crate::bytebuf::ByteBuffer,
) -> Result<Self, crate::bytebuf::DeserializerError> {
Ok(Self {
entity_id: bytebuf.get_var_int(),
typ: bytebuf.get_var_int(),
target_x: bytebuf.get_option(|v| v.get_f32()),
})
}
}

View File

@ -1,4 +1,5 @@
mod c_client_information;
mod c_interact;
mod s_chat_command;
mod s_chat_message;
mod s_confirm_teleport;
@ -9,6 +10,7 @@ mod s_player_rotation;
mod s_swing_arm;
pub use c_client_information::*;
pub use c_interact::*;
pub use s_chat_command::*;
pub use s_chat_message::*;
pub use s_confirm_teleport::*;

View File

@ -16,6 +16,7 @@ pub struct SChatMessage {
// acknowledged: BitSet,
}
// TODO
impl ServerPacket for SChatMessage {
fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> {
Ok(Self {

View File

@ -4,7 +4,7 @@ use lazy_static::lazy_static;
use crate::world::WorldError;
const BLOCKS_JSON: &'static str = include_str!("../blocks.json");
const BLOCKS_JSON: &str = include_str!("../blocks.json");
#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
struct BlockDefinition {

View File

@ -25,10 +25,9 @@
// }
// }
use std::{collections::HashMap, io::Write};
use std::collections::HashMap;
use fastnbt::LongArray;
use itertools::Itertools;
use crate::{world::WorldError, WORLD_HEIGHT};
@ -157,7 +156,7 @@ impl ChunkData {
// }
// dbg!("{}", blocks.iter().filter(|v| **v == 2005).collect_vec().len());
Ok(ChunkData {
blocks: blocks,
blocks,
position: at,
heightmaps: chunk_data.heightmaps,
})

View File

@ -1,15 +1,15 @@
use std::{collections::VecDeque, future, io::Read, path::PathBuf, sync::Arc};
use std::{io::Read, path::PathBuf, sync::Arc};
use flate2::bufread::ZlibDecoder;
use itertools::Itertools;
use thiserror::Error;
use tokio::{
fs::File,
io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
io::{AsyncReadExt, AsyncSeekExt},
sync::Mutex,
};
use crate::{chunk::ChunkData, dimension::Dimension};
use crate::chunk::ChunkData;
pub struct Level {
root_folder: PathBuf,

View File

@ -27,8 +27,8 @@ use pumpkin_protocol::{
handshake::SHandShake,
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
play::{
SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SPlayerCommand,
SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SSwingArm,
SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract,
SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SSwingArm,
},
status::{SPingRequest, SStatusRequest},
},
@ -159,6 +159,8 @@ impl Client {
}
pub fn set_gamemode(&mut self, gamemode: GameMode) {
let player = self.player.as_mut().unwrap();
player.gamemode = gamemode;
self.send_packet(&CGameEvent::new(3, gamemode.to_f32().unwrap()));
}
@ -282,6 +284,9 @@ impl Client {
server,
SClientInformationPlay::read(bytebuf).unwrap(),
),
SInteract::PACKET_ID => {
self.handle_interact(server, SInteract::read(bytebuf).unwrap());
}
_ => log::error!("Failed to handle player packet id {}", packet.id.0),
}
}

View File

@ -1,13 +1,14 @@
use num_traits::FromPrimitive;
use pumpkin_entity::EntityId;
use pumpkin_inventory::WindowType;
use pumpkin_protocol::{
client::play::{
Animation, CEntityAnimation, CHeadRot, COpenScreen, CUpdateEntityPos, CUpdateEntityPosRot,
CUpdateEntityRot,
Animation, CEntityAnimation, CHeadRot, CHurtAnimation, COpenScreen, CUpdateEntityPos,
CUpdateEntityPosRot, CUpdateEntityRot,
},
server::play::{
SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SPlayerCommand,
SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SSwingArm,
SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract,
SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SSwingArm,
},
VarInt,
};
@ -15,7 +16,7 @@ use pumpkin_text::TextComponent;
use crate::{
commands::{handle_command, CommandSender},
entity::player::{ChatMode, Hand},
entity::player::{ChatMode, GameMode, Hand},
server::Server,
util::math::wrap_degrees,
};
@ -256,4 +257,24 @@ impl Client {
server_listing: client_information.server_listing,
});
}
pub fn handle_interact(&mut self, server: &mut Server, interact: SInteract) {
// TODO: do validation and stuff
let config = &server.advanced_config.pvp;
if config.enabled {
let attacked_client = server.get_by_entityid(self, interact.entity_id.0 as EntityId);
if let Some(client) = attacked_client {
if config.protect_creative
&& client.player.as_ref().unwrap().gamemode == GameMode::Creative
{
return;
}
drop(client);
if config.hurt_animation {
// todo
server.broadcast_packet(self, &CHurtAnimation::new(interact.entity_id, 10.0))
}
}
}
}
}

View File

@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use crate::client::authentication::ProfileAction;
#[derive(Deserialize, Serialize)]
pub struct Authentication {
pub struct AuthenticationConfig {
/// Whether to use Mojang authentication.
pub enabled: bool,
@ -81,7 +81,7 @@ impl Default for TextureTypes {
}
}
impl Default for Authentication {
impl Default for AuthenticationConfig {
fn default() -> Self {
Self {
enabled: true,

View File

@ -1,7 +1,7 @@
use std::path::Path;
use auth_config::Authentication;
use resource_pack::ResourcePack;
use auth_config::AuthenticationConfig;
use resource_pack::ResourcePackConfig;
use serde::{Deserialize, Serialize};
use crate::{entity::player::GameMode, server::Difficulty};
@ -17,11 +17,12 @@ const CURRENT_BASE_VERSION: &str = "1.0.0";
/// This also allows you get some Performance or Resource boosts.
/// Important: The Configuration should match Vanilla by default
pub struct AdvancedConfiguration {
pub commands: Commands,
pub authentication: Authentication,
pub packet_compression: Compression,
pub resource_pack: ResourcePack,
pub commands: CommandsConfig,
pub authentication: AuthenticationConfig,
pub packet_compression: CompressionConfig,
pub resource_pack: ResourcePackConfig,
pub rcon: RCONConfig,
pub pvp: PVPConfig,
}
#[derive(Deserialize, Serialize, Clone)]
@ -44,26 +45,52 @@ impl Default for RCONConfig {
}
#[derive(Deserialize, Serialize)]
pub struct Commands {
pub struct CommandsConfig {
/// Are commands from the Console accepted ?
pub use_console: bool,
// TODO: commands...
}
impl Default for CommandsConfig {
fn default() -> Self {
Self { use_console: true }
}
}
#[derive(Deserialize, Serialize)]
pub struct PVPConfig {
/// Is PVP enabled ?
pub enabled: bool,
/// Do we want to have the Red hurt animation & fov bobbing
pub hurt_animation: bool,
/// Should players in creative be protected against PVP
pub protect_creative: bool,
}
impl Default for PVPConfig {
fn default() -> Self {
Self {
enabled: true,
hurt_animation: true,
protect_creative: true,
}
}
}
#[derive(Deserialize, Serialize)]
// Packet compression
pub struct Compression {
// Is compression enabled ?
pub struct CompressionConfig {
/// Is compression enabled ?
pub enabled: bool,
// The compression threshold used when compression is enabled
/// The compression threshold used when compression is enabled
pub compression_threshold: u32,
// A value between 0..9
// 1 = Optimize for the best speed of encoding.
// 9 = Optimize for the size of data being encoded.
/// A value between 0..9
/// 1 = Optimize for the best speed of encoding.
/// 9 = Optimize for the size of data being encoded.
pub compression_level: u32,
}
impl Default for Compression {
impl Default for CompressionConfig {
fn default() -> Self {
Self {
enabled: true,
@ -73,21 +100,16 @@ impl Default for Compression {
}
}
impl Default for Commands {
fn default() -> Self {
Self { use_console: true }
}
}
/// Important: The Configuration should match Vanilla by default
impl Default for AdvancedConfiguration {
fn default() -> Self {
Self {
authentication: Authentication::default(),
commands: Commands::default(),
packet_compression: Compression::default(),
resource_pack: ResourcePack::default(),
authentication: AuthenticationConfig::default(),
commands: CommandsConfig::default(),
packet_compression: CompressionConfig::default(),
resource_pack: ResourcePackConfig::default(),
rcon: RCONConfig::default(),
pvp: PVPConfig::default(),
}
}
}

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
pub struct ResourcePack {
pub struct ResourcePackConfig {
pub enabled: bool,
/// The path to the resource pack.
pub resource_pack_url: String,
@ -13,7 +13,7 @@ pub struct ResourcePack {
pub force: bool,
}
impl ResourcePack {
impl ResourcePackConfig {
pub fn validate(&self) {
assert_eq!(
!self.resource_pack_url.is_empty(),
@ -27,7 +27,7 @@ impl ResourcePack {
}
}
impl Default for ResourcePack {
impl Default for ResourcePackConfig {
fn default() -> Self {
Self {
enabled: false,

View File

@ -7,6 +7,8 @@ use serde::{Deserialize, Serialize};
pub struct Player {
pub entity: Entity,
// current gamemode
pub gamemode: GameMode,
// Client side value, Should be not trusted
pub on_ground: bool,
@ -19,13 +21,14 @@ pub struct Player {
}
impl Player {
pub fn new(entity_id: EntityId) -> Self {
pub fn new(entity_id: EntityId, gamemode: GameMode) -> Self {
Self {
entity: Entity::new(entity_id, EntityType::Player),
on_ground: false,
awaiting_teleport: None,
sneaking: false,
sprinting: false,
gamemode,
}
}

View File

@ -1,5 +1,5 @@
use std::{
cell::RefCell,
cell::{RefCell, RefMut},
collections::HashMap,
io::Cursor,
rc::Rc,
@ -23,7 +23,7 @@ use pumpkin_protocol::{
},
},
uuid::UUID,
BitSet, ClientPacket, Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL,
ClientPacket, Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL,
};
use pumpkin_world::dimension::Dimension;
@ -146,8 +146,12 @@ impl Server {
pub async fn spawn_player(&mut self, client: &mut Client) {
// This code follows the vanilla packet order
let entity_id = self.new_entity_id();
let gamemode = match self.base_config.default_gamemode {
GameMode::Undefined => GameMode::Survival,
game_mode => game_mode,
};
log::debug!("spawning player, entity id {}", entity_id);
let player = Player::new(entity_id);
let player = Player::new(entity_id, gamemode);
client.player = Some(player);
// login packet for our new player
@ -164,12 +168,7 @@ impl Server {
0.into(),
"minecraft:overworld",
0, // seed
match self.base_config.default_gamemode {
GameMode::Undefined => GameMode::Survival,
game_mode => game_mode,
}
.to_u8()
.unwrap(),
gamemode.to_u8().unwrap(),
self.base_config.default_gamemode.to_i8().unwrap(),
false,
false,
@ -291,8 +290,20 @@ impl Server {
Server::spawn_test_chunk(client).await;
}
/// TODO: This definitly should be in world
pub fn get_by_entityid(&self, from: &Client, id: EntityId) -> Option<RefMut<Client>> {
for (_, client) in self.current_clients.iter().filter(|c| c.0 != &from.token) {
// Check if client is a player
let client = client.borrow_mut();
if client.is_player() && client.player.as_ref().unwrap().entity_id() == id {
return Some(client);
}
}
None
}
/// Sends a Packet to all Players
pub fn broadcast_packet<P>(&mut self, from: &mut Client, packet: &P)
pub fn broadcast_packet<P>(&self, from: &mut Client, packet: &P)
where
P: ClientPacket,
{
@ -308,7 +319,7 @@ impl Server {
}
}
pub fn broadcast_packet_expect<P>(&mut self, from: &mut Client, packet: &P)
pub fn broadcast_packet_expect<P>(&self, from: &Client, packet: &P)
where
P: ClientPacket,
{