From bfc0741b08c63302c23b008c3801380a5633c7dd Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 16 May 2025 21:11:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=AF=BB=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/pack/AbstractPackManager.java | 140 +++++++++++------- .../core/pack/CachedAssetFile.java | 25 ++++ .../core/pack/CachedConfigFile.java | 8 +- ...edConfig.java => CachedConfigSection.java} | 4 +- .../craftengine/core/pack/PackManager.java | 2 + .../craftengine/core/plugin/CraftEngine.java | 1 + 6 files changed, 121 insertions(+), 59 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/CachedAssetFile.java rename core/src/main/java/net/momirealms/craftengine/core/pack/{CachedConfig.java => CachedConfigSection.java} (81%) diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index ebcd6caa5..1c0e96027 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -19,7 +19,6 @@ import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator; import net.momirealms.craftengine.core.pack.obfuscation.ObfA; -import net.momirealms.craftengine.core.pack.obfuscation.ObfH; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; @@ -32,6 +31,7 @@ import net.momirealms.craftengine.core.sound.AbstractSoundManager; import net.momirealms.craftengine.core.sound.SoundEvent; import net.momirealms.craftengine.core.util.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; @@ -73,11 +73,11 @@ public abstract class AbstractPackManager implements PackManager { private final BiConsumer eventDispatcher; private final Map loadedPacks = new HashMap<>(); private final Map sectionParsers = new HashMap<>(); + private Map cachedConfigFiles = Collections.emptyMap(); + private Map cachedAssetFiles = Collections.emptyMap(); protected BiConsumer zipGenerator; protected ResourcePackHost resourcePackHost; - private Map cachedConfigFiles = Collections.emptyMap(); - public AbstractPackManager(CraftEngine plugin, BiConsumer eventDispatcher) { this.plugin = plugin; this.eventDispatcher = eventDispatcher; @@ -206,6 +206,15 @@ public abstract class AbstractPackManager implements PackManager { } } + @Override + public void initCachedAssets() { + try { + this.updateCachedAssets(null); + } catch (Exception e) { + this.plugin.logger().warn("Failed to update cached assets", e); + } + } + @NotNull @Override public Collection loadedPacks() { @@ -420,7 +429,8 @@ public abstract class AbstractPackManager implements PackManager { CachedConfigFile cachedFile = previousFiles.get(path); try { long lastModifiedTime = Files.getLastModifiedTime(path).toMillis(); - if (cachedFile != null && cachedFile.lastModified() == lastModifiedTime) { + long size = Files.size(path); + if (cachedFile != null && cachedFile.lastModified() == lastModifiedTime && cachedFile.size() == size) { this.cachedConfigFiles.put(path, cachedFile); continue; } @@ -428,7 +438,8 @@ public abstract class AbstractPackManager implements PackManager { Yaml yaml = new Yaml(new StringKeyConstructor(new LoaderOptions())); Map data = yaml.load(inputStream); if (data == null) continue; - this.cachedConfigFiles.put(path, new CachedConfigFile(data, pack, lastModifiedTime)); + + this.cachedConfigFiles.put(path, new CachedConfigFile(data, pack, lastModifiedTime, size)); } catch (Exception e) { this.plugin.logger().warn(path, "Error loading config file", e); } @@ -440,7 +451,7 @@ public abstract class AbstractPackManager implements PackManager { } private void loadResourceConfigs(Predicate predicate) { - TreeMap> cachedConfigs = new TreeMap<>(); + TreeMap> cachedConfigs = new TreeMap<>(); long o1 = System.nanoTime(); this.updateCachedConfigFiles(); for (Map.Entry fileEntry : this.cachedConfigFiles.entrySet()) { @@ -452,10 +463,10 @@ public abstract class AbstractPackManager implements PackManager { } long o2 = System.nanoTime(); this.plugin.logger().info("Loaded packs. Took " + String.format("%.2f", ((o2 - o1) / 1_000_000.0)) + " ms"); - for (Map.Entry> entry : cachedConfigs.entrySet()) { + for (Map.Entry> entry : cachedConfigs.entrySet()) { ConfigParser parser = entry.getKey(); long t1 = System.nanoTime(); - for (CachedConfig cached : entry.getValue()) { + for (CachedConfigSection cached : entry.getValue()) { for (Map.Entry configEntry : cached.config().entrySet()) { String key = configEntry.getKey(); try { @@ -491,14 +502,14 @@ public abstract class AbstractPackManager implements PackManager { } } - private void processConfigEntry(Map.Entry entry, Path path, Pack pack, BiConsumer callback) { + private void processConfigEntry(Map.Entry entry, Path path, Pack pack, BiConsumer callback) { if (entry.getValue() instanceof Map typeSections0) { String key = entry.getKey(); int hashIndex = key.indexOf('#'); String configType = hashIndex != -1 ? key.substring(0, hashIndex) : key; Optional.ofNullable(this.sectionParsers.get(configType)) .ifPresent(parser -> { - callback.accept(parser, new CachedConfig(key, castToMap(typeSections0, false), path, pack)); + callback.accept(parser, new CachedConfigSection(key, castToMap(typeSections0, false), path, pack)); }); } } @@ -533,22 +544,79 @@ public abstract class AbstractPackManager implements PackManager { // } // } - private void updateAssetsCache(List folders) { - + private List>> updateCachedAssets(@Nullable FileSystem fs) throws IOException { + List folders = new ArrayList<>(); + Map> conflictChecker = new HashMap<>(Math.max(128, this.cachedAssetFiles.size())); + Map previousFiles = this.cachedAssetFiles; + this.cachedAssetFiles = new HashMap<>(Math.max(128, this.cachedAssetFiles.size())); + folders.addAll(loadedPacks().stream().filter(Pack::enabled).map(Pack::resourcePackFolder).toList()); + folders.addAll(Config.foldersToMerge().stream().map(it -> plugin.dataFolderPath().getParent().resolve(it)).filter(Files::exists).toList()); + for (Path sourceFolder : folders) { + if (Files.exists(sourceFolder)) { + Files.walkFileTree(sourceFolder, new SimpleFileVisitor<>() { + @Override + public @NotNull FileVisitResult visitFile(@NotNull Path file, @NotNull BasicFileAttributes attrs) throws IOException { + CachedAssetFile cachedAsset = previousFiles.get(file); + long lastModified = Files.getLastModifiedTime(file).toMillis(); + long size = Files.size(file); + if (cachedAsset != null && cachedAsset.lastModified() == lastModified && cachedAsset.size() == size) { + AbstractPackManager.this.cachedAssetFiles.put(file, cachedAsset); + } else { + cachedAsset = new CachedAssetFile(Files.readAllBytes(file), lastModified, size); + AbstractPackManager.this.cachedAssetFiles.put(file, cachedAsset); + } + if (fs == null) return FileVisitResult.CONTINUE; + Path relative = sourceFolder.relativize(file); + Path targetPath = fs.getPath("resource_pack/" + relative.toString().replace("\\", "/")); + List conflicts = conflictChecker.get(relative); + if (conflicts == null) { + Files.createDirectories(targetPath.getParent()); + Files.write(targetPath, cachedAsset.data()); + conflictChecker.put(relative, List.of(file)); + } else { + PathContext relativeCTX = PathContext.of(relative); + PathContext targetCTX = PathContext.of(targetPath); + PathContext fileCTX = PathContext.of(file); + for (ResolutionConditional resolution : Config.resolutions()) { + if (resolution.matcher().test(relativeCTX)) { + resolution.resolution().run(targetCTX, fileCTX); + return FileVisitResult.CONTINUE; + } + } + switch (conflicts.size()) { + case 1 -> conflictChecker.put(relative, List.of(conflicts.get(0), file)); + case 2 -> conflictChecker.put(relative, List.of(conflicts.get(0), conflicts.get(1), file)); + case 3 -> conflictChecker.put(relative, List.of(conflicts.get(0), conflicts.get(1), conflicts.get(2), file)); + case 4 -> conflictChecker.put(relative, List.of(conflicts.get(0), conflicts.get(1), conflicts.get(2), conflicts.get(3), file)); + default -> { + // just ignore it if it has many conflict files + } + } + } + return FileVisitResult.CONTINUE; + } + }); + } + } + List>> conflicts = new ArrayList<>(); + for (Map.Entry> entry : conflictChecker.entrySet()) { + if (entry.getValue().size() > 1) { + conflicts.add(Pair.of(entry.getKey(), entry.getValue())); + } + } + return conflicts; } @Override public void generateResourcePack() throws IOException { this.plugin.logger().info("Generating resource pack..."); long start = System.currentTimeMillis(); + // get the target location try (FileSystem fs = Jimfs.newFileSystem(Configuration.forCurrentPlatform())) { // firstly merge existing folders Path generatedPackPath = fs.getPath("resource_pack"); - List folders = new ArrayList<>(); - folders.addAll(loadedPacks().stream().filter(Pack::enabled).map(Pack::resourcePackFolder).toList()); - folders.addAll(Config.foldersToMerge().stream().map(it -> plugin.dataFolderPath().getParent().resolve(it)).filter(Files::exists).toList()); - List>> duplicated = mergeFolder(folders, fs); + List>> duplicated = this.updateCachedAssets(fs); if (!duplicated.isEmpty()) { plugin.logger().severe(AdventureHelper.miniMessage().stripTags(TranslationManager.instance().miniMessageTranslation("warning.config.pack.duplicated_files"))); int x = 1; @@ -1182,44 +1250,4 @@ public abstract class AbstractPackManager implements PackManager { } } } - - private List>> mergeFolder(Collection sourceFolders, FileSystem fs) throws IOException { - Map> conflictChecker = new HashMap<>(); - for (Path sourceFolder : sourceFolders) { - if (Files.exists(sourceFolder)) { - Files.walkFileTree(sourceFolder, new SimpleFileVisitor<>() { - @Override - public @NotNull FileVisitResult visitFile(@NotNull Path file, @NotNull BasicFileAttributes attrs) throws IOException { - Path relative = sourceFolder.relativize(file); - Path targetPath = fs.getPath("resource_pack/" + relative.toString().replace("\\", "/")); - List conflicts = conflictChecker.computeIfAbsent(relative, k -> new ArrayList<>()); - if (conflicts.isEmpty()) { - Files.createDirectories(targetPath.getParent()); - Files.copy(file, targetPath, StandardCopyOption.REPLACE_EXISTING); - conflicts.add(file); - } else { - PathContext relativeCTX = PathContext.of(relative); - PathContext targetCTX = PathContext.of(targetPath); - PathContext fileCTX = PathContext.of(file); - for (ResolutionConditional resolution : Config.resolutions()) { - if (resolution.matcher().test(relativeCTX)) { - resolution.resolution().run(targetCTX, fileCTX); - return FileVisitResult.CONTINUE; - } - } - conflicts.add(file); - } - return FileVisitResult.CONTINUE; - } - }); - } - } - List>> conflicts = new ArrayList<>(); - for (Map.Entry> entry : conflictChecker.entrySet()) { - if (entry.getValue().size() > 1) { - conflicts.add(Pair.of(entry.getKey(), entry.getValue())); - } - } - return conflicts; - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedAssetFile.java b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedAssetFile.java new file mode 100644 index 000000000..f86687e40 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedAssetFile.java @@ -0,0 +1,25 @@ +package net.momirealms.craftengine.core.pack; + +public class CachedAssetFile { + private final byte[] data; + private final long lastModified; + private final long size; + + public CachedAssetFile(byte[] data, long lastModified, long size) { + this.data = data; + this.lastModified = lastModified; + this.size = size; + } + + public byte[] data() { + return data; + } + + public long lastModified() { + return lastModified; + } + + public long size() { + return size; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigFile.java b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigFile.java index c39cf4e36..f4cb38fd5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigFile.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigFile.java @@ -5,10 +5,12 @@ import java.util.Map; public class CachedConfigFile { private final Map config; private final long lastModified; + private final long size; private final Pack pack; - public CachedConfigFile(Map config, Pack pack, long lastModified) { + public CachedConfigFile(Map config, Pack pack, long lastModified, long size) { this.config = config; + this.size = size; this.lastModified = lastModified; this.pack = pack; } @@ -24,4 +26,8 @@ public class CachedConfigFile { public long lastModified() { return lastModified; } + + public long size() { + return size; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfig.java b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigSection.java similarity index 81% rename from core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfig.java rename to core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigSection.java index e98faf8f8..e8016ce2a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigSection.java @@ -3,13 +3,13 @@ package net.momirealms.craftengine.core.pack; import java.nio.file.Path; import java.util.Map; -public class CachedConfig { +public class CachedConfigSection { private final Pack pack; private final Path filePath; private final String prefix; private final Map config; - public CachedConfig(String prefix, Map config, Path filePath, Pack pack) { + public CachedConfigSection(String prefix, Map config, Path filePath, Pack pack) { this.config = config; this.filePath = filePath; this.pack = pack; diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java index 6e5391656..839de5886 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java @@ -14,6 +14,8 @@ public interface PackManager extends Manageable { void loadResources(boolean recipe); + void initCachedAssets(); + @NotNull Collection loadedPacks(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java index 02c552472..53f8b5da5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java @@ -234,6 +234,7 @@ public abstract class CraftEngine implements Plugin { // set up some platform extra tasks this.platformDelayedEnable(); this.isInitializing = false; + this.scheduler.executeAsync(() -> this.packManager.initCachedAssets()); }); }