Cloak loading
This commit is contained in:
@@ -69,6 +69,8 @@ public class ServiceClient {
|
|||||||
|
|
||||||
SaturnPlayer.set(new SaturnPlayer(uuid, username, response.cloak(), response.hat()));
|
SaturnPlayer.set(new SaturnPlayer(uuid, username, response.cloak(), response.hat()));
|
||||||
|
|
||||||
|
Cloaks.loadCloak(response.cloak());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Providers.saturn.logError("Authentication failed", e);
|
Providers.saturn.logError("Authentication failed", e);
|
||||||
|
|||||||
@@ -10,10 +10,7 @@ import java.awt.image.BufferedImage;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import org.saturnclient.cosmetics.utils.AnimatedCloakData;
|
import org.saturnclient.cosmetics.utils.AnimatedCloakData;
|
||||||
import org.saturnclient.cosmetics.utils.GifDecoder;
|
import org.saturnclient.cosmetics.utils.GifDecoder;
|
||||||
@@ -33,17 +30,8 @@ public class Cloaks {
|
|||||||
private static final String CLOAKS_RESOURCE_PATH = "assets/saturnclient/textures/cloaks/";
|
private static final String CLOAKS_RESOURCE_PATH = "assets/saturnclient/textures/cloaks/";
|
||||||
public static final List<String> availableCloaks = new ArrayList<>();
|
public static final List<String> availableCloaks = new ArrayList<>();
|
||||||
public static IdentifierRef cloakCacheIdentifier = null;
|
public static IdentifierRef cloakCacheIdentifier = null;
|
||||||
public static final Map<UUID, List<AnimatedCloakData>> animatedCloaks = new ConcurrentHashMap<>();
|
public static final Map<String, List<AnimatedCloakData>> animatedCloaks = new ConcurrentHashMap<>();
|
||||||
private static final Map<UUID, Long> lastFrameTime = new ConcurrentHashMap<>();
|
private static final Map<String, Long> lastFrameTime = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private static final ExecutorService CLOAK_LOADER_EXECUTOR = Executors.newFixedThreadPool(
|
|
||||||
Math.min(4, Runtime.getRuntime().availableProcessors()),
|
|
||||||
r -> {
|
|
||||||
Thread t = new Thread(r, "cloak-loader");
|
|
||||||
t.setDaemon(true);
|
|
||||||
t.setPriority(Thread.NORM_PRIORITY - 1);
|
|
||||||
return t;
|
|
||||||
});
|
|
||||||
|
|
||||||
private static final ConcurrentHashMap<String, List<AnimatedCloakData>> CLOAK_CACHE = new ConcurrentHashMap<>();
|
private static final ConcurrentHashMap<String, List<AnimatedCloakData>> CLOAK_CACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@@ -80,7 +68,7 @@ public class Cloaks {
|
|||||||
player.cloak = cloakName;
|
player.cloak = cloakName;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadCloak(uuid);
|
loadCloak(cloakName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,22 +77,12 @@ public class Cloaks {
|
|||||||
*
|
*
|
||||||
* @param cloakName Name of the cloak file to load
|
* @param cloakName Name of the cloak file to load
|
||||||
*/
|
*/
|
||||||
public static void loadCloak(UUID uuid) {
|
public static void loadCloak(String cloakName) {
|
||||||
SaturnPlayer player = SaturnPlayer.get(uuid);
|
if (cloakName != null && !cloakName.isEmpty()) {
|
||||||
|
if (Arrays.asList(ANIMATED_CLOAKS).contains(cloakName)) {
|
||||||
if (player != null && player.cloak != null) {
|
Providers.saturn.getClient().executeOnThread(() -> loadAnimatedCloak(cloakName));
|
||||||
if (!player.cloak.isEmpty()) {
|
} else {
|
||||||
if (Arrays.asList(ANIMATED_CLOAKS).contains(player.cloak)) {
|
Providers.saturn.getClient().executeOnThread(() -> loadStaticCloak(cloakName + ".png"));
|
||||||
loadAnimatedCloakAsync(uuid, player.cloak)
|
|
||||||
.exceptionally(throwable -> {
|
|
||||||
Providers.saturn.logError("Failed to load animated cloak for player: " + player.cloak,
|
|
||||||
throwable);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Providers.saturn.getClient().executeOnThread(() -> loadStaticCloak(player.cloak + ".png"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,94 +115,84 @@ public class Cloaks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompletableFuture<Void> loadAnimatedCloakAsync(UUID uuid, String cloakName) {
|
public static void loadAnimatedCloak(String cloakName) {
|
||||||
return CompletableFuture.runAsync(() -> {
|
String fileName = cloakName + ".gif";
|
||||||
String fileName = cloakName + ".gif";
|
|
||||||
|
|
||||||
// Check cache first
|
// Check cache first
|
||||||
List<AnimatedCloakData> cached = CLOAK_CACHE.get(fileName);
|
List<AnimatedCloakData> cached = CLOAK_CACHE.get(fileName);
|
||||||
if (cached != null) {
|
if (cached != null) {
|
||||||
animatedCloaks.put(uuid, cached);
|
animatedCloaks.put(cloakName, cached);
|
||||||
lastFrameTime.put(uuid, System.currentTimeMillis());
|
lastFrameTime.put(cloakName, System.currentTimeMillis());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String resourcePath = CLOAKS_RESOURCE_PATH + fileName;
|
||||||
|
|
||||||
|
try (InputStream inputStream = Cloaks.class.getClassLoader().getResourceAsStream(resourcePath)) {
|
||||||
|
if (inputStream == null) {
|
||||||
|
Providers.saturn.logError("Cloak resource not found: " + resourcePath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String resourcePath = CLOAKS_RESOURCE_PATH + fileName;
|
// Read all bytes at once - more efficient than multiple reads
|
||||||
|
byte[] data = inputStream.readAllBytes();
|
||||||
|
GifDecoder.GifImage gif = GifDecoder.read(data);
|
||||||
|
|
||||||
try (InputStream inputStream = Cloaks.class.getClassLoader().getResourceAsStream(resourcePath)) {
|
int frameCount = gif.getFrameCount();
|
||||||
if (inputStream == null) {
|
if (frameCount == 0) {
|
||||||
Providers.saturn.logError("Cloak resource not found: " + resourcePath);
|
Providers.saturn.logError("No frames found in animated cloak: " + fileName);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Read all bytes at once - more efficient than multiple reads
|
|
||||||
byte[] data = inputStream.readAllBytes();
|
|
||||||
GifDecoder.GifImage gif = GifDecoder.read(data);
|
|
||||||
|
|
||||||
int frameCount = gif.getFrameCount();
|
|
||||||
if (frameCount == 0) {
|
|
||||||
Providers.saturn.logError("No frames found in animated cloak: " + fileName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-allocate collections with known size
|
|
||||||
List<AnimatedCloakData> animatedFrames = new ArrayList<>(frameCount);
|
|
||||||
String baseFrameId = fileName.replace(".gif", "");
|
|
||||||
|
|
||||||
// Process frames in batch
|
|
||||||
for (int i = 0; i < frameCount; i++) {
|
|
||||||
BufferedImage frame = gif.getFrame(i);
|
|
||||||
int delay = Math.max(gif.getDelay(i) * 10, 50); // Minimum 50ms delay
|
|
||||||
|
|
||||||
String frameId = baseFrameId + "_frame_" + i;
|
|
||||||
IdentifierRef frameIdentifier = IdentifierRef.ofSaturn("cloaks_" + frameId);
|
|
||||||
|
|
||||||
try {
|
|
||||||
Providers.saturn.registerBufferedImageTexture(frameIdentifier, frame);
|
|
||||||
animatedFrames.add(new AnimatedCloakData(frameIdentifier, delay));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Providers.saturn.logError("Failed to register frame " + i + " for cloak: " + fileName, e);
|
|
||||||
// Continue with other frames instead of failing completely
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!animatedFrames.isEmpty()) {
|
|
||||||
// Cache the result for future use
|
|
||||||
CLOAK_CACHE.put(fileName, animatedFrames);
|
|
||||||
animatedCloaks.put(uuid, animatedFrames);
|
|
||||||
lastFrameTime.put(uuid, System.currentTimeMillis());
|
|
||||||
|
|
||||||
Providers.saturn.logInfo("Loaded " + animatedFrames.size() + " frames for animated cloak: "
|
|
||||||
+ fileName + " (cached)");
|
|
||||||
} else {
|
|
||||||
Providers.saturn.logError("No valid frames could be loaded for cloak: " + fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
Providers.saturn.logError("Failed to load animated cloak from resources: " + fileName, e);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Providers.saturn.logError("Unexpected error loading animated cloak: " + fileName, e);
|
|
||||||
}
|
}
|
||||||
}, CLOAK_LOADER_EXECUTOR);
|
|
||||||
|
// Pre-allocate collections with known size
|
||||||
|
List<AnimatedCloakData> animatedFrames = new ArrayList<>(frameCount);
|
||||||
|
String baseFrameId = fileName.replace(".gif", "");
|
||||||
|
|
||||||
|
// Process frames in batch
|
||||||
|
for (int i = 0; i < frameCount; i++) {
|
||||||
|
BufferedImage frame = gif.getFrame(i);
|
||||||
|
int delay = Math.max(gif.getDelay(i) * 10, 50); // Minimum 50ms delay
|
||||||
|
|
||||||
|
String frameId = baseFrameId + "_frame_" + i;
|
||||||
|
IdentifierRef frameIdentifier = IdentifierRef.ofSaturn("cloaks_" + frameId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Providers.saturn.registerBufferedImageTexture(frameIdentifier, frame);
|
||||||
|
animatedFrames.add(new AnimatedCloakData(frameIdentifier, delay));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Providers.saturn.logError("Failed to register frame " + i + " for cloak: " + fileName, e);
|
||||||
|
// Continue with other frames instead of failing completely
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!animatedFrames.isEmpty()) {
|
||||||
|
// Cache the result for future use
|
||||||
|
CLOAK_CACHE.put(fileName, animatedFrames);
|
||||||
|
animatedCloaks.put(cloakName, animatedFrames);
|
||||||
|
lastFrameTime.put(cloakName, System.currentTimeMillis());
|
||||||
|
|
||||||
|
Providers.saturn.logInfo("Loaded " + animatedFrames.size() + " frames for animated cloak: "
|
||||||
|
+ fileName + " (cached)");
|
||||||
|
} else {
|
||||||
|
Providers.saturn.logError("No valid frames could be loaded for cloak: " + fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
Providers.saturn.logError("Failed to load animated cloak from resources: " + fileName, e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Providers.saturn.logError("Unexpected error loading animated cloak: " + fileName, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IdentifierRef getCurrentCloakTexture(UUID uuid) {
|
public static IdentifierRef getCurrentCloakTexture(String cloakName) {
|
||||||
SaturnPlayer player = SaturnPlayer.get(uuid);
|
|
||||||
|
|
||||||
if (player == null || player.cloak.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String cloakName = player.cloak;
|
|
||||||
|
|
||||||
if (Arrays.asList(ANIMATED_CLOAKS).contains(cloakName)) {
|
if (Arrays.asList(ANIMATED_CLOAKS).contains(cloakName)) {
|
||||||
List<AnimatedCloakData> frames = animatedCloaks.get(uuid);
|
List<AnimatedCloakData> frames = animatedCloaks.get(cloakName);
|
||||||
if (frames == null || frames.isEmpty()) {
|
if (frames == null || frames.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
long currentTime = System.currentTimeMillis();
|
long currentTime = System.currentTimeMillis();
|
||||||
long lastTime = lastFrameTime.getOrDefault(uuid, currentTime);
|
long lastTime = lastFrameTime.getOrDefault(cloakName, currentTime);
|
||||||
int currentFrame = 0;
|
int currentFrame = 0;
|
||||||
|
|
||||||
long elapsedTime = currentTime - lastTime;
|
long elapsedTime = currentTime - lastTime;
|
||||||
@@ -238,7 +206,7 @@ public class Cloaks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (elapsedTime >= totalTime) {
|
if (elapsedTime >= totalTime) {
|
||||||
lastFrameTime.put(uuid, currentTime);
|
lastFrameTime.put(cloakName, currentTime);
|
||||||
currentFrame = 0;
|
currentFrame = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user