diff options
| author | Linnea Gräf <nea@nea.moe> | 2023-11-24 14:59:12 +0100 |
|---|---|---|
| committer | Linnea Gräf <nea@nea.moe> | 2023-11-24 15:01:39 +0100 |
| commit | a77fdc7edfaae1f00b298fe97152178341bdee50 (patch) | |
| tree | 164990ef5c4484b572ad4057893fd2565176cdb7 | |
| parent | b32344991743f53c94a48f8a0054d0126b15bbfd (diff) | |
| download | SkyHanni-a77fdc7edfaae1f00b298fe97152178341bdee50.tar.gz SkyHanni-a77fdc7edfaae1f00b298fe97152178341bdee50.tar.bz2 SkyHanni-a77fdc7edfaae1f00b298fe97152178341bdee50.zip | |
Add API: HolographicEntities
7 files changed, 278 insertions, 3 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/data/MinecraftData.kt b/src/main/java/at/hannibal2/skyhanni/data/MinecraftData.kt index 56d565299..34b880daf 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/MinecraftData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/MinecraftData.kt @@ -3,6 +3,7 @@ package at.hannibal2.skyhanni.data import at.hannibal2.skyhanni.events.ItemInHandChangeEvent import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent +import at.hannibal2.skyhanni.events.NeaTickEvent import at.hannibal2.skyhanni.events.PacketEvent import at.hannibal2.skyhanni.events.PlaySoundEvent import at.hannibal2.skyhanni.events.ReceiveParticleEvent @@ -65,7 +66,9 @@ object MinecraftData { } } + @Deprecated("totalTicks counts twice as fast as it should", ReplaceWith("totalNonDupedTicks")) var totalTicks = 0 + var totalNonDupedTicks = 0 @SubscribeEvent fun onTick(event: TickEvent.ClientTickEvent) { @@ -73,6 +76,10 @@ object MinecraftData { totalTicks++ LorenzTickEvent(totalTicks).postAndCatch() DelayedRun.checkRuns() + if (event.phase == TickEvent.Phase.END) { + totalNonDupedTicks++ + NeaTickEvent(totalNonDupedTicks).postAndCatch() + } } @SubscribeEvent diff --git a/src/main/java/at/hannibal2/skyhanni/events/LorenzTickEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/LorenzTickEvent.kt index 5bf8d7c2e..f81a8345b 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/LorenzTickEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/LorenzTickEvent.kt @@ -1,7 +1,20 @@ package at.hannibal2.skyhanni.events -class LorenzTickEvent(private val tick: Int) : LorenzEvent() { +import at.hannibal2.skyhanni.utils.TimeUtils.inWholeTicks +import kotlin.time.Duration + +@Deprecated( + "LorenzTickEvent ticks with double speed", + ReplaceWith("NeaTickEvent", "at.hannibal2.skyhanni.events.NeaTickEvent") +) +class LorenzTickEvent constructor(private val tick: Int) : LorenzEvent() { fun isMod(i: Int) = tick % i == 0 fun repeatSeconds(i: Int) = isMod(i * 20) -}
\ No newline at end of file +} + +data class NeaTickEvent(val tick: Int) : LorenzEvent() { + fun isMod(order: Int, seed: Int = 0) = (tick % order) == (seed % order) + fun repeatEvery(duration: Duration, seed: Int = 0) = isMod(duration.inWholeTicks, seed) + fun repeatSeconds(seconds: Int) = isMod(seconds * 20) +} diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorRender.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorRender.java new file mode 100644 index 000000000..c92a49c01 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorRender.java @@ -0,0 +1,15 @@ +package at.hannibal2.skyhanni.mixins.transformers; + +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(Render.class) +public interface AccessorRender<T extends Entity> { + + @Invoker("bindEntityTexture") + boolean bindEntityTexture_skyhanni(T entity); + + +} diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorRendererLivingEntity.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorRendererLivingEntity.java new file mode 100644 index 000000000..7e7a8974f --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorRendererLivingEntity.java @@ -0,0 +1,15 @@ +package at.hannibal2.skyhanni.mixins.transformers; + +import net.minecraft.client.renderer.entity.RendererLivingEntity; +import net.minecraft.entity.EntityLivingBase; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(RendererLivingEntity.class) +public interface AccessorRendererLivingEntity<T extends EntityLivingBase> + extends AccessorRender<T> { + @Invoker("setBrightness") + boolean setBrightness_skyhanni(T entitylivingbaseIn, float partialTicks, boolean combineTextures); + @Invoker("unsetBrightness") + void setBrightness_skyhanni(); +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/HolographicEntities.kt b/src/main/java/at/hannibal2/skyhanni/utils/HolographicEntities.kt new file mode 100644 index 000000000..5110c40c8 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/HolographicEntities.kt @@ -0,0 +1,212 @@ +package at.hannibal2.skyhanni.utils + +import at.hannibal2.skyhanni.mixins.transformers.AccessorRendererLivingEntity +import at.hannibal2.skyhanni.utils.RenderUtils.getViewerPos +import at.hannibal2.skyhanni.utils.TimeUtils.inWholeTicks +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.entity.RendererLivingEntity +import net.minecraft.entity.EntityLivingBase +import net.minecraft.entity.boss.EntityWither +import net.minecraft.entity.item.EntityArmorStand +import net.minecraft.entity.monster.EntityBlaze +import net.minecraft.entity.monster.EntityCaveSpider +import net.minecraft.entity.monster.EntityCreeper +import net.minecraft.entity.monster.EntityEnderman +import net.minecraft.entity.monster.EntityEndermite +import net.minecraft.entity.monster.EntityGhast +import net.minecraft.entity.monster.EntityGiantZombie +import net.minecraft.entity.monster.EntityGuardian +import net.minecraft.entity.monster.EntityIronGolem +import net.minecraft.entity.monster.EntityMagmaCube +import net.minecraft.entity.monster.EntityPigZombie +import net.minecraft.entity.monster.EntitySilverfish +import net.minecraft.entity.monster.EntitySkeleton +import net.minecraft.entity.monster.EntitySlime +import net.minecraft.entity.monster.EntitySnowman +import net.minecraft.entity.monster.EntitySpider +import net.minecraft.entity.monster.EntityWitch +import net.minecraft.entity.monster.EntityZombie +import net.minecraft.entity.passive.EntityBat +import net.minecraft.entity.passive.EntityChicken +import net.minecraft.entity.passive.EntityCow +import net.minecraft.entity.passive.EntityHorse +import net.minecraft.entity.passive.EntityMooshroom +import net.minecraft.entity.passive.EntityOcelot +import net.minecraft.entity.passive.EntityPig +import net.minecraft.entity.passive.EntityRabbit +import net.minecraft.entity.passive.EntitySheep +import net.minecraft.entity.passive.EntitySquid +import net.minecraft.entity.passive.EntityVillager +import net.minecraft.entity.passive.EntityWolf +import org.lwjgl.opengl.GL11 + + +/** + * Utility for creating fake entities without an associated world in order to avoid contaminating the world state. + */ +object HolographicEntities { + + /** + * An instance of a holographic entity. Maintains a minimal controlled state, + * which has just enough information for rendering and basic manipulations, such as + * interpolated positioning. The underlying [entity] should not be accessed directly. + */ + class HolographicEntity<T : EntityLivingBase> internal constructor( + val entity: T, + var position: LorenzVec, + var yaw: Float, + ) { + var isChild: Boolean = false + var lastPosition: LorenzVec = position + var lastYaw: Float = yaw + val createdAt = TimeMark.now() + + val monotonicProgress get() = createdAt.passedTime().inWholeTicks + + /** + * Should be called exactly once per tick or never over the lifetime of this [HolographicEntity]. + */ + fun moveTo(position: LorenzVec, yaw: Float, isTeleport: Boolean = false) { + if (isTeleport) { + this.lastYaw = yaw + this.lastPosition = position + } else { + this.lastYaw = this.yaw + this.lastPosition = this.position + } + this.position = position + this.yaw = yaw + } + + fun interpolatedPosition(partialTicks: Float): LorenzVec { + return lastPosition.slope(position, partialTicks.toDouble()) + } + + fun interpolatedYaw(partialTicks: Float): Float { + return interpolateRotation(lastYaw, yaw, partialTicks) + } + } + + /** + * Template for a [HolographicEntity]. This class exists as a guard for + * [HolographicEntity] to prevent untested entities with potential NPEs + * being instantiated. A list of tested entities exist in [HolographicEntities]. + * Some of these entities rely on mixins from NEU for their proper null + * world handling. + */ + class HolographicBase<T : EntityLivingBase> internal constructor( + private val entity: T + ) { + fun instance(position: LorenzVec, yaw: Float): HolographicEntity<T> { + return HolographicEntity(entity, position, yaw) + } + } + + val zombie = HolographicBase(EntityZombie(null)) + val chicken = HolographicBase(EntityChicken(null)) + val slime = HolographicBase(EntitySlime(null)) + val wolf = HolographicBase(EntityWolf(null)) + val skeleton = HolographicBase(EntitySkeleton(null)) + val creeper = HolographicBase(EntityCreeper(null)) + val ocelot = HolographicBase(EntityOcelot(null)) + val blaze = HolographicBase(EntityBlaze(null)) + val rabbit = HolographicBase(EntityRabbit(null)) + val sheep = HolographicBase(EntitySheep(null)) + val horse = HolographicBase(EntityHorse(null)) + val eisengolem = HolographicBase(EntityIronGolem(null)) + val silverfish = HolographicBase(EntitySilverfish(null)) + val witch = HolographicBase(EntityWitch(null)) + val endermite = HolographicBase(EntityEndermite(null)) + val snowman = HolographicBase(EntitySnowman(null)) + val villager = HolographicBase(EntityVillager(null)) + val guardian = HolographicBase(EntityGuardian(null)) + val armorStand = HolographicBase(EntityArmorStand(null)) + val squid = HolographicBase(EntitySquid(null)) + val bat = HolographicBase(EntityBat(null)) + val spider = HolographicBase(EntitySpider(null)) + val caveSpider = HolographicBase(EntityCaveSpider(null)) + val pigman = HolographicBase(EntityPigZombie(null)) + val ghast = HolographicBase(EntityGhast(null)) + val magmaCube = HolographicBase(EntityMagmaCube(null)) + val wither = HolographicBase(EntityWither(null)) + val enderman = HolographicBase(EntityEnderman(null)) + val mooshroom = HolographicBase(EntityMooshroom(null)) + val witherSkeleton = HolographicBase(EntitySkeleton(null).also { it.skeletonType = 1 }) + val cow = HolographicBase(EntityCow(null)) + val pig = HolographicBase(EntityPig(null)) + val giant = HolographicBase(EntityGiantZombie(null)) + + + private fun interpolateRotation(last: Float, next: Float, progress: Float): Float { + var direction: Float = next - last + while (direction < -180.0f) { + direction += 360.0f + } + while (direction >= 180.0f) { + direction -= 360.0f + } + return last + progress * direction + } + + /** + * Render a fake [HolographicEntity]. In order to render a fully opaque entity, set [holographicness] to `1F`. + */ + fun <T : EntityLivingBase> renderHolographicEntity( + holographicEntity: HolographicEntity<T>, + partialTicks: Float, + holographicness: Float = 0.3f + ) { + val renderManager = Minecraft.getMinecraft().renderManager + val renderer = renderManager.getEntityRenderObject<EntityLivingBase>(holographicEntity.entity) + renderer as RendererLivingEntity<T> + renderer as AccessorRendererLivingEntity<T> + + renderer.setRenderOutlines(false) + if (!renderer.bindEntityTexture_skyhanni(holographicEntity.entity)) + return + + GlStateManager.pushMatrix() + val viewerPosition = getViewerPos(partialTicks) + val mobPosition = holographicEntity.interpolatedPosition(partialTicks) + val renderingOffset = mobPosition.subtract(viewerPosition) + renderingOffset.applyTranslationToGL() + GlStateManager.disableCull() + GlStateManager.enableRescaleNormal() + GlStateManager.scale(-1f, -1f, 1f) + GlStateManager.translate(0F, -1.5078125f, 0f) + val limbSwing: Float = 0F + val limbSwingAmount: Float = 0F + val ageInTicks: Float = 1_000_000.toFloat() + val netHeadYaw: Float = holographicEntity.interpolatedYaw(partialTicks) + val headPitch: Float = 0F + val scaleFactor: Float = 0.0625f + renderer.setBrightness_skyhanni(holographicEntity.entity, 0f, true) + GlStateManager.color(1.0f, 1.0f, 1.0f, holographicness) + GlStateManager.depthMask(false) + GlStateManager.enableBlend() + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA) + GlStateManager.alphaFunc(GL11.GL_GREATER, 1 / 255F) + + GlStateManager.enableTexture2D() + renderer.mainModel.isChild = holographicEntity.isChild + renderer.mainModel.setRotationAngles( + limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scaleFactor, holographicEntity.entity + ) + renderer.mainModel.render( + holographicEntity.entity, + limbSwing, + limbSwingAmount, + ageInTicks, + netHeadYaw, + headPitch, + scaleFactor + ) + GlStateManager.alphaFunc(GL11.GL_GREATER, 0.1f) + GlStateManager.color(1f, 1f, 1f, 1f) + GlStateManager.depthMask(true) + GlStateManager.disableBlend() + GlStateManager.popMatrix() + } + +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt index fd38fd441..39459edf5 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt @@ -1,6 +1,7 @@ package at.hannibal2.skyhanni.utils import at.hannibal2.skyhanni.utils.LorenzUtils.round +import net.minecraft.client.renderer.GlStateManager import net.minecraft.entity.Entity import net.minecraft.network.play.server.S2APacketParticles import net.minecraft.util.AxisAlignedBB @@ -48,7 +49,8 @@ data class LorenzVec( return (dx * dx + dz * dz) } - fun add(x: Double = 0.0, y: Double = 0.0, z: Double = 0.0): LorenzVec = LorenzVec(this.x + x, this.y + y, this.z + z) + fun add(x: Double = 0.0, y: Double = 0.0, z: Double = 0.0): LorenzVec = + LorenzVec(this.x + x, this.y + y, this.z + z) fun add(x: Int, y: Int, z: Int): LorenzVec = LorenzVec(this.x + x, this.y + y, this.z + z) @@ -130,7 +132,14 @@ data class LorenzVec( return LorenzVec(scalar * x, scalar * y, scalar * z) } + fun applyTranslationToGL() { + GlStateManager.translate(x, y, z) + } + fun axisAlignedTo(other: LorenzVec) = AxisAlignedBB(x, y, z, other.x, other.y, other.z) + fun up(offset: Double): LorenzVec { + return copy(y = y + offset) + } companion object { fun getFromYawPitch(yaw: Double, pitch: Double): LorenzVec { diff --git a/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt index 5bf6bfcf0..815fd0aeb 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt @@ -75,6 +75,10 @@ object TimeUtils { return builder.toString().trim() } + val Duration.inWholeTicks: Int + get() = (inWholeMilliseconds / 50).toInt() + + @Deprecated("Do no longer use long for time", ReplaceWith("getDuration()")) fun getMillis(string: String) = getDuration(string).inWholeMilliseconds |
