diff --git a/lophine-server/src/main/java/fun/bm/lophine/config/modules/function/ReplayAPIConfig.java b/lophine-server/src/main/java/fun/bm/lophine/config/modules/function/ReplayAPIConfig.java new file mode 100644 index 0000000..09e90a6 --- /dev/null +++ b/lophine-server/src/main/java/fun/bm/lophine/config/modules/function/ReplayAPIConfig.java @@ -0,0 +1,34 @@ +package fun.bm.lophine.config.modules.function; + +import com.electronwill.nightconfig.core.file.CommentedFileConfig; +import fun.bm.lophine.utils.RandomProfilePool; +import me.earthme.luminol.config.IConfigModule; +import me.earthme.luminol.config.flags.ConfigClassInfo; +import me.earthme.luminol.config.flags.ConfigInfo; +import me.earthme.luminol.config.flags.HotReloadUnsupported; +import me.earthme.luminol.enums.EnumConfigCategory; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "replay-api") +public class ReplayAPIConfig implements IConfigModule { + @HotReloadUnsupported + @ConfigInfo(name = "enable-cache") + public static boolean enableCache = true; + + @HotReloadUnsupported + @ConfigInfo(name = "cache-photographer-time", comments = """ + Time to cache photographer profile(in seconds)""") + public static int cachePhotographerTime = 3600; + + @HotReloadUnsupported + @ConfigInfo(name = "cache-photographer-size", comments = """ + Maximum size of cache photographer profile""") + public static int cachePhotographerSize = 100; + + @Override + public void onLoaded(CommentedFileConfig configInstance, @Nullable Set e) { + RandomProfilePool.init(); + } +} diff --git a/lophine-server/src/main/java/fun/bm/lophine/utils/RandomProfilePool.java b/lophine-server/src/main/java/fun/bm/lophine/utils/RandomProfilePool.java new file mode 100644 index 0000000..f9e4ab4 --- /dev/null +++ b/lophine-server/src/main/java/fun/bm/lophine/utils/RandomProfilePool.java @@ -0,0 +1,59 @@ +package fun.bm.lophine.utils; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.mojang.authlib.GameProfile; +import fun.bm.lophine.config.modules.function.ReplayAPIConfig; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class RandomProfilePool { + private static int lastUsedId = 0; + private static final Object lock = new Object(); + private static Cache cache; + private static final Object lockCache = new Object(); + + public static void init() { + if (ReplayAPIConfig.enableCache) { + cache = CacheBuilder.newBuilder() + .maximumSize(ReplayAPIConfig.cachePhotographerSize) + .expireAfterWrite(ReplayAPIConfig.cachePhotographerTime, TimeUnit.SECONDS) + .build(); + } + } + + public static GameProfile getRandomProfile(String id) { + if (ReplayAPIConfig.enableCache) { + synchronized (lockCache) { + for (Map.Entry entry : cache.asMap().entrySet()) { + int key = entry.getKey(); + cache.invalidate(key); + GameProfile gp = entry.getValue(); + return new GameProfile(gp.id(), id, gp.properties()); + } + } + } + return new GameProfile(UUID.randomUUID(), id); + } + + public static void putProfile(GameProfile profile) { + if (ReplayAPIConfig.enableCache) { + synchronized (lockCache) { + cache.put(getNextId(), profile); + } + } + } + + private static int getNextId() { + synchronized (lock) { + int newId = lastUsedId + 1; + while (cache.getIfPresent(newId) != null) { + newId++; + } + lastUsedId = newId; + return newId; + } + } +} diff --git a/lophine-server/src/main/java/org/leavesmc/leaves/replay/ServerPhotographer.java b/lophine-server/src/main/java/org/leavesmc/leaves/replay/ServerPhotographer.java index 9dd2a7b..93766b6 100644 --- a/lophine-server/src/main/java/org/leavesmc/leaves/replay/ServerPhotographer.java +++ b/lophine-server/src/main/java/org/leavesmc/leaves/replay/ServerPhotographer.java @@ -19,6 +19,7 @@ package org.leavesmc.leaves.replay; import ca.spottedleaf.moonrise.common.util.TickThread; import com.mojang.authlib.GameProfile; +import fun.bm.lophine.utils.RandomProfilePool; import io.papermc.paper.threadedregions.RegionizedServer; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ClientInformation; @@ -38,6 +39,7 @@ import org.leavesmc.leaves.entity.photographer.Photographer; import java.io.File; import java.io.IOException; import java.util.List; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; @@ -70,7 +72,7 @@ public class ServerPhotographer extends ServerPlayer { MinecraftServer server = MinecraftServer.getServer(); ServerLevel world = ((CraftWorld) state.loc.getWorld()).getHandle(); - GameProfile profile = new GameProfile(UUID.randomUUID(), state.id); + GameProfile profile = RandomProfilePool.getRandomProfile(state.id); // Lophine - add cache ServerPhotographer photographer = new ServerPhotographer(server, world, profile); photographer.absSnapTo(state.loc.x(), state.loc.y(), state.loc.z(), state.loc.getYaw(), state.loc.getPitch()); @@ -165,6 +167,7 @@ public class ServerPhotographer extends ServerPlayer { photographers.remove(this); MinecraftServer.getServer().getPlayerList().removePhotographer(this); + RandomProfilePool.putProfile(this.gameProfile); // Lophine - add cache if (!recorder.isSaved()) { CompletableFuture future = recorder.saveRecording(saveFile, save); if (!async) {