From 930155f028dd5e189359c97b58569f742e4eed53 Mon Sep 17 00:00:00 2001 From: Doc Date: Sat, 13 Sep 2025 09:49:38 -0300 Subject: [PATCH 1/5] Check valid level bounds for teleport Took 1 hour 1 minute --- .../java/org/bukkit/craftbukkit/entity/CraftEntity.java | 7 ++++--- .../org/bukkit/craftbukkit/event/CraftEventFactory.java | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index b38073628d3b..8a0636535573 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -48,6 +48,7 @@ import org.bukkit.craftbukkit.CraftSound; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.event.CraftEventFactory; import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; import org.bukkit.craftbukkit.util.CraftChatMessage; @@ -295,6 +296,7 @@ public boolean teleport(Location location, TeleportCause cause) { public boolean teleport(Location location, TeleportCause cause, io.papermc.paper.entity.TeleportFlag... flags) { // Paper end Preconditions.checkArgument(location != null, "location cannot be null"); + Preconditions.checkArgument(ServerLevel.isInSpawnableBounds(CraftLocation.toBlockPosition(location)), "location is out of spawnable bounds [x/z between %s and %s or y between %s and %s]", -ServerLevel.MAX_LEVEL_SIZE, ServerLevel.MAX_LEVEL_SIZE, ServerLevel.MIN_ENTITY_SPAWN_Y, ServerLevel.MAX_ENTITY_SPAWN_Y); location.checkFinite(); // Paper start - Teleport passenger API Set flagSet = new HashSet<>(List.of(flags)); // Wrap into list while multiple old flags link to the same new one @@ -316,11 +318,10 @@ public boolean teleport(Location location, TeleportCause cause, io.papermc.paper } // Paper start - fix teleport event not being called - org.bukkit.event.entity.EntityTeleportEvent event = new org.bukkit.event.entity.EntityTeleportEvent( - this, this.getLocation(), location); + org.bukkit.event.entity.EntityTeleportEvent event = CraftEventFactory.callEntityTeleportEvent(this.getHandle(), location); // cancelling the event is handled differently for players and entities, // entities just stop teleporting, players will still teleport to the "from" location of the event - if (!event.callEvent() || event.getTo() == null) { + if (event.isCancelled() || event.getTo() == null) { return false; } location = event.getTo(); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index 9878d6842ec7..2ebfa98eeb88 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -100,6 +100,7 @@ import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.craftbukkit.inventory.CraftItemType; import org.bukkit.craftbukkit.potion.CraftPotionUtil; +import org.bukkit.craftbukkit.util.CraftLocation; import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.craftbukkit.util.CraftVector; import org.bukkit.entity.AbstractHorse; @@ -1942,6 +1943,10 @@ public static EntityTeleportEvent callEntityTeleportEvent(Entity nmsEntity, Loca Bukkit.getPluginManager().callEvent(event); + if (!event.isCancelled() && event.getTo() != null) { + Preconditions.checkArgument(ServerLevel.isInSpawnableBounds(CraftLocation.toBlockPosition(event.getTo())), "destination for EntityTeleportEvent is out of spawnable bounds [x/z between %s and %s or y between %s and %s]", -ServerLevel.MAX_LEVEL_SIZE, ServerLevel.MAX_LEVEL_SIZE, ServerLevel.MIN_ENTITY_SPAWN_Y, ServerLevel.MAX_ENTITY_SPAWN_Y); + } + return event; } From ce0a7eefc84dece0e74a1e1d6f79056d86c1c768 Mon Sep 17 00:00:00 2001 From: Doc Date: Sat, 13 Sep 2025 10:09:26 -0300 Subject: [PATCH 2/5] Check world for teleport entity Took 19 minutes --- .../src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 8a0636535573..885f275e4af9 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -296,6 +296,7 @@ public boolean teleport(Location location, TeleportCause cause) { public boolean teleport(Location location, TeleportCause cause, io.papermc.paper.entity.TeleportFlag... flags) { // Paper end Preconditions.checkArgument(location != null, "location cannot be null"); + Preconditions.checkArgument(location.getWorld() != null, "world of location cannot be null"); Preconditions.checkArgument(ServerLevel.isInSpawnableBounds(CraftLocation.toBlockPosition(location)), "location is out of spawnable bounds [x/z between %s and %s or y between %s and %s]", -ServerLevel.MAX_LEVEL_SIZE, ServerLevel.MAX_LEVEL_SIZE, ServerLevel.MIN_ENTITY_SPAWN_Y, ServerLevel.MAX_ENTITY_SPAWN_Y); location.checkFinite(); // Paper start - Teleport passenger API From 5c72e30002de229037f4881a38506976fb653f90 Mon Sep 17 00:00:00 2001 From: Doc Date: Sat, 13 Sep 2025 10:09:53 -0300 Subject: [PATCH 3/5] Validate locations for Events Took 27 seconds --- .../org/bukkit/event/entity/EntityTeleportEvent.java | 10 +++++++++- .../java/org/bukkit/event/player/PlayerMoveEvent.java | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/paper-api/src/main/java/org/bukkit/event/entity/EntityTeleportEvent.java b/paper-api/src/main/java/org/bukkit/event/entity/EntityTeleportEvent.java index e27cd3a6f4bc..26cae93a065d 100644 --- a/paper-api/src/main/java/org/bukkit/event/entity/EntityTeleportEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/entity/EntityTeleportEvent.java @@ -44,8 +44,10 @@ public Location getFrom() { * Sets the location that this entity moved from * * @param from New location this entity moved from + * @throws IllegalArgumentException if the location is not finite */ public void setFrom(@NotNull Location from) { + from.checkFinite(); this.from = from.clone(); } @@ -63,9 +65,15 @@ public Location getTo() { * Sets the location that this entity moved to * * @param to New Location this entity moved to + * @throws IllegalArgumentException if the location is not finite */ public void setTo(@Nullable Location to) { - this.to = to != null ? to.clone() : null; + if (to != null) { + to.checkFinite(); + this.to = to.clone(); + } else { + this.to = null; + } } @Override diff --git a/paper-api/src/main/java/org/bukkit/event/player/PlayerMoveEvent.java b/paper-api/src/main/java/org/bukkit/event/player/PlayerMoveEvent.java index 5a086cb5f459..22368d495882 100644 --- a/paper-api/src/main/java/org/bukkit/event/player/PlayerMoveEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/player/PlayerMoveEvent.java @@ -137,9 +137,10 @@ public void setCancelled(boolean cancel) { this.cancelled = cancel; } - private void validateLocation(@NotNull Location loc) { + private void validateLocation(Location loc) { Preconditions.checkArgument(loc != null, "Cannot use null location!"); Preconditions.checkArgument(loc.getWorld() != null, "Cannot use null location with null world!"); + loc.checkFinite(); } @NotNull From ecf71cb63d659fb58317e829c50f37c73d27d52c Mon Sep 17 00:00:00 2001 From: Doc Date: Sat, 13 Sep 2025 10:10:10 -0300 Subject: [PATCH 4/5] Validate locations for Player teleport Took 16 seconds --- .../org/bukkit/craftbukkit/entity/CraftPlayer.java | 9 +++++---- .../bukkit/craftbukkit/event/CraftEventFactory.java | 13 +++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 322a1e38799a..378dedf39f41 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -1378,8 +1378,9 @@ public boolean teleport(Location location, org.bukkit.event.player.PlayerTelepor boolean dismount = !allFlags.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_VEHICLE); boolean ignorePassengers = allFlags.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS); // Paper end - Teleport API - Preconditions.checkArgument(location != null, "location"); - Preconditions.checkArgument(location.getWorld() != null, "location.world"); + Preconditions.checkArgument(location != null, "location cannot be null"); + Preconditions.checkArgument(location.getWorld() != null, "world of location cannot be null"); + Preconditions.checkArgument(ServerLevel.isInSpawnableBounds(CraftLocation.toBlockPosition(location)), "location is out of spawnable bounds [x/z between %s and %s or y between %s and %s]", -ServerLevel.MAX_LEVEL_SIZE, ServerLevel.MAX_LEVEL_SIZE, ServerLevel.MIN_ENTITY_SPAWN_Y, ServerLevel.MAX_ENTITY_SPAWN_Y); // Paper start - Teleport passenger API // Don't allow teleporting between worlds while keeping passengers if (ignorePassengers && entity.isVehicle() && location.getWorld() != this.getWorld()) { @@ -1412,8 +1413,7 @@ public boolean teleport(Location location, org.bukkit.event.player.PlayerTelepor // To = Players new Location if Teleport is Successful Location to = location; // Create & Call the Teleport Event. - PlayerTeleportEvent event = new PlayerTeleportEvent(this, from, to, cause, Set.copyOf(relativeArguments)); // Paper - Teleport API - this.server.getPluginManager().callEvent(event); + PlayerTeleportEvent event = CraftEventFactory.callPlayerTeleportEvent(this, from, to, cause, Set.copyOf(relativeArguments)); // Paper - Teleport API // Return False to inform the Plugin that the Teleport was unsuccessful/cancelled. if (event.isCancelled()) { @@ -1432,6 +1432,7 @@ public boolean teleport(Location location, org.bukkit.event.player.PlayerTelepor from = event.getFrom(); // Grab the new To Location dependent on whether the event was cancelled. to = event.getTo(); + Preconditions.checkArgument(ServerLevel.isInSpawnableBounds(CraftLocation.toBlockPosition(to)), "destination for PlayerTeleportEvent is out of spawnable bounds [x/z between %s and %s or y between %s and %s]", -ServerLevel.MAX_LEVEL_SIZE, ServerLevel.MAX_LEVEL_SIZE, ServerLevel.MIN_ENTITY_SPAWN_Y, ServerLevel.MAX_ENTITY_SPAWN_Y); // Grab the To and From World Handles. ServerLevel fromWorld = ((CraftWorld) from.getWorld()).getHandle(); ServerLevel toWorld = ((CraftWorld) to.getWorld()).getHandle(); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index 2ebfa98eeb88..d480cd146cce 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -17,6 +17,7 @@ import io.papermc.paper.adventure.PaperAdventure; import io.papermc.paper.connection.HorriblePlayerLoginEventHack; import io.papermc.paper.connection.PlayerConnection; +import io.papermc.paper.entity.TeleportFlag; import io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -243,6 +244,7 @@ import org.bukkit.event.player.PlayerShearEntityEvent; import org.bukkit.event.player.PlayerSignOpenEvent; import org.bukkit.event.player.PlayerStatisticIncrementEvent; +import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerUnleashEntityEvent; import org.bukkit.event.raid.RaidFinishEvent; import org.bukkit.event.raid.RaidSpawnWaveEvent; @@ -1950,6 +1952,17 @@ public static EntityTeleportEvent callEntityTeleportEvent(Entity nmsEntity, Loca return event; } + public static PlayerTeleportEvent callPlayerTeleportEvent(Player player, Location from, Location to, PlayerTeleportEvent.TeleportCause cause, Set teleportFlags) { + PlayerTeleportEvent event = new PlayerTeleportEvent(player, from, to, cause, teleportFlags); + event.callEvent(); + + if (!event.isCancelled()) { + Preconditions.checkArgument(ServerLevel.isInSpawnableBounds(CraftLocation.toBlockPosition(event.getTo())), "destination for PlayerTeleportEvent is out of spawnable bounds [x/z between %s and %s or y between %s and %s]", -ServerLevel.MAX_LEVEL_SIZE, ServerLevel.MAX_LEVEL_SIZE, ServerLevel.MIN_ENTITY_SPAWN_Y, ServerLevel.MAX_ENTITY_SPAWN_Y); + } + + return event; + } + public static boolean callEntityInteractEvent(Entity nmsEntity, Block block) { EntityInteractEvent event = new EntityInteractEvent(nmsEntity.getBukkitEntity(), block); Bukkit.getPluginManager().callEvent(event); From addff4c4e02f3a77a9d8ae6e18ba04d9303771cd Mon Sep 17 00:00:00 2001 From: Doc Date: Wed, 17 Sep 2025 09:18:36 -0300 Subject: [PATCH 5/5] Use CraftEventFactory.callPlayerTeleportEvent in ServerGamePacketListenerImpl Took 5 minutes --- .../server/network/ServerGamePacketListenerImpl.java.patch | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index 1f7ee400cd73..ec9b3a462c01 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -1000,7 +1000,7 @@ return true; } else { -@@ -1175,10 +_,77 @@ +@@ -1175,10 +_,76 @@ } public void teleport(double x, double y, double z, float yaw, float pitch) { @@ -1036,9 +1036,8 @@ + final io.papermc.paper.entity.TeleportFlag.Relative flag = org.bukkit.craftbukkit.entity.CraftPlayer.deltaRelativeToAPI(relativeArgument); + if (flag != null) relativeFlags.add(flag); + } -+ PlayerTeleportEvent event = new PlayerTeleportEvent(player, from.clone(), to.clone(), cause, java.util.Set.copyOf(relativeFlags)); ++ PlayerTeleportEvent event = CraftEventFactory.callPlayerTeleportEvent(player, from.clone(), to.clone(), cause, java.util.Set.copyOf(relativeFlags)); + // Paper end - Teleport API -+ this.cserver.getPluginManager().callEvent(event); + + if (event.isCancelled() || !to.equals(event.getTo())) { + // set = Collections.emptySet(); // Can't relative teleport // Paper - Teleport API; Now you can!