diff --git a/patches/net/minecraft/server/commands/SpreadPlayersCommand.java.patch b/patches/net/minecraft/server/commands/SpreadPlayersCommand.java.patch index 9f297b76ee..bb667b75fe 100644 --- a/patches/net/minecraft/server/commands/SpreadPlayersCommand.java.patch +++ b/patches/net/minecraft/server/commands/SpreadPlayersCommand.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/commands/SpreadPlayersCommand.java +++ b/net/minecraft/server/commands/SpreadPlayersCommand.java -@@ -259,15 +_,22 @@ +@@ -259,15 +_,23 @@ spreadplayerscommand$position = p_138732_[i++]; } @@ -12,14 +12,15 @@ - Set.of(), - entity.getYRot(), - entity.getXRot() -+ net.neoforged.neoforge.event.entity.EntityTeleportEvent.SpreadPlayersCommand event = net.neoforged.neoforge.event.EventHooks.onEntityTeleportSpreadPlayersCommand(entity, ++ net.neoforged.neoforge.event.entity.EntityTeleportEvent.SpreadPlayersCommand event = net.neoforged.neoforge.event.EventHooks.onEntityTeleportSpreadPlayersCommand(entity, new net.neoforged.neoforge.common.util.GlobalVec3( ++ p_138731_, + (double)Mth.floor(spreadplayerscommand$position.x) + 0.5, + spreadplayerscommand$position.getSpawnY(p_138731_, p_138733_), -+ (double)Mth.floor(spreadplayerscommand$position.z) + 0.5 ++ (double)Mth.floor(spreadplayerscommand$position.z) + 0.5) ); + if (!event.isCanceled()) { + entity.teleportTo( -+ p_138731_, ++ event.getTargetServerLevelOr(p_138731_), + event.getTargetX(), + event.getTargetY(), + event.getTargetZ(), diff --git a/patches/net/minecraft/server/commands/TeleportCommand.java.patch b/patches/net/minecraft/server/commands/TeleportCommand.java.patch index 2fe8f7c34f..01a8cd99da 100644 --- a/patches/net/minecraft/server/commands/TeleportCommand.java.patch +++ b/patches/net/minecraft/server/commands/TeleportCommand.java.patch @@ -1,13 +1,14 @@ --- a/net/minecraft/server/commands/TeleportCommand.java +++ b/net/minecraft/server/commands/TeleportCommand.java -@@ -270,6 +_,14 @@ +@@ -270,6 +_,15 @@ float p_139023_, @Nullable TeleportCommand.LookAt p_139024_ ) throws CommandSyntaxException { -+ net.neoforged.neoforge.event.entity.EntityTeleportEvent.TeleportCommand event = net.neoforged.neoforge.event.EventHooks.onEntityTeleportCommand(p_139016_, p_139018_, p_139019_, p_139020_); ++ net.neoforged.neoforge.event.entity.EntityTeleportEvent.TeleportCommand event = net.neoforged.neoforge.event.EventHooks.onEntityTeleportCommand(p_139016_, new net.neoforged.neoforge.common.util.GlobalVec3(p_139017_, p_139018_, p_139019_, p_139020_)); + if (event.isCanceled()) { + return; + } ++ p_139017_ = event.getTargetServerLevelOr(p_139017_); + p_139018_ = event.getTargetX(); + p_139019_ = event.getTargetY(); + p_139020_ = event.getTargetZ(); diff --git a/patches/net/minecraft/world/entity/monster/EnderMan.java.patch b/patches/net/minecraft/world/entity/monster/EnderMan.java.patch index a46e783702..c315d4bd9a 100644 --- a/patches/net/minecraft/world/entity/monster/EnderMan.java.patch +++ b/patches/net/minecraft/world/entity/monster/EnderMan.java.patch @@ -29,7 +29,7 @@ boolean flag = blockstate.blocksMotion(); boolean flag1 = blockstate.getFluidState().is(FluidTags.WATER); if (flag && !flag1) { -+ net.neoforged.neoforge.event.entity.EntityTeleportEvent.EnderEntity event = net.neoforged.neoforge.event.EventHooks.onEnderTeleport(this, p_32544_, p_32545_, p_32546_); ++ net.neoforged.neoforge.event.entity.EntityTeleportEvent.EnderEntity event = net.neoforged.neoforge.event.EventHooks.onEnderTeleport(this, new net.neoforged.neoforge.common.util.GlobalVec3(this.level(), p_32544_, p_32545_, p_32546_)); + if (event.isCanceled()) return false; Vec3 vec3 = this.position(); - boolean flag2 = this.randomTeleport(p_32544_, p_32545_, p_32546_, true); diff --git a/patches/net/minecraft/world/entity/monster/Shulker.java.patch b/patches/net/minecraft/world/entity/monster/Shulker.java.patch index f7260e87d4..e4450f4746 100644 --- a/patches/net/minecraft/world/entity/monster/Shulker.java.patch +++ b/patches/net/minecraft/world/entity/monster/Shulker.java.patch @@ -4,7 +4,7 @@ && this.level().noCollision(this, new AABB(blockpos1).deflate(1.0E-6))) { Direction direction = this.findAttachableSurface(blockpos1); if (direction != null) { -+ net.neoforged.neoforge.event.entity.EntityTeleportEvent.EnderEntity event = net.neoforged.neoforge.event.EventHooks.onEnderTeleport(this, blockpos1.getX(), blockpos1.getY(), blockpos1.getZ()); ++ net.neoforged.neoforge.event.entity.EntityTeleportEvent.EnderEntity event = net.neoforged.neoforge.event.EventHooks.onEnderTeleport(this, new net.neoforged.neoforge.common.util.GlobalVec3(this.level(), blockpos1)); + if (event.isCanceled()) direction = null; + blockpos1 = BlockPos.containing(event.getTargetX(), event.getTargetY(), event.getTargetZ()); + } diff --git a/patches/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch b/patches/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch index 483428eed1..537e9dc9e7 100644 --- a/patches/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch +++ b/patches/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch @@ -4,7 +4,7 @@ if (entity instanceof ServerPlayer serverplayer) { if (serverplayer.connection.isAcceptingMessages()) { -+ net.neoforged.neoforge.event.entity.EntityTeleportEvent.EnderPearl event = net.neoforged.neoforge.event.EventHooks.onEnderPearlLand(serverplayer, this.getX(), this.getY(), this.getZ(), this, 5.0F, p_37504_); ++ net.neoforged.neoforge.event.entity.EntityTeleportEvent.EnderPearl event = net.neoforged.neoforge.event.EventHooks.onEnderPearlLand(serverplayer, new net.neoforged.neoforge.common.util.GlobalVec3(this), this, 5.0F, p_37504_); + if (!event.isCanceled()) { // Don't indent to lower patch size if (this.random.nextFloat() < 0.05F && serverlevel.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { Endermite endermite = EntityType.ENDERMITE.create(serverlevel); @@ -14,14 +14,15 @@ entity.changeDimension( new DimensionTransition( - serverlevel, this.position(), entity.getDeltaMovement(), entity.getYRot(), entity.getXRot(), DimensionTransition.DO_NOTHING -+ serverlevel, event.getTarget(), entity.getDeltaMovement(), entity.getYRot(), entity.getXRot(), DimensionTransition.DO_NOTHING ++ event.getTargetServerLevelOr(serverlevel), event.getTarget(), entity.getDeltaMovement(), entity.getYRot(), entity.getXRot(), DimensionTransition.DO_NOTHING ) ); entity.resetFallDistance(); serverplayer.resetCurrentImpulseContext(); - entity.hurt(this.damageSources().fall(), 5.0F); +- this.playSound(serverlevel, this.position()); + entity.hurt(this.damageSources().fall(), event.getAttackDamage()); - this.playSound(serverlevel, this.position()); ++ this.playSound(event.getTargetServerLevelOr(serverlevel), this.position()); + } //Forge: End } } else { diff --git a/patches/net/minecraft/world/item/ChorusFruitItem.java.patch b/patches/net/minecraft/world/item/ChorusFruitItem.java.patch index 1080f1730c..6ade86c960 100644 --- a/patches/net/minecraft/world/item/ChorusFruitItem.java.patch +++ b/patches/net/minecraft/world/item/ChorusFruitItem.java.patch @@ -5,7 +5,7 @@ Vec3 vec3 = p_40714_.position(); - if (p_40714_.randomTeleport(d0, d1, d2, true)) { -+ net.neoforged.neoforge.event.entity.EntityTeleportEvent.ChorusFruit event = net.neoforged.neoforge.event.EventHooks.onChorusFruitTeleport(p_40714_, d0, d1, d2); ++ net.neoforged.neoforge.event.entity.EntityTeleportEvent.ChorusFruit event = net.neoforged.neoforge.event.EventHooks.onChorusFruitTeleport(p_40714_, new net.neoforged.neoforge.common.util.GlobalVec3(p_40714_.level(), d0, d1, d2)); + if (event.isCanceled()) return itemstack; + if (p_40714_.randomTeleport(event.getTargetX(), event.getTargetY(), event.getTargetZ(), true)) { p_40713_.gameEvent(GameEvent.TELEPORT, vec3, GameEvent.Context.of(p_40714_)); diff --git a/src/main/java/net/neoforged/neoforge/common/util/GlobalVec3.java b/src/main/java/net/neoforged/neoforge/common/util/GlobalVec3.java new file mode 100644 index 0000000000..05070edfdd --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/util/GlobalVec3.java @@ -0,0 +1,300 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.common.util; + +import java.lang.ref.WeakReference; +import java.util.EnumSet; +import java.util.Objects; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; +import net.minecraft.core.GlobalPos; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; + +/** + * A subclass of {@link Vec3} that also holds a {@link Level}. + *

+ * Note that the Level is held as a weak reference and is not required. Only the dimension ({@link ResourceKey}) is always available. + *

+ * Methods that take a {@link Vec3} as parameter will throw an {@link IllegalStateException} if they are supplied with a {@link GlobalVec3} + * that has a different dimension. To avoid this, downgrade the parameter with {@link #toVec3()} if necessary. + */ +public class GlobalVec3 extends Vec3 { + public final ResourceKey dimension; + private final WeakReference level; + + protected GlobalVec3(ResourceKey dimension, WeakReference level, double x, double y, double z) { + super(x, y, z); + this.dimension = dimension; + this.level = level; + } + + public GlobalVec3(Level level, double x, double y, double z) { + this(level.dimension(), new WeakReference<>(level), x, y, z); + } + + public GlobalVec3(ResourceKey dimension, double x, double y, double z) { + this(dimension, new WeakReference<>(null), x, y, z); + } + + public GlobalVec3(Level level, Vector3f vector3f) { + this(level.dimension(), new WeakReference<>(level), vector3f.x, vector3f.y, vector3f.z); + } + + public GlobalVec3(ResourceKey dimension, Vector3f vector3f) { + this(dimension, new WeakReference<>(null), vector3f.x, vector3f.y, vector3f.z); + } + + protected GlobalVec3(ResourceKey dimension, WeakReference level, Vec3 vec3) { + this(dimension, level, vec3.x, vec3.y, vec3.z); + } + + public GlobalVec3(Level level, Vec3 vec3) { + this(level.dimension(), new WeakReference<>(level), vec3); + } + + public GlobalVec3(ResourceKey dimension, Vec3 vec3) { + this(dimension, new WeakReference<>(null), vec3); + } + + public GlobalVec3(Entity entity) { + this(entity.level(), entity.position()); + } + + public GlobalVec3(Level level, BlockPos blockpos) { + this(level.dimension(), new WeakReference<>(level), blockpos.getX(), blockpos.getY(), blockpos.getZ()); + } + + public GlobalVec3(ResourceKey dimension, BlockPos blockpos) { + this(dimension, new WeakReference<>(null), blockpos.getX(), blockpos.getY(), blockpos.getZ()); + } + + public GlobalVec3(GlobalPos globalpos) { + this(globalpos.dimension(), globalpos.pos()); + } + + // setters + + public GlobalVec3 withX(double x) { + return new GlobalVec3(dimension, level, x, y, z); + } + + public GlobalVec3 withY(double y) { + return new GlobalVec3(dimension, level, x, y, z); + } + + public GlobalVec3 withZ(double z) { + return new GlobalVec3(dimension, level, x, y, z); + } + + public GlobalVec3 withLevel(Level level) { + return new GlobalVec3(level.dimension(), new WeakReference<>(level), x, y, z); + } + + public GlobalVec3 withDimension(ResourceKey dimension) { + return new GlobalVec3(dimension, new WeakReference<>(null), x, y, z); + } + + // getters + + public ResourceKey getDimension() { + return dimension; + } + + /** + * Returns the level of this global position if it is available. + *

+ * Use {@link #getLevel(MinecraftServer)} or {@link #getLevel(ServerLevel)} if possible. + * + * @return The level. + */ + public @Nullable Level getLevel() { + return level.get(); + } + + public Level getLevel(ServerLevel anotherLevel) { + return getLevel(anotherLevel.getServer()); + } + + public Level getLevel(MinecraftServer server) { + final Level level2 = level.get(); + if (level2 == null) { + return server.getLevel(dimension); + } + return level2; + } + + // converters + + public Vec3 toVec3() { + return super.add(0, 0, 0); + } + + public BlockPos toBlockPos() { + return BlockPos.containing(this); + } + + public GlobalPos toGlobalPos() { + return new GlobalPos(dimension, toBlockPos()); + } + + public GlobalVec3 copy() { + return new GlobalVec3(dimension, level, x, y, z); + } + + // method overrides + + protected void sameDim(Vec3 other) { + if (other instanceof GlobalVec3 other3 && other3.dimension != dimension) { + throw new IllegalStateException("Cannot compute with GlobalPoses (" + this + " and " + other + ") that are in different dimensions"); + } + } + + @Override + public GlobalVec3 vectorTo(Vec3 other) { + sameDim(other); + return new GlobalVec3(dimension, level, super.vectorTo(other)); + } + + @Override + public GlobalVec3 cross(Vec3 other) { + sameDim(other); + return new GlobalVec3(dimension, level, super.cross(other)); + } + + @Override + public GlobalVec3 add(Vec3 other) { + sameDim(other); + return this.add(other.x, other.y, other.z); + } + + @Override + public GlobalVec3 add(double otherX, double otherY, double otherZ) { + return new GlobalVec3(dimension, level, super.add(otherX, otherY, otherZ)); + } + + @Override + public double distanceTo(Vec3 other) { + sameDim(other); + return super.distanceTo(other); + } + + @Override + public double distanceToSqr(Vec3 other) { + sameDim(other); + return super.distanceToSqr(other); + } + + @Override + public boolean closerThan(Vec3 other, double horizontal, double vertical) { + sameDim(other); + return super.closerThan(other, horizontal, vertical); + } + + @Override + public boolean equals(Object other) { + if (other instanceof GlobalVec3 other3 && other3.dimension == dimension) { + return super.equals(other); + } + return false; + } + + @Override + public GlobalVec3 align(EnumSet axis) { + return new GlobalVec3(dimension, level, super.align(axis)); + } + + @Override + public GlobalVec3 normalize() { + return new GlobalVec3(dimension, level, super.normalize()); + } + + @Override + public GlobalVec3 subtract(Vec3 other) { + sameDim(other); + return new GlobalVec3(dimension, level, super.subtract(other)); + } + + @Override + public GlobalVec3 subtract(double otherX, double otherY, double otherZ) { + return new GlobalVec3(dimension, level, super.subtract(otherX, otherY, otherZ)); + } + + @Override + public GlobalVec3 scale(double by) { + return new GlobalVec3(dimension, level, super.scale(by)); + } + + @Override + public GlobalVec3 reverse() { + return new GlobalVec3(dimension, level, super.reverse()); + } + + @Override + public GlobalVec3 multiply(Vec3 other) { + sameDim(other); + return new GlobalVec3(dimension, level, super.multiply(other)); + } + + @Override + public GlobalVec3 multiply(double otherX, double otherY, double otherZ) { + return new GlobalVec3(dimension, level, super.multiply(otherX, otherY, otherZ)); + } + + @Override + public GlobalVec3 offsetRandom(RandomSource rand, float factor) { + return new GlobalVec3(dimension, level, super.offsetRandom(rand, factor)); + } + + @Override + public int hashCode() { + return 31 * super.hashCode() + Objects.hash(dimension); + } + + @Override + public String toString() { + return "(" + dimension + ", " + x + ", " + y + ", " + z + ")"; + } + + @Override + public GlobalVec3 lerp(Vec3 other, double scale) { + sameDim(other); + return new GlobalVec3(dimension, level, super.lerp(other, scale)); + } + + @Override + public GlobalVec3 xRot(float amount) { + return new GlobalVec3(dimension, level, super.xRot(amount)); + } + + @Override + public GlobalVec3 yRot(float amount) { + return new GlobalVec3(dimension, level, super.yRot(amount)); + } + + @Override + public GlobalVec3 zRot(float amount) { + return new GlobalVec3(dimension, level, super.zRot(amount)); + } + + @Override + public GlobalVec3 relative(Direction direction, double scale) { + return new GlobalVec3(dimension, level, super.relative(direction, scale)); + } + + @Override + public GlobalVec3 with(Axis axis, double value) { + return new GlobalVec3(dimension, level, super.with(axis, value)); + } +} diff --git a/src/main/java/net/neoforged/neoforge/event/EventHooks.java b/src/main/java/net/neoforged/neoforge/event/EventHooks.java index b25e1ad2b3..57b7a21994 100644 --- a/src/main/java/net/neoforged/neoforge/event/EventHooks.java +++ b/src/main/java/net/neoforged/neoforge/event/EventHooks.java @@ -113,6 +113,7 @@ import net.neoforged.neoforge.common.extensions.IFluidStateExtension; import net.neoforged.neoforge.common.extensions.IOwnedSpawner; import net.neoforged.neoforge.common.util.BlockSnapshot; +import net.neoforged.neoforge.common.util.GlobalVec3; import net.neoforged.neoforge.common.util.InsertableLinkedOpenCustomHashSet; import net.neoforged.neoforge.event.brewing.PlayerBrewedPotionEvent; import net.neoforged.neoforge.event.brewing.PotionBrewEvent; @@ -828,35 +829,45 @@ public static void onLivingConvert(LivingEntity entity, LivingEntity outcome) { NeoForge.EVENT_BUS.post(new LivingConversionEvent.Post(entity, outcome)); } + @Deprecated(since = "1.21.1", forRemoval = true) public static EntityTeleportEvent.TeleportCommand onEntityTeleportCommand(Entity entity, double targetX, double targetY, double targetZ) { - EntityTeleportEvent.TeleportCommand event = new EntityTeleportEvent.TeleportCommand(entity, targetX, targetY, targetZ); - NeoForge.EVENT_BUS.post(event); - return event; + return NeoForge.EVENT_BUS.post(new EntityTeleportEvent.TeleportCommand(entity, targetX, targetY, targetZ)); } + public static EntityTeleportEvent.TeleportCommand onEntityTeleportCommand(Entity entity, GlobalVec3 target) { + return NeoForge.EVENT_BUS.post(new EntityTeleportEvent.TeleportCommand(entity, target)); + } + + @Deprecated(since = "1.21.1", forRemoval = true) public static EntityTeleportEvent.SpreadPlayersCommand onEntityTeleportSpreadPlayersCommand(Entity entity, double targetX, double targetY, double targetZ) { - EntityTeleportEvent.SpreadPlayersCommand event = new EntityTeleportEvent.SpreadPlayersCommand(entity, targetX, targetY, targetZ); - NeoForge.EVENT_BUS.post(event); - return event; + return NeoForge.EVENT_BUS.post(new EntityTeleportEvent.SpreadPlayersCommand(entity, targetX, targetY, targetZ)); + } + + public static EntityTeleportEvent.SpreadPlayersCommand onEntityTeleportSpreadPlayersCommand(Entity entity, GlobalVec3 target) { + return NeoForge.EVENT_BUS.post(new EntityTeleportEvent.SpreadPlayersCommand(entity, target)); } + @Deprecated(since = "1.21.1", forRemoval = true) public static EntityTeleportEvent.EnderEntity onEnderTeleport(LivingEntity entity, double targetX, double targetY, double targetZ) { - EntityTeleportEvent.EnderEntity event = new EntityTeleportEvent.EnderEntity(entity, targetX, targetY, targetZ); - NeoForge.EVENT_BUS.post(event); - return event; + return NeoForge.EVENT_BUS.post(new EntityTeleportEvent.EnderEntity(entity, targetX, targetY, targetZ)); + } + + public static EntityTeleportEvent.EnderEntity onEnderTeleport(LivingEntity entity, GlobalVec3 target) { + return NeoForge.EVENT_BUS.post(new EntityTeleportEvent.EnderEntity(entity, target)); } @ApiStatus.Internal - public static EntityTeleportEvent.EnderPearl onEnderPearlLand(ServerPlayer entity, double targetX, double targetY, double targetZ, ThrownEnderpearl pearlEntity, float attackDamage, HitResult hitResult) { - EntityTeleportEvent.EnderPearl event = new EntityTeleportEvent.EnderPearl(entity, targetX, targetY, targetZ, pearlEntity, attackDamage, hitResult); - NeoForge.EVENT_BUS.post(event); - return event; + public static EntityTeleportEvent.EnderPearl onEnderPearlLand(ServerPlayer entity, GlobalVec3 target, ThrownEnderpearl pearlEntity, float attackDamage, HitResult hitResult) { + return NeoForge.EVENT_BUS.post(new EntityTeleportEvent.EnderPearl(entity, target, pearlEntity, attackDamage, hitResult)); } + @Deprecated(since = "1.21.1", forRemoval = true) public static EntityTeleportEvent.ChorusFruit onChorusFruitTeleport(LivingEntity entity, double targetX, double targetY, double targetZ) { - EntityTeleportEvent.ChorusFruit event = new EntityTeleportEvent.ChorusFruit(entity, targetX, targetY, targetZ); - NeoForge.EVENT_BUS.post(event); - return event; + return NeoForge.EVENT_BUS.post(new EntityTeleportEvent.ChorusFruit(entity, targetX, targetY, targetZ)); + } + + public static EntityTeleportEvent.ChorusFruit onChorusFruitTeleport(LivingEntity entity, GlobalVec3 target) { + return NeoForge.EVENT_BUS.post(new EntityTeleportEvent.ChorusFruit(entity, target)); } public static boolean onPermissionChanged(GameProfile gameProfile, int newLevel, PlayerList playerList) { diff --git a/src/main/java/net/neoforged/neoforge/event/entity/EntityTeleportEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/EntityTeleportEvent.java index 386e45466b..1d4a95f436 100644 --- a/src/main/java/net/neoforged/neoforge/event/entity/EntityTeleportEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/entity/EntityTeleportEvent.java @@ -5,16 +5,19 @@ package net.neoforged.neoforge.event.entity; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.projectile.ThrownEnderpearl; +import net.minecraft.world.level.Level; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.neoforged.bus.api.Event; import net.neoforged.bus.api.ICancellableEvent; import net.neoforged.fml.LogicalSide; import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.common.util.GlobalVec3; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -29,43 +32,115 @@ * All children of this event are fired on the {@link NeoForge#EVENT_BUS}.
**/ public class EntityTeleportEvent extends EntityEvent implements ICancellableEvent { + private GlobalVec3 target; + /** + * @deprecated Use {@link #getTarget()} and {@link #setTargetX(double)} instead. + */ + @Deprecated(since = "1.21.1", forRemoval = true) protected double targetX; + /** + * @deprecated Use {@link #getTarget()} and {@link #setTargetY(double)} instead. + */ + @Deprecated(since = "1.21.1", forRemoval = true) protected double targetY; + /** + * @deprecated Use {@link #getTarget()} and {@link #setTargetZ(double)} instead. + */ + @Deprecated(since = "1.21.1", forRemoval = true) protected double targetZ; + @Deprecated(since = "1.21.1", forRemoval = true) public EntityTeleportEvent(Entity entity, double targetX, double targetY, double targetZ) { + this(entity, new GlobalVec3(entity.level(), targetX, targetY, targetZ)); + } + + public EntityTeleportEvent(Entity entity, GlobalVec3 target) { super(entity); - this.targetX = targetX; - this.targetY = targetY; - this.targetZ = targetZ; + this.target = target; } public double getTargetX() { - return targetX; + return target.x; } public void setTargetX(double targetX) { - this.targetX = targetX; + target = target.withX(targetX); } public double getTargetY() { - return targetY; + return target.y; } public void setTargetY(double targetY) { - this.targetY = targetY; + target = target.withY(targetY); } public double getTargetZ() { - return targetZ; + return target.z; } public void setTargetZ(double targetZ) { - this.targetZ = targetZ; + target = target.withZ(targetZ); + } + + public Level getTargetLevel() { + return target.getLevel(); } + @ApiStatus.Internal + // Casting helper. Backup case shouldn't ever happen, see protection in setTargetLevel(). + public ServerLevel getTargetServerLevelOr(ServerLevel backup) { + return target.getLevel(backup) instanceof ServerLevel serverlevel ? serverlevel : backup; + } + + // TODO: Update Javadoc (remove apiNote) when the deprecated constructors get removed + /** + * Checks if the event sub-type support changing the target level. + *

+ * + * @apiNote At the moment, a positive result is not a guarantee that the change will be honored by a modded mode of teleportation. + * Only changing the target level when the original target level is different from the source level can be considered safe in 1.21.1. + * @implSpec This needs to be overridden by subclasses depending on the capabilities of the code firing the event. + * @return {@code true} if the event also supports changing the target's level, {@code false} if it only supports changing its coordinates. + */ + public boolean supportsTargetLevelChange() { + return true; + } + + // TODO: Update Javadoc (remove apiNote) when the deprecated constructors get removed + /** + * Changes the target Level of the teleportation. Will throw an {@link IllegalStateException} if that is not supported or if the type + * of the provided level (client/server) doesn't match the original value's. + * + * @apiNote At the moment, there's no guarantee that the change will be honored by a modded mode of teleportation. Only changing the + * target level when the original target level is different from the source level can be considered safe in 1.21.1. + * @param targetLevel The new level. + * @throws IllegalStateException if the event does not support changing the target level or if the type of the provided level + * (client/server) doesn't match the original value's. + */ + public void setTargetLevel(Level targetLevel) { + if ((target.getLevel() instanceof ServerLevel) != (targetLevel instanceof ServerLevel)) { + throw new IllegalStateException("Type of Level (ServerLevel/ClientLevel) must match to avoid side confusion"); + } + if (!supportsTargetLevelChange()) { + throw new IllegalStateException("This kind of teleporting does not support dimension changing. Cancel the event and teleport manually instead."); + } + target = target.withLevel(targetLevel); + } + + // TODO: Merge with getGlobalTarget() into "GlobalVec3 getTarget()" after 1.21.1 public Vec3 getTarget() { - return new Vec3(this.targetX, this.targetY, this.targetZ); + return target; + } + + /** + * Returns the current target location, which is either the original target location or the one set by an earlier event subscriber. + * + * @apiNote This method will be removed after 1.21.1 and its return value will be available from {@link #getTarget()}. + * @return The target location, including the level. + */ + public GlobalVec3 getGlobalTarget() { + return target; } public double getPrevX() { @@ -80,8 +155,19 @@ public double getPrevZ() { return getEntity().getZ(); } + // TODO: Merge with getGlobalPrev() into "GlobalVec3 getPrev()" after 1.21.1 public Vec3 getPrev() { - return getEntity().position(); + return new GlobalVec3(getEntity()); + } + + /** + * Returns the source location. + * + * @apiNote This method will be removed after 1.21.1 and its return value will be available from {@link #getPrev()}. + * @return The source location, including the level. + */ + public GlobalVec3 getGlobalPrev() { + return new GlobalVec3(getEntity()); } /** @@ -91,8 +177,6 @@ public Vec3 getPrev() { * This event is {@link ICancellableEvent}.
* If the event is not canceled, the entity will be teleported. *
- * This event does not have a result. {@link HasResult}
- *
* This event is fired on the {@link NeoForge#EVENT_BUS}.
*
* This event is only fired on the {@link LogicalSide#SERVER} side.
@@ -100,9 +184,14 @@ public Vec3 getPrev() { * If this event is canceled, the entity will not be teleported. */ public static class TeleportCommand extends EntityTeleportEvent implements ICancellableEvent { + @Deprecated(since = "1.21.1", forRemoval = true) public TeleportCommand(Entity entity, double targetX, double targetY, double targetZ) { super(entity, targetX, targetY, targetZ); } + + public TeleportCommand(Entity entity, GlobalVec3 target) { + super(entity, target); + } } /** @@ -112,8 +201,6 @@ public TeleportCommand(Entity entity, double targetX, double targetY, double tar * This event is {@link ICancellableEvent}.
* If the event is not canceled, the entity will be teleported. *
- * This event does not have a result. {@link HasResult}
- *
* This event is fired on the {@link NeoForge#EVENT_BUS}.
*
* This event is only fired on the {@link LogicalSide#SERVER} side.
@@ -121,9 +208,14 @@ public TeleportCommand(Entity entity, double targetX, double targetY, double tar * If this event is canceled, the entity will not be teleported. */ public static class SpreadPlayersCommand extends EntityTeleportEvent implements ICancellableEvent { + @Deprecated(since = "1.21.1", forRemoval = true) public SpreadPlayersCommand(Entity entity, double targetX, double targetY, double targetZ) { super(entity, targetX, targetY, targetZ); } + + public SpreadPlayersCommand(Entity entity, GlobalVec3 target) { + super(entity, target); + } } /** @@ -132,8 +224,6 @@ public SpreadPlayersCommand(Entity entity, double targetX, double targetY, doubl * This event is {@link ICancellableEvent}.
* If the event is not canceled, the entity will be teleported. *
- * This event does not have a result. {@link HasResult}
- *
* This event is fired on the {@link NeoForge#EVENT_BUS}.
*
* This event is only fired on the {@link LogicalSide#SERVER} side.
@@ -143,14 +233,24 @@ public SpreadPlayersCommand(Entity entity, double targetX, double targetY, doubl public static class EnderEntity extends EntityTeleportEvent implements ICancellableEvent { private final LivingEntity entityLiving; + @Deprecated(since = "1.21.1", forRemoval = true) public EnderEntity(LivingEntity entity, double targetX, double targetY, double targetZ) { super(entity, targetX, targetY, targetZ); this.entityLiving = entity; } + public EnderEntity(LivingEntity entity, GlobalVec3 target) { + super(entity, target); + this.entityLiving = entity; + } + public LivingEntity getEntityLiving() { return entityLiving; } + + public boolean supportsTargetLevelChange() { + return false; + } } /** @@ -159,8 +259,6 @@ public LivingEntity getEntityLiving() { * This event is {@link ICancellableEvent}.
* If the event is not canceled, the entity will be teleported. *
- * This event does not have a result. {@link HasResult}
- *
* This event is fired on the {@link NeoForge#EVENT_BUS}.
*
* This event is only fired on the {@link LogicalSide#SERVER} side.
@@ -174,8 +272,8 @@ public static class EnderPearl extends EntityTeleportEvent implements ICancellab private final HitResult hitResult; @ApiStatus.Internal - public EnderPearl(ServerPlayer entity, double targetX, double targetY, double targetZ, ThrownEnderpearl pearlEntity, float attackDamage, HitResult hitResult) { - super(entity, targetX, targetY, targetZ); + public EnderPearl(ServerPlayer entity, GlobalVec3 target, ThrownEnderpearl pearlEntity, float attackDamage, HitResult hitResult) { + super(entity, target); this.pearlEntity = pearlEntity; this.player = entity; this.attackDamage = attackDamage; @@ -210,8 +308,6 @@ public void setAttackDamage(float attackDamage) { * This event is {@link ICancellableEvent}.
* If the event is not canceled, the entity will be teleported. *
- * This event does not have a result. {@link HasResult}
- *
* This event is fired on the {@link NeoForge#EVENT_BUS}.
*
* This event is only fired on the {@link LogicalSide#SERVER} side.
@@ -221,13 +317,23 @@ public void setAttackDamage(float attackDamage) { public static class ChorusFruit extends EntityTeleportEvent implements ICancellableEvent { private final LivingEntity entityLiving; + @Deprecated(since = "1.21.1", forRemoval = true) public ChorusFruit(LivingEntity entity, double targetX, double targetY, double targetZ) { super(entity, targetX, targetY, targetZ); this.entityLiving = entity; } + public ChorusFruit(LivingEntity entity, GlobalVec3 target) { + super(entity, target); + this.entityLiving = entity; + } + public LivingEntity getEntityLiving() { return entityLiving; } + + public boolean supportsTargetLevelChange() { + return false; + } } }