Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Teleport to different world #532

Merged
merged 12 commits into from
Feb 11, 2025
Merged

Conversation

vyPal
Copy link
Contributor

@vyPal vyPal commented Feb 7, 2025

Implements #511

Description

This PR adds one new public function to the Player struct:

  • pub async fn teleport_world(self: Arc<Self>, new_world: Arc<World>, position: Option<Vector3<f64>>, yaw: Option<f32>, pitch: Option<f32>) - Teleport player to new world, this will also teleport the player to the specific coordinates provided by the caller. It will not check the coordinates for correctness (like if the y isn't below the surface). If no coordinates are provided, it will select the default spawn coordinates for the new_world.

Testing

Please follow our Coding Guidelines

@vyPal vyPal marked this pull request as ready for review February 7, 2025 17:17
Comment on lines 612 to 619
.broadcast_packet_all(&CRemovePlayerInfo::new(1.into(), &[uuid]))
.await;
current_world
.players
.lock()
.await
.remove(&self.gameprofile.id);
self.client
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we just do world.remove_player ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well the world.remove_player function also emits the PlayerLeaveEvent which is meant for when the player leaves the entire server, not just a specific world. I could try to modify the remove_player method to take a boolean argument which would decide if the event should be fired

Comment on lines 620 to 679
.send_packet(&CGameEvent::new(GameEvent::StartWaitingChunks, 0.0))
.await;
*self.living_entity.entity.world.write().await = new_world.clone();
new_world.players.lock().await.insert(uuid, self.clone());
self.unload_watched_chunks(&current_world).await;
let last_pos = self.living_entity.last_pos.load();
let death_dimension = self.world().await.dimension_type.name();
let death_location = BlockPos(Vector3::new(
last_pos.x.round() as i32,
last_pos.y.round() as i32,
last_pos.z.round() as i32,
));
self.client
.send_packet(&CRespawn::new(
(new_world.dimension_type as u8).into(),
new_world.dimension_type.name(),
0, // seed
self.gamemode.load() as u8,
self.gamemode.load() as i8,
false,
false,
Some((death_dimension, death_location)),
0.into(),
0.into(),
1,
))
.await;
self.send_abilities_update().await;
self.send_permission_lvl_update().await;
let info = &new_world.level.level_info;
let position = if let Some(pos) = position {
pos
} else {
Vector3::new(
f64::from(info.spawn_x),
f64::from(
new_world
.get_top_block(Vector2::new(
f64::from(info.spawn_x) as i32,
f64::from(info.spawn_x) as i32,
))
.await
+ 1,
),
f64::from(info.spawn_z),
)
};
let yaw = yaw.unwrap_or(info.spawn_angle);
let pitch = pitch.unwrap_or(10.0);
self.request_teleport(position, yaw, pitch).await;
self.living_entity.last_pos.store(position);
new_world
.worldborder
.lock()
.await
.init_client(&self.client)
.await;
self.client
.send_packet(&CGameEvent::new(GameEvent::StartWaitingChunks, 0.0))
.await;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why GameEvent::StartWaitingChunks is here twice?

.await
.remove(&self.gameprofile.id);
self.client
.send_packet(&CGameEvent::new(GameEvent::StartWaitingChunks, 0.0))
Copy link
Member

@Snowiiii Snowiiii Feb 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also i think we should have some kinda send_world_info function like in vanilla which is being send on join, respawn and here. Like this

 public void sendWorldInfo(ServerPlayerEntity player, ServerWorld world) {
        WorldBorder worldBorder = this.server.getOverworld().getWorldBorder();
        player.networkHandler.sendPacket(new WorldBorderInitializeS2CPacket(worldBorder));
        player.networkHandler.sendPacket(new WorldTimeUpdateS2CPacket(world.getTime(), world.getTimeOfDay(), world.getGameRules().getBoolean(GameRules.DO_DAYLIGHT_CYCLE)));
        player.networkHandler.sendPacket(new PlayerSpawnPositionS2CPacket(world.getSpawnPos(), world.getSpawnAngle()));
        if (world.isRaining()) {
            player.networkHandler.sendPacket(new GameStateChangeS2CPacket(GameStateChangeS2CPacket.RAIN_STARTED, GameStateChangeS2CPacket.DEMO_OPEN_SCREEN));
            player.networkHandler.sendPacket(new GameStateChangeS2CPacket(GameStateChangeS2CPacket.RAIN_GRADIENT_CHANGED, world.getRainGradient(1.0f)));
            player.networkHandler.sendPacket(new GameStateChangeS2CPacket(GameStateChangeS2CPacket.THUNDER_GRADIENT_CHANGED, world.getThunderGradient(1.0f)));
        }
        player.networkHandler.sendPacket(new GameStateChangeS2CPacket(GameStateChangeS2CPacket.INITIAL_CHUNKS_COMING, GameStateChangeS2CPacket.DEMO_OPEN_SCREEN));
        this.server.getTickManager().sendPackets(player);
    }

@Snowiiii
Copy link
Member

Looks good now. Thank you @vyPal 👍

@Snowiiii Snowiiii merged commit af0573d into Pumpkin-MC:master Feb 11, 2025
9 checks passed
urisinger pushed a commit to urisinger/Pumpkin that referenced this pull request Feb 16, 2025
* make entity world RwLock

* Add teleport function

* Mark client as not loaded before change dim

* clippy

* Post-merge issues

* FIx post merge issues

* Make world.remove_player fire event conditionally

* Remove extra StartWaitingChunks packet

* Add world.send_world_info()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants