fancynpcs: Add uuid cache

This commit is contained in:
Oliver
2025-06-21 12:24:11 +02:00
parent 527c46cbca
commit 607f9f0719
9 changed files with 197 additions and 7 deletions

View File

@@ -38,6 +38,7 @@ import de.oliver.fancynpcs.skins.cache.SkinCacheFile;
import de.oliver.fancynpcs.skins.cache.SkinCacheMemory;
import de.oliver.fancynpcs.skins.mineskin.MineSkinQueue;
import de.oliver.fancynpcs.skins.mojang.MojangQueue;
import de.oliver.fancynpcs.skins.uuidcache.UUIDFileCache;
import de.oliver.fancynpcs.tests.PlaceholderApiEnv;
import de.oliver.fancynpcs.tracker.TurnToPlayerTracker;
import de.oliver.fancynpcs.tracker.VisibilityTracker;
@@ -214,7 +215,7 @@ public class FancyNpcs extends JavaPlugin implements FancyNpcsPlugin {
actionManager.registerAction(new NeedPermissionAction());
actionManager.registerAction(new PlaySoundAction());
skinManager = new SkinManagerImpl(new SkinCacheFile(), new SkinCacheMemory(), MojangQueue.get(), MineSkinQueue.get());
skinManager = new SkinManagerImpl(new UUIDFileCache(), new SkinCacheFile(), new SkinCacheMemory(), MojangQueue.get(), MineSkinQueue.get());
OldSkinCacheMigrator.migrate();
textConfig = new TextConfig("#E33239", "#AD1D23", "#81E366", "#E3CA66", "#E36666", "");

View File

@@ -90,4 +90,10 @@ public final class FancyNpcsDebugCMD {
translator.translate("fancynpcs_skin_system_clear_cache_success").send(player);
}
@Command("fancynpcs skin_system clear_uuid_cache")
@Permission("fancynpcs.command.fancynpcs.skin_system.clear_uuid_cache")
public void onInvalidateUUidCache(final Player player) {
FancyNpcs.getInstance().getSkinManagerImpl().getUuidCache().clearCache();
translator.translate("fancynpcs_skin_system_clear_uuid_cache_success").send(player);
}
}

View File

@@ -10,6 +10,7 @@ import de.oliver.fancynpcs.api.skins.SkinLoadException;
import de.oliver.fancynpcs.api.skins.SkinManager;
import de.oliver.fancynpcs.skins.cache.SkinCache;
import de.oliver.fancynpcs.skins.cache.SkinCacheData;
import de.oliver.fancynpcs.skins.uuidcache.UUIDCache;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.lushplugins.chatcolorhandler.ChatColorHandler;
@@ -33,12 +34,20 @@ public class SkinManagerImpl implements SkinManager, Listener {
private final String SKINS_DIRECTORY = "plugins/FancyNpcs/skins/";
private final UUIDCache uuidCache;
private final SkinCache fileCache;
private final SkinCache memCache;
private final SkinGenerationQueue mojangQueue;
private final SkinGenerationQueue mineSkinQueue;
public SkinManagerImpl(SkinCache fileCache, SkinCache memCache, SkinGenerationQueue mojangQueue, SkinGenerationQueue mineSkinQueue) {
public SkinManagerImpl(
UUIDCache uuidCache,
SkinCache fileCache,
SkinCache memCache,
SkinGenerationQueue mojangQueue,
SkinGenerationQueue mineSkinQueue
) {
this.uuidCache = uuidCache;
this.fileCache = fileCache;
this.memCache = memCache;
this.mojangQueue = mojangQueue;
@@ -99,10 +108,15 @@ public class SkinManagerImpl implements SkinManager, Listener {
return cached;
}
UUID uuid = UUIDFetcher.getUUID(username);
UUID uuid = uuidCache.getUUID(username);
if (uuid == null) {
throw new SkinLoadException(SkinLoadException.Reason.INVALID_USERNAME, "(USERNAME = '" + username + "')");
uuid = UUIDFetcher.getUUID(username);
if (uuid == null) {
throw new SkinLoadException(SkinLoadException.Reason.INVALID_USERNAME, "(USERNAME = '" + username + "')");
}
uuidCache.cacheUUID(username, uuid);
}
SkinData dataByUUID = getByUUID(uuid, variant);
return new SkinData(username, dataByUUID.getVariant(), dataByUUID.getTextureValue(), dataByUUID.getTextureSignature());
@@ -158,8 +172,13 @@ public class SkinManagerImpl implements SkinManager, Listener {
String id = skin.getParsedIdentifier();
if (SkinUtils.isUsername(id)) {
UUID uuid = UUIDFetcher.getUUID(id);
UUID uuid = uuidCache.getUUID(id);
if (uuid == null) {
uuid = UUIDFetcher.getUUID(id);
}
if (uuid != null) {
uuidCache.cacheUUID(id, uuid);
id = uuid.toString();
}
}
@@ -212,6 +231,10 @@ public class SkinManagerImpl implements SkinManager, Listener {
fileCache.addSkin(skinData);
}
public UUIDCache getUuidCache() {
return uuidCache;
}
public SkinCache getFileCache() {
return fileCache;
}

View File

@@ -0,0 +1,15 @@
package de.oliver.fancynpcs.skins.uuidcache;
import java.util.UUID;
public interface UUIDCache {
long CACHE_TIME = 1000 * 60 * 60 * 24 * 30L; // 1 month
UUID getUUID(String username);
void cacheUUID(String username, UUID uuid);
void clearCache();
}

View File

@@ -0,0 +1,14 @@
package de.oliver.fancynpcs.skins.uuidcache;
import java.util.UUID;
public record UUIDCacheData(
String username,
UUID uuid,
long lastUpdated,
long timeToLive
) {
public boolean isExpired() {
return timeToLive > 0 && System.currentTimeMillis() - lastUpdated > timeToLive;
}
}

View File

@@ -0,0 +1,88 @@
package de.oliver.fancynpcs.skins.uuidcache;
import de.oliver.fancynpcs.FancyNpcs;
import de.oliver.jdb.JDB;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class UUIDFileCache implements UUIDCache {
private final JDB storage;
private final Map<String, UUIDCacheData> cache;
public UUIDFileCache() {
this.storage = new JDB("plugins/FancyNpcs/.data");
this.cache = new ConcurrentHashMap<>();
}
private void load() {
UUIDCacheData[] uuids;
try {
uuids = this.storage.get("uuids", UUIDCacheData[].class);
} catch (IOException e) {
FancyNpcs.getInstance().getFancyLogger().error("Failed to load UUID cache");
FancyNpcs.getInstance().getFancyLogger().error(e);
return;
}
if (uuids == null) {
return;
}
cache.clear();
for (UUIDCacheData data : uuids) {
if (!data.isExpired()) {
cache.put(data.username(), data);
}
}
}
private void save() {
UUIDCacheData[] uuids = cache.values().toArray(new UUIDCacheData[0]);
try {
this.storage.set("uuids", uuids);
} catch (IOException e) {
throw new RuntimeException("Failed to save UUID cache", e);
}
}
@Override
public UUID getUUID(String username) {
if (cache.isEmpty()) {
load(); // Load from storage if not present in cache
}
if (!cache.containsKey(username)) {
return null; // If still not present after loading, return null
}
UUIDCacheData data = cache.get(username);
if (data.isExpired()) {
cache.remove(username);
save(); // Save changes after removing expired entry
return null; // If expired, remove from cache and return null
}
FancyNpcs.getInstance().getFancyLogger().debug("Found UUID for " + username + ": " + data.uuid() + " in file cache");
return data.uuid();
}
@Override
public void cacheUUID(String username, UUID uuid) {
UUIDCacheData data = new UUIDCacheData(username, uuid, System.currentTimeMillis(), CACHE_TIME);
cache.put(username, data);
save(); // Save changes after adding new entry
FancyNpcs.getInstance().getFancyLogger().debug("Cached UUID for " + username + ": " + uuid);
}
@Override
public void clearCache() {
cache.clear();
storage.delete("uuids");
}
}

View File

@@ -0,0 +1,40 @@
package de.oliver.fancynpcs.skins.uuidcache;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class UUIDMemoryCache implements UUIDCache {
private final Map<String, UUIDCacheData> cache;
public UUIDMemoryCache() {
this.cache = new ConcurrentHashMap<>();
}
@Override
public UUID getUUID(String username) {
if (!cache.containsKey(username)) {
return null;
}
UUIDCacheData data = cache.get(username);
if (data.isExpired()) {
cache.remove(username);
return null;
}
return data.uuid();
}
@Override
public void cacheUUID(String username, UUID uuid) {
UUIDCacheData data = new UUIDCacheData(username, uuid, System.currentTimeMillis(), CACHE_TIME);
cache.put(username, data);
}
@Override
public void clearCache() {
cache.clear();
}
}

View File

@@ -8,6 +8,7 @@ import de.oliver.fancynpcs.skins.cache.SkinCache;
import de.oliver.fancynpcs.skins.cache.SkinCacheData;
import de.oliver.fancynpcs.skins.cache.SkinCacheFile;
import de.oliver.fancynpcs.skins.cache.SkinCacheMemory;
import de.oliver.fancynpcs.skins.uuidcache.UUIDMemoryCache;
import de.oliver.plugintests.annotations.FPAfterEach;
import de.oliver.plugintests.annotations.FPBeforeEach;
import de.oliver.plugintests.annotations.FPTest;
@@ -31,7 +32,7 @@ public class SkinManagerTest {
fileCache = new SkinCacheFile();
mojangQueue = new FakeSkinQueue();
mineSkinQueue = new FakeSkinQueue();
manager = new SkinManagerImpl(fileCache, memCache, mojangQueue, mineSkinQueue);
manager = new SkinManagerImpl(new UUIDMemoryCache(), fileCache, memCache, mojangQueue, mineSkinQueue);
}
@FPAfterEach
@@ -65,7 +66,8 @@ public class SkinManagerTest {
SkinData expData,
boolean expQueued,
SkinGenerationRequest mojangRequest
) {}
) {
}
TestCase[] testCases = {
new TestCase(

View File

@@ -143,6 +143,7 @@ messages:
fancynpcs_skin_system_scheduler_status: "<dark_gray> {successColor}Status ({scheduler}): {status}"
fancynpcs_skin_system_clear_queues_success: "<dark_gray> {successColor}Skin generation queues have been cleared."
fancynpcs_skin_system_clear_cache_success: "<dark_gray> {successColor}Skin cache has been cleared."
fancynpcs_skin_system_clear_uuid_cache_success: "<dark_gray> {successColor}UUID cache has been cleared."
# Commands (npc help)
npc_help_page_header: "<dark_gray><st>-------------</st> {primaryColor}FancyNpcs<gray> Commands ({primaryColor}{page}<gray>/{primaryColor}{max_page}<gray>) <dark_gray><st>--------------</st><newline>"