Search in sources :

Example 6 with MoveEntityEvent

use of org.spongepowered.api.event.entity.MoveEntityEvent in project SpongeCommon by SpongePowered.

the class EntityTickPhaseState method fireMovementEvents.

private void fireMovementEvents(net.minecraft.entity.Entity entity) {
    // Note: Projectiles are handled with CollideBlockEvent.Impact
    if (entity.isDead || entity instanceof IProjectile || entity instanceof EntityItem) {
        return;
    }
    Entity spongeEntity = (Entity) entity;
    if (entity.lastTickPosX != entity.posX || entity.lastTickPosY != entity.posY || entity.lastTickPosZ != entity.posZ || entity.rotationPitch != entity.prevRotationPitch || entity.rotationYaw != entity.prevRotationYaw) {
        // yes we have a move event.
        final double currentPosX = entity.posX;
        final double currentPosY = entity.posY;
        final double currentPosZ = entity.posZ;
        final Vector3d oldPositionVector = new Vector3d(entity.lastTickPosX, entity.lastTickPosY, entity.lastTickPosZ);
        final Vector3d currentPositionVector = new Vector3d(currentPosX, currentPosY, currentPosZ);
        Vector3d oldRotationVector = new Vector3d(entity.prevRotationPitch, entity.prevRotationYaw, 0);
        Vector3d currentRotationVector = new Vector3d(entity.rotationPitch, entity.rotationYaw, 0);
        final Transform<World> oldTransform = new Transform<>(spongeEntity.getWorld(), oldPositionVector, oldRotationVector, spongeEntity.getScale());
        final Transform<World> newTransform = new Transform<>(spongeEntity.getWorld(), currentPositionVector, currentRotationVector, spongeEntity.getScale());
        final MoveEntityEvent event = SpongeEventFactory.createMoveEntityEvent(Sponge.getCauseStackManager().getCurrentCause(), oldTransform, newTransform, spongeEntity);
        if (SpongeImpl.postEvent(event)) {
            entity.posX = entity.lastTickPosX;
            entity.posY = entity.lastTickPosY;
            entity.posZ = entity.lastTickPosZ;
            entity.rotationPitch = entity.prevRotationPitch;
            entity.rotationYaw = entity.prevRotationYaw;
        } else {
            Vector3d newPosition = event.getToTransform().getPosition();
            if (!newPosition.equals(currentPositionVector)) {
                entity.posX = newPosition.getX();
                entity.posY = newPosition.getY();
                entity.posZ = newPosition.getZ();
            }
            if (!event.getToTransform().getRotation().equals(currentRotationVector)) {
                entity.rotationPitch = (float) currentRotationVector.getX();
                entity.rotationYaw = (float) currentRotationVector.getY();
            }
        // entity.setPositionWithRotation(position.getX(), position.getY(), position.getZ(), rotation.getFloorX(), rotation.getFloorY());
        /*
                    Some thoughts from gabizou: The interesting thing here is that while this is only called
                    in World.updateEntityWithOptionalForce, by default, it supposedly handles updating the rider entity
                    of the entity being handled here. The interesting issue is that since we are setting the transform,
                    the rider entity (and the rest of the rider entities) are being updated as well with the new position
                    and potentially world, which results in a dirty world usage (since the world transfer is handled by
                    us). Now, the thing is, the previous position is not updated either, and likewise, the current position
                    is being set by us as well. So, there's some issue I'm sure that is bound to happen with this
                    logic.
                     */
        // ((Entity) entity).setTransform(event.getToTransform());
        }
    }
}
Also used : Entity(org.spongepowered.api.entity.Entity) MoveEntityEvent(org.spongepowered.api.event.entity.MoveEntityEvent) Vector3d(com.flowpowered.math.vector.Vector3d) World(org.spongepowered.api.world.World) Transform(org.spongepowered.api.entity.Transform) EntityItem(net.minecraft.entity.item.EntityItem) IProjectile(net.minecraft.entity.IProjectile)

Example 7 with MoveEntityEvent

use of org.spongepowered.api.event.entity.MoveEntityEvent in project SpongeCommon by SpongePowered.

the class SpongeCommonEventFactory method callNaturalMoveEntityEvent.

/**
 * Performs the logic necessary to post the {@link MoveEntityEvent position event} for an {@link Entity}.
 *
 * @param entity The event
 */
public static void callNaturalMoveEntityEvent(final net.minecraft.world.entity.Entity entity) {
    if (entity.removed) {
        return;
    }
    final double deltaX = entity.xOld - entity.getX();
    final double deltaY = entity.yOld - entity.getY();
    final double deltaZ = entity.zOld - entity.getZ();
    final double deltaChange = Math.pow(deltaX, 2) + Math.pow(deltaY, 2) + Math.pow(deltaZ, 2);
    if (deltaChange < 1f / 256) {
        return;
    }
    try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) {
        frame.pushCause(entity);
        frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.NATURAL);
        final MoveEntityEvent event = SpongeEventFactory.createMoveEntityEvent(frame.currentCause(), (Entity) entity, new Vector3d(entity.xOld, entity.yOld, entity.zOld), new Vector3d(entity.getX(), entity.getY(), entity.getZ()), new Vector3d(entity.getX(), entity.getY(), entity.getZ()));
        if (SpongeCommon.post(event)) {
            entity.setPos(entity.xOld, entity.yOld, entity.zOld);
        } else {
            entity.setPos(event.destinationPosition().x(), event.destinationPosition().y(), event.destinationPosition().z());
        }
    }
}
Also used : MoveEntityEvent(org.spongepowered.api.event.entity.MoveEntityEvent) Vector3d(org.spongepowered.math.vector.Vector3d) CauseStackManager(org.spongepowered.api.event.CauseStackManager)

Example 8 with MoveEntityEvent

use of org.spongepowered.api.event.entity.MoveEntityEvent in project SpongeCommon by SpongePowered.

the class ServerGamePacketListenerImplMixin method impl$callMoveEntityEvent.

/**
 * Effectively, hooking into the following code block:
 * <pre>
 *       if (isMovePlayerPacketInvalid(packetIn)) {
 *          this.disconnect(new TranslationTextComponent("multiplayer.disconnect.invalid_player_movement"));
 *       } else {
 *          ServerWorld serverworld = this.server.world(this.player.dimension);
 *          if (!this.player.queuedEndExit) { // <---- Here is where we're injecting
 *             if (this.networkTickCount == 0) {
 *                this.captureCurrentPosition();
 *             }
 * </pre>
 * we can effectively short circuit the method to handle movement code where
 * returning {@code true} will escape the packet being processed further entirely and
 * {@code false} will allow the remaining processing of the method run.
 *
 * @param packetIn The movement packet
 */
@Inject(method = "handleMovePlayer", at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;awaitingPositionFromClient:Lnet/minecraft/world/phys/Vec3;"), slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/server/level/ServerPlayer;wonGame:Z"), to = @At(value = "FIELD", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;tickCount:I", ordinal = 1)), cancellable = true)
private void impl$callMoveEntityEvent(final ServerboundMovePlayerPacket packetIn, final CallbackInfo ci) {
    // If the movement is modified we pretend that the player has queuedEndExit = true
    // so that vanilla wont process that packet further
    final ServerboundMovePlayerPacketAccessor packetInAccessor = (ServerboundMovePlayerPacketAccessor) packetIn;
    // We don't fire an event to avoid confusing plugins.
    if (!packetInAccessor.accessor$hasPos() && !packetInAccessor.accessor$hasRot()) {
        return;
    }
    final boolean goodMovementPacket = this.receivedMovePacketCount - this.knownMovePacketCount <= 5;
    final boolean fireMoveEvent = goodMovementPacket && packetInAccessor.accessor$hasPos() && ShouldFire.MOVE_ENTITY_EVENT;
    final boolean fireRotationEvent = goodMovementPacket && packetInAccessor.accessor$hasRot() && ShouldFire.ROTATE_ENTITY_EVENT;
    final ServerPlayer player = (ServerPlayer) this.player;
    final Vector3d fromRotation = new Vector3d(this.player.yRot, this.player.xRot, 0);
    // Use the position of the last movement with an event or the current player position if never called
    // We need this because we ignore very small position changes as to not spam as many move events.
    final Vector3d fromPosition = player.position();
    Vector3d toPosition = new Vector3d(packetIn.getX(this.player.getX()), packetIn.getY(this.player.getY()), packetIn.getZ(this.player.getZ()));
    Vector3d toRotation = new Vector3d(packetIn.getYRot(this.player.yRot), packetIn.getXRot(this.player.xRot), 0);
    final boolean significantRotation = fromRotation.distanceSquared(toRotation) > (.15f * .15f);
    final Vector3d originalToPosition = toPosition;
    boolean cancelMovement = false;
    boolean cancelRotation = false;
    // Call move & rotate event as needed...
    if (fireMoveEvent) {
        final MoveEntityEvent event = SpongeEventFactory.createMoveEntityEvent(PhaseTracker.getCauseStackManager().currentCause(), (ServerPlayer) this.player, fromPosition, toPosition, toPosition);
        if (SpongeCommon.post(event)) {
            cancelMovement = true;
        } else {
            toPosition = event.destinationPosition();
        }
    }
    if (significantRotation && fireRotationEvent) {
        final RotateEntityEvent event = SpongeEventFactory.createRotateEntityEvent(PhaseTracker.getCauseStackManager().currentCause(), (ServerPlayer) this.player, fromRotation, toRotation);
        if (SpongeCommon.post(event)) {
            cancelRotation = true;
            toRotation = fromRotation;
        } else {
            toRotation = event.toRotation();
        }
    }
    // player position and update the player's relation in the chunk manager.
    if (cancelMovement) {
        if (fromPosition.distanceSquared(toPosition) > 0) {
            // Set the location, as if the player was teleporting
            this.awaitingTeleportTime = this.tickCount;
            this.shadow$teleport(fromPosition.x(), fromPosition.y(), fromPosition.z(), (float) toRotation.x(), (float) toRotation.y());
        } else {
            // If this is only rotation do not teleport back
            this.player.absMoveTo(fromPosition.x(), fromPosition.y(), fromPosition.z(), (float) toRotation.x(), (float) toRotation.y());
        }
        ci.cancel();
        return;
    }
    // Handle event results
    if (!toPosition.equals(originalToPosition)) {
        // Check if we have to say it's a "teleport" vs a standard move
        final double d4 = packetIn.getX(this.player.getX());
        final double d5 = packetIn.getY(this.player.getY());
        final double d6 = packetIn.getZ(this.player.getZ());
        final double d7 = d4 - this.firstGoodX;
        final double d8 = d5 - this.firstGoodY;
        final double d9 = d6 - this.firstGoodZ;
        final double d10 = this.player.getDeltaMovement().lengthSqr();
        final double d11 = d7 * d7 + d8 * d8 + d9 * d9;
        final float f2 = this.player.isFallFlying() ? 300.0F : 100.0F;
        final int i = this.receivedMovePacketCount - this.knownMovePacketCount;
        if (d11 - d10 > (double) (f2 * (float) i) && !this.shadow$isSingleplayerOwner()) {
            // At this point, we need to set the target position so the teleport code forces it
            this.awaitingPositionFromClient = VecHelper.toVanillaVector3d(toPosition);
            ((EntityAccessor) this.player).invoker$setRot((float) toRotation.x(), (float) toRotation.y());
            // And reset the position update so the force set is done.
            this.awaitingTeleportTime = this.tickCount - Constants.Networking.MAGIC_TRIGGER_TELEPORT_CONFIRM_DIFF;
        } else {
            // otherwise, set the data back onto the packet
            packetInAccessor.accessor$hasPos(true);
            packetInAccessor.accessor$x(toPosition.x());
            packetInAccessor.accessor$y(toPosition.y());
            packetInAccessor.accessor$z(toPosition.z());
        }
    }
}
Also used : EntityAccessor(org.spongepowered.common.accessor.world.entity.EntityAccessor) ServerboundMovePlayerPacketAccessor(org.spongepowered.common.accessor.network.protocol.game.ServerboundMovePlayerPacketAccessor) MoveEntityEvent(org.spongepowered.api.event.entity.MoveEntityEvent) Vector3d(org.spongepowered.math.vector.Vector3d) ServerPlayer(org.spongepowered.api.entity.living.player.server.ServerPlayer) RotateEntityEvent(org.spongepowered.api.event.entity.RotateEntityEvent) Inject(org.spongepowered.asm.mixin.injection.Inject)

Example 9 with MoveEntityEvent

use of org.spongepowered.api.event.entity.MoveEntityEvent in project SpongeCommon by SpongePowered.

the class EntityMixin method impl$fireMoveEvent.

@org.checkerframework.checker.nullness.qual.Nullable
protected final Vector3d impl$fireMoveEvent(final PhaseTracker phaseTracker, final Vector3d originalDestinationPosition) {
    final boolean hasMovementContext = phaseTracker.currentContext().containsKey(EventContextKeys.MOVEMENT_TYPE);
    if (!hasMovementContext) {
        phaseTracker.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.PLUGIN);
    }
    final MoveEntityEvent event = SpongeEventFactory.createMoveEntityEvent(phaseTracker.currentCause(), (org.spongepowered.api.entity.Entity) this, VecHelper.toVector3d(this.shadow$position()), originalDestinationPosition, originalDestinationPosition);
    if (!hasMovementContext) {
        phaseTracker.popCause();
        phaseTracker.removeContext(EventContextKeys.MOVEMENT_TYPE);
    }
    if (SpongeCommon.post(event)) {
        return null;
    }
    return event.destinationPosition();
}
Also used : MoveEntityEvent(org.spongepowered.api.event.entity.MoveEntityEvent)

Example 10 with MoveEntityEvent

use of org.spongepowered.api.event.entity.MoveEntityEvent in project SpongeCommon by SpongePowered.

the class LivingEntityMixin method impl$callMoveEntityEventForTeleport.

@Inject(method = "randomTeleport", at = @At(value = "RETURN", ordinal = 0, shift = At.Shift.BY, by = 2), cancellable = true)
private void impl$callMoveEntityEventForTeleport(final double x, final double y, final double z, final boolean changeState, final CallbackInfoReturnable<Boolean> cir) {
    if (!ShouldFire.MOVE_ENTITY_EVENT) {
        return;
    }
    try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) {
        frame.pushCause(this);
        // ENTITY_TELEPORT is our fallback context
        if (!frame.currentContext().containsKey(EventContextKeys.MOVEMENT_TYPE)) {
            frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.ENTITY_TELEPORT);
        }
        final MoveEntityEvent event = SpongeEventFactory.createMoveEntityEvent(frame.currentCause(), (org.spongepowered.api.entity.Entity) this, this.impl$preTeleportPosition, new Vector3d(this.shadow$getX(), this.shadow$getY(), this.shadow$getZ()), new Vector3d(x, y, z));
        if (SpongeCommon.post(event)) {
            this.shadow$teleportTo(this.impl$preTeleportPosition.x(), this.impl$preTeleportPosition.y(), this.impl$preTeleportPosition.z());
            cir.setReturnValue(false);
            return;
        }
        this.shadow$teleportTo(event.destinationPosition().x(), event.destinationPosition().y(), event.destinationPosition().z());
    }
}
Also used : MoveEntityEvent(org.spongepowered.api.event.entity.MoveEntityEvent) Vector3d(org.spongepowered.math.vector.Vector3d) CauseStackManager(org.spongepowered.api.event.CauseStackManager) Inject(org.spongepowered.asm.mixin.injection.Inject)

Aggregations

MoveEntityEvent (org.spongepowered.api.event.entity.MoveEntityEvent)10 Vector3d (org.spongepowered.math.vector.Vector3d)6 CauseStackManager (org.spongepowered.api.event.CauseStackManager)5 Vector3d (com.flowpowered.math.vector.Vector3d)3 RotateEntityEvent (org.spongepowered.api.event.entity.RotateEntityEvent)3 World (org.spongepowered.api.world.World)3 Inject (org.spongepowered.asm.mixin.injection.Inject)3 BlockPos (net.minecraft.core.BlockPos)2 Entity (net.minecraft.world.entity.Entity)2 ChunkPos (net.minecraft.world.level.ChunkPos)2 ServerPlayer (org.spongepowered.api.entity.living.player.server.ServerPlayer)2 ChangeEntityWorldEvent (org.spongepowered.api.event.entity.ChangeEntityWorldEvent)2 Location (org.spongepowered.api.world.Location)2 Overwrite (org.spongepowered.asm.mixin.Overwrite)2 Redirect (org.spongepowered.asm.mixin.injection.Redirect)2 Identity (net.kyori.adventure.identity.Identity)1 Entity (net.minecraft.entity.Entity)1 IProjectile (net.minecraft.entity.IProjectile)1 EntityItem (net.minecraft.entity.item.EntityItem)1 EntityPlayer (net.minecraft.entity.player.EntityPlayer)1