Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,197 +24,30 @@
*/
package org.spongepowered.common.mixin.core.server.commands;

import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.BlockPos;
import net.minecraft.server.commands.LookAt;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.server.commands.TeleportCommand;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.PositionMoveRotation;
import net.minecraft.world.entity.Relative;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.phys.Vec3;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.event.CauseStackManager;
import org.spongepowered.api.event.EventContextKeys;
import org.spongepowered.api.event.SpongeEventFactory;
import org.spongepowered.api.event.cause.entity.MovementTypes;
import org.spongepowered.api.event.entity.ChangeEntityWorldEvent;
import org.spongepowered.api.event.entity.MoveEntityEvent;
import org.spongepowered.api.event.entity.RotateEntityEvent;
import org.spongepowered.api.world.server.ServerWorld;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.common.SpongeCommon;
import org.spongepowered.common.event.ShouldFire;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.common.event.tracking.PhaseTracker;
import org.spongepowered.common.hooks.PlatformHooks;
import org.spongepowered.common.util.VecHelper;
import org.spongepowered.math.vector.Vector3d;

import java.util.Set;

@Mixin(TeleportCommand.class)
public abstract class TeleportCommandMixin {

/**
* @author Zidane
* @reason Have the teleport command respect our events
*/
@Overwrite
// TODO check if this is still correct - check if we can get rid of the overwrite
private static void performTeleport(CommandSourceStack source, Entity entityIn, ServerLevel worldIn, double x, double y, double z,
Set<Relative> relativeList, float yaw, float pitch, @Nullable LookAt facing) {

double actualX = x;
double actualY = y;
double actualZ = z;
double actualYaw = yaw;
double actualPitch = pitch;

if (!(entityIn instanceof ServerPlayer)) {
actualYaw = Mth.wrapDegrees(yaw);
actualPitch = Mth.wrapDegrees(pitch);
actualPitch = Mth.clamp(actualPitch, -90.0F, 90.0F);
}

if (worldIn == entityIn.level()) {
try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) {
frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.COMMAND);

// TODO Should honor the relative list before the event..

if (ShouldFire.MOVE_ENTITY_EVENT) {
final MoveEntityEvent posEvent = SpongeEventFactory.createMoveEntityEvent(frame.currentCause(),
(org.spongepowered.api.entity.Entity) entityIn, VecHelper.toVector3d(entityIn.position()),
new Vector3d(x, y, z), new Vector3d(x, y, z));

if (SpongeCommon.post(posEvent)) {
return;
}

actualX = posEvent.destinationPosition().x();
actualY = posEvent.destinationPosition().y();
actualZ = posEvent.destinationPosition().z();
}

if (ShouldFire.ROTATE_ENTITY_EVENT) {
final RotateEntityEvent rotateEvent = SpongeEventFactory.createRotateEntityEvent(frame.currentCause(),
(org.spongepowered.api.entity.Entity) entityIn, new Vector3d(actualPitch, actualYaw, 0),
new Vector3d(pitch, yaw, 0));

SpongeCommon.post(rotateEvent);
actualYaw = rotateEvent.isCancelled() ? entityIn.getYRot() : rotateEvent.toRotation().y();
actualPitch = rotateEvent.isCancelled() ? entityIn.getXRot() : rotateEvent.toRotation().x();
}

if (entityIn instanceof ServerPlayer sp) {

ChunkPos chunkpos = new ChunkPos(new BlockPos((int) actualX, (int) actualY, (int) actualZ));
worldIn.getChunkSource().addRegionTicket(TicketType.FORCED, chunkpos, 1, chunkpos);

entityIn.stopRiding();

if (sp.isSleeping()) {
sp.stopSleepInBed(true, true);
}

sp.connection.teleport(
new PositionMoveRotation(new Vec3( actualX, actualY, actualZ), Vec3.ZERO, (float) actualYaw, (float) actualPitch), relativeList);
} else {
entityIn.moveTo(actualX, actualY, actualZ, (float) actualYaw, (float) actualPitch);
}

entityIn.setYHeadRot((float) actualYaw);
}
} else {
if (entityIn instanceof ServerPlayer sp) {
// To ensure mod code is caught, handling the world change for players happens in teleport
// Teleport will create a frame but we want to ensure it'll be the command movement type
// TODO check if this is still correct
PhaseTracker.getInstance().addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.COMMAND);
sp.teleportTo(worldIn, x, y, z, relativeList, yaw, pitch, true);
PhaseTracker.getInstance().removeContext(EventContextKeys.MOVEMENT_TYPE);
} else {
try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) {
frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.COMMAND);

final ServerLevel fromWorld = (ServerLevel) entityIn.getCommandSenderWorld();

final ChangeEntityWorldEvent.Pre preEvent = PlatformHooks.INSTANCE.getEventHooks().callChangeEntityWorldEventPre(entityIn,
worldIn);
if (SpongeCommon.post(preEvent)) {
return;
}

final ChangeEntityWorldEvent.Reposition posEvent =
SpongeEventFactory.createChangeEntityWorldEventReposition(frame.currentCause(),
(org.spongepowered.api.entity.Entity) entityIn,
(org.spongepowered.api.world.server.ServerWorld) entityIn.getCommandSenderWorld(),
VecHelper.toVector3d(entityIn.position()), new Vector3d(x, y, z), preEvent.originalDestinationWorld(),
new Vector3d(x, y, z), preEvent.destinationWorld());

if (SpongeCommon.post(posEvent)) {
return;
}

entityIn.unRide();
final Entity result = entityIn.getType().create(worldIn, EntitySpawnReason.DIMENSION_TRAVEL);
if (result == null) {
return;
}

if (ShouldFire.ROTATE_ENTITY_EVENT) {
final RotateEntityEvent rotateEvent = SpongeEventFactory.createRotateEntityEvent(frame.currentCause(),
(org.spongepowered.api.entity.Entity) entityIn, new Vector3d(entityIn.getXRot(), entityIn.getYRot(), 0),
new Vector3d(actualPitch, actualYaw, 0));

if (!SpongeCommon.post(rotateEvent)) {
actualYaw = Mth.wrapDegrees(rotateEvent.toRotation().y());
actualPitch = Mth.wrapDegrees(rotateEvent.toRotation().x());
actualPitch = Mth.clamp(actualPitch, -90.0F, 90.0F);
} else {
actualYaw = entityIn.getYRot();
actualPitch = entityIn.getXRot();
}
}

result.restoreFrom(entityIn);
result.moveTo(posEvent.destinationPosition().x(), posEvent.destinationPosition().y(),
posEvent.destinationPosition().z(), (float) actualYaw, (float) actualPitch);
result.setYHeadRot((float) actualYaw);
worldIn.addDuringTeleport(result);
entityIn.setRemoved(Entity.RemovalReason.CHANGED_DIMENSION);

Sponge.eventManager().post(SpongeEventFactory.createChangeEntityWorldEventPost(
PhaseTracker.getInstance().currentCause(),
(org.spongepowered.api.entity.Entity) result,
(ServerWorld) fromWorld,
preEvent.originalDestinationWorld(),
preEvent.destinationWorld()
));
}
}
}

if (facing != null) {
facing.perform(source, entityIn);
}

if (!(entityIn instanceof LivingEntity) || !((LivingEntity)entityIn).isFallFlying()) {
entityIn.setDeltaMovement(entityIn.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D));
entityIn.setOnGround(true);
}

if (entityIn instanceof PathfinderMob) {
((PathfinderMob)entityIn).getNavigation().stop();
@WrapOperation(method = "performTeleport", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;teleportTo(Lnet/minecraft/server/level/ServerLevel;DDDLjava/util/Set;FFZ)Z"))
private static boolean impl$createCauseFrameForPerformTeleport(
final Entity instance, final ServerLevel $$0, final double $$1, final double $$2, final double $$3, final Set<Relative> $$4, final float $$5, final float $$6, final boolean $$7, final Operation<Boolean> original) {
try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) {
frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.COMMAND);
return original.call(instance, $$0, $$1, $$2, $$3, $$4, $$5, $$6, $$7);
}
}
}