-
Notifications
You must be signed in to change notification settings - Fork 144
Expand file tree
/
Copy pathentity.rs
More file actions
174 lines (156 loc) · 5.72 KB
/
Copy pathentity.rs
File metadata and controls
174 lines (156 loc) · 5.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//! Sends entity-related packets to clients.
//! Spawn packets, position updates, equipment, animations, etc.
use common::entities::player::HotbarSlot;
use common::Game;
use libcraft::{
entity_metadata::{EntityBitMask, Pose, META_INDEX_ENTITY_BITMASK, META_INDEX_POSE},
Area, EntityMetadata,
};
use quill::events::HeldItemChangeEvent;
use quill::{
components::{EntityInventory, EntityPosition, EntityWorld, PlayerGamemode, PreviousGamemode},
events::InventorySlotUpdateEvent,
};
use quill::{
components::{OnGround, Sprinting},
events::{SneakEvent, SprintEvent},
};
use vane::{SysResult, SystemExecutor};
use crate::{
entities::{PreviousOnGround, PreviousPosition},
NetworkId, Server,
};
mod spawn_packet;
pub fn register(game: &mut Game, systems: &mut SystemExecutor<Game>) {
spawn_packet::register(game, systems);
systems
.group::<Server>()
.add_system(send_entity_movement)
.add_system(send_entity_sneak_metadata)
.add_system(send_entity_sprint_metadata)
.add_system(send_entity_equipment);
}
/// Sends entity movement packets.
fn send_entity_movement(game: &mut Game, server: &mut Server) -> SysResult {
for (entity, (position, mut prev_position, on_ground, network_id, mut prev_on_ground, world)) in
game.ecs
.query::<(
&EntityPosition,
&mut PreviousPosition,
&OnGround,
&NetworkId,
&mut PreviousOnGround,
&EntityWorld,
)>()
.iter()
{
if position.0 != prev_position.0 {
let world = game.world(world.0)?;
server.broadcast_nearby_with_mut(world.id(), position.0, |client| {
client.update_entity_position(
*network_id,
position.0,
*prev_position,
*on_ground,
*prev_on_ground,
&*world,
game.ecs.get::<PlayerGamemode>(entity).ok().map(|g| g.0),
game.ecs.get::<PreviousGamemode>(entity).ok().map(|g| *g),
);
});
prev_position.0 = position.0;
}
if *on_ground != prev_on_ground.0 {
prev_on_ground.0 = *on_ground;
}
}
Ok(())
}
/// Sends [SendEntityMetadata](protocol::packets::server::play::SendEntityMetadata) packet for when an entity is sneaking.
fn send_entity_sneak_metadata(game: &mut Game, server: &mut Server) -> SysResult {
for (_, (position, sneak_event, is_sprinting, network_id, world)) in game
.ecs
.query::<(
&EntityPosition,
&SneakEvent,
&Sprinting,
&NetworkId,
&EntityWorld,
)>()
.iter()
{
let mut metadata = EntityMetadata::entity_base();
let mut bit_mask = EntityBitMask::empty();
// The Entity can sneak and sprint at the same time, what happens is that when it stops sneaking you immediately start running again.
bit_mask.set(EntityBitMask::CROUCHED, sneak_event.is_sneaking);
bit_mask.set(EntityBitMask::SPRINTING, is_sprinting.0);
metadata.set(META_INDEX_ENTITY_BITMASK, bit_mask.bits());
if sneak_event.is_sneaking {
metadata.set(META_INDEX_POSE, Pose::Sneaking);
} else {
metadata.set(META_INDEX_POSE, Pose::Standing);
}
server.broadcast_nearby_with(world.0, position.0, |client| {
client.send_entity_metadata(*network_id, metadata.clone());
});
}
Ok(())
}
/// Sends [SendEntityMetadata](protocol::packets::server::play::SendEntityMetadata) packet for when an entity is sprinting.
fn send_entity_sprint_metadata(game: &mut Game, server: &mut Server) -> SysResult {
for (_, (position, sprint_event, network_id, world)) in game
.ecs
.query::<(&EntityPosition, &SprintEvent, &NetworkId, &EntityWorld)>()
.iter()
{
let mut metadata = EntityMetadata::entity_base();
let mut bit_mask = EntityBitMask::empty();
bit_mask.set(EntityBitMask::SPRINTING, sprint_event.is_sprinting);
metadata.set(META_INDEX_ENTITY_BITMASK, bit_mask.bits());
server.broadcast_nearby_with(world.0, position.0, |client| {
client.send_entity_metadata(*network_id, metadata.clone());
});
}
Ok(())
}
/// Resends entity equipment when inventory slots change.
fn send_entity_equipment(game: &mut Game, server: &mut Server) -> SysResult {
let mut updated_entities = Vec::new();
for (_, event) in game.ecs.query::<&InventorySlotUpdateEvent>().iter() {
if !game.ecs.contains(event.entity) {
continue;
}
if is_equipment_area(event.area) {
updated_entities.push(event.entity);
}
}
for (entity, _event) in game.ecs.query::<&HeldItemChangeEvent>().iter() {
updated_entities.push(entity);
}
for entity in updated_entities {
let network_id = *game.ecs.get::<NetworkId>(entity)?;
let inventory = game.ecs.get::<EntityInventory>(entity)?;
let hotbar_slot = game
.ecs
.get::<HotbarSlot>(entity)
.map(|r| *r)
.unwrap_or_else(|_| HotbarSlot::new(0));
server.broadcast_nearby_with_mut(
game.ecs.get::<EntityWorld>(entity)?.0,
game.ecs.get::<EntityPosition>(entity)?.0,
|client| client.send_entity_equipment(network_id, &inventory, &hotbar_slot),
);
}
Ok(())
}
fn is_equipment_area(area: Area) -> bool {
matches!(
area,
Area::Boots
| Area::Leggings
| Area::Chestplate
| Area::Helmet
| Area::Offhand
| Area::Hotbar
)
}