feat(bedrock): add basic combat
This commit is contained in:
parent
e1967bed2b
commit
fa5e07f8da
89
pumpkin-protocol/src/bedrock/server/actor_event.rs
Normal file
89
pumpkin-protocol/src/bedrock/server/actor_event.rs
Normal file
@ -0,0 +1,89 @@
|
||||
use crate::serial::PacketWrite;
|
||||
use std::io::{Error, Write};
|
||||
|
||||
use pumpkin_macros::packet;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::codec::var_int::VarInt;
|
||||
|
||||
#[derive(Debug, PacketWrite)]
|
||||
#[packet(27)]
|
||||
pub struct SActorEvent {
|
||||
pub entity_runtime_id: VarInt,
|
||||
pub event_type: ActorEventType,
|
||||
pub event_data: VarInt,
|
||||
pub fire_at_position: Option<Vector3<f32>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum ActorEventType {
|
||||
Jump = 1,
|
||||
Hurt = 2,
|
||||
Death = 3,
|
||||
StartAttacking = 4,
|
||||
StopAttacking = 5,
|
||||
TamingFailed = 6,
|
||||
TamingSucceeded = 7,
|
||||
ShakeWetness = 8,
|
||||
UseItem = 9,
|
||||
EatGrass = 10,
|
||||
FishhookBubble = 11,
|
||||
FishhookFishPosition = 12,
|
||||
FishhookHookTime = 13,
|
||||
FishhookTease = 14,
|
||||
SquidFleeing = 15,
|
||||
ZombieConverting = 16,
|
||||
PlayAmbient = 17,
|
||||
SpawnAlive = 18,
|
||||
StartOfferFlower = 19,
|
||||
StopOfferFlower = 20,
|
||||
LoveHearts = 21,
|
||||
VillagerAngry = 22,
|
||||
VillagerHappy = 23,
|
||||
WitchHatMagic = 24,
|
||||
FireworksExplode = 25,
|
||||
InLoveHearts = 26,
|
||||
SilverfishMergeAnimation = 27,
|
||||
GuardianAttackSound = 28,
|
||||
DrinkPotion = 29,
|
||||
ThrowPotion = 30,
|
||||
CartWithPrimeTNT = 31,
|
||||
PrimeCreeper = 32,
|
||||
AirSupply = 33,
|
||||
AddPlayerLevels = 34,
|
||||
GuardianMiningFatigue = 35,
|
||||
AgentSwingArm = 36,
|
||||
DragonStartDeathAnim = 37,
|
||||
GroundDust = 38,
|
||||
Shake = 39,
|
||||
Feed = 57,
|
||||
BabyEat = 60,
|
||||
InstantDeath = 61,
|
||||
NotifyTrade = 62,
|
||||
LeashDestroyed = 63,
|
||||
CaravanUpdated = 64,
|
||||
TalismanActivate = 65,
|
||||
UpdateStructureFeature = 66,
|
||||
PlayerSpawnedMob = 67,
|
||||
Puke = 68,
|
||||
UpdateStackSize = 69,
|
||||
StartSwimming = 70,
|
||||
BalloonPop = 71,
|
||||
TreasureHunt = 72,
|
||||
SummonAgent = 73,
|
||||
FinishedChargingItem = 74,
|
||||
LandedOnGround = 75,
|
||||
ActorGrowUp = 76,
|
||||
VibrationDetected = 77,
|
||||
DrinkMilk = 78,
|
||||
WetnessStop = 79,
|
||||
KineticDamageDealt = 80,
|
||||
HurtWithoutReceivingDamage = 81,
|
||||
}
|
||||
|
||||
impl PacketWrite for ActorEventType {
|
||||
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||
(*self as u8).write(writer)
|
||||
}
|
||||
}
|
||||
@ -19,6 +19,7 @@ pub struct SInteraction {
|
||||
pub enum Action {
|
||||
Invalid = 0,
|
||||
Interact = 1,
|
||||
// No longer used in newer versions
|
||||
Attack = 2,
|
||||
StopRiding = 3,
|
||||
InteractUpdate = 4,
|
||||
|
||||
179
pumpkin-protocol/src/bedrock/server/inventory_transaction.rs
Normal file
179
pumpkin-protocol/src/bedrock/server/inventory_transaction.rs
Normal file
@ -0,0 +1,179 @@
|
||||
use std::io::{Error, ErrorKind, Read};
|
||||
|
||||
use pumpkin_macros::packet;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
use crate::{
|
||||
codec::{var_int::VarInt, var_uint::VarUInt, var_ulong::VarULong},
|
||||
serial::PacketRead,
|
||||
};
|
||||
|
||||
pub const WINDOW_ID_INVENTORY: i32 = 0;
|
||||
pub const WINDOW_ID_OFF_HAND: i32 = 119;
|
||||
pub const WINDOW_ID_ARMOUR: i32 = 120;
|
||||
pub const WINDOW_ID_UI: i32 = 124;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum InventoryActionSource {
|
||||
Container,
|
||||
World,
|
||||
Creative,
|
||||
Todo,
|
||||
Unknown(u32),
|
||||
}
|
||||
|
||||
impl From<u32> for InventoryActionSource {
|
||||
fn from(value: u32) -> Self {
|
||||
match value {
|
||||
0 => Self::Container,
|
||||
2 => Self::World,
|
||||
3 => Self::Creative,
|
||||
99999 => Self::Todo,
|
||||
_ => Self::Unknown(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TransactionData {
|
||||
Normal(NormalTransactionData),
|
||||
Mismatch(MismatchTransactionData),
|
||||
UseItem(UseItemTransactionData),
|
||||
UseItemOnEntity(UseItemOnEntityTransactionData),
|
||||
ReleaseItem(ReleaseItemTransactionData),
|
||||
}
|
||||
|
||||
#[derive(Debug, PacketRead)]
|
||||
pub struct LegacySetItemSlot {
|
||||
pub container_id: u8,
|
||||
pub slots: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InventoryAction {
|
||||
pub source_type: u32,
|
||||
pub window_id: Option<i32>,
|
||||
pub source_flags: Option<u32>,
|
||||
pub inventory_slot: u32,
|
||||
// TODO
|
||||
pub old_item: (),
|
||||
pub new_item: (),
|
||||
}
|
||||
|
||||
impl PacketRead for InventoryAction {
|
||||
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
|
||||
let source_type = VarULong::read(buf)?.0 as u32;
|
||||
|
||||
let mut window_id = None;
|
||||
let mut source_flags = None;
|
||||
|
||||
match InventoryActionSource::from(source_type) {
|
||||
InventoryActionSource::Container | InventoryActionSource::Todo => {
|
||||
window_id = Some(VarInt::read(buf)?.0);
|
||||
}
|
||||
InventoryActionSource::World => {
|
||||
source_flags = Some(VarULong::read(buf)?.0 as u32);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let inventory_slot = VarULong::read(buf)?.0 as u32;
|
||||
|
||||
// let old_item = ItemStack::read(buf)?;
|
||||
// let new_item = ItemStack::read(buf)?;
|
||||
|
||||
Ok(Self {
|
||||
source_type,
|
||||
window_id,
|
||||
source_flags,
|
||||
inventory_slot,
|
||||
old_item: (),
|
||||
new_item: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PacketRead)]
|
||||
pub struct NormalTransactionData;
|
||||
|
||||
#[derive(Debug, PacketRead)]
|
||||
pub struct MismatchTransactionData;
|
||||
|
||||
#[derive(Debug, PacketRead)]
|
||||
pub struct UseItemTransactionData {
|
||||
pub action_type: VarULong,
|
||||
pub trigger_type: VarULong,
|
||||
pub block_position: BlockPos,
|
||||
pub block_face: VarInt,
|
||||
pub hot_bar_slot: VarInt,
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[derive(Debug, PacketRead)]
|
||||
pub struct UseItemOnEntityTransactionData {
|
||||
pub target_entity_runtime_id: VarULong,
|
||||
pub action_type: VarULong,
|
||||
pub hot_bar_slot: VarInt,
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[derive(Debug, PacketRead)]
|
||||
pub struct ReleaseItemTransactionData {
|
||||
pub action_type: VarULong,
|
||||
pub hot_bar_slot: VarInt,
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[packet(30)]
|
||||
pub struct SInventoryTransaction {
|
||||
pub legacy_request_id: VarInt,
|
||||
pub legacy_set_item_slots: Vec<LegacySetItemSlot>,
|
||||
pub actions: Vec<InventoryAction>,
|
||||
pub transaction_type: VarUInt,
|
||||
pub transaction_data: TransactionData,
|
||||
}
|
||||
|
||||
impl PacketRead for SInventoryTransaction {
|
||||
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
|
||||
let legacy_request_id = VarInt::read(buf)?;
|
||||
|
||||
let mut legacy_set_item_slots = Vec::new();
|
||||
if legacy_request_id.0 != 0 {
|
||||
let len = VarUInt::read(buf)?.0;
|
||||
for _ in 0..len {
|
||||
legacy_set_item_slots.push(LegacySetItemSlot::read(buf)?);
|
||||
}
|
||||
}
|
||||
|
||||
let transaction_type = VarUInt::read(buf)?;
|
||||
|
||||
let actions_len = VarUInt::read(buf)?.0;
|
||||
let mut actions = Vec::new();
|
||||
for _ in 0..actions_len {
|
||||
actions.push(InventoryAction::read(buf)?);
|
||||
}
|
||||
|
||||
let transaction_data = match transaction_type.0 {
|
||||
0 => TransactionData::Normal(NormalTransactionData::read(buf)?),
|
||||
1 => TransactionData::Mismatch(MismatchTransactionData::read(buf)?),
|
||||
2 => TransactionData::UseItem(UseItemTransactionData::read(buf)?),
|
||||
3 => TransactionData::UseItemOnEntity(UseItemOnEntityTransactionData::read(buf)?),
|
||||
4 => TransactionData::ReleaseItem(ReleaseItemTransactionData::read(buf)?),
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
format!("Unknown inventory transaction type: {}", transaction_type.0),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
legacy_request_id,
|
||||
legacy_set_item_slots,
|
||||
actions,
|
||||
transaction_type,
|
||||
transaction_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,10 @@
|
||||
pub mod actor_event;
|
||||
pub mod animate;
|
||||
pub mod client_cache_status;
|
||||
pub mod command_request;
|
||||
pub mod container_close;
|
||||
pub mod interaction;
|
||||
pub mod inventory_transaction;
|
||||
pub mod loading_screen;
|
||||
pub mod login;
|
||||
pub mod modal_form_response;
|
||||
|
||||
@ -6,6 +6,7 @@ use pumpkin_data::tracked_data::{TrackedData, TrackedId};
|
||||
use pumpkin_inventory::build_equipment_slots;
|
||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||
use pumpkin_inventory::screen_handler::InventoryPlayer;
|
||||
use pumpkin_protocol::bedrock::server::actor_event::{ActorEventType, SActorEvent};
|
||||
use pumpkin_util::GameMode;
|
||||
use pumpkin_util::Hand;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
@ -2025,7 +2026,15 @@ impl EntityBase for LivingEntity {
|
||||
(src.z - tgt.z).atan2(src.x - tgt.x).to_degrees() as f32
|
||||
- self.entity.yaw.load()
|
||||
});
|
||||
world.broadcast_packet_all(&CHurtAnimation::new(entity_id, hurt_yaw));
|
||||
let hurt_event = SActorEvent {
|
||||
entity_runtime_id: entity_id,
|
||||
event_type: ActorEventType::Hurt,
|
||||
event_data: VarInt(0),
|
||||
fire_at_position: None,
|
||||
};
|
||||
world
|
||||
.broadcast_editioned(&CHurtAnimation::new(entity_id, hurt_yaw), &hurt_event)
|
||||
.await;
|
||||
}
|
||||
|
||||
world.broadcast_packet_all(&CDamageEvent::new(
|
||||
|
||||
@ -32,6 +32,7 @@ use pumpkin_data::{
|
||||
sound::{Sound, SoundCategory},
|
||||
};
|
||||
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
||||
use pumpkin_protocol::bedrock::client::CSetActorMotion;
|
||||
use pumpkin_protocol::java::client::play::{CUpdateEntityPos, CUpdateEntityPosRot};
|
||||
use pumpkin_protocol::{
|
||||
PositionFlag,
|
||||
@ -701,9 +702,14 @@ impl Entity {
|
||||
pub fn send_velocity(&self) {
|
||||
let velocity = self.velocity.load();
|
||||
let chunk_pos = self.chunk_pos.load();
|
||||
self.world.load().broadcast_to_chunk(
|
||||
self.world.load().broadcast_to_chunk_editioned_sync(
|
||||
chunk_pos,
|
||||
&CEntityVelocity::new(self.entity_id.into(), velocity),
|
||||
&CSetActorMotion::new(
|
||||
VarULong(self.entity_id as u64),
|
||||
Vector3::new(velocity.x as f32, velocity.y as f32, velocity.z as f32),
|
||||
VarULong(0),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@ use pumpkin_protocol::{
|
||||
command_request::SCommandRequest,
|
||||
container_close::SContainerClose,
|
||||
interaction::SInteraction,
|
||||
inventory_transaction::SInventoryTransaction,
|
||||
loading_screen::SLoadingScreen,
|
||||
login::SLogin,
|
||||
player_action::SPlayerAction,
|
||||
@ -906,6 +907,9 @@ impl BedrockClient {
|
||||
self.handle_request_chunk_radius(player, SRequestChunkRadius::read(reader)?)
|
||||
.await;
|
||||
}
|
||||
SInventoryTransaction::PACKET_ID => {
|
||||
self.handle_inventory_action(player, SInventoryTransaction::read(reader)?).await;
|
||||
}
|
||||
SInteraction::PACKET_ID => {
|
||||
self.handle_interaction(player, SInteraction::read(reader)?)
|
||||
.await;
|
||||
|
||||
@ -12,6 +12,7 @@ use pumpkin_protocol::{
|
||||
command_request::SCommandRequest,
|
||||
container_close::SContainerClose,
|
||||
interaction::{Action, SInteraction},
|
||||
inventory_transaction::{SInventoryTransaction, TransactionData},
|
||||
player_action::{Action as PlayerAction, SPlayerAction},
|
||||
player_auth_input::{InputData, SPlayerAuthInput},
|
||||
request_chunk_radius::SRequestChunkRadius,
|
||||
@ -337,6 +338,51 @@ impl BedrockClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_inventory_action(
|
||||
&self,
|
||||
player: &Arc<Player>,
|
||||
packet: SInventoryTransaction,
|
||||
) {
|
||||
match packet.transaction_data {
|
||||
TransactionData::Normal(_data) => {
|
||||
// TODO
|
||||
}
|
||||
TransactionData::Mismatch(_data) => {
|
||||
// TODO
|
||||
}
|
||||
TransactionData::UseItem(_data) => {
|
||||
// TODO
|
||||
}
|
||||
TransactionData::UseItemOnEntity(data) => {
|
||||
let target_runtime_id = data.target_entity_runtime_id.0 as i32;
|
||||
// TODO: replace with consts, i'm too lazy
|
||||
match data.action_type.0 {
|
||||
// Interact
|
||||
0 => {
|
||||
// TODO
|
||||
}
|
||||
// Attack
|
||||
1 => {
|
||||
let world = player.world();
|
||||
if let Some(target) = world.get_entity_by_id(target_runtime_id) {
|
||||
player.attack(target).await;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tracing::warn!(
|
||||
"invalid UseItemOnEntity action type {}",
|
||||
data.action_type.0
|
||||
);
|
||||
// Kick?
|
||||
}
|
||||
}
|
||||
}
|
||||
TransactionData::ReleaseItem(_data) => {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_interaction(&self, player: &Arc<Player>, packet: SInteraction) {
|
||||
match packet.action {
|
||||
Action::OpenInventory => {
|
||||
@ -348,6 +394,7 @@ impl BedrockClient {
|
||||
})
|
||||
.await;
|
||||
}
|
||||
// No longer used in newer versions
|
||||
Action::Attack => {
|
||||
let target_runtime_id = packet.target_runtime_id.0 as i32;
|
||||
let world = player.world();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user