Skip to main content

steel_core/player/lifecycle/
world_transition.rs

1use super::*;
2
3impl Player {
4    fn apply_post_teleport_transition(&self, post_transition: &TeleportPostTransition) {
5        for action in post_transition.actions() {
6            match *action {
7                TeleportPostAction::PlayPortalSound => {
8                    self.send_packet(CLevelEvent::new(
9                        level_events::SOUND_PORTAL_TRAVEL,
10                        BlockPos::ZERO,
11                        0,
12                        false,
13                    ));
14                }
15                TeleportPostAction::PlacePortalTicket(target) => {
16                    let ticket_position = match target {
17                        PortalTicketTarget::Destination => BlockPos::from(self.position()),
18                        PortalTicketTarget::Block(pos) => pos,
19                    };
20                    self.get_world().place_portal_ticket(ticket_position);
21                }
22            }
23        }
24    }
25
26    /// Applies an ordinary player transition that has already passed server world-change checks.
27    /// Cross-domain player state is restored only by the domain-switch workflow.
28    pub(crate) fn change_world_within_domain(
29        self: &Arc<Self>,
30        teleport_transition: &TeleportTransition,
31    ) -> bool {
32        let current_world = self.get_world();
33        let new_world = Arc::clone(&teleport_transition.target_world);
34        if current_world.domain() != new_world.domain() {
35            tracing::error!(
36                entity_id = self.id(),
37                source_domain = current_world.domain(),
38                target_domain = new_world.domain(),
39                "Refusing player world change outside the domain-switch workflow"
40            );
41            return false;
42        }
43
44        let current_position = self.position();
45        let current_rotation = self.rotation();
46        let current_velocity = self.velocity();
47        let position = teleport_transition.resolved_position(current_position);
48        let rotation = teleport_transition.resolved_rotation(current_rotation);
49        let velocity =
50            teleport_transition.resolved_velocity(current_velocity, current_rotation, rotation);
51        self.set_portal_cooldown(teleport_transition.portal_cooldown);
52        if !teleport_transition.as_passenger {
53            self.stop_riding();
54        }
55        if Arc::ptr_eq(&current_world, &new_world) {
56            if let Err(error) = self.teleport_with_velocity_packet(
57                position,
58                velocity,
59                rotation,
60                teleport_transition.position,
61                teleport_transition.velocity,
62                teleport_transition.rotation,
63                teleport_transition.relatives,
64            ) {
65                panic!(
66                    "failed to commit same-world portal teleport for player {}: {error}",
67                    self.id()
68                );
69            }
70            self.reset_flying_ticks();
71        } else {
72            self.reset(new_world, ResetReason::WorldChange);
73            if !self.spawn_with_velocity_packet(
74                position,
75                rotation,
76                velocity,
77                ResetReason::WorldChange,
78                teleport_transition.position,
79                teleport_transition.rotation,
80                teleport_transition.velocity,
81                teleport_transition.relatives,
82            ) {
83                return false;
84            }
85            // Vanilla: PlayerList.sendAllPlayerInfo -> inventoryMenu.sendAllDataToRemote
86            self.send_inventory_to_remote();
87        }
88        self.apply_post_teleport_transition(&teleport_transition.post_transition);
89        true
90    }
91}