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