1use std::ptr;
2
3use crate::player::connection::NetworkConnection;
4
5use super::{
6 Arc, CLogin, CSetDefaultSpawnPosition, CommonPlayerSpawnInfo, DVec3, DomainPlayerData,
7 DomainPlayerState, DomainResidenceToken, EnderPearlRestoreJob, Entity, IMMEDIATE_RESPAWN,
8 Identifier, LIMITED_CRAFTING, PersistentEnderPearl, PersistentRootVehicle, Player,
9 PreparedSpawn, REDUCED_DEBUG_INFO, RegistryEntry, RespawnData, RootVehicleRestoreJob, Server,
10 UnpreparedDomainPlayerData, UnpreparedDomainPlayerState, Uuid, World, apply_default_spawn,
11 local_respawn_data_for_world,
12};
13
14pub(super) struct PreparedDomainRestores {
15 pub(super) target_world: Arc<World>,
16 pub(super) root_vehicle: Option<PersistentRootVehicle>,
17 pub(super) ender_pearls: Vec<PreparedEnderPearlRestore>,
18}
19
20pub(super) struct PreparedEnderPearlRestore {
21 pub(super) world: Arc<World>,
22 pub(super) payload: PersistentEnderPearl,
23}
24
25impl Server {
26 pub(super) async fn load_join_domain(&self, player: &Player) -> Result<String, String> {
27 match self
28 .player_data_storage
29 .load_global(player.gameprofile.id)
30 .await
31 {
32 Ok(Some(global)) if self.worlds.has_domain(&global.last_active_domain) => {
33 Ok(global.last_active_domain)
34 }
35 Ok(Some(global)) => {
36 log::warn!(
37 "Player {} last active domain {} no longer exists, using default domain",
38 player.gameprofile.name,
39 global.last_active_domain
40 );
41 Ok(self.worlds.default_domain().to_owned())
42 }
43 Ok(None) => Ok(self.worlds.default_domain().to_owned()),
44 Err(e) => Err(format!("failed to load global player data: {e}")),
45 }
46 }
47
48 pub(super) async fn load_domain_player_state(
49 &self,
50 player: &Player,
51 target_domain: &str,
52 explicit_target_world: Option<Arc<World>>,
53 ) -> Result<DomainPlayerState, String> {
54 let state = self
55 .load_unprepared_domain_player_state(player, target_domain, explicit_target_world)
56 .await?;
57 self.prepare_domain_player_state(target_domain, state).await
58 }
59
60 pub(in crate::server) async fn load_unprepared_domain_player_state(
61 &self,
62 player: &Player,
63 target_domain: &str,
64 explicit_target_world: Option<Arc<World>>,
65 ) -> Result<UnpreparedDomainPlayerState, String> {
66 let has_explicit_target = explicit_target_world.is_some();
67 let default_world = self
68 .worlds
69 .default_world(target_domain)
70 .cloned()
71 .ok_or_else(|| format!("domain {target_domain} has no default world"))?;
72
73 match self
74 .player_data_storage
75 .load_domain(target_domain, player.gameprofile.id)
76 .await
77 {
78 Ok(Some(saved_data)) => {
79 let saved_world = self.resolve_saved_world(
80 &saved_data.world,
81 target_domain,
82 explicit_target_world.as_ref().map(|world| &world.key),
83 &player.gameprofile.name,
84 );
85 let (world, data) = if let Some(saved_world) = saved_world {
86 (
87 saved_world,
88 UnpreparedDomainPlayerData::SavedRestored {
89 data: Box::new(saved_data),
90 },
91 )
92 } else {
93 (
94 explicit_target_world.unwrap_or(default_world),
95 UnpreparedDomainPlayerData::SavedWithoutLocation {
96 data: Box::new(saved_data),
97 },
98 )
99 };
100 log::info!("Loaded saved data for player {}", player.gameprofile.name);
101 Ok(UnpreparedDomainPlayerState {
102 world,
103 data,
104 explicit_target: has_explicit_target,
105 })
106 }
107 Ok(None) => {
108 log::debug!(
109 "No saved data for player {} in domain {}, using defaults",
110 player.gameprofile.name,
111 target_domain
112 );
113 Ok(UnpreparedDomainPlayerState {
114 world: explicit_target_world.unwrap_or(default_world),
115 data: UnpreparedDomainPlayerData::FirstVisit,
116 explicit_target: has_explicit_target,
117 })
118 }
119 Err(e) => Err(format!(
120 "failed to load domain player data for {} in domain {}: {e}",
121 player.gameprofile.name, target_domain
122 )),
123 }
124 }
125
126 async fn prepare_domain_player_state(
127 &self,
128 target_domain: &str,
129 state: UnpreparedDomainPlayerState,
130 ) -> Result<DomainPlayerState, String> {
131 let UnpreparedDomainPlayerState {
132 mut world,
133 explicit_target,
134 data,
135 } = state;
136 let (data, spawn_position) = match data {
137 UnpreparedDomainPlayerData::SavedRestored { data } => {
138 let spawn_position = DVec3::new(data.pos[0], data.pos[1], data.pos[2]);
139 (DomainPlayerData::SavedRestored { data }, spawn_position)
140 }
141 UnpreparedDomainPlayerData::SavedWithoutLocation { data } => {
142 let (spawn_world, spawn) = self
143 .prepare_target_spawn(target_domain, explicit_target, &world)
144 .await?;
145 world = spawn_world;
146 (
147 DomainPlayerData::SavedWithoutLocation { data, spawn },
148 spawn.position,
149 )
150 }
151 UnpreparedDomainPlayerData::FirstVisit => {
152 let (spawn_world, spawn) = self
153 .prepare_target_spawn(target_domain, explicit_target, &world)
154 .await?;
155 world = spawn_world;
156 (DomainPlayerData::FirstVisit { spawn }, spawn.position)
157 }
158 };
159 let spawn_chunk_request = world.prepare_player_spawn_chunks(spawn_position).await?;
160 Ok(DomainPlayerState {
161 world,
162 data,
163 spawn_chunk_request,
164 })
165 }
166
167 async fn prepare_target_spawn(
168 &self,
169 target_domain: &str,
170 explicit_target_world: bool,
171 world: &Arc<World>,
172 ) -> Result<(Arc<World>, PreparedSpawn), String> {
173 if explicit_target_world {
174 return Ok((world.clone(), Self::prepare_default_spawn(world).await?));
175 }
176
177 let (world, respawn_data) = self.respawn_world_and_data_for_domain(target_domain)?;
178 let spawn = Self::prepare_respawn_spawn(&world, &respawn_data).await?;
179 Ok((world, spawn))
180 }
181
182 async fn prepare_default_spawn(world: &Arc<World>) -> Result<PreparedSpawn, String> {
183 let (spawn, spawn_pos) = {
184 let level_data = world.level_data.read();
185 (
186 level_data.data().spawn.clone(),
187 level_data.data().spawn_pos(),
188 )
189 };
190 let position = world
191 .find_adjusted_shared_spawn_pos(spawn_pos, world.default_gamemode)
192 .await?;
193 Ok(PreparedSpawn {
194 position,
195 rotation: (spawn.angle, 0.0),
196 })
197 }
198
199 async fn prepare_respawn_spawn(
200 world: &Arc<World>,
201 respawn_data: &RespawnData,
202 ) -> Result<PreparedSpawn, String> {
203 let position = world
204 .find_adjusted_shared_spawn_pos(respawn_data.pos(), world.default_gamemode)
205 .await?;
206 Ok(PreparedSpawn {
207 position,
208 rotation: (respawn_data.yaw, respawn_data.pitch),
209 })
210 }
211
212 fn resolve_saved_world(
213 &self,
214 saved_world: &str,
215 target_domain: &str,
216 explicit_target_world: Option<&Identifier>,
217 player_name: &str,
218 ) -> Option<Arc<World>> {
219 let Ok(saved_world_key) = saved_world.parse::<Identifier>() else {
220 log::warn!(
221 "Saved world {saved_world} for player {player_name} is invalid, using target spawn"
222 );
223 return None;
224 };
225 if saved_world_key.namespace.as_ref() != target_domain {
226 log::warn!(
227 "Saved world {saved_world_key} for player {player_name} is outside target domain {target_domain}, using target spawn"
228 );
229 return None;
230 }
231 if let Some(explicit_target_world) = explicit_target_world
232 && explicit_target_world != &saved_world_key
233 {
234 return None;
235 }
236 let Some(saved_world) = self.worlds.get(&saved_world_key) else {
237 log::warn!(
238 "Saved world {saved_world_key} for player {player_name} is missing, using target spawn"
239 );
240 return None;
241 };
242 Some(Arc::clone(saved_world))
243 }
244
245 pub(super) fn apply_domain_player_state(player: &Arc<Player>, state: &DomainPlayerState) {
246 match &state.data {
247 DomainPlayerData::SavedRestored { data } => {
248 data.apply_to_player(player);
249 }
250 DomainPlayerData::SavedWithoutLocation { data, spawn } => {
251 apply_default_spawn(player, &state.world, *spawn);
252 data.apply_to_player_without_location(player);
253 }
254 DomainPlayerData::FirstVisit { spawn } => {
255 player.reset_domain_data_for_first_visit();
256 apply_default_spawn(player, &state.world, *spawn);
257 }
258 }
259 }
260
261 pub(super) fn prepare_domain_restores(
262 &self,
263 player: &Player,
264 state: &DomainPlayerState,
265 ) -> PreparedDomainRestores {
266 let target_domain = state.world.domain();
267 let ender_pearls = Self::ender_pearls_to_restore(state)
268 .into_iter()
269 .filter_map(|payload| {
270 self.resolve_pearl_world(&payload.world, target_domain, player)
271 .map(|world| PreparedEnderPearlRestore { world, payload })
272 })
273 .collect();
274 PreparedDomainRestores {
275 target_world: Arc::clone(&state.world),
276 root_vehicle: Self::root_vehicle_to_restore(state),
277 ender_pearls,
278 }
279 }
280
281 pub(super) fn install_domain_restores(
282 player: &Player,
283 residence_token: DomainResidenceToken,
284 restores: &PreparedDomainRestores,
285 ) -> bool {
286 player.install_pending_domain_restores(
287 residence_token,
288 &restores.target_world,
289 restores.root_vehicle.clone(),
290 restores
291 .ender_pearls
292 .iter()
293 .map(|restore| restore.payload.clone())
294 .collect(),
295 )
296 }
297
298 pub(super) fn schedule_domain_restores(
299 &self,
300 player: &Arc<Player>,
301 residence_token: DomainResidenceToken,
302 restores: PreparedDomainRestores,
303 ) {
304 if let Some(root_vehicle) = restores.root_vehicle {
305 if let Some(job) = RootVehicleRestoreJob::new(
306 Arc::clone(player),
307 Arc::clone(&restores.target_world),
308 &root_vehicle,
309 residence_token,
310 ) {
311 self.jobs.spawn(job);
312 } else {
313 player.take_matching_pending_root_vehicle(
314 residence_token,
315 &restores.target_world,
316 root_vehicle.attach,
317 root_vehicle.entity.uuid,
318 );
319 }
320 }
321
322 for restore in restores.ender_pearls {
323 let pearl_uuid = Uuid::from_bytes(restore.payload.entity.uuid);
324 if let Some(job) = EnderPearlRestoreJob::new(
325 Arc::clone(player),
326 restore.world,
327 restore.payload.entity,
328 residence_token,
329 ) {
330 self.jobs.spawn(job);
331 } else {
332 player.discard_pending_ender_pearl(residence_token, pearl_uuid);
333 }
334 }
335 }
336
337 pub(super) fn root_vehicle_to_restore(
338 state: &DomainPlayerState,
339 ) -> Option<PersistentRootVehicle> {
340 match &state.data {
341 DomainPlayerData::SavedRestored { data } => data.root_vehicle.clone(),
342 DomainPlayerData::SavedWithoutLocation { .. } | DomainPlayerData::FirstVisit { .. } => {
343 None
344 }
345 }
346 }
347
348 fn ender_pearls_to_restore(state: &DomainPlayerState) -> Vec<PersistentEnderPearl> {
349 match &state.data {
350 DomainPlayerData::SavedRestored { data }
351 | DomainPlayerData::SavedWithoutLocation { data, .. } => data.ender_pearls.clone(),
352 DomainPlayerData::FirstVisit { .. } => Vec::new(),
353 }
354 }
355
356 pub(super) fn resolve_pearl_world(
357 &self,
358 world_key: &str,
359 expected_domain: &str,
360 player: &Player,
361 ) -> Option<Arc<World>> {
362 let Ok(key) = world_key.parse::<Identifier>() else {
363 log::warn!(
364 "Saved ender pearl world {world_key} for player {} is invalid, skipping",
365 player.gameprofile.name
366 );
367 return None;
368 };
369 let Some(world) = self.worlds.get(&key) else {
370 log::warn!(
371 "Saved ender pearl world {key} for player {} is missing, skipping",
372 player.gameprofile.name
373 );
374 return None;
375 };
376 if world.domain() != expected_domain {
377 log::warn!(
378 "Saved ender pearl world {key} belongs to domain {}, not player domain \
379 {expected_domain}, skipping",
380 world.domain()
381 );
382 return None;
383 }
384 Some(world.clone())
385 }
386
387 pub(super) fn send_login_packet(&self, player: &Player, world: &World) {
388 let reduced_debug_info = world.get_game_rule(&REDUCED_DEBUG_INFO);
389 let immediate_respawn = world.get_game_rule(&IMMEDIATE_RESPAWN);
390 let do_limited_crafting = world.get_game_rule(&LIMITED_CRAFTING);
391
392 let hashed_seed = world.obfuscated_seed();
394
395 player.send_packet(CLogin {
396 player_id: player.id(),
397 hardcore: false,
398 levels: self.worlds.keys().cloned().collect(),
399 max_players: self.config.max_players as i32,
400 chunk_radius: self.config.view_distance.into(),
401 simulation_distance: self.config.simulation_distance.into(),
402 reduced_debug_info,
403 show_death_screen: !immediate_respawn,
404 do_limited_crafting,
405 common_player_spawn_info: CommonPlayerSpawnInfo {
406 dimension_type: world.dimension_type.id() as i32,
407 dimension: world.key.clone(),
408 seed: hashed_seed,
409 game_type: player.game_mode(),
410 previous_game_type: player.previous_game_mode(),
411 is_debug: false,
412 is_flat: world.is_flat,
413 last_death_location: None,
414 portal_cooldown: 0,
415 sea_level: world.sea_level,
416 },
417 online_mode: self.config.online_mode,
418 enforces_secure_chat: self.config.enforce_secure_chat,
419 });
420 }
421
422 pub fn get_players(&self) -> Vec<Arc<Player>> {
424 let mut players = vec![];
425 self.online_players.iter_players(|_, p: &Arc<Player>| {
426 players.push(p.clone());
427 true
428 });
429 players
430 }
431
432 pub(crate) fn owns_online_player(&self, player: &Player) -> bool {
434 self.online_players
435 .get_by_uuid(&player.gameprofile.id)
436 .is_some_and(|online| ptr::eq(online.as_ref(), player))
437 }
438
439 pub(crate) fn live_world_for_player(&self, player: &Player) -> Option<Arc<World>> {
445 if !self.owns_online_player(player) {
446 return None;
447 }
448
449 let world = player.get_world();
450 world.contains_player(player).then_some(world)
451 }
452
453 pub(crate) fn command_world_for_player(&self, player: &Player) -> Option<Arc<World>> {
459 if player.connection.closed() || player.domain_switch_blocks_gameplay() {
460 return None;
461 }
462 self.live_world_for_player(player)
463 }
464
465 #[must_use]
467 pub fn player_count(&self) -> usize {
468 self.online_players.len()
469 }
470
471 #[must_use]
473 pub fn player_sample(&self) -> Vec<(String, String)> {
474 const MAX_SAMPLE: usize = 12;
475
476 let players = self.get_players();
477 if players.is_empty() {
478 return vec![];
479 }
480
481 let sample_size = players.len().min(MAX_SAMPLE);
482 let offset = if players.len() > sample_size {
484 (rand::random::<u64>() as usize) % (players.len() - sample_size + 1)
485 } else {
486 0
487 };
488
489 let mut sample: Vec<(String, String)> = players[offset..offset + sample_size]
490 .iter()
491 .map(|p| {
492 (
493 p.gameprofile.name.clone(),
494 p.gameprofile.id.hyphenated().to_string(),
495 )
496 })
497 .collect();
498
499 for i in (1..sample.len()).rev() {
501 let j = (rand::random::<u64>() as usize) % (i + 1);
502 sample.swap(i, j);
503 }
504
505 sample
506 }
507
508 pub fn overworld(&self) -> &Arc<World> {
512 self.worlds.server_default_world().unwrap_or_else(|| {
513 self.worlds
514 .values()
515 .next()
516 .expect("At least one world must exist")
517 })
518 }
519
520 pub fn respawn_world_and_data_for_domain(
522 &self,
523 domain: &str,
524 ) -> Result<(Arc<World>, RespawnData), String> {
525 let default_world = self
526 .worlds
527 .default_world(domain)
528 .cloned()
529 .ok_or_else(|| format!("domain {domain} has no default world"))?;
530 let respawn_data = {
531 let level_data = default_world.level_data.read();
532 level_data.data().respawn_data_or_local(&default_world.key)
533 };
534
535 let Some(target_world) = self
536 .worlds
537 .get(respawn_data.dimension())
538 .filter(|world| world.domain() == domain)
539 .cloned()
540 else {
541 let respawn_data = default_world
542 .world_border_adjusted_respawn_data(local_respawn_data_for_world(&default_world));
543 return Ok((default_world.clone(), respawn_data));
544 };
545
546 let respawn_data = target_world.world_border_adjusted_respawn_data(respawn_data);
547 Ok((target_world, respawn_data))
548 }
549
550 pub fn respawn_data_for_domain(&self, domain: &str) -> Result<RespawnData, String> {
552 self.respawn_world_and_data_for_domain(domain)
553 .map(|(_, respawn_data)| respawn_data)
554 }
555
556 pub fn set_respawn_data(&self, respawn_data: RespawnData) -> Result<(), String> {
558 let domain = respawn_data.dimension().namespace.as_ref();
559 let default_world = self
560 .worlds
561 .default_world(domain)
562 .cloned()
563 .ok_or_else(|| format!("domain {domain} has no default world"))?;
564 let target_world = self
565 .worlds
566 .get(respawn_data.dimension())
567 .filter(|world| world.domain() == domain)
568 .cloned()
569 .ok_or_else(|| {
570 format!(
571 "respawn dimension {} is not loaded in domain {domain}",
572 respawn_data.dimension()
573 )
574 })?;
575
576 if Arc::ptr_eq(&default_world, &target_world) {
577 let mut level_data = default_world.level_data.write();
578 let data = level_data.data_mut();
579 data.set_spawn_pos(respawn_data.pos());
580 data.spawn.angle = respawn_data.yaw;
581 data.set_respawn_data(respawn_data.clone());
582 } else {
583 default_world
584 .level_data
585 .write()
586 .data_mut()
587 .set_respawn_data(respawn_data.clone());
588
589 let mut level_data = target_world.level_data.write();
590 let data = level_data.data_mut();
591 data.set_spawn_pos(respawn_data.pos());
592 data.spawn.angle = respawn_data.yaw;
593 }
594
595 let packet = CSetDefaultSpawnPosition {
596 global_pos: respawn_data.global_pos.clone(),
597 yaw: respawn_data.yaw,
598 pitch: respawn_data.pitch,
599 };
600 for world in self
601 .worlds
602 .values()
603 .filter(|world| world.domain() == domain)
604 {
605 world.broadcast_to_all(packet.clone());
606 }
607
608 Ok(())
609 }
610
611 pub fn nether(&self) -> Option<&Arc<World>> {
613 let key = Identifier::new(self.worlds.default_domain().to_owned(), "the_nether");
614 self.worlds.get(&key)
615 }
616
617 pub fn the_end(&self) -> Option<&Arc<World>> {
619 let key = Identifier::new(self.worlds.default_domain().to_owned(), "the_end");
620 self.worlds.get(&key)
621 }
622}