forked from feather-rs/feather
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmovement.rs
More file actions
192 lines (168 loc) · 6.35 KB
/
Copy pathmovement.rs
File metadata and controls
192 lines (168 loc) · 6.35 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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
//! Broadcasting of movement updates.
use feather_core::network::packets::{
EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove, EntityVelocity,
};
use feather_core::network::Packet;
use feather_core::util::Position;
use feather_server_types::{
EntityClientRemoveEvent, EntityId, EntitySendEvent, Game, LastKnownPositions, Network,
PreviousPosition, PreviousVelocity, Velocity,
};
use feather_server_util::{calculate_relative_move, degrees_to_stops, protocol_velocity};
use fecs::{IntoQuery, Read, World};
use smallvec::SmallVec;
use std::ops::Deref;
/// System to broadcast when an entity moves.
#[fecs::system]
pub fn broadcast_movement(game: &mut Game, world: &mut World) {
<(Read<Position>, Read<PreviousPosition>, Read<EntityId>)>::query().par_entities_for_each(
world.inner(),
|(entity, (pos, prev_pos, id))| {
let pos: Position = *pos;
let prev_pos: Position = prev_pos.0;
if pos == prev_pos {
return;
}
let entity_id = id.0;
let chunk = pos.chunk();
let players = game.chunk_holders.holders_for(chunk);
for player in players.iter().filter(|player| **player != entity) {
if let Some(network) = world.try_get::<Network>(*player) {
let last_known_positions = world.get::<LastKnownPositions>(*player);
let last_known_positions = last_known_positions.deref();
if let Some(mut last_known_pos) = last_known_positions.0.get_mut(&entity) {
for packet in
packets_for_movement_update(entity_id, *last_known_pos.value(), pos)
{
network.send_boxed(packet);
}
log::trace!("Updated position of {:?} on client {:?}", entity, player);
*last_known_pos.value_mut() = pos;
} else {
log::trace!(
"Missing last position entry for {:?} on client {:?}",
entity,
player
);
};
}
}
},
);
}
#[fecs::event_handler]
pub fn on_entity_send_update_last_known_positions(event: &EntitySendEvent, world: &mut World) {
if let Some(last_known_positions) = world.try_get::<LastKnownPositions>(event.client) {
let pos = *world.get::<Position>(event.entity);
last_known_positions.0.insert(event.entity, pos);
log::trace!(
"Inserted last position entry for {:?} (player: {:?})",
event.entity,
event.client
);
}
}
#[fecs::event_handler]
pub fn on_entity_client_remove_update_last_known_positions(
event: &EntityClientRemoveEvent,
world: &mut World,
) {
if let Some(last_known_positions) = world.try_get::<LastKnownPositions>(event.client) {
log::trace!(
"Removing last position entry for {:?} (player: {:?})",
event.entity,
event.client
);
last_known_positions.0.remove(&event.entity);
}
}
/// Broadcasts an entity's velocity.
#[fecs::system]
pub fn broadcast_velocity(world: &mut World, game: &mut Game) {
<(Read<Velocity>, Read<PreviousVelocity>, Read<EntityId>)>::query().par_entities_for_each(
world.inner(),
|(entity, (vel, prev_vel, entity_id))| {
let entity_id = entity_id.0;
if vel.0 == prev_vel.0 {
return;
}
let (velocity_x, velocity_y, velocity_z) = protocol_velocity(vel.0);
if velocity_x == 0 && velocity_y == 0 && velocity_z == 0 {
return;
}
let packet = EntityVelocity {
entity_id,
velocity_x,
velocity_y,
velocity_z,
};
game.broadcast_entity_update(world, packet, entity, None);
},
);
}
/// Returns the packet needed to notify a client
/// of a position update, from the old position to the new one.
#[allow(clippy::float_cmp)]
fn packets_for_movement_update(
entity_id: i32,
old_pos: Position,
new_pos: Position,
) -> SmallVec<[Box<dyn Packet>; 2]> {
if old_pos == new_pos {
return SmallVec::new();
}
let mut packets = SmallVec::new();
let has_moved = old_pos.x != new_pos.x || old_pos.y != new_pos.y || old_pos.z != new_pos.z;
let has_looked = old_pos.pitch != new_pos.pitch
|| old_pos.yaw != new_pos.yaw
|| old_pos.on_ground != new_pos.on_ground;
if has_moved {
let (rx, ry, rz) = calculate_relative_move(old_pos, new_pos);
if (rx == 0 && ry == 0 && rz == 0) && !has_looked {
// Because of floating point errors,
// the physics system may trigger an
// event when the distance moved is minuscule,
// which causes jittering on the client.
// Don't send the packet if it has no effect.
return SmallVec::new();
}
if has_looked {
let packet: Box<dyn Packet> = Box::new(EntityLookAndRelativeMove {
entity_id,
delta_x: rx,
delta_y: ry,
delta_z: rz,
yaw: degrees_to_stops(new_pos.yaw),
pitch: degrees_to_stops(new_pos.pitch),
on_ground: new_pos.on_ground,
});
packets.push(packet);
} else {
let packet: Box<dyn Packet> = Box::new(EntityRelativeMove {
entity_id,
delta_x: rx,
delta_y: ry,
delta_z: rz,
on_ground: new_pos.on_ground,
});
packets.push(packet);
}
} else {
let packet: Box<dyn Packet> = Box::new(EntityLook {
entity_id,
yaw: degrees_to_stops(new_pos.yaw),
pitch: degrees_to_stops(new_pos.pitch),
on_ground: new_pos.on_ground,
});
packets.push(packet);
}
// Entity Head Look also needs to be sent if the entity turned its head
if has_looked {
let packet: Box<dyn Packet> = Box::new(EntityHeadLook {
entity_id,
head_yaw: degrees_to_stops(new_pos.yaw),
});
packets.push(packet);
}
packets
}