From e8cb2f513b316706aaf8dd47cfff39aad647dce2 Mon Sep 17 00:00:00 2001 From: Phoenix616 Date: Sun, 12 Jan 2020 01:02:13 +0100 Subject: [PATCH] SPIGOT-5252: Nether portal fixes and additions - Fixes PlayerTeleportEvent getting called multiple times and with wrong coordinates - Implement PlayerPortalEvent and EntityPortalEvent additions --- nms-patches/Entity.patch | 62 +++++--- nms-patches/EntityPlayer.patch | 222 ++++++++++++++++++---------- nms-patches/PortalTravelAgent.patch | 114 +++++++++++++- 3 files changed, 285 insertions(+), 113 deletions(-) diff --git a/nms-patches/Entity.patch b/nms-patches/Entity.patch index aec4967580..39cb497d16 100644 --- a/nms-patches/Entity.patch +++ b/nms-patches/Entity.patch @@ -573,8 +573,9 @@ + this.setOnFire(entityCombustEvent.getDuration(), false); + } + // CraftBukkit end -+ } -+ + } + +- this.damageEntity(DamageSource.LIGHTNING, 5.0F); + // CraftBukkit start + if (thisBukkitEntity instanceof Hanging) { + HangingBreakByEntityEvent hangingEvent = new HangingBreakByEntityEvent((Hanging) thisBukkitEntity, stormBukkitEntity); @@ -583,9 +584,8 @@ + if (hangingEvent.isCancelled()) { + return; + } - } - -- this.damageEntity(DamageSource.LIGHTNING, 5.0F); ++ } ++ + if (this.isFireProof()) { + return; + } @@ -598,7 +598,7 @@ } public void j(boolean flag) { -@@ -2042,33 +2383,46 @@ +@@ -2042,33 +2383,66 @@ @Nullable public Entity a(DimensionManager dimensionmanager) { @@ -630,6 +630,26 @@ float f = 0.0F; - BlockPosition blockposition; + BlockPosition blockposition = location; // CraftBukkit ++ ++ // CraftBukkit start - EntityPortalEvent ++ // SPIGOT-5136 - don't fire event for CraftEntity.teleport ++ int searchRadius = 128; ++ if (location == null) { ++ Location enter = this.getBukkitEntity().getLocation(); ++ Location exit = new Location(worldserver1.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ ++ EntityPortalEvent event = new EntityPortalEvent(this.getBukkitEntity(), enter, exit, searchRadius); ++ event.getEntity().getServer().getPluginManager().callEvent(event); ++ if (event.isCancelled() || event.getTo() == null || event.getTo().getWorld() == null || !this.isAlive()) { ++ return null; ++ } ++ ++ exit = event.getTo(); ++ worldserver1 = ((CraftWorld) exit.getWorld()).getHandle(); ++ blockposition = new BlockPosition(exit.getX(), exit.getY(), exit.getZ()); ++ searchRadius = event.getSearchRadius(); ++ } ++ // CraftBukkit end - if (dimensionmanager1 == DimensionManager.THE_END && dimensionmanager == DimensionManager.OVERWORLD) { + if (blockposition == null) { // CraftBukkit @@ -652,28 +672,22 @@ d0 *= 8.0D; d1 *= 8.0D; } -@@ -2093,6 +2447,28 @@ +@@ -2083,7 +2457,7 @@ + Vec3D vec3d1 = this.getPortalOffset(); + + blockposition = new BlockPosition(d0, this.locY(), d1); +- ShapeDetector.Shape shapedetector_shape = worldserver1.getTravelAgent().a(blockposition, vec3d, this.getPortalDirection(), vec3d1.x, vec3d1.y, this instanceof EntityHuman); ++ ShapeDetector.Shape shapedetector_shape = worldserver1.getTravelAgent().findPortal(blockposition, vec3d, this.getPortalDirection(), vec3d1.x, vec3d1.y, this instanceof EntityHuman, searchRadius); // CraftBukkit - search radius + + if (shapedetector_shape == null) { + return null; +@@ -2093,6 +2467,13 @@ vec3d = shapedetector_shape.velocity; f = (float) shapedetector_shape.yaw; } + } // CraftBukkit + + // CraftBukkit start -+ // SPIGOT-5136 - don't fire event for CraftEntity.teleport -+ if (location == null) { -+ Location enter = this.getBukkitEntity().getLocation(); -+ Location exit = new Location(worldserver1.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); -+ -+ EntityPortalEvent event = new EntityPortalEvent(this.getBukkitEntity(), enter, exit); -+ event.getEntity().getServer().getPluginManager().callEvent(event); -+ if (event.isCancelled() || event.getTo() == null || event.getTo().getWorld() == null || !this.isAlive()) { -+ return null; -+ } -+ -+ exit = event.getTo(); -+ worldserver1 = ((CraftWorld) exit.getWorld()).getHandle(); -+ blockposition = new BlockPosition(exit.getX(), exit.getY(), exit.getZ()); -+ } + + this.dimension = dimensionmanager; + this.decouple(); @@ -681,7 +695,7 @@ this.world.getMethodProfiler().exitEnter("reloading"); Entity entity = this.getEntityType().a((World) worldserver1); -@@ -2102,6 +2478,14 @@ +@@ -2102,6 +2483,14 @@ entity.setPositionRotation(blockposition, entity.yaw + f, entity.pitch); entity.setMot(vec3d); worldserver1.addEntityTeleport(entity); @@ -696,7 +710,7 @@ } this.dead = true; -@@ -2308,7 +2692,26 @@ +@@ -2308,7 +2697,26 @@ } public void a(AxisAlignedBB axisalignedbb) { diff --git a/nms-patches/EntityPlayer.patch b/nms-patches/EntityPlayer.patch index d80159c865..79c0ff7081 100644 --- a/nms-patches/EntityPlayer.patch +++ b/nms-patches/EntityPlayer.patch @@ -135,13 +135,13 @@ NBTTagCompound nbttagcompound2 = new NBTTagCompound(); NBTTagCompound nbttagcompound3 = new NBTTagCompound(); -@@ -168,8 +259,34 @@ +@@ -168,7 +259,33 @@ } nbttagcompound.set("recipeBook", this.recipeBook.save()); + this.getBukkitEntity().setExtraData(nbttagcompound); // CraftBukkit - } - ++ } ++ + // CraftBukkit start - World fallback code, either respawn location or global spawn + public void spawnIn(World world) { + super.spawnIn(world); @@ -164,12 +164,11 @@ + } + this.dimension = ((WorldServer) this.world).getWorldProvider().getDimensionManager(); + this.playerInteractManager.a((WorldServer) world); -+ } + } + // CraftBukkit end -+ + public void a(int i) { float f = (float) this.getExpToLevel(); - float f1 = (f - 1.0F) / f; @@ -223,6 +340,11 @@ @Override @@ -386,14 +385,15 @@ if (dimensionmanager1 == DimensionManager.OVERWORLD && dimensionmanager == DimensionManager.NETHER) { this.cr = this.getPositionVector(); d0 /= 8.0D; -@@ -540,6 +741,52 @@ +@@ -540,7 +741,26 @@ f = 0.0F; } +- this.setPositionRotation(d0, d1, d2, f1, f); + // CraftBukkit start + Location enter = this.getBukkitEntity().getLocation(); + Location exit = (worldserver1 == null) ? null : new Location(worldserver1.getWorld(), d0, d1, d2, f1, f); -+ PlayerPortalEvent event = new PlayerPortalEvent(this.getBukkitEntity(), enter, exit, cause); ++ PlayerPortalEvent event = new PlayerPortalEvent(this.getBukkitEntity(), enter, exit, cause, 128, true, dimensionmanager.getType() == DimensionManager.THE_END ? 0 : 16); + Bukkit.getServer().getPluginManager().callEvent(event); + if (event.isCancelled() || event.getTo() == null) { + return null; @@ -403,7 +403,94 @@ + if (exit == null) { + return null; + } ++ worldserver1 = ((CraftWorld) exit.getWorld()).getHandle(); ++ d0 = exit.getX(); ++ d1 = exit.getY(); ++ d2 = exit.getZ(); ++ // CraftBukkit end + ++ // this.setPositionRotation(d0, d1, d2, f1, f); // CraftBukkit - PlayerTeleportEvent handles position changes + worldserver.getMethodProfiler().exit(); + worldserver.getMethodProfiler().enter("placing"); + double d4 = Math.min(-2.9999872E7D, worldserver1.getWorldBorder().c() + 16.0D); +@@ -550,13 +770,19 @@ + + d0 = MathHelper.a(d0, d4, d6); + d2 = MathHelper.a(d2, d5, d7); +- this.setPositionRotation(d0, d1, d2, f1, f); +- if (dimensionmanager == DimensionManager.THE_END) { +- int i = MathHelper.floor(this.locX()); +- int j = MathHelper.floor(this.locY()) - 1; +- int k = MathHelper.floor(this.locZ()); ++ // this.setPositionRotation(d0, d1, d2, f1, f); // CraftBukkit - PlayerTeleportEvent handles position changes ++ // CraftBukkit start - PlayerPortalEvent implementation ++ Vec3D exitVelocity = Vec3D.a; ++ BlockPosition exitPosition = new BlockPosition(d0, d1, d2); ++ if (dimensionmanager.getType() == DimensionManager.THE_END) { // CraftBukkit - getType() ++ int i = exitPosition.getX(); ++ int j = exitPosition.getY() - 1; ++ int k = exitPosition.getZ(); ++ if (event.getCanCreatePortal()) { ++ // CraftBukkit end + boolean flag = true; + boolean flag1 = false; ++ org.bukkit.craftbukkit.util.BlockStateListPopulator blockList = new org.bukkit.craftbukkit.util.BlockStateListPopulator(worldserver1); // CraftBukkit - Use BlockStateListPopulator + + for (int l = -2; l <= 2; ++l) { + for (int i1 = -2; i1 <= 2; ++i1) { +@@ -566,23 +792,80 @@ + int i2 = k + i1 * 0 - l * 1; + boolean flag2 = j1 < 0; + +- worldserver1.setTypeUpdate(new BlockPosition(k1, l1, i2), flag2 ? Blocks.OBSIDIAN.getBlockData() : Blocks.AIR.getBlockData()); ++ blockList.setTypeAndData(new BlockPosition(k1, l1, i2), flag2 ? Blocks.OBSIDIAN.getBlockData() : Blocks.AIR.getBlockData(), 3); // CraftBukkit + } + } + } + +- this.setPositionRotation((double) i, (double) j, (double) k, f1, 0.0F); +- this.setMot(Vec3D.a); +- } else if (!worldserver1.getTravelAgent().a(this, f2)) { +- worldserver1.getTravelAgent().a((Entity) this); +- worldserver1.getTravelAgent().a(this, f2); ++ // CraftBukkit start ++ org.bukkit.World bworld = worldserver1.getWorld(); ++ org.bukkit.event.world.PortalCreateEvent portalEvent = new org.bukkit.event.world.PortalCreateEvent((List) (List) blockList.getList(), bworld, this.getBukkitEntity(), org.bukkit.event.world.PortalCreateEvent.CreateReason.END_PLATFORM); ++ ++ this.world.getServer().getPluginManager().callEvent(portalEvent); ++ if (!portalEvent.isCancelled()) { ++ blockList.updateList(); ++ } ++ } ++ // handled below for PlayerTeleportEvent ++ // this.setPositionRotation((double) i, (double) j, (double) k, f1, 0.0F); ++ exit.setX(i); ++ exit.setY(j); ++ exit.setZ(k); ++ // this.setMot(Vec3D.a); ++ exitVelocity = Vec3D.a; ++ } else { ++ ShapeDetector.Shape portalShape = worldserver1.getTravelAgent().findAndTeleport(this, exitPosition, f2, event.getSearchRadius(), true); ++ if (portalShape == null && event.getCanCreatePortal()) { ++ if (worldserver1.getTravelAgent().createPortal(this, exitPosition, event.getCreationRadius())) { // Only check for new portal if creation succeeded ++ portalShape = worldserver1.getTravelAgent().findAndTeleport(this, exitPosition, f2, event.getSearchRadius(), true); ++ } ++ } ++ // Check if portal was found ++ if (portalShape == null) { ++ return null; ++ } ++ // Teleport handling - logic from PortalTravelAgent#findAndTeleport ++ exitVelocity = portalShape.velocity; ++ exit.setX(portalShape.position.getX()); ++ exit.setY(portalShape.position.getY()); ++ exit.setZ(portalShape.position.getZ()); ++ exit.setYaw(f2 + (float) portalShape.yaw); ++ // CraftBukkit end + } + + worldserver.getMethodProfiler().exit(); ++ // CraftBukkit start - PlayerTeleportEvent + PlayerTeleportEvent tpEvent = new PlayerTeleportEvent(this.getBukkitEntity(), enter, exit, cause); + Bukkit.getServer().getPluginManager().callEvent(tpEvent); + if (tpEvent.isCancelled() || tpEvent.getTo() == null) { @@ -415,16 +502,9 @@ + return null; + } + worldserver1 = ((CraftWorld) exit.getWorld()).getHandle(); -+ d0 = exit.getX(); -+ d1 = exit.getY(); -+ d2 = exit.getZ(); -+ f1 = exit.getYaw(); -+ f = exit.getPitch(); + this.worldChangeInvuln = true; // CraftBukkit - Set teleport invulnerability only if player changing worlds + dimensionmanager = worldserver1.getWorldProvider().getDimensionManager(); -+ // CraftBukkit end + -+ // CraftBukkit start + this.dimension = dimensionmanager; + + this.playerConnection.sendPacket(new PacketPlayOutRespawn(worldserver1.worldProvider.getDimensionManager().getType(), WorldData.c(this.world.getWorldData().getSeed()), this.world.getWorldData().getType(), this.playerInteractManager.getGameMode())); @@ -434,49 +514,19 @@ + playerlist.d(this); + worldserver.removePlayer(this); + this.dead = false; ++ ++ this.setMot(exitVelocity); + // CraftBukkit end -+ - this.setPositionRotation(d0, d1, d2, f1, f); - worldserver.getMethodProfiler().exit(); - worldserver.getMethodProfiler().enter("placing"); -@@ -551,12 +798,13 @@ - d0 = MathHelper.a(d0, d4, d6); - d2 = MathHelper.a(d2, d5, d7); - this.setPositionRotation(d0, d1, d2, f1, f); -- if (dimensionmanager == DimensionManager.THE_END) { -+ if (dimensionmanager.getType() == DimensionManager.THE_END) { // CraftBukkit - getType() - int i = MathHelper.floor(this.locX()); - int j = MathHelper.floor(this.locY()) - 1; - int k = MathHelper.floor(this.locZ()); - boolean flag = true; - boolean flag1 = false; -+ org.bukkit.craftbukkit.util.BlockStateListPopulator blockList = new org.bukkit.craftbukkit.util.BlockStateListPopulator(worldserver1); // CraftBukkit - Use BlockStateListPopulator - - for (int l = -2; l <= 2; ++l) { - for (int i1 = -2; i1 <= 2; ++i1) { -@@ -566,11 +814,20 @@ - int i2 = k + i1 * 0 - l * 1; - boolean flag2 = j1 < 0; - -- worldserver1.setTypeUpdate(new BlockPosition(k1, l1, i2), flag2 ? Blocks.OBSIDIAN.getBlockData() : Blocks.AIR.getBlockData()); -+ blockList.setTypeAndData(new BlockPosition(k1, l1, i2), flag2 ? Blocks.OBSIDIAN.getBlockData() : Blocks.AIR.getBlockData(), 3); // CraftBukkit - } - } - } - -+ // CraftBukkit start -+ org.bukkit.World bworld = worldserver1.getWorld(); -+ org.bukkit.event.world.PortalCreateEvent portalEvent = new org.bukkit.event.world.PortalCreateEvent((List) (List) blockList.getList(), bworld, this.getBukkitEntity(), org.bukkit.event.world.PortalCreateEvent.CreateReason.END_PLATFORM); -+ -+ this.world.getServer().getPluginManager().callEvent(portalEvent); -+ if (!portalEvent.isCancelled()) { -+ blockList.updateList(); -+ } -+ // CraftBukkit end - this.setPositionRotation((double) i, (double) j, (double) k, f1, 0.0F); - this.setMot(Vec3D.a); - } else if (!worldserver1.getTravelAgent().a(this, f2)) { -@@ -599,6 +856,11 @@ + this.spawnIn(worldserver1); + worldserver1.addPlayerPortal(this); + this.triggerDimensionAdvancements(worldserver); +- this.playerConnection.a(this.locX(), this.locY(), this.locZ(), f1, f); ++ this.playerConnection.teleport(exit); // CraftBukkit - use internal teleport without event ++ this.playerConnection.syncPosition(); // CraftBukkit - sync position after changing it (from PortalTravelAgent#findAndteleport) + this.playerInteractManager.a(worldserver1); + this.playerConnection.sendPacket(new PacketPlayOutAbilities(this.abilities)); + playerlist.a(this, worldserver1); +@@ -599,6 +882,11 @@ this.lastSentExp = -1; this.lastHealthSent = -1.0F; this.lastFoodSent = -1; @@ -488,7 +538,7 @@ return this; } } -@@ -640,9 +902,16 @@ +@@ -640,9 +928,16 @@ this.activeContainer.c(); } @@ -507,7 +557,7 @@ this.a(StatisticList.SLEEP_IN_BED); CriterionTriggers.q.a(this); }); -@@ -650,6 +919,7 @@ +@@ -650,6 +945,7 @@ @Override public void wakeup(boolean flag, boolean flag1) { @@ -515,7 +565,7 @@ if (this.isSleeping()) { this.getWorldServer().getChunkProvider().broadcastIncludingSelf(this, new PacketPlayOutAnimation(this, 2)); } -@@ -723,8 +993,9 @@ +@@ -723,8 +1019,9 @@ this.playerConnection.sendPacket(new PacketPlayOutOpenSignEditor(tileentitysign.getPosition())); } @@ -526,7 +576,7 @@ } @Override -@@ -739,6 +1010,24 @@ +@@ -739,6 +1036,24 @@ this.nextContainerCounter(); Container container = itileinventory.createMenu(this.containerCounter, this.inventory, this); @@ -551,7 +601,7 @@ if (container == null) { if (this.isSpectator()) { this.a((new ChatMessage("container.spectatorCantOpen", new Object[0])).a(EnumChatFormat.RED), true); -@@ -746,9 +1035,11 @@ +@@ -746,9 +1061,11 @@ return OptionalInt.empty(); } else { @@ -565,7 +615,7 @@ return OptionalInt.of(this.containerCounter); } } -@@ -761,13 +1052,24 @@ +@@ -761,13 +1078,24 @@ @Override public void openHorseInventory(EntityHorseAbstract entityhorseabstract, IInventory iinventory) { @@ -592,7 +642,7 @@ this.activeContainer.addSlotListener(this); } -@@ -812,6 +1114,11 @@ +@@ -812,6 +1140,11 @@ public void a(Container container, NonNullList nonnulllist) { this.playerConnection.sendPacket(new PacketPlayOutWindowItems(container.windowId, nonnulllist)); this.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.inventory.getCarried())); @@ -604,7 +654,7 @@ } @Override -@@ -821,6 +1128,7 @@ +@@ -821,6 +1154,7 @@ @Override public void closeInventory() { @@ -612,7 +662,7 @@ this.playerConnection.sendPacket(new PacketPlayOutCloseWindow(this.activeContainer.windowId)); this.m(); } -@@ -855,7 +1163,7 @@ +@@ -855,7 +1189,7 @@ @Override public void a(Statistic statistic, int i) { this.serverStatisticManager.b(this, statistic, i); @@ -621,7 +671,7 @@ scoreboardscore.addScore(i); }); } -@@ -863,7 +1171,7 @@ +@@ -863,7 +1197,7 @@ @Override public void a(Statistic statistic) { this.serverStatisticManager.setStatistic(this, statistic, 0); @@ -630,24 +680,25 @@ } @Override -@@ -912,7 +1220,16 @@ +@@ -912,8 +1246,17 @@ public void triggerHealthUpdate() { this.lastHealthSent = -1.0E8F; + this.lastSentExp = -1; // CraftBukkit - Added to reset -+ } -+ + } + + // CraftBukkit start - Support multi-line messages + public void sendMessage(IChatBaseComponent[] ichatbasecomponent) { + for (IChatBaseComponent component : ichatbasecomponent) { + this.sendMessage(component); + } - } ++ } + // CraftBukkit end - ++ @Override public void a(IChatBaseComponent ichatbasecomponent, boolean flag) { -@@ -967,12 +1284,14 @@ + this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent, flag ? ChatMessageType.GAME_INFO : ChatMessageType.CHAT)); +@@ -967,12 +1310,14 @@ this.lastSentExp = -1; this.lastHealthSent = -1.0F; this.lastFoodSent = -1; @@ -663,7 +714,16 @@ } @Override -@@ -1040,6 +1359,18 @@ +@@ -1011,7 +1356,7 @@ + } + + @Override +- public void b(double d0, double d1, double d2) { ++ public void b(double d0, double d1, double d2) { // PAIL: rename to teleportAndSync + this.playerConnection.a(d0, d1, d2, this.yaw, this.pitch); + this.playerConnection.syncPosition(); + } +@@ -1040,6 +1385,18 @@ @Override public void a(EnumGamemode enumgamemode) { @@ -682,7 +742,7 @@ this.playerInteractManager.setGameMode(enumgamemode); this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(3, (float) enumgamemode.getId())); if (enumgamemode == EnumGamemode.SPECTATOR) { -@@ -1090,6 +1421,17 @@ +@@ -1090,6 +1447,17 @@ } public void a(PacketPlayInSettings packetplayinsettings) { @@ -700,7 +760,7 @@ this.locale = packetplayinsettings.b(); this.ch = packetplayinsettings.d(); this.ci = packetplayinsettings.e(); -@@ -1126,13 +1468,13 @@ +@@ -1126,13 +1494,13 @@ if (entity instanceof EntityHuman) { this.playerConnection.sendPacket(new PacketPlayOutEntityDestroy(new int[]{entity.getId()})); } else { @@ -716,7 +776,7 @@ } @Override -@@ -1156,7 +1498,7 @@ +@@ -1156,7 +1524,7 @@ this.spectatedEntity = (Entity) (entity == null ? this : entity); if (entity1 != this.spectatedEntity) { this.playerConnection.sendPacket(new PacketPlayOutCamera(this.spectatedEntity)); @@ -725,7 +785,7 @@ } } -@@ -1185,7 +1527,7 @@ +@@ -1185,7 +1553,7 @@ @Nullable public IChatBaseComponent getPlayerListName() { @@ -734,7 +794,7 @@ } @Override -@@ -1206,9 +1548,16 @@ +@@ -1206,9 +1574,16 @@ return this.advancementDataPlayer; } @@ -751,7 +811,7 @@ if (worldserver == this.world) { this.playerConnection.a(d0, d1, d2, f, f1); } else { -@@ -1231,6 +1580,9 @@ +@@ -1231,6 +1606,9 @@ this.server.getPlayerList().a(this, worldserver); this.server.getPlayerList().updateClient(this); } @@ -761,7 +821,7 @@ } -@@ -1285,4 +1637,144 @@ +@@ -1285,4 +1663,144 @@ return entityitem; } } diff --git a/nms-patches/PortalTravelAgent.patch b/nms-patches/PortalTravelAgent.patch index a524e53ecd..263b957471 100644 --- a/nms-patches/PortalTravelAgent.patch +++ b/nms-patches/PortalTravelAgent.patch @@ -1,24 +1,122 @@ --- a/net/minecraft/server/PortalTravelAgent.java +++ b/net/minecraft/server/PortalTravelAgent.java -@@ -43,7 +43,7 @@ +@@ -17,13 +17,20 @@ + this.b = new Random(worldserver.getSeed()); + } + +- public boolean a(Entity entity, float f) { ++ public boolean a(Entity entity, float f) { // PAIL: rename to findAndTeleport ++ // CraftBukkit start ++ return findAndTeleport(entity, new BlockPosition(entity), f, 128, false) != null; ++ } ++ ++ public ShapeDetector.Shape findAndTeleport(Entity entity, BlockPosition findPosition, float f, int searchRadius, boolean searchOnly) { ++ // CraftBukkit end + Vec3D vec3d = entity.getPortalOffset(); + EnumDirection enumdirection = entity.getPortalDirection(); +- ShapeDetector.Shape shapedetector_shape = this.a(new BlockPosition(entity), entity.getMot(), enumdirection, vec3d.x, vec3d.y, entity instanceof EntityHuman); ++ ShapeDetector.Shape shapedetector_shape = this.findPortal(findPosition, entity.getMot(), enumdirection, vec3d.x, vec3d.y, entity instanceof EntityHuman, searchRadius); // CraftBukkit - add location and searchRadius ++ if (searchOnly) return shapedetector_shape; // CraftBukkit - optional teleporting + + if (shapedetector_shape == null) { +- return false; ++ return null; // CraftBukkit - return shape + } else { + Vec3D vec3d1 = shapedetector_shape.position; + Vec3D vec3d2 = shapedetector_shape.velocity; +@@ -31,19 +38,26 @@ + entity.setMot(vec3d2); + entity.yaw = f + (float) shapedetector_shape.yaw; + entity.b(vec3d1.x, vec3d1.y, vec3d1.z); +- return true; ++ return shapedetector_shape; // CraftBukkit - return shape + } + } + + @Nullable +- public ShapeDetector.Shape a(BlockPosition blockposition, Vec3D vec3d, EnumDirection enumdirection, double d0, double d1, boolean flag) { ++ public ShapeDetector.Shape a(BlockPosition blockposition, Vec3D vec3d, EnumDirection enumdirection, double d0, double d1, boolean flag) { // PAIL: rename to findPortal, d0 = portal offset x, d1 = portal offset z, flag = instanceof EntityHuman ++ // CraftBukkit start ++ return findPortal(blockposition, vec3d, enumdirection, d0, d1, flag, 128); ++ } ++ ++ @Nullable ++ public ShapeDetector.Shape findPortal(BlockPosition blockposition, Vec3D vec3d, EnumDirection enumdirection, double d0, double d1, boolean flag, int searchRadius) { ++ // CraftBukkit end + VillagePlace villageplace = this.world.B(); + + villageplace.a(this.world, blockposition, 128); List list = (List) villageplace.b((villageplacetype) -> { return villageplacetype == VillagePlaceType.u; - }, blockposition, 128, VillagePlace.Occupancy.ANY).collect(Collectors.toList()); +- }, blockposition, 128, VillagePlace.Occupancy.ANY).collect(Collectors.toList()); - Optional optional = list.stream().min(Comparator.comparingDouble((villageplacerecord) -> { ++ }, blockposition, searchRadius, VillagePlace.Occupancy.ANY).collect(Collectors.toList()); // CraftBukkit - searchRadius + Optional optional = list.stream().min(Comparator.comparingDouble((villageplacerecord) -> { // CraftBukkit - decompile error return villageplacerecord.f().m(blockposition); }).thenComparingInt((villageplacerecord) -> { return villageplacerecord.f().getY(); -@@ -56,7 +56,7 @@ +@@ -56,15 +70,23 @@ ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = BlockPortal.c((GeneratorAccess) this.world, blockposition1); return shapedetector_shapedetectorcollection.a(enumdirection, blockposition1, d1, vec3d, d0); - }).orElse((Object) null); + }).orElse(null); // CraftBukkit - decompile error ++ } ++ ++ public boolean a(Entity entity) { // PAIL: rename to createPortal ++ // CraftBukkit start - providable position and creation radius ++ return createPortal(entity, new BlockPosition(entity), 16); } - public boolean a(Entity entity) { -@@ -197,6 +197,7 @@ +- public boolean a(Entity entity) { ++ public boolean createPortal(Entity entity, BlockPosition createPosition, int createRadius) { ++ // CraftBukkit end + boolean flag = true; + double d0 = -1.0D; +- int i = MathHelper.floor(entity.locX()); +- int j = MathHelper.floor(entity.locY()); +- int k = MathHelper.floor(entity.locZ()); ++ // CraftBukkit start - providable position ++ int i = createPosition.getX(); ++ int j = createPosition.getY(); ++ int k = createPosition.getZ(); ++ // CraftBukkit end + int l = i; + int i1 = j; + int j1 = k; +@@ -88,11 +110,11 @@ + double d3; + double d4; + +- for (i2 = i - 16; i2 <= i + 16; ++i2) { +- d1 = (double) i2 + 0.5D - entity.locX(); ++ for (i2 = i - createRadius; i2 <= i + createRadius; ++i2) { // CraftBukkit - createRadius ++ d1 = (double) i2 + 0.5D - createPosition.getX(); // CraftBukkit - providable position + +- for (j2 = k - 16; j2 <= k + 16; ++j2) { +- d2 = (double) j2 + 0.5D - entity.locZ(); ++ for (j2 = k - createRadius; j2 <= k + createRadius; ++j2) { // CraftBukkit - createRadius ++ d2 = (double) j2 + 0.5D - createPosition.getZ(); // CraftBukkit - providable position + + label257: + for (k2 = this.world.getHeight() - 1; k2 >= 0; --k2) { +@@ -140,11 +162,11 @@ + } + + if (d0 < 0.0D) { +- for (i2 = i - 16; i2 <= i + 16; ++i2) { +- d1 = (double) i2 + 0.5D - entity.locX(); ++ for (i2 = i - createRadius; i2 <= i + createRadius; ++i2) { // CraftBukkit - createRadius ++ d1 = (double) i2 + 0.5D - createPosition.getX(); // CraftBukkit - providable position + +- for (j2 = k - 16; j2 <= k + 16; ++j2) { +- d2 = (double) j2 + 0.5D - entity.locZ(); ++ for (j2 = k - createRadius; j2 <= k + createRadius; ++j2) { // CraftBukkit - createRadius ++ d2 = (double) j2 + 0.5D - createPosition.getZ(); // CraftBukkit - providable position + + label205: + for (k2 = this.world.getHeight() - 1; k2 >= 0; --k2) { +@@ -197,6 +219,7 @@ l5 = -l5; } @@ -26,7 +124,7 @@ if (d0 < 0.0D) { i1 = MathHelper.clamp(i1, 70, this.world.getHeight() - 10); j5 = i1; -@@ -210,7 +211,7 @@ +@@ -210,7 +233,7 @@ boolean flag1 = l2 < 0; blockposition_mutableblockposition.d(j3, l3, i4); @@ -35,7 +133,7 @@ } } } -@@ -220,7 +221,7 @@ +@@ -220,7 +243,7 @@ for (i3 = -1; i3 < 4; ++i3) { if (k2 == -1 || k2 == 2 || i3 == -1 || i3 == 3) { blockposition_mutableblockposition.d(i5 + k2 * k5, j5 + i3, j2 + k2 * l5); @@ -44,7 +142,7 @@ } } } -@@ -230,10 +231,19 @@ +@@ -230,10 +253,19 @@ for (i3 = 0; i3 < 2; ++i3) { for (l2 = 0; l2 < 3; ++l2) { blockposition_mutableblockposition.d(i5 + i3 * k5, j5 + l2, j2 + i3 * l5);