Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion feather/protocol/src/packets/server/play.rs
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,7 @@ packets! {
#[derive(Debug, Clone)]
pub struct EntityEquipment {
Comment thread
tim-kt marked this conversation as resolved.
pub entity_id: i32,
pub entries: Vec<EquipmentEntry>,
pub entries: Vec<EquipmentEntry>, // cannot be empty (if nothing is equipped, send an empty main hand)
}

impl Readable for EntityEquipment {
Expand Down
83 changes: 72 additions & 11 deletions feather/server/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ use ahash::AHashSet;
use flume::{Receiver, Sender};
use uuid::Uuid;

use crate::{
entities::PreviousPosition, initial_handler::NewPlayer, network_id_registry::NetworkId, Options,
};
use anyhow::anyhow;
use base::{
BlockId, ChunkHandle, ChunkPosition, EntityKind, EntityMetadata, Gamemode, Position,
ProfileProperty, Text, ValidBlockPosition,
Area, BlockId, ChunkHandle, ChunkPosition, EntityKind, EntityMetadata, Gamemode, Inventory,
Position, ProfileProperty, Text, ValidBlockPosition,
};
use common::{
chat::{ChatKind, ChatMessage},
entities::player::HotbarSlot,
Window,
};
use libcraft_items::InventorySlot;
Expand All @@ -27,18 +32,15 @@ use protocol::{
self,
server::{
AddPlayer, Animation, BlockChange, ChatPosition, ChunkData, ChunkDataKind,
DestroyEntities, Disconnect, EntityAnimation, EntityHeadLook, JoinGame, KeepAlive,
PlayerInfo, PlayerPositionAndLook, PluginMessage, SendEntityMetadata, SpawnPlayer,
Title, UnloadChunk, UpdateViewPosition, WindowItems,
DestroyEntities, Disconnect, EntityAnimation, EntityEquipment, EntityHeadLook,
EquipmentEntry, EquipmentSlot::{Boots, Chestplate, Helmet, Leggings, MainHand, OffHand},
JoinGame, KeepAlive, PlayerInfo, PlayerPositionAndLook, PluginMessage,
SendEntityMetadata, SpawnPlayer, Title, UnloadChunk, UpdateViewPosition, WindowItems,
},
},
ClientPlayPacket, Nbt, ProtocolVersion, ServerPlayPacket, Writeable,
};
use quill_common::components::{OnGround, PreviousGamemode};

use crate::{
entities::PreviousPosition, initial_handler::NewPlayer, network_id_registry::NetworkId, Options,
};
use slab::Slab;

/// Max number of chunks to send to a client per tick.
Expand Down Expand Up @@ -539,11 +541,11 @@ impl Client {
self.set_slot(-1, item);
}

pub fn send_player_model_flags(&self, netowrk_id: NetworkId, model_flags: u8) {
pub fn send_player_model_flags(&self, network_id: NetworkId, model_flags: u8) {
let mut entity_metadata = EntityMetadata::new();
entity_metadata.set(16, model_flags);
self.send_packet(SendEntityMetadata {
entity_id: netowrk_id.0,
entity_id: network_id.0,
entries: entity_metadata,
});
}
Expand All @@ -558,6 +560,65 @@ impl Client {
});
}

pub fn send_entity_equipment(
&self,
network_id: NetworkId,
inventory: &Inventory,
hotbar_slot: &HotbarSlot,
) {
if self.network_id == Some(network_id) {
return;
}
log::debug!("Sending entity equipment packet");

let mut equipment = Vec::<EquipmentEntry>::new();
if let Some(m) = inventory.item(Area::Hotbar, hotbar_slot.get()) {
equipment.push(EquipmentEntry {
slot: MainHand,
item: m.clone(),
})
};
if let Some(m) = inventory.item(Area::Offhand, 0) {
equipment.push(EquipmentEntry {
slot: OffHand,
item: m.clone(),
})
};
if let Some(m) = inventory.item(Area::Boots, 0) {
equipment.push(EquipmentEntry {
slot: Boots,
item: m.clone(),
})
};
if let Some(m) = inventory.item(Area::Leggings, 0) {
equipment.push(EquipmentEntry {
slot: Leggings,
item: m.clone(),
})
};
if let Some(m) = inventory.item(Area::Chestplate, 0) {
equipment.push(EquipmentEntry {
slot: Chestplate,
item: m.clone(),
})
};
if let Some(m) = inventory.item(Area::Helmet, 0) {
equipment.push(EquipmentEntry {
slot: Helmet,
item: m.clone(),
})
};

if equipment.is_empty() {
log::warn!("could not get entity equipment");
} else {
self.send_packet(EntityEquipment {
entity_id: network_id.0,
entries: equipment,
});
Comment thread
tim-kt marked this conversation as resolved.
Outdated
}
}

pub fn send_abilities(&self, abilities: &base::anvil::player::PlayerAbilities) {
let mut bitfield = 0;
if *abilities.invulnerable {
Expand Down
2 changes: 1 addition & 1 deletion feather/server/src/packet_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub fn handle_packet(
handle_player_block_placement(game, server, packet, player_id)
}

ClientPlayPacket::HeldItemChange(packet) => handle_held_item_change(player, packet),
ClientPlayPacket::HeldItemChange(packet) => handle_held_item_change(server, player, packet),
ClientPlayPacket::InteractEntity(packet) => {
handle_interact_entity(game, server, packet, player_id)
}
Expand Down
20 changes: 18 additions & 2 deletions feather/server/src/packet_handlers/interaction.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::{ClientId, NetworkId, Server};
use base::inventory::{SLOT_HOTBAR_OFFSET, SLOT_OFFHAND};
use base::{
inventory::{SLOT_HOTBAR_OFFSET, SLOT_OFFHAND},
Area, Inventory, Position,
};
use common::entities::player::HotbarSlot;
use common::interactable::InteractableRegistry;
use common::{Game, Window};
Expand Down Expand Up @@ -224,13 +227,26 @@ pub fn handle_interact_entity(
Ok(())
}

pub fn handle_held_item_change(player: EntityRef, packet: HeldItemChange) -> SysResult {
pub fn handle_held_item_change(
server: &mut Server,
player: EntityRef,
packet: HeldItemChange,
) -> SysResult {
let new_id = packet.slot as usize;
let mut slot = player.get_mut::<HotbarSlot>()?;

log::trace!("Got player slot change from {} to {}", slot.get(), new_id);

slot.set(new_id)?;

// Send an entity equipment packet to nearby players
let position = *player.get::<Position>()?;
let network_id = *player.get::<NetworkId>()?;
let inventory = player.get::<Inventory>()?;
server.broadcast_nearby_with(position, |client| {
client.send_entity_equipment(network_id, &inventory, &slot);
});

Ok(())
}

Expand Down
39 changes: 34 additions & 5 deletions feather/server/src/packet_handlers/inventory.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use anyhow::bail;
use base::Gamemode;
use common::{window::BackingWindow, Window};
use anyhow::{anyhow, bail};
use base::{Area, Gamemode, Inventory, Position};
use common::{entities::player::HotbarSlot, window::BackingWindow, Window};
use ecs::{EntityRef, SysResult};
use protocol::packets::client::{ClickWindow, CreativeInventoryAction};

use crate::{ClientId, Server};
use crate::{ClientId, NetworkId, Server};

pub fn handle_creative_inventory_action(
player: EntityRef,
Expand All @@ -30,6 +30,35 @@ pub fn handle_creative_inventory_action(
let client_id = *player.get::<ClientId>()?;
let client = server.clients.get(client_id).unwrap();
client.send_window_items(&window);

let visible_change =
if let Some((_, area, slot)) = window.inner().index_to_slot(packet.slot as usize) {
match area {
Area::Hotbar => {
let hotbar_slot = *player.get::<HotbarSlot>()?;
hotbar_slot.get() == slot
}
Area::Helmet => true,
Area::Chestplate => true,
Area::Leggings => true,
Area::Boots => true,
Area::Offhand => true,
_ => false,
}
} else {
false
};

if visible_change {
// Send an entity equipment packet to nearby players
let position = *player.get::<Position>()?;
let network_id = *player.get::<NetworkId>()?;
let inventory = player.get::<Inventory>()?;
let hotbar_slot = player.get::<HotbarSlot>()?;
server.broadcast_nearby_with(position, |client| {
client.send_entity_equipment(network_id, &inventory, &hotbar_slot);
});
}
}

Ok(())
Expand Down Expand Up @@ -67,7 +96,7 @@ fn _handle_click_window(player: &EntityRef, packet: &ClickWindow) -> SysResult {
0 => match packet.button {
0 => window.left_click(packet.slot as usize)?,
1 => window.right_click(packet.slot as usize)?,
_ => bail!("unrecgonized click"),
_ => bail!("unrecognized click"),
},
1 => window.shift_click(packet.slot as usize)?,
5 => match packet.button {
Expand Down
9 changes: 7 additions & 2 deletions feather/server/src/systems/entity.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
//! Sends entity-related packets to clients.
//! Spawn packets, position updates, equipment, animations, etc.

use anyhow::anyhow;
use base::{
metadata::{EntityBitMask, Pose, META_INDEX_ENTITY_BITMASK, META_INDEX_POSE},
EntityMetadata, Position,
Area, EntityMetadata, Inventory, Position,
};
use common::Game;
use common::{entities::player::HotbarSlot, Game};
use ecs::{SysResult, SystemExecutor};
use protocol::packets::server::{
EquipmentEntry,
EquipmentSlot::{Boots, Chestplate, Helmet, Leggings, MainHand, OffHand},
};
use quill_common::{
components::{OnGround, Sprinting},
events::{SneakEvent, SprintEvent},
Expand Down