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
+ * 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
+ *
+ * @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}.
**/
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.
+ *
* 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;
+ }
}
}