steel_core/player/connection/
mod.rs1mod java;
7
8pub use java::{
9 BundleBuilder, JavaConnection, JavaNetworkReader, JavaNetworkWriter, JavaTransportRead,
10 JavaTransportWrite, OutboundPacket,
11};
12pub(crate) use java::{ScheduledPacketExecution, ScheduledPlayPacket};
13
14use enum_dispatch::enum_dispatch;
15use steel_protocol::packet_traits::{ClientPacket, CompressionInfo, EncodedPacket};
16use steel_protocol::packets::common::{
17 ChatVisibility, HumanoidArm, ParticleStatus, SClientInformation,
18};
19use steel_protocol::packets::game::CPlayerInfoUpdate;
20use steel_protocol::utils::ConnectionProtocol;
21use text_components::TextComponent;
22
23use crate::player::Player;
24
25#[derive(Debug, Clone)]
28pub struct ClientInformation {
29 pub language: String,
31 pub view_distance: u8,
33 pub chat_visibility: ChatVisibility,
35 pub chat_colors: bool,
37 pub model_customization: u8,
39 pub main_hand: HumanoidArm,
41 pub text_filtering_enabled: bool,
43 pub allows_listing: bool,
45 pub particle_status: ParticleStatus,
47}
48
49impl Default for ClientInformation {
50 fn default() -> Self {
51 Self {
52 language: "en_us".to_string(),
53 view_distance: 8,
54 chat_visibility: ChatVisibility::Full,
55 chat_colors: true,
56 model_customization: 0,
57 main_hand: HumanoidArm::Right,
58 text_filtering_enabled: false,
59 allows_listing: true,
60 particle_status: ParticleStatus::All,
61 }
62 }
63}
64
65#[enum_dispatch]
77pub trait NetworkConnection: Send + Sync {
78 fn compression(&self) -> Option<CompressionInfo>;
82
83 fn send_encoded(&self, packet: EncodedPacket);
88
89 fn send_encoded_bundle(&self, packets: Vec<EncodedPacket>);
95
96 fn disconnect_with_reason(&self, reason: TextComponent);
98
99 fn tick(&self);
101
102 fn latency(&self) -> i32;
104
105 fn close(&self);
107
108 fn closed(&self) -> bool;
110}
111
112#[enum_dispatch(NetworkConnection)]
117pub enum PlayerConnection {
118 Java(JavaConnection),
120 Other(Box<dyn NetworkConnection>),
122}
123
124impl NetworkConnection for Box<dyn NetworkConnection> {
125 fn compression(&self) -> Option<CompressionInfo> {
126 (**self).compression()
127 }
128
129 fn send_encoded(&self, packet: EncodedPacket) {
130 (**self).send_encoded(packet);
131 }
132
133 fn send_encoded_bundle(&self, packets: Vec<EncodedPacket>) {
134 (**self).send_encoded_bundle(packets);
135 }
136
137 fn disconnect_with_reason(&self, reason: TextComponent) {
138 (**self).disconnect_with_reason(reason);
139 }
140
141 fn tick(&self) {
142 (**self).tick();
143 }
144
145 fn latency(&self) -> i32 {
146 (**self).latency()
147 }
148
149 fn close(&self) {
150 (**self).close();
151 }
152
153 fn closed(&self) -> bool {
154 (**self).closed()
155 }
156}
157
158impl Player {
159 pub fn send_packet<P: ClientPacket>(&self, packet: P) {
168 let encoded = EncodedPacket::from_bare(
169 packet,
170 self.connection.compression(),
171 ConnectionProtocol::Play,
172 )
173 .expect("Failed to encode packet");
174 self.connection.send_encoded(encoded);
175 }
176
177 pub fn send_bundle<F>(&self, f: F)
183 where
184 F: FnOnce(&mut BundleBuilder),
185 {
186 let mut builder = BundleBuilder::new(self.connection.compression());
187 f(&mut builder);
188 let packets = builder.into_packets();
189 if !packets.is_empty() {
190 self.connection.send_encoded_bundle(packets);
191 }
192 }
193
194 pub fn disconnect(&self, reason: impl Into<TextComponent>) {
196 self.connection.disconnect_with_reason(reason.into());
197 }
198
199 pub fn close_connection(&self) {
206 self.connection.close();
207 }
208
209 pub fn handle_client_information(&self, packet: SClientInformation) {
211 let old_view_distance = self.view_distance();
212 let was_hat_shown = self.shows_hat();
213
214 let info = ClientInformation {
216 language: packet.language,
217 view_distance: packet
218 .view_distance
219 .max(2)
220 .cast_unsigned()
221 .min(self.config.view_distance.max(2)),
222 chat_visibility: packet.chat_visibility,
223 chat_colors: packet.chat_colors,
224 model_customization: packet.model_customization,
225 main_hand: packet.main_hand,
226 text_filtering_enabled: packet.text_filtering_enabled,
227 allows_listing: packet.allows_listing,
228 particle_status: packet.particle_status,
229 };
230 self.set_client_information(info);
231
232 let show_hat = self.shows_hat();
233 if show_hat != was_hat_shown {
234 self.server()
235 .broadcast_to_online(CPlayerInfoUpdate::update_hat(self.gameprofile.id, show_hat));
236 }
237
238 if old_view_distance != self.view_distance() {
241 self.get_world().chunk_map.update_player_status(self);
242 }
243 }
244
245 #[must_use]
247 pub fn client_information(&self) -> ClientInformation {
248 self.client_information.lock().clone()
249 }
250
251 pub fn set_client_information(&self, info: ClientInformation) {
253 Self::apply_client_information_to_entity_data(&mut self.entity_data.lock(), &info);
254 *self.client_information.lock() = info;
255 }
256
257 #[must_use]
262 pub fn view_distance(&self) -> u8 {
263 let client_view_distance = self.client_information.lock().view_distance;
264 client_view_distance.min(self.world.load().view_distance)
265 }
266}