Commit 481f5a25 by Jonathan Thomas

Refactor of client mixin for PlayerSkinTexture, moving to a static class, and…

Refactor of client mixin for PlayerSkinTexture, moving to a static class, and conditional mixins for different Minecraft versions.
parent 996c493b
Pipeline #13258 passed with stages
in 2 minutes 12 seconds
package com.owlmaddie.mixin.client;
import com.owlmaddie.skin.PlayerCustomTexture;
import com.owlmaddie.skin.SkinUtils;
import net.minecraft.util.Identifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
/**
* A Mixin for PlayerCustomTexture to implement hasCustomIcon using SkinUtils.
*/
@Mixin(PlayerCustomTexture.class)
public abstract class MixinPlayerCustomTexture {
private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat");
/**
* Overwrites the default implementation of hasCustomIcon to provide custom skin support.
*
* @param skinId the Identifier of the skin
* @return true if the skin has a custom icon; false otherwise
*
* @reason Add functionality to determine custom icons based on SkinUtils logic.
* @author jonoomph
*/
@Overwrite
public static boolean hasCustomIcon(Identifier skinId) {
// Delegate to SkinUtils to check for custom skin properties
LOGGER.info("mixin hasCustomIcon called");
return SkinUtils.checkCustomSkinKey(skinId);
}
}
package com.owlmaddie.mixin.client;
import com.owlmaddie.utils.IPlayerSkinTexture;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.PlayerSkinTexture;
import net.minecraft.client.texture.ResourceTexture;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(PlayerSkinTexture.class)
public abstract class MixinPlayerSkinTexture1201Pre extends ResourceTexture implements IPlayerSkinTexture {
@Unique
private NativeImage cachedSkinImage;
public MixinPlayerSkinTexture1201Pre(Identifier location) {
super(location);
}
@Inject(method = "onTextureLoaded", at = @At("HEAD"))
private void captureNativeImage(NativeImage image, CallbackInfo ci) {
this.cachedSkinImage = cloneNativeImage(image);
}
@Override
public NativeImage getLoadedImage() {
return this.cachedSkinImage;
}
private static NativeImage cloneNativeImage(NativeImage source) {
NativeImage copy = new NativeImage(source.getFormat(), source.getWidth(), source.getHeight(), false);
copy.copyFrom(source);
return copy;
}
}
package com.owlmaddie.mixin.client;
import com.owlmaddie.utils.IPlayerSkinTexture;
import com.owlmaddie.skin.IPlayerSkinTexture;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.PlayerSkinTexture;
import net.minecraft.client.texture.ResourceTexture;
......
package com.owlmaddie.utils;
package com.owlmaddie.skin;
import net.minecraft.client.texture.NativeImage;
import org.jetbrains.annotations.Nullable;
......
package com.owlmaddie.skin;
import net.minecraft.util.Identifier;
public class PlayerCustomTexture {
public static boolean hasCustomIcon(Identifier skinId) {
// By default, do nothing special
return false;
}
}
package com.owlmaddie.skin;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.AbstractTexture;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.util.Identifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SkinUtils {
private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat");
public static boolean checkCustomSkinKey(Identifier skinId) {
LOGGER.info("mixin checkCustomSkinKey called");
// 1. Grab the AbstractTexture from the TextureManager
AbstractTexture tex = MinecraftClient.getInstance().getTextureManager().getTexture(skinId);
// 2. Check if it implements our Mixin interface: IPlayerSkinTexture
if (tex instanceof IPlayerSkinTexture iSkin) {
// 3. Get the NativeImage we stored in the Mixin
NativeImage image = iSkin.getLoadedImage();
if (image != null) {
int width = image.getWidth();
int height = image.getHeight();
// Check we have the full 64x64
if (width == 64 && height == 64) {
// Example: black & white pixel at (31,48) and (32,48)
int color31_48 = image.getColor(31, 49);
int color32_48 = image.getColor(32, 49);
return (color31_48 == 0xFF000000 && color32_48 == 0xFFFFFFFF);
}
}
}
// If it's still loading, or not a PlayerSkinTexture, or no NativeImage loaded yet
return false;
}
}
package com.owlmaddie.ui;
import com.mojang.authlib.GameProfile;
import com.mojang.blaze3d.systems.RenderSystem;
import com.owlmaddie.chat.ChatDataManager;
import com.owlmaddie.chat.EntityChatData;
import com.owlmaddie.chat.PlayerData;
import com.owlmaddie.utils.EntityHeights;
import com.owlmaddie.utils.EntityRendererAccessor;
import com.owlmaddie.utils.IPlayerSkinTexture;
import com.owlmaddie.utils.TextureLoader;
import com.owlmaddie.skin.PlayerCustomTexture;
import com.owlmaddie.utils.*;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
......@@ -16,8 +13,6 @@ import net.minecraft.client.font.TextRenderer.TextLayerType;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.render.*;
import net.minecraft.client.render.entity.EntityRenderer;
import net.minecraft.client.texture.AbstractTexture;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import net.minecraft.entity.boss.dragon.EnderDragonEntity;
......@@ -30,7 +25,6 @@ import net.minecraft.util.math.Box;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.slf4j.Logger;
......@@ -234,7 +228,7 @@ public class BubbleRenderer {
Identifier playerTexture = renderer.getTexture(entity);
// Check for black and white pixels (using the Mixin-based check)
boolean customSkinFound = checkCustomSkinKey(playerTexture);
boolean customSkinFound = PlayerCustomTexture.hasCustomIcon(playerTexture);
// Set shader & texture
RenderSystem.setShader(GameRenderer::getPositionColorTexLightmapProgram);
......@@ -333,52 +327,6 @@ public class BubbleRenderer {
RenderSystem.disableDepthTest();
}
public static boolean checkCustomSkinKey(Identifier skinId) {
// 1. Grab the AbstractTexture from the TextureManager
AbstractTexture tex = MinecraftClient.getInstance().getTextureManager().getTexture(skinId);
// 2. Check if it implements our Mixin interface: IPlayerSkinTexture
if (tex instanceof IPlayerSkinTexture iSkin) {
// 3. Get the NativeImage we stored in the Mixin
NativeImage image = iSkin.getLoadedImage();
if (image != null) {
int width = image.getWidth();
int height = image.getHeight();
// Check we have the full 64x64
if (width == 64 && height == 64) {
// Example: black & white pixel at (31,48) and (32,48)
int color31_48 = image.getColor(31, 49);
int color32_48 = image.getColor(32, 49);
return (color31_48 == 0xFF000000 && color32_48 == 0xFFFFFFFF);
}
}
}
// If it's still loading, or not a PlayerSkinTexture, or no NativeImage loaded yet
return false;
}
@Nullable
public static NativeImage getPlayerSkinAsNativeImage(GameProfile profile) {
// 1. Ask the SkinProvider for the textures
if (MinecraftClient.getInstance().getSkinProvider().getSkinTextures(profile) == null) {
return null; // No skin or still loading
}
// 2. Identify the texture ID
Identifier skinId = MinecraftClient.getInstance().getSkinProvider().getSkinTextures(profile).texture();
// 3. Get the AbstractTexture from the TextureManager
AbstractTexture tex = MinecraftClient.getInstance().getTextureManager().getTexture(skinId);
if (tex instanceof IPlayerSkinTexture iSkin) {
// 4. Get the in-memory NativeImage
return iSkin.getLoadedImage();
}
return null; // Not yet loaded or is a different texture type
}
private static void drawMessageText(Matrix4f matrix, List<String> lines, int starting_line, int ending_line,
VertexConsumerProvider immediate, float lineSpacing, int fullBright, float yOffset) {
TextRenderer fontRenderer = MinecraftClient.getInstance().textRenderer;
......
......@@ -3,12 +3,13 @@
"package": "com.owlmaddie.mixin.client",
"compatibilityLevel": "JAVA_17",
"client": [
"EntityRendererMixin"
"EntityRendererMixin",
"MixinPlayerCustomTexture",
"MixinPlayerSkinTexture1202Plus"
],
"env": {
"client.MixinPlayerSkinTexture1201Pre": {
"minVersion": "1.20",
"maxVersion": "1.20.1"
"client.MixinPlayerCustomTexture": {
"minVersion": "1.20.2"
},
"client.MixinPlayerSkinTexture1202Plus": {
"minVersion": "1.20.2"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment