Moving cloaks to common

This commit is contained in:
2026-03-08 11:29:45 +01:00
parent b6f164b43b
commit 8039862142
7 changed files with 41 additions and 154 deletions
+1 -1
Submodule common updated: a54aa9eaba...5b34d3eb37
@@ -282,7 +282,7 @@ public class CloakFeatureRenderer extends FeatureRenderer<PlayerEntityRenderStat
return;
}
Identifier customCape = Cloaks.getCurrentCloakTexture(player.uuid);
Identifier customCape = (Identifier) (Object) Cloaks.getCurrentCloakTexture(player.uuid);
if (customCape == null
|| this.hasCustomModelForLayer(playerEntityRenderState.equippedChestStack, LayerType.WINGS)) {
return;
@@ -1,9 +1,8 @@
package org.saturnclient.cosmetics.cloak;
import net.minecraft.util.Identifier;
import org.saturnclient.client.ServiceClient;
import org.saturnclient.client.player.SaturnPlayer;
import org.saturnclient.common.ref.asset.IdentifierRef;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
@@ -15,7 +14,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.saturnclient.cosmetics.cloak.utils.AnimatedCloakData;
import org.saturnclient.cosmetics.utils.AnimatedCloakData;
import org.saturnclient.cosmetics.cloak.utils.IdentifierUtils;
import org.saturnclient.saturnclient.SaturnClient;
@@ -32,7 +31,7 @@ public class Cloaks {
private static final String CLOAKS_RESOURCE_PATH = "assets/saturnclient/textures/cloaks/";
public static final List<String> availableCloaks = new ArrayList<>();
public static Identifier cloakCacheIdentifier = null;
public static IdentifierRef cloakCacheIdentifier = null;
public static final Map<UUID, List<AnimatedCloakData>> animatedCloaks = new ConcurrentHashMap<>();
private static final Map<UUID, Long> lastFrameTime = new ConcurrentHashMap<>();
@@ -129,8 +128,8 @@ public class Cloaks {
.replace(' ', '_')
.replaceAll("[^a-z0-9/._-]", "");
cloakCacheIdentifier = Identifier.of(SaturnClient.MOD_ID, "cloaks_" + safeFileName);
IdentifierUtils.registerBufferedImageTexture(cloakCacheIdentifier, image);
cloakCacheIdentifier = IdentifierRef.of(SaturnClient.MOD_ID, "cloaks_" + safeFileName);
IdentifierUtils.registerBufferedImageTextureFast(cloakCacheIdentifier, image);
}
} catch (IOException e) {
SaturnClient.LOGGER.error("Failed to load static cloak from resources: " + fileName, e);
@@ -177,7 +176,7 @@ public class Cloaks {
int delay = Math.max(gif.getDelay(i) * 10, 50); // Minimum 50ms delay
String frameId = baseFrameId + "_frame_" + i;
Identifier frameIdentifier = Identifier.of(SaturnClient.MOD_ID, "cloaks_" + frameId);
IdentifierRef frameIdentifier = IdentifierRef.of(SaturnClient.MOD_ID, "cloaks_" + frameId);
try {
IdentifierUtils.registerBufferedImageTextureFast(frameIdentifier, frame);
@@ -217,7 +216,7 @@ public class Cloaks {
}, CLOAK_LOADER_EXECUTOR);
}
public static Identifier getCurrentCloakTexture(UUID uuid) {
public static IdentifierRef getCurrentCloakTexture(UUID uuid) {
SaturnPlayer player = SaturnPlayer.get(uuid);
if (player == null || player.cloak.isEmpty()) {
@@ -251,15 +250,15 @@ public class Cloaks {
currentFrame = 0;
}
Identifier identifier = frames.get(currentFrame).getTextureId();
IdentifierRef identifier = frames.get(currentFrame).getTextureId();
if (identifier == null) {
return Identifier.of(SaturnClient.MOD_ID, "textures/cloaks/" + cloakName + ".png");
return IdentifierRef.of(SaturnClient.MOD_ID, "textures/cloaks/" + cloakName + ".png");
}
return identifier;
} else {
return Identifier.of(SaturnClient.MOD_ID, "textures/cloaks/" + cloakName + ".png");
return IdentifierRef.of(SaturnClient.MOD_ID, "textures/cloaks/" + cloakName + ".png");
}
}
}
@@ -1,36 +0,0 @@
package org.saturnclient.cosmetics.cloak.utils;
import net.minecraft.util.Identifier;
/**
* Represents a single frame of an animated cloak.
*/
public class AnimatedCloakData {
private final Identifier textureId;
private final int delayMs;
/**
* Creates a new animated cloak frame.
*
* @param textureId The texture identifier for this frame
* @param delayMs The delay in milliseconds before the next frame
*/
public AnimatedCloakData(Identifier textureId, int delayMs) {
this.textureId = textureId;
this.delayMs = delayMs;
}
/**
* @return The texture identifier for this frame
*/
public Identifier getTextureId() {
return textureId;
}
/**
* @return The delay in milliseconds before the next frame
*/
public int getDelayMs() {
return delayMs;
}
}
@@ -1,98 +1,70 @@
package org.saturnclient.cosmetics.cloak.utils;
import org.saturnclient.common.ref.asset.IdentifierRef;
import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.util.Identifier;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.Map;
public class IdentifierUtils {
/**
* ORIGINAL METHOD - EXTREMELY SLOW - causes 10-20 second freezes
* This converts BufferedImage -> PNG bytes -> ByteBuffer -> NativeImage ->
* Texture
* This is completely unnecessary and wastes massive amounts of CPU time!
*/
public static void registerBufferedImageTexture(Identifier i, BufferedImage bi) {
public static void registerBufferedImageTextureFast(IdentifierRef identifierRef, BufferedImage bufferedImage) {
try {
// This is the performance killer:
// 1. Converts BufferedImage to PNG (expensive compression)
// 2. Creates ByteBuffer copy (memory allocation)
// 3. NativeImage.read() parses PNG back to raw pixels (expensive decompression)
// This is doing compression -> decompression for no reason!
Identifier identifier = (Identifier) (Object) identifierRef;
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
javax.imageio.ImageIO.write(bi, "png", baos);
byte[] bytes = baos.toByteArray();
java.nio.ByteBuffer bb = org.lwjgl.BufferUtils.createByteBuffer(bytes.length).put(bytes);
bb.flip();
NativeImageBackedTexture nibt = new NativeImageBackedTexture(NativeImage.read(bb));
SaturnClient.client.getTextureManager().registerTexture(i, nibt);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* OPTIMIZED METHOD - Direct pixel conversion without unnecessary PNG
* encoding/decoding
* This should reduce loading time from 10-20 seconds to under 1 second
*/
public static void registerBufferedImageTextureFast(Identifier identifier, BufferedImage bufferedImage) {
try {
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
// Create NativeImage directly with the correct format
NativeImage nativeImage = new NativeImage(NativeImage.Format.RGBA, width, height, false);
// Direct pixel copy - much faster than PNG conversion
if (bufferedImage.getType() == BufferedImage.TYPE_INT_ARGB) {
// Fast path for ARGB images - direct memory access
int[] pixels = ((DataBufferInt) bufferedImage.getRaster().getDataBuffer()).getData();
int index = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int pixel = pixels[y * width + x];
nativeImage.setColorArgb(x, y, pixel);
}
}
} else {
// Fallback for other image types - still faster than PNG conversion
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rgb = bufferedImage.getRGB(x, y);
nativeImage.setColorArgb(x, y, rgb);
}
nativeImage.setColorArgb(x, y, pixels[index++]);
}
}
// Register the texture
} else {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
nativeImage.setColorArgb(x, y, bufferedImage.getRGB(x, y));
}
}
}
NativeImageBackedTexture texture = new NativeImageBackedTexture(nativeImage);
SaturnClient.client.execute(
() -> SaturnClient.client.getTextureManager().registerTexture(identifier, texture));
SaturnClient.client.execute(() ->
SaturnClient.client.getTextureManager().registerTexture(identifier, texture)
);
} catch (Exception e) {
SaturnClient.LOGGER.error("Failed to register texture: " + identifier, e);
// Fallback to slow method if fast method fails
registerBufferedImageTexture(identifier, bufferedImage);
SaturnClient.LOGGER.error("Failed to register texture: {}", identifierRef, e);
}
}
/**
* EVEN FASTER VERSION - Bulk texture registration
* Use this when registering many textures at once (like animated cloak frames)
*/
public static void registerBufferedImageTexturesBulk(java.util.Map<Identifier, BufferedImage> textures) {
long startTime = System.currentTimeMillis();
public static void registerBufferedImageTexturesBulk(Map<IdentifierRef, BufferedImage> textures) {
for (java.util.Map.Entry<Identifier, BufferedImage> entry : textures.entrySet()) {
registerBufferedImageTextureFast(entry.getKey(), entry.getValue());
}
long start = System.currentTimeMillis();
long endTime = System.currentTimeMillis();
SaturnClient.LOGGER.info("Registered {} textures in {}ms", textures.size(), (endTime - startTime));
textures.forEach(IdentifierUtils::registerBufferedImageTextureFast);
long end = System.currentTimeMillis();
SaturnClient.LOGGER.info(
"Registered {} textures in {}ms",
textures.size(),
(end - start)
);
}
}
@@ -1,48 +0,0 @@
package org.saturnclient.cosmetics.cloak.utils;
import java.util.Random;
/**
* Utility class for generating random strings and numbers.
*/
public final class RandomUtils {
private static final String ALPHANUMERIC = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private static final String ALPHANUMERIC_LOWERCASE = "0123456789abcdefghijklmnopqrstuvwxyz";
private static final Random RANDOM = new Random();
private RandomUtils() {
// Prevent instantiation
}
/**
* Generates a random string of specified length using given characters.
*/
private static String random(final int length, final char[] chars) {
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
sb.append(chars[RANDOM.nextInt(chars.length)]);
}
return sb.toString();
}
/**
* Generates a random string of specified length using alphanumeric characters.
*/
public static String randomString(final int length) {
return random(length, ALPHANUMERIC.toCharArray());
}
/**
* Generates a random lowercase string of specified length.
*/
public static String randomStringLowercase(final int length) {
return random(length, ALPHANUMERIC_LOWERCASE.toCharArray());
}
/**
* Generates a random integer between min and max (inclusive).
*/
public static int randBetween(int min, int max) {
return RANDOM.nextInt(max - min + 1) + min;
}
}
@@ -67,6 +67,6 @@ public class SaturnProviderImpl implements SaturnProvider {
@Override
public void registerBufferedImageTexture(IdentifierRef i, BufferedImage bi) {
IdentifierUtils.registerBufferedImageTextureFast((Identifier) (Object) i, bi);
IdentifierUtils.registerBufferedImageTextureFast(i, bi);
}
}