package com.thebrokenrail.gestus.mixin; import com.mojang.authlib.GameProfile; import com.thebrokenrail.gestus.Gestus; import com.thebrokenrail.gestus.entity.FakePlayerEntity; import com.thebrokenrail.gestus.util.ServerPlayerEntityExtension; import net.minecraft.entity.effect.StatusEffects; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.EulerAngle; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ServerPlayerEntity.class) public abstract class MixinServerPlayerEntity extends PlayerEntity implements ServerPlayerEntityExtension { @Unique private FakePlayerEntity shadow; public MixinServerPlayerEntity(World world, BlockPos pos, float yaw, GameProfile profile) { super(world, pos, yaw, profile); } private void updateShadow() { if (shadow != null && (shadow.getEntityWorld() != getEntityWorld() || shadow.removed)) { if (!shadow.removed) { shadow.remove(); } shadow = null; } boolean spawn = false; if (shadow == null) { shadow = new FakePlayerEntity(Gestus.FAKE_PLAYER_ENTITY_TYPE, getEntityWorld(), getDisplayName(), (ServerPlayerEntity) (Object) this); spawn = true; } boolean invisible = hasStatusEffect(StatusEffects.INVISIBILITY); boolean glowing = hasStatusEffect(StatusEffects.GLOWING); shadow.setInvisible(invisible); shadow.setGlowing(glowing); shadow.setWorld(getEntityWorld()); shadow.refreshPositionAndAngles(getX(), getY(), getZ(), yaw, pitch); shadow.setHeadRotation(new EulerAngle(pitch, getHeadYaw() - yaw, 0f)); shadow.setVelocity(getVelocity()); shadow.setCustomNameVisible(!invisible && !isSneaking()); shadow.update(); if (spawn) { getEntityWorld().spawnEntity(shadow); } } @Unique private Vec3d lastPos = null; @Inject(at = @At("HEAD"), method = "tick") public void tick(CallbackInfo info) { setInvisible(true); setGlowing(false); updateShadow(); lastPos = getPos(); } @Override public Vec3d getLastPos() { return lastPos; } @Override public FakePlayerEntity getShadow() { return shadow; } @Override public void remove() { super.remove(); shadow.remove(); } @Override public void swingHand(Hand hand, boolean bl) { super.swingHand(hand, bl); shadow.hit(hand); } }