diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1e133f9..b94ce06 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,9 +7,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: set up JDK 21 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '21' @@ -18,7 +18,7 @@ jobs: - name: build with gradle run: ./gradlew build - name: capture build artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: artifacts path: build/libs/ diff --git a/build.gradle b/build.gradle index 9de9fd6..6745493 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,4 @@ +//file:noinspection GroovyAssignabilityCheck plugins { id "fabric-loom" version "1.7.+" id "com.github.johnrengelman.shadow" version "7.1.+" diff --git a/gradle.properties b/gradle.properties index a1f7c89..9651b37 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,20 +3,20 @@ org.gradle.jvmargs = -Xmx1G # minecraft, mappings and loader dependencies # check these on https://modmuss50.me/fabric.html -minecraft_version = 1.21.3 -quilt_mappings = 6 -loader_version = 0.16.7 +minecraft_version = 1.21.4 +quilt_mappings = 3 +loader_version = 0.16.10 kaleido_config_version = 0.3.1+1.3.2 # mod properties -mod_version = 1.3.5+mc1.21.3 +mod_version = 1.3.5+mc1.21.4 maven_group = rainglow archives_base_name = rainglow # other dependencies java_version = 21 -mod_menu_version = 12.0.0-beta.1 -fabric_api_version = 0.107.0+1.21.3 +mod_menu_version = 13.0.0-beta.1 +fabric_api_version = 0.118.5+1.21.4 pub.should_publish = true -pub.additional_versions = 1.21.2 \ No newline at end of file +pub.additional_versions = 1.21.4 diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index 4588d2a..c1f97c1 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -8,6 +8,7 @@ import io.ix0rai.rainglow.config.RainglowConfig; import io.ix0rai.rainglow.data.*; import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; @@ -71,8 +72,14 @@ public void onInitialize() { }); ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> { - COLOURS.clear(); + // Only clear colours on disconnect if server is NOT single-player to prevent NBT save failure (Unsure how this works with Lan-instances) + if (!server.isSingleplayer()) { + COLOURS.clear(); + } }); + + // Instead use SERVER_STOPPED for clearing colours from single-player worlds. (Doesn't affect others because mod would most likely be shutdown in non-single-player instances) + ServerLifecycleEvents.SERVER_STOPPED.register(server -> COLOURS.clear()); } public static Identifier id(String id) { @@ -116,6 +123,11 @@ public static RainglowColour getColour(Entity entity) { return colour; } + // Simplified method without any colour checks (Entity information isn't being passed through Rendering anymore, can be adjusted to apply in the RenderStateInteract if needed) + public static RainglowColour getColour(UUID entity) { + return COLOURS.get(entity); + } + public static void setColour(Entity entity, RainglowColour colour) { setColour(entity.getUuid(), colour); diff --git a/src/main/java/io/ix0rai/rainglow/data/EntityRenderStateTracker.java b/src/main/java/io/ix0rai/rainglow/data/EntityRenderStateTracker.java new file mode 100644 index 0000000..9e9fdb5 --- /dev/null +++ b/src/main/java/io/ix0rai/rainglow/data/EntityRenderStateTracker.java @@ -0,0 +1,23 @@ +package io.ix0rai.rainglow.data; + +import net.minecraft.entity.Entity; + +import java.util.UUID; + +/** + * @author A5ho9999 + * Interface to track entity data in render states + */ +public interface EntityRenderStateTracker { + /** + * Set the associated entity + * @param entity The entity to associate with this render state + */ + void rainglow$setEntity(Entity entity); + + /** + * Get the associated entity UUID + * @return The UUID of the associated entity, or null if not set + */ + UUID rainglow$getEntityUuid(); +} \ No newline at end of file diff --git a/src/main/java/io/ix0rai/rainglow/data/GlowSquidEntityData.java b/src/main/java/io/ix0rai/rainglow/data/GlowSquidEntityData.java index 2daa42a..5d03ccf 100644 --- a/src/main/java/io/ix0rai/rainglow/data/GlowSquidEntityData.java +++ b/src/main/java/io/ix0rai/rainglow/data/GlowSquidEntityData.java @@ -1,6 +1,17 @@ package io.ix0rai.rainglow.data; -import net.minecraft.entity.EntityData; +import net.minecraft.entity.passive.PassiveEntity; -public record GlowSquidEntityData(RainglowColour colour) implements EntityData { +public class GlowSquidEntityData extends PassiveEntity.PassiveData { + private final RainglowColour colour; + + public GlowSquidEntityData(RainglowColour colour) { + // copied from SquidEntity#initialize. as far as i can tell we have to duplicate this constant + super(0.05F); + this.colour = colour; + } + + public RainglowColour getColour() { + return colour; + } } diff --git a/src/main/java/io/ix0rai/rainglow/data/ParticleHelper.java b/src/main/java/io/ix0rai/rainglow/data/ParticleHelper.java new file mode 100644 index 0000000..7b01172 --- /dev/null +++ b/src/main/java/io/ix0rai/rainglow/data/ParticleHelper.java @@ -0,0 +1,22 @@ +package io.ix0rai.rainglow.data; + +import net.minecraft.client.particle.ItemBreakParticle; +import net.minecraft.client.particle.Particle; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.unmapped.C_hvackyip; + +/** + * @author A5ho9999 + * Helper for creating particles because Mojang likes to torture everyone + */ +public class ParticleHelper { + public static class CustomItemBreakParticle extends ItemBreakParticle { + public CustomItemBreakParticle(ClientWorld world, double d, double e, double f, C_hvackyip c_hvackyip) { + super(world, d, e, f, c_hvackyip); + } + } + + public static Particle createItemBreakParticle(ClientWorld world, double d, double e, double f, C_hvackyip c_hvackyip) { + return new CustomItemBreakParticle(world, d, e, f, c_hvackyip); + } +} diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java index 618ed4e..7d5cdc0 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java @@ -1,6 +1,7 @@ package io.ix0rai.rainglow.data; import io.ix0rai.rainglow.Rainglow; +import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityData; import net.minecraft.entity.mob.SlimeEntity; @@ -13,10 +14,11 @@ import net.minecraft.util.random.RandomGenerator; import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Arrays; import java.util.HashMap; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; public enum RainglowEntity { @@ -99,14 +101,31 @@ public static RainglowEntity get(Entity entity) { return null; } - public void overrideTexture(Entity entity, CallbackInfoReturnable cir) { + @Nullable + public Identifier overrideTexture(ClientWorld world, UUID uuid) { + AtomicReference texture = new AtomicReference<>(); + + world.getEntities().forEach(entity -> { + if (entity.getUuid().equals(uuid)) { + texture.set(this.overrideTexture(entity)); + } + }); + + return texture.get(); + } + + // Return the override texture instead of applying through callback + public Identifier overrideTexture(Entity entity) { RainglowColour colour = Rainglow.getColour(entity); + // Returning null will just use default texture, no need for extra checks + // if the colour is default we don't need to override the method // this optimises a tiny bit if (Rainglow.CONFIG.isEntityEnabled(this) && colour != this.getDefaultColour()) { - Identifier texture = colour.getTexture(this); - cir.setReturnValue(texture != null ? texture : this.getDefaultTexture()); + return colour.getTexture(this); } + + return null; } } diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java b/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java index 8eadb7a..1225c3e 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java @@ -25,7 +25,7 @@ public static void syncConfig(ServerPlayerEntity player) { public record ConfigSyncPayload(String currentMode, List customMode, Map enabledMobs, Map rarities) implements CustomPayload { public static final CustomPayload.Id PACKET_ID = new CustomPayload.Id<>(Rainglow.id("config_sync")); - public static final PacketCodec PACKET_CODEC = PacketCodec.create(ConfigSyncPayload::write, ConfigSyncPayload::read); + public static final PacketCodec PACKET_CODEC = PacketCodec.of(ConfigSyncPayload::write, ConfigSyncPayload::read); public void write(RegistryByteBuf buf) { buf.writeString(this.currentMode); @@ -55,7 +55,7 @@ public static void syncModes(ServerPlayerEntity player) { public record ModeSyncPayload(Collection modes) implements CustomPayload { public static final CustomPayload.Id PACKET_ID = new CustomPayload.Id<>(Rainglow.id("mode_sync")); - public static final PacketCodec PACKET_CODEC = PacketCodec.create(ModeSyncPayload::write, ModeSyncPayload::read); + public static final PacketCodec PACKET_CODEC = PacketCodec.of(ModeSyncPayload::write, ModeSyncPayload::read); public void write(RegistryByteBuf buf) { buf.writeCollection(this.modes, RainglowMode::write); @@ -91,7 +91,7 @@ public static void sendColourChangeToClients(Entity entity, RainglowColour colou public record ColourPayload(Map colours) implements CustomPayload { public static final CustomPayload.Id PACKET_ID = new CustomPayload.Id<>(Rainglow.id("colour_change")); - public static final PacketCodec PACKET_CODEC = PacketCodec.create(ColourPayload::write, ColourPayload::read); + public static final PacketCodec PACKET_CODEC = PacketCodec.of(ColourPayload::write, ColourPayload::read); public void write(RegistryByteBuf buf) { buf.writeMap(this.colours, (b, uuid) -> b.writeUuid(uuid), RainglowColour::write); diff --git a/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java index ea090f9..2a69c87 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java @@ -4,18 +4,17 @@ import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.RainglowEntity; import io.ix0rai.rainglow.data.SlimeVariantProvider; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityType; +import net.minecraft.entity.*; import net.minecraft.entity.mob.SlimeEntity; import net.minecraft.nbt.NbtCompound; import net.minecraft.particle.ParticleEffect; +import net.minecraft.scoreboard.Team; import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -43,11 +42,40 @@ public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { /** * @reason make smaller slimes spawn with the same colour as the parent in a split */ - @Redirect(method = "remove", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z")) - public boolean spawnWithParentColour(World instance, Entity entity) { - RainglowColour colour = Rainglow.getColour(this); - ((SlimeVariantProvider) entity).setVariant(colour); - return this.getWorld().spawnEntity(entity); + @Inject(method = "remove", at = @At("HEAD"), cancellable = true) + private void preserveColorOnSplit(Entity.RemovalReason reason, CallbackInfo ci) { + SlimeEntity thisSlime = (SlimeEntity) (Object) this; + int size = thisSlime.getSize(); + + if (!thisSlime.getWorld().isClient && size > 1 && thisSlime.isDead()) { + RainglowColour parentColor = Rainglow.getColour(thisSlime.getUuid()); + + float width = thisSlime.getDimensions(thisSlime.getPose()).width(); + float halfWidth = width / 2.0F; + int newSize = size / 2; + Team team = thisSlime.getScoreboardTeam(); + + int count = 2 + thisSlime.getRandom().nextInt(3); + + // Create multiple slimes individually while making sure it matches vanilla + for (int i = 0; i < count; i++) { + float offsetX = ((float) (i % 2) - 0.5F) * halfWidth; + float offsetZ = ((float) (i / 2) - 0.5F) * halfWidth; + + //noinspection unchecked + thisSlime.convert((EntityType) thisSlime.getType(), new EntityConversionParameters(EntityConversionType.SPLIT_ON_DEATH, false, false, team), SpawnReason.TRIGGERED, (newSlime) -> { + newSlime.setSize(newSize, true); + newSlime.refreshPositionAndAngles(thisSlime.getX() + offsetX, thisSlime.getY() + 0.5, thisSlime.getZ() + offsetZ, thisSlime.getRandom().nextFloat() * 360.0F, 0.0F); + + // Now that headache is done, finally set the child slime color to match the parent + ((SlimeVariantProvider) newSlime).setVariant(parentColor); + }); + } + + // Don't forget this, boy was that a mistake + super.remove(reason); + ci.cancel(); + } } /** diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java index 7df11b2..0a5563c 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java @@ -1,18 +1,33 @@ package io.ix0rai.rainglow.mixin.client; +import io.ix0rai.rainglow.data.EntityRenderStateTracker; import io.ix0rai.rainglow.data.RainglowEntity; +import net.minecraft.class_9996; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.entity.AllayEntityRenderer; -import net.minecraft.entity.passive.AllayEntity; +import net.minecraft.client.world.ClientWorld; import net.minecraft.util.Identifier; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.UUID; + @Mixin(AllayEntityRenderer.class) public class AllayEntityRendererMixin { - @Inject(method = "getTexture*", at = @At("HEAD"), cancellable = true) - public void getTexture(AllayEntity allayEntity, CallbackInfoReturnable cir) { - RainglowEntity.ALLAY.overrideTexture(allayEntity, cir); + @Inject(method = "m_vhdjjpxx", at = @At("HEAD"), cancellable = true) + public void getTexture(class_9996 state, CallbackInfoReturnable cir) { + if (state instanceof EntityRenderStateTracker) { + UUID entityUuid = ((EntityRenderStateTracker) state).rainglow$getEntityUuid(); + if (entityUuid != null) { + ClientWorld world = MinecraftClient.getInstance().world; + if (world != null) { + RainglowEntity type = RainglowEntity.ALLAY; + Identifier texture = type.overrideTexture(world, entityUuid); + cir.setReturnValue(texture != null ? texture : type.getDefaultTexture()); + } + } + } } } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/EntityRenderStateMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/EntityRenderStateMixin.java new file mode 100644 index 0000000..3c6628f --- /dev/null +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/EntityRenderStateMixin.java @@ -0,0 +1,29 @@ +package io.ix0rai.rainglow.mixin.client; + +import io.ix0rai.rainglow.data.EntityRenderStateTracker; +import net.minecraft.class_10017; +import net.minecraft.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +import java.util.UUID; + +@Mixin(class_10017.class) +public class EntityRenderStateMixin implements EntityRenderStateTracker { + @Unique + private UUID entityUuid; + + @Override + public void rainglow$setEntity(Entity entity) { + if (entity != null) { + this.entityUuid = entity.getUuid(); + } + } + + // TODO: This could be used to just get the Entity as well as the UUID but just saving the UUID is better for long term memory usage + + @Override + public UUID rainglow$getEntityUuid() { + return this.entityUuid; + } +} \ No newline at end of file diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/EntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/EntityRendererMixin.java new file mode 100644 index 0000000..d3c3778 --- /dev/null +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/EntityRendererMixin.java @@ -0,0 +1,20 @@ +package io.ix0rai.rainglow.mixin.client; + +import io.ix0rai.rainglow.data.EntityRenderStateTracker; +import net.minecraft.class_10017; +import net.minecraft.client.render.entity.EntityRenderer; +import net.minecraft.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(EntityRenderer.class) +public class EntityRendererMixin { + @Inject(method = "method_62354", at = @At("HEAD")) + private void updateRenderState(T entity, S state, float f, CallbackInfo ci) { + if (state instanceof EntityRenderStateTracker) { + ((EntityRenderStateTracker) state).rainglow$setEntity(entity); + } + } +} diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/GlowSquidEntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/GlowSquidEntityRendererMixin.java index 183d38a..d3d1921 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/GlowSquidEntityRendererMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/GlowSquidEntityRendererMixin.java @@ -1,22 +1,37 @@ package io.ix0rai.rainglow.mixin.client; +import io.ix0rai.rainglow.data.EntityRenderStateTracker; import io.ix0rai.rainglow.data.RainglowEntity; +import net.minecraft.class_10069; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.entity.GlowSquidEntityRenderer; -import net.minecraft.entity.passive.GlowSquidEntity; +import net.minecraft.client.world.ClientWorld; import net.minecraft.util.Identifier; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.UUID; + @Mixin(GlowSquidEntityRenderer.class) public class GlowSquidEntityRendererMixin { /** * @reason use the colour from the entity's NBT data for textures * @author ix0rai */ - @Inject(method = "getTexture*", at = @At("HEAD"), cancellable = true) - public void getTexture(GlowSquidEntity glowSquidEntity, CallbackInfoReturnable cir) { - RainglowEntity.GLOW_SQUID.overrideTexture(glowSquidEntity, cir); + @Inject(method = "m_vhdjjpxx", at = @At("HEAD"), cancellable = true) + public void getTexture(class_10069 state, CallbackInfoReturnable cir) { + if (state instanceof EntityRenderStateTracker) { + UUID entityUuid = ((EntityRenderStateTracker) state).rainglow$getEntityUuid(); + if (entityUuid != null) { + ClientWorld world = MinecraftClient.getInstance().world; + if (world != null) { + RainglowEntity type = RainglowEntity.GLOW_SQUID; + Identifier texture = type.overrideTexture(world, entityUuid); + cir.setReturnValue(texture != null ? texture : type.getDefaultTexture()); + } + } + } } } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java index b41e81a..cf77e8b 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java @@ -1,19 +1,33 @@ package io.ix0rai.rainglow.mixin.client; +import io.ix0rai.rainglow.data.EntityRenderStateTracker; import io.ix0rai.rainglow.data.RainglowEntity; +import net.minecraft.class_10067; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.entity.SlimeEntityRenderer; -import net.minecraft.entity.mob.SlimeEntity; -import net.minecraft.unmapped.C_oeatwgky; +import net.minecraft.client.world.ClientWorld; import net.minecraft.util.Identifier; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.UUID; + @Mixin(SlimeEntityRenderer.class) public class SlimeEntityRendererMixin { @Inject(method = "m_vhdjjpxx", at = @At("HEAD"), cancellable = true) - public void getTexture(C_oeatwgky c_oeatwgky, CallbackInfoReturnable cir) { - RainglowEntity.SLIME.overrideTexture(entity, cir); + public void getTexture(class_10067 state, CallbackInfoReturnable cir) { + if (state instanceof EntityRenderStateTracker) { + UUID entityUuid = ((EntityRenderStateTracker) state).rainglow$getEntityUuid(); + if (entityUuid != null) { + ClientWorld world = MinecraftClient.getInstance().world; + if (world != null) { + RainglowEntity type = RainglowEntity.SLIME; + Identifier texture = type.overrideTexture(world, entityUuid); + cir.setReturnValue(texture != null ? texture : type.getDefaultTexture()); + } + } + } } } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeOverlayFeatureRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeOverlayFeatureRendererMixin.java new file mode 100644 index 0000000..48bc6a2 --- /dev/null +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeOverlayFeatureRendererMixin.java @@ -0,0 +1,64 @@ +package io.ix0rai.rainglow.mixin.client; + +import io.ix0rai.rainglow.data.EntityRenderStateTracker; +import io.ix0rai.rainglow.data.RainglowEntity; +import net.minecraft.class_10067; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.SlimeEntityRenderer; +import net.minecraft.client.render.entity.feature.SlimeOverlayFeatureRenderer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.util.Identifier; +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.Redirect; + +import java.util.UUID; + +/** + * @author A5ho9999 + * They've updated the render to display the outer and inner serperately, this could be used for some fun random combinations in the future. + */ +@Mixin(SlimeOverlayFeatureRenderer.class) +public class SlimeOverlayFeatureRendererMixin { + /** + * Override the outline texture for slimes, defaults back to normal if null + */ + @Redirect(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/class_10067;FF)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayer;getOutline(Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/RenderLayer;")) + private RenderLayer rainglow$getOutline(Identifier defaultTexture, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, class_10067 state, float f, float g) { + Identifier overrideTexture = getOverrideTexture(state); + return overrideTexture != null ? RenderLayer.getOutline(overrideTexture) : RenderLayer.getOutline(SlimeEntityRenderer.TEXTURE); + } + + /** + * Override the translucent texture for slimes, defaults back to normal if null + */ + @Redirect(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/class_10067;FF)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayer;getEntityTranslucent(Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/RenderLayer;")) + private RenderLayer rainglow$getEntityTranslucent(Identifier defaultTexture, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, class_10067 state, float f, float g) { + Identifier overrideTexture = getOverrideTexture(state); + return overrideTexture != null ? RenderLayer.getEntityTranslucent(overrideTexture) : RenderLayer.getEntityTranslucent(SlimeEntityRenderer.TEXTURE); + } + + @Unique + private Identifier getOverrideTexture(class_10067 state) { + if (state instanceof EntityRenderStateTracker) { + UUID entityUuid = ((EntityRenderStateTracker) state).rainglow$getEntityUuid(); + if (entityUuid != null) { + ClientWorld world = MinecraftClient.getInstance().world; + if (world != null) { + try { + return RainglowEntity.SLIME.overrideTexture(world, entityUuid); + } catch (Exception e) { + // ignore any errors and just let return null for default textures + } + } + } + } + return null; + } +} diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeParticleMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeParticleMixin.java index 83327e6..fc3ad08 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeParticleMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeParticleMixin.java @@ -1,13 +1,17 @@ package io.ix0rai.rainglow.mixin.client; import io.ix0rai.rainglow.Rainglow; +import io.ix0rai.rainglow.data.ParticleHelper; import io.ix0rai.rainglow.data.RainglowEntity; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.particle.ItemBreakParticle; import net.minecraft.client.particle.Particle; +import net.minecraft.client.render.model.json.ModelTransformationMode; import net.minecraft.client.world.ClientWorld; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.particle.DefaultParticleType; +import net.minecraft.unmapped.C_hvackyip; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -16,19 +20,24 @@ @Mixin(ItemBreakParticle.SlimeballFactory.class) public class SlimeParticleMixin { /** - * @author ix0rai + * @author ix0rai, A5ho9999 * @reason recolor particles */ @Inject(method = "createParticle*", at = @At("HEAD"), cancellable = true) public void createParticle(DefaultParticleType defaultParticleType, ClientWorld clientWorld, double d, double e, double f, double g, double h, double i, CallbackInfoReturnable cir) { + C_hvackyip itemRenderState = new C_hvackyip(); + if (!Rainglow.CONFIG.isEntityEnabled(RainglowEntity.SLIME)) { - cir.setReturnValue(new ItemBreakParticle(clientWorld, d, e, f, new ItemStack(Items.SLIME_BALL))); + MinecraftClient.getInstance().method_65386().method_65598(itemRenderState, new ItemStack(Items.SLIME_BALL), ModelTransformationMode.GROUND, false, clientWorld, null, 0); + cir.setReturnValue(ParticleHelper.createItemBreakParticle(clientWorld, d, e, f, itemRenderState)); // 99.9d and 100.1d are used to account for floating point errors } else if (h >= 99.9d && h <= 100.1d) { ItemStack stack = RainglowEntity.SLIME.getItem((int) g).getDefaultStack(); - cir.setReturnValue(new ItemBreakParticle(clientWorld, d, e, f, stack)); + MinecraftClient.getInstance().method_65386().method_65598(itemRenderState, stack, ModelTransformationMode.GROUND, false, clientWorld, null, 0); + cir.setReturnValue(ParticleHelper.createItemBreakParticle(clientWorld, d, e, f, itemRenderState)); } else { cir.setReturnValue(null); } } + } diff --git a/src/main/resources/rainglow.accesswidener b/src/main/resources/rainglow.accesswidener index 9225a7c..f26e0cb 100644 --- a/src/main/resources/rainglow.accesswidener +++ b/src/main/resources/rainglow.accesswidener @@ -14,7 +14,7 @@ accessible class net/minecraft/client/option/Option$CyclingValueSet accessible method net/minecraft/client/particle/GlowParticle (Lnet/minecraft/client/world/ClientWorld;DDDDDDLnet/minecraft/client/particle/SpriteProvider;)V accessible field net/minecraft/client/particle/GlowParticle RANDOM Lnet/minecraft/util/random/RandomGenerator; -accessible method net/minecraft/client/particle/ItemBreakParticle (Lnet/minecraft/client/world/ClientWorld;DDDLnet/minecraft/item/ItemStack;)V +accessible class net/minecraft/client/particle/ItemBreakParticle accessible method net/minecraft/client/particle/SquidInkParticle (Lnet/minecraft/client/world/ClientWorld;DDDDDDILnet/minecraft/client/particle/SpriteProvider;)V diff --git a/src/main/resources/rainglow.mixins.json b/src/main/resources/rainglow.mixins.json index 00c88d0..1688a03 100644 --- a/src/main/resources/rainglow.mixins.json +++ b/src/main/resources/rainglow.mixins.json @@ -14,9 +14,12 @@ ], "client": [ "client.AllayEntityRendererMixin", + "client.EntityRendererMixin", + "client.EntityRenderStateMixin", "client.GlowParticleMixin", "client.GlowSquidEntityRendererMixin", "client.SlimeEntityRendererMixin", + "client.SlimeOverlayFeatureRendererMixin", "client.SlimeParticleMixin", "client.SquidInkParticleMixin", "client.screen.CyclingValueSetMixin",