1use super::{
2 CPlayerChat, CSystemChat, ChunkPos, ClientPacket, ConnectionProtocol, EncodedPacket, Entity,
3 EntityMovementSyncPacket, LastSeen, NetworkConnection, Player, PlayerChunkView, World,
4};
5
6impl World {
7 pub fn broadcast_chat(
12 &self,
13 mut packet: CPlayerChat,
14 _sender: &Player,
15 sender_last_seen: &LastSeen,
16 message_signature: Option<&[u8; 256]>,
17 ) {
18 log::debug!(
19 "broadcast_chat: sender_last_seen has {} signatures, message_signature present: {}",
20 sender_last_seen.len(),
21 message_signature.is_some()
22 );
23
24 self.players.iter_players(|_, recipient| {
25 let messages_received = recipient.get_and_increment_messages_received();
26 packet.global_index = messages_received;
27
28 log::debug!(
29 "Broadcasting to player {} (UUID: {}), global_index={}",
30 recipient.gameprofile.name(),
31 recipient.gameprofile.id(),
32 messages_received
33 );
34
35 let previous_messages = {
38 let chat = recipient.chat().lock();
39 chat.signature_cache
40 .index_previous_messages(sender_last_seen)
41 };
42
43 log::debug!(
44 " Indexed {} previous messages for recipient",
45 previous_messages.len()
46 );
47
48 packet.previous_messages.clone_from(&previous_messages);
49
50 recipient.send_packet(packet.clone());
52
53 {
56 let mut chat = recipient.chat().lock();
57 if let Some(signature) = message_signature {
58 chat.signature_cache.push(sender_last_seen, Some(signature));
59
60 log::debug!(" Added signature to recipient's cache and pending list");
61
62 chat.message_validator
64 .add_pending(Some(Box::new(*signature) as Box<[u8]>));
65 } else {
66 chat.message_validator.add_pending(None);
68 log::debug!(" Added unsigned message to pending list");
69 }
70 }
71
72 true
73 });
74 }
75
76 pub fn broadcast_system_chat(&self, packet: CSystemChat) {
78 self.broadcast_to_all(packet);
79 }
80
81 pub fn broadcast_to_all<P: ClientPacket>(&self, packet: P) {
83 let Ok(encoded) =
84 EncodedPacket::from_bare(packet, self.compression, ConnectionProtocol::Play)
85 else {
86 return;
87 };
88 self.broadcast_to_all_encoded(encoded);
89 }
90
91 pub fn broadcast_to_all_except<P: ClientPacket>(&self, packet: P, exclude: i32) {
93 let Ok(encoded) =
94 EncodedPacket::from_bare(packet, self.compression, ConnectionProtocol::Play)
95 else {
96 return;
97 };
98 self.broadcast_to_all_encoded_except(encoded, exclude);
99 }
100
101 pub fn broadcast_to_all_with<P: ClientPacket, F: Fn(&Player) -> P>(&self, packet: F) {
105 self.players.iter_players(|_, player| {
106 let Ok(encoded) = EncodedPacket::from_bare(
107 packet(player),
108 self.compression,
109 ConnectionProtocol::Play,
110 ) else {
111 return false;
112 };
113 player.connection.send_encoded(encoded);
114 true
115 });
116 }
117
118 pub fn broadcast_to_all_encoded(&self, packet: EncodedPacket) {
120 self.players.iter_players(|_, player| {
121 player.connection.send_encoded(packet.clone());
122 true
123 });
124 }
125
126 pub fn broadcast_to_all_encoded_except(&self, packet: EncodedPacket, exclude: i32) {
128 self.players.iter_players(|_, player| {
129 if player.id() != exclude {
130 player.connection.send_encoded(packet.clone());
131 }
132 true
133 });
134 }
135
136 pub fn broadcast_unsigned_chat(&self, mut packet: CPlayerChat) {
138 self.players.iter_players(|_, recipient| {
139 let messages_received = recipient.get_and_increment_messages_received();
140 packet.global_index = messages_received;
141
142 recipient.send_packet(packet.clone());
143 true
144 });
145 }
146
147 pub fn broadcast_to_nearby<P: ClientPacket>(
152 &self,
153 chunk: ChunkPos,
154 packet: P,
155 exclude: Option<i32>,
156 ) {
157 let Ok(encoded) =
158 EncodedPacket::from_bare(packet, self.compression, ConnectionProtocol::Play)
159 else {
160 return;
161 };
162 self.broadcast_to_nearby_encoded(chunk, encoded, exclude);
163 }
164
165 pub fn broadcast_to_nearby_encoded(
169 &self,
170 chunk: ChunkPos,
171 packet: EncodedPacket,
172 exclude: Option<i32>,
173 ) {
174 let tracking_players = self.get_packet_tracking_players(chunk);
175 for entity_id in tracking_players {
176 if Some(entity_id) == exclude {
177 continue;
178 }
179 if let Some(player) = self.players.get_by_entity_id(entity_id) {
180 player.connection.send_encoded(packet.clone());
181 }
182 }
183 }
184
185 pub fn get_packet_tracking_players(&self, chunk: ChunkPos) -> Vec<i32> {
187 self.player_area_map
188 .get_tracking_players(chunk)
189 .into_iter()
190 .filter(|entity_id| {
191 self.players
192 .get_by_entity_id(*entity_id)
193 .is_some_and(|player| player.chunk_sender().lock().is_chunk_sent(chunk))
194 })
195 .collect()
196 }
197
198 pub fn get_light_packet_tracking_players(&self, chunk: ChunkPos) -> Vec<i32> {
200 self.player_area_map
201 .get_tracking_players(chunk)
202 .into_iter()
203 .filter(|entity_id| {
204 let Some(player) = self.players.get_by_entity_id(*entity_id) else {
205 return false;
206 };
207 let Some(view) = *player.last_tracking_view.lock() else {
208 return false;
209 };
210 let chunk_sender = player.chunk_sender().lock();
211 let is_chunk_sent = |pos| chunk_sender.is_chunk_sent(pos);
212 Self::chunk_is_on_packet_tracked_border(view, chunk, &is_chunk_sent)
213 })
214 .collect()
215 }
216
217 pub(super) fn chunk_is_on_packet_tracked_border(
218 view: PlayerChunkView,
219 chunk: ChunkPos,
220 is_chunk_sent: &impl Fn(ChunkPos) -> bool,
221 ) -> bool {
222 if !Self::chunk_is_packet_tracked(view, chunk, is_chunk_sent) {
223 return false;
224 }
225
226 for dx in -1..=1 {
227 for dz in -1..=1 {
228 if dx == 0 && dz == 0 {
229 continue;
230 }
231
232 let neighbor = ChunkPos::new(chunk.0.x + dx, chunk.0.y + dz);
233 if !Self::chunk_is_packet_tracked(view, neighbor, is_chunk_sent) {
234 return true;
235 }
236 }
237 }
238
239 false
240 }
241
242 pub(super) fn chunk_is_packet_tracked(
243 view: PlayerChunkView,
244 chunk: ChunkPos,
245 is_chunk_sent: &impl Fn(ChunkPos) -> bool,
246 ) -> bool {
247 view.contains(chunk) && is_chunk_sent(chunk)
248 }
249
250 pub fn broadcast_to_entity_trackers<P: ClientPacket>(
252 &self,
253 entity_id: i32,
254 packet: P,
255 exclude: Option<i32>,
256 ) {
257 let Ok(encoded) =
258 EncodedPacket::from_bare(packet, self.compression, ConnectionProtocol::Play)
259 else {
260 return;
261 };
262 self.broadcast_to_entity_trackers_encoded(entity_id, encoded, exclude);
263 }
264
265 pub fn broadcast_to_entity_trackers_except_many<P: ClientPacket>(
267 &self,
268 entity_id: i32,
269 packet: P,
270 excluded_player_ids: &[i32],
271 ) {
272 let Ok(encoded) =
273 EncodedPacket::from_bare(packet, self.compression, ConnectionProtocol::Play)
274 else {
275 return;
276 };
277
278 for player_id in self.entity_tracker.tracking_player_ids(entity_id) {
279 if excluded_player_ids.contains(&player_id) {
280 continue;
281 }
282 if let Some(player) = self.players.get_by_entity_id(player_id) {
283 player.connection.send_encoded(encoded.clone());
284 }
285 }
286 }
287
288 pub fn broadcast_movement_sync_to_entity_trackers(
290 &self,
291 entity_id: i32,
292 packet: EntityMovementSyncPacket,
293 exclude: Option<i32>,
294 ) {
295 let Some(encoded) = self.encode_movement_sync_packet(packet) else {
296 return;
297 };
298 self.broadcast_to_entity_trackers_encoded(entity_id, encoded, exclude);
299 }
300
301 pub fn broadcast_to_entity_trackers_encoded(
303 &self,
304 entity_id: i32,
305 packet: EncodedPacket,
306 exclude: Option<i32>,
307 ) {
308 for player_id in self.entity_tracker.tracking_player_ids(entity_id) {
309 if Some(player_id) == exclude {
310 continue;
311 }
312 if let Some(player) = self.players.get_by_entity_id(player_id) {
313 player.connection.send_encoded(packet.clone());
314 }
315 }
316 }
317
318 pub(super) fn encode_movement_sync_packet(
319 &self,
320 packet: EntityMovementSyncPacket,
321 ) -> Option<EncodedPacket> {
322 let encoded = match packet {
323 EntityMovementSyncPacket::Position(packet) => {
324 EncodedPacket::from_bare(packet, self.compression, ConnectionProtocol::Play)
325 }
326 EntityMovementSyncPacket::PositionRotation(packet) => {
327 EncodedPacket::from_bare(packet, self.compression, ConnectionProtocol::Play)
328 }
329 EntityMovementSyncPacket::Rotation(packet) => {
330 EncodedPacket::from_bare(packet, self.compression, ConnectionProtocol::Play)
331 }
332 EntityMovementSyncPacket::HeadRotation(packet) => {
333 EncodedPacket::from_bare(packet, self.compression, ConnectionProtocol::Play)
334 }
335 EntityMovementSyncPacket::PositionSync(packet) => {
336 EncodedPacket::from_bare(packet, self.compression, ConnectionProtocol::Play)
337 }
338 EntityMovementSyncPacket::Velocity(packet) => {
339 EncodedPacket::from_bare(packet, self.compression, ConnectionProtocol::Play)
340 }
341 };
342 encoded.ok()
343 }
344}