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