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 {
|
pub enum Action {
|
||||||
Invalid = 0,
|
Invalid = 0,
|
||||||
Interact = 1,
|
Interact = 1,
|
||||||
|
// No longer used in newer versions
|
||||||
Attack = 2,
|
Attack = 2,
|
||||||
StopRiding = 3,
|
StopRiding = 3,
|
||||||
InteractUpdate = 4,
|
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 animate;
|
||||||
pub mod client_cache_status;
|
pub mod client_cache_status;
|
||||||
pub mod command_request;
|
pub mod command_request;
|
||||||
pub mod container_close;
|
pub mod container_close;
|
||||||
pub mod interaction;
|
pub mod interaction;
|
||||||
|
pub mod inventory_transaction;
|
||||||
pub mod loading_screen;
|
pub mod loading_screen;
|
||||||
pub mod login;
|
pub mod login;
|
||||||
pub mod modal_form_response;
|
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::build_equipment_slots;
|
||||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||||
use pumpkin_inventory::screen_handler::InventoryPlayer;
|
use pumpkin_inventory::screen_handler::InventoryPlayer;
|
||||||
|
use pumpkin_protocol::bedrock::server::actor_event::{ActorEventType, SActorEvent};
|
||||||
use pumpkin_util::GameMode;
|
use pumpkin_util::GameMode;
|
||||||
use pumpkin_util::Hand;
|
use pumpkin_util::Hand;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
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
|
(src.z - tgt.z).atan2(src.x - tgt.x).to_degrees() as f32
|
||||||
- self.entity.yaw.load()
|
- 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(
|
world.broadcast_packet_all(&CDamageEvent::new(
|
||||||
|
|||||||
@ -32,6 +32,7 @@ use pumpkin_data::{
|
|||||||
sound::{Sound, SoundCategory},
|
sound::{Sound, SoundCategory},
|
||||||
};
|
};
|
||||||
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
||||||
|
use pumpkin_protocol::bedrock::client::CSetActorMotion;
|
||||||
use pumpkin_protocol::java::client::play::{CUpdateEntityPos, CUpdateEntityPosRot};
|
use pumpkin_protocol::java::client::play::{CUpdateEntityPos, CUpdateEntityPosRot};
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
PositionFlag,
|
PositionFlag,
|
||||||
@ -701,9 +702,14 @@ impl Entity {
|
|||||||
pub fn send_velocity(&self) {
|
pub fn send_velocity(&self) {
|
||||||
let velocity = self.velocity.load();
|
let velocity = self.velocity.load();
|
||||||
let chunk_pos = self.chunk_pos.load();
|
let chunk_pos = self.chunk_pos.load();
|
||||||
self.world.load().broadcast_to_chunk(
|
self.world.load().broadcast_to_chunk_editioned_sync(
|
||||||
chunk_pos,
|
chunk_pos,
|
||||||
&CEntityVelocity::new(self.entity_id.into(), velocity),
|
&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,
|
command_request::SCommandRequest,
|
||||||
container_close::SContainerClose,
|
container_close::SContainerClose,
|
||||||
interaction::SInteraction,
|
interaction::SInteraction,
|
||||||
|
inventory_transaction::SInventoryTransaction,
|
||||||
loading_screen::SLoadingScreen,
|
loading_screen::SLoadingScreen,
|
||||||
login::SLogin,
|
login::SLogin,
|
||||||
player_action::SPlayerAction,
|
player_action::SPlayerAction,
|
||||||
@ -906,6 +907,9 @@ impl BedrockClient {
|
|||||||
self.handle_request_chunk_radius(player, SRequestChunkRadius::read(reader)?)
|
self.handle_request_chunk_radius(player, SRequestChunkRadius::read(reader)?)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
SInventoryTransaction::PACKET_ID => {
|
||||||
|
self.handle_inventory_action(player, SInventoryTransaction::read(reader)?).await;
|
||||||
|
}
|
||||||
SInteraction::PACKET_ID => {
|
SInteraction::PACKET_ID => {
|
||||||
self.handle_interaction(player, SInteraction::read(reader)?)
|
self.handle_interaction(player, SInteraction::read(reader)?)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@ -12,6 +12,7 @@ use pumpkin_protocol::{
|
|||||||
command_request::SCommandRequest,
|
command_request::SCommandRequest,
|
||||||
container_close::SContainerClose,
|
container_close::SContainerClose,
|
||||||
interaction::{Action, SInteraction},
|
interaction::{Action, SInteraction},
|
||||||
|
inventory_transaction::{SInventoryTransaction, TransactionData},
|
||||||
player_action::{Action as PlayerAction, SPlayerAction},
|
player_action::{Action as PlayerAction, SPlayerAction},
|
||||||
player_auth_input::{InputData, SPlayerAuthInput},
|
player_auth_input::{InputData, SPlayerAuthInput},
|
||||||
request_chunk_radius::SRequestChunkRadius,
|
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) {
|
pub async fn handle_interaction(&self, player: &Arc<Player>, packet: SInteraction) {
|
||||||
match packet.action {
|
match packet.action {
|
||||||
Action::OpenInventory => {
|
Action::OpenInventory => {
|
||||||
@ -348,6 +394,7 @@ impl BedrockClient {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
// No longer used in newer versions
|
||||||
Action::Attack => {
|
Action::Attack => {
|
||||||
let target_runtime_id = packet.target_runtime_id.0 as i32;
|
let target_runtime_id = packet.target_runtime_id.0 as i32;
|
||||||
let world = player.world();
|
let world = player.world();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user