From 633332e4bc4553cad6978c47d35f2eeed3f19e75 Mon Sep 17 00:00:00 2001 From: Luke Bemish Date: Wed, 31 Jan 2024 20:02:03 -0600 Subject: [PATCH] Data versioning, default extraction, and removing marker files Marker files have silently been removed - this should not affect anything in a breaking fashion. --- .../api/OutdatedResourcesListener.java | 42 +++ .../api/ResourceProvider.java | 10 +- .../defaultresources/impl/Config.java | 17 +- .../impl/DefaultResources.java | 241 ++++++++++++++---- .../defaultresources/impl/ModMetaFile.java | 10 +- .../impl/forge/DefaultResourcesForge.java | 2 +- .../impl/forge/PlatformImpl.java | 3 +- .../impl/quilt/DefaultResourcesQuilt.java | 2 +- .../impl/quilt/PlatformImpl.java | 4 +- version.properties | 2 +- 10 files changed, 258 insertions(+), 75 deletions(-) create mode 100644 Common/src/main/java/dev/lukebemish/defaultresources/api/OutdatedResourcesListener.java diff --git a/Common/src/main/java/dev/lukebemish/defaultresources/api/OutdatedResourcesListener.java b/Common/src/main/java/dev/lukebemish/defaultresources/api/OutdatedResourcesListener.java new file mode 100644 index 0000000..07b089c --- /dev/null +++ b/Common/src/main/java/dev/lukebemish/defaultresources/api/OutdatedResourcesListener.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 Luke Bemish, and contributors + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +package dev.lukebemish.defaultresources.api; + +import dev.lukebemish.defaultresources.impl.DefaultResources; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +/** + * Allows mods to provide custom logic for when the extracted default resources are outdated, but cannot be updated due + * to having been changed on disk. + */ +public interface OutdatedResourcesListener { + + /** + * Called when the listener is fired. + * @param oldDataVersion the data version of the outdated resources on disk, or {@code null} if none is present + * @param newDataVersion the data version of the new resources the mod provides, or {@code null} if none is present + */ + void resourcesOutdated(@Nullable String oldDataVersion, @Nullable String newDataVersion); + + /** + * Registers a listener to be notified when the extracted default resources are outdated, but cannot be updated due + * to having been changed on disk. + * @param modId the mod ID to listen for + * @param listener the listener to call if the resources cannot be updated + */ + @SuppressWarnings("unused") + static void register(String modId, OutdatedResourcesListener listener) { + DefaultResources.delegate(() -> { + Optional oldVersion = DefaultResources.OUTDATED_TARGETS.get(modId); + Optional newVersion = DefaultResources.MOD_TARGETS.get(modId); + if (oldVersion != null && newVersion != null) { + listener.resourcesOutdated(oldVersion.orElse(null), newVersion.orElse(null)); + } + }, () -> DefaultResources.addListener(modId, listener)); + } +} diff --git a/Common/src/main/java/dev/lukebemish/defaultresources/api/ResourceProvider.java b/Common/src/main/java/dev/lukebemish/defaultresources/api/ResourceProvider.java index 95dc056..ca248f5 100644 --- a/Common/src/main/java/dev/lukebemish/defaultresources/api/ResourceProvider.java +++ b/Common/src/main/java/dev/lukebemish/defaultresources/api/ResourceProvider.java @@ -8,15 +8,12 @@ import dev.lukebemish.defaultresources.impl.DefaultResources; import dev.lukebemish.defaultresources.impl.Services; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.PackResources; -import net.minecraft.server.packs.PackType; import net.minecraft.server.packs.resources.IoSupplier; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.io.InputStream; import java.util.Collection; -import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Stream; @@ -25,9 +22,10 @@ */ public interface ResourceProvider { /** - * This should be run before your config file is written too, to ensure that DefaultResources takes it into account - * when figuring out whether to extract default resources. + * This is no longer needed; simply call {@link ResourceProvider#instance()} when you need resources. DefaultResources + * no longer uses config files as markers. */ + @Deprecated static void forceInitialization() { ResourceProvider.instance(); } @@ -41,8 +39,8 @@ static void forceInitialization() { static ResourceProvider instance() { if (DefaultResources.RESOURCE_PROVIDER == null) { Services.PLATFORM.extractResources(); - DefaultResources.cleanupExtraction(); DefaultResources.RESOURCE_PROVIDER = DefaultResources.assembleResourceProvider(); + DefaultResources.cleanupExtraction(); } return DefaultResources.RESOURCE_PROVIDER; } diff --git a/Common/src/main/java/dev/lukebemish/defaultresources/impl/Config.java b/Common/src/main/java/dev/lukebemish/defaultresources/impl/Config.java index efef137..4b6cc67 100644 --- a/Common/src/main/java/dev/lukebemish/defaultresources/impl/Config.java +++ b/Common/src/main/java/dev/lukebemish/defaultresources/impl/Config.java @@ -60,7 +60,7 @@ private static Config readFromConfig() { ModMetaFile metaFile = ModMetaFile.CODEC.parse(JsonOps.INSTANCE, object).getOrThrow(false, e -> { }); if (!map.containsKey(modId)) { - map.put(modId, metaFile.extractsByDefault() ? ExtractionState.EXTRACT : ExtractionState.UNEXTRACTED); + map.put(modId, metaFile.extract() ? ExtractionState.EXTRACT : ExtractionState.UNEXTRACTED); } } catch (IOException | RuntimeException e) { DefaultResources.LOGGER.warn("We thought there was a readable {} for mod {}, but we got an error when reading it!", @@ -162,17 +162,10 @@ public void save() { } enum ExtractionState implements StringRepresentable { - UNEXTRACTED(false, false), - EXTRACT(true, true), - EXTRACTED(true, false); - - public final boolean extractIfMissing; - public final boolean extractRegardless; - - ExtractionState(boolean extractIfMissing, boolean extractRegardless) { - this.extractIfMissing = extractIfMissing; - this.extractRegardless = extractRegardless; - } + UNEXTRACTED, + EXTRACT, + EXTRACTED, + OUTDATED; @Override public @NotNull String getSerializedName() { diff --git a/Common/src/main/java/dev/lukebemish/defaultresources/impl/DefaultResources.java b/Common/src/main/java/dev/lukebemish/defaultresources/impl/DefaultResources.java index 692c279..2c3c8d2 100644 --- a/Common/src/main/java/dev/lukebemish/defaultresources/impl/DefaultResources.java +++ b/Common/src/main/java/dev/lukebemish/defaultresources/impl/DefaultResources.java @@ -10,6 +10,7 @@ import com.google.gson.JsonObject; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.JsonOps; +import dev.lukebemish.defaultresources.api.OutdatedResourcesListener; import dev.lukebemish.defaultresources.api.ResourceProvider; import net.minecraft.server.packs.PackResources; import net.minecraft.server.packs.PackType; @@ -17,6 +18,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; import java.io.IOException; @@ -28,25 +30,46 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; +import java.util.zip.Adler32; +import java.util.zip.Checksum; public class DefaultResources { public static final String MOD_ID = "defaultresources"; public static final Logger LOGGER = LogManager.getLogger(MOD_ID); + private static final int BUFFER_SIZE = 1024; + public static final Map> OUTDATED_TARGETS = new ConcurrentHashMap<>(); + public static final Map> MOD_TARGETS = new ConcurrentHashMap<>(); + private static final Map> OUTDATED_RESOURCES_LISTENERS = new ConcurrentHashMap<>(); public static final String META_FILE_PATH = DefaultResources.MOD_ID + ".meta.json"; + public static final String CHECK_FILE_PATH = "." + DefaultResources.MOD_ID; public static final Gson GSON = new GsonBuilder().setLenient().setPrettyPrinting().create(); public static ResourceProvider RESOURCE_PROVIDER; - private static final List QUEUED_PROVIDERS = new ArrayList<>(); - private static final Map>> QUEUED_RESOURCES = new HashMap<>(); + private static final Map QUEUED_PROVIDERS = new ConcurrentHashMap<>(); + private static final Map>> QUEUED_RESOURCES = new ConcurrentHashMap<>(); - public static ResourceProvider assembleResourceProvider() { - List providers = new ArrayList<>(QUEUED_PROVIDERS); + public static void addListener(String modId, OutdatedResourcesListener listener) { + OUTDATED_RESOURCES_LISTENERS.computeIfAbsent(modId, s -> new ArrayList<>()).add(listener); + } + + public synchronized static void delegate(Runnable ifInitialized, Runnable ifUninitialized) { + if (RESOURCE_PROVIDER == null) { + ifInitialized.run(); + } else { + ifUninitialized.run(); + } + } + + public synchronized static ResourceProvider assembleResourceProvider() { + List providers = new ArrayList<>(); + QUEUED_PROVIDERS.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(Map.Entry::getValue).forEach(providers::add); try (var paths = Files.list(Services.PLATFORM.getGlobalFolder())) { paths.forEach(path -> { if (Files.isDirectory(path)) { @@ -74,63 +97,183 @@ public static ResourceProvider assembleResourceProvider() { return new GroupedResourceProvider(providers); } - public static void forMod(Path configDir, Function inJarPathGetter, String modId) { + public static void forMod(Function inJarPathGetter, String modId) { Path defaultResourcesMeta = inJarPathGetter.apply(META_FILE_PATH); + ModMetaFile meta; if (Files.exists(defaultResourcesMeta)) { try (InputStream is = Files.newInputStream(defaultResourcesMeta)) { JsonObject obj = GSON.fromJson(new BufferedReader(new InputStreamReader(is)), JsonObject.class); - ModMetaFile meta = ModMetaFile.CODEC.parse(JsonOps.INSTANCE, obj).getOrThrow(false, e -> { + meta = ModMetaFile.CODEC.parse(JsonOps.INSTANCE, obj).getOrThrow(false, e -> { }); - Path defaultResources = inJarPathGetter.apply(meta.resourcesPath()); - - if (Files.exists(defaultResources)) { - Config.ExtractionState extractionState = Config.INSTANCE.get().extract().getOrDefault(modId, Config.ExtractionState.UNEXTRACTED); - if (extractionState == Config.ExtractionState.UNEXTRACTED) { - QUEUED_PROVIDERS.add(new PathResourceProvider(defaultResources)); - QUEUED_RESOURCES.put("__extracted_" + modId, (s, type) -> { - if (!Files.exists(defaultResources.resolve(type.getDirectory()))) return null; - return () -> new AutoMetadataFolderPackResources(s, type, defaultResources); - }); - } else if ((meta.markerPath().isPresent() && !Files.exists(configDir.resolve(meta.markerPath().get())) && extractionState.extractIfMissing) || extractionState.extractRegardless) { - Config.INSTANCE.get().extract().put(modId, Config.ExtractionState.EXTRACTED); - if (!meta.zip()) { - Path outPath = Services.PLATFORM.getGlobalFolder().resolve(modId); - if (!Files.exists(outPath)) - copyResources(defaultResources, outPath); - } else { + } catch (IOException | RuntimeException e) { + DefaultResources.LOGGER.error("Could not read meta file for mod {}", modId, e); + return; + } + } else { + try { + meta = ModMetaFile.CODEC.parse(JsonOps.INSTANCE, new JsonObject()).getOrThrow(false, e -> {}); + } catch (RuntimeException e) { + DefaultResources.LOGGER.error("Could not parse default meta file", e); + return; + } + } + Path defaultResources = inJarPathGetter.apply(meta.resourcesPath()); + + try { + if (Files.exists(defaultResources)) { + MOD_TARGETS.put(modId, meta.dataVersion()); + var defaultExtraction = meta.extract() ? Config.ExtractionState.EXTRACT : Config.ExtractionState.UNEXTRACTED; + Config.ExtractionState extractionState = Config.INSTANCE.get().extract().getOrDefault(modId, defaultExtraction); + if (extractionState == Config.ExtractionState.OUTDATED) { + extractionState = defaultExtraction; + } + if (!Config.INSTANCE.get().extract().containsKey(modId)) { + Config.INSTANCE.get().extract().put(modId, defaultExtraction); + } + if (extractionState == Config.ExtractionState.UNEXTRACTED) { + QUEUED_PROVIDERS.put(modId, new PathResourceProvider(defaultResources)); + QUEUED_RESOURCES.put("__extracted_" + modId, (s, type) -> { + if (!Files.exists(defaultResources.resolve(type.getDirectory()))) return null; + return () -> new AutoMetadataFolderPackResources(s, type, defaultResources); + }); + } else if (extractionState == Config.ExtractionState.EXTRACT) { + Config.INSTANCE.get().extract().put(modId, meta.extract() ? Config.ExtractionState.EXTRACT : Config.ExtractionState.EXTRACTED); + if (!meta.zip()) { + Path outPath = Services.PLATFORM.getGlobalFolder().resolve(modId); + String checksum = shouldCopy(defaultResources, outPath, Files.exists(outPath), modId, meta); + if (checksum != null) { + copyResources(defaultResources, outPath, checksum, meta.dataVersion().orElse(null)); + } + } else { + Path zipPath = Services.PLATFORM.getGlobalFolder().resolve(modId + ".zip"); + boolean zipExists = Files.exists(zipPath); + String checksum; + try (FileSystem zipFs = FileSystems.newFileSystem( + URI.create("jar:" + zipPath.toAbsolutePath().toUri()), + Collections.singletonMap("create", "true"))) { + Path outPath = zipFs.getPath("/"); + checksum = shouldCopy(defaultResources, outPath, zipExists, modId, meta); + if (checksum != null && !zipExists) { + copyResources(defaultResources, outPath, checksum, meta.dataVersion().orElse(null)); + } + } + if (checksum != null && zipExists) { + Files.delete(zipPath); try (FileSystem zipFs = FileSystems.newFileSystem( - URI.create("jar:" + Services.PLATFORM.getGlobalFolder().resolve(modId + ".zip").toAbsolutePath().toUri()), + URI.create("jar:" + zipPath.toAbsolutePath().toUri()), Collections.singletonMap("create", "true"))) { Path outPath = zipFs.getPath("/"); - copyResources(defaultResources, outPath); + copyResources(defaultResources, outPath, checksum, meta.dataVersion().orElse(null)); } } - if (meta.createsMarker() && meta.markerPath().isPresent() && !Files.exists(configDir.resolve(meta.markerPath().get()))) { - try { - Path markerPath = configDir.resolve(meta.markerPath().get()); - String comment; - if (meta.markerPath().get().endsWith(".json5") || meta.markerPath().get().endsWith(".json")) - comment = "// "; - else if (meta.markerPath().get().endsWith(".toml")) - comment = "# "; - else - comment = ""; - Files.writeString(markerPath, comment + "This is a marker file created by " + modId + ". If the mod is marked as already extracted, default resources will not be re-extracted while this file exists.\n"); - } catch (IOException e) { - LOGGER.error("Issues writing marker file at {} for mod {}: ", meta.markerPath(), modId, e); + } + } + } + } catch (IOException | RuntimeException e) { + DefaultResources.LOGGER.error("Could not handle default resources for mod {}", modId, e); + } + } + + private static void couldNotUpdate(String modId, Path outPath, ModMetaFile meta) { + String oldDataVersion; + try { + oldDataVersion = dataVersion(outPath); + } catch (IOException e) { + DefaultResources.LOGGER.error("Could not read old data version for mod {}", modId, e); + oldDataVersion = null; + } + DefaultResources.LOGGER.error("Could not extract default resources for mod {} (data version {} to version {}) because they are already extracted and have been changed on disk", modId, oldDataVersion, meta.dataVersion().orElse(null)); + OUTDATED_TARGETS.put(modId, Optional.ofNullable(oldDataVersion)); + Config.INSTANCE.get().extract().put(modId, Config.ExtractionState.OUTDATED); + } + + private static @Nullable String shouldCopy(Path defaultResources, Path outPath, boolean alreadyExists, String modId, ModMetaFile meta) { + try { + if (alreadyExists) { + Path checksumPath = outPath.resolve(CHECK_FILE_PATH); + String oldChecksum; + String oldVersion; + if (Files.exists(checksumPath)) { + var parts = Files.readString(checksumPath).split(":", 2); + oldChecksum = parts[0]; + if (parts.length == 2) { + oldVersion = parts[1]; + } else { + oldVersion = null; + } + } else { + couldNotUpdate(modId, outPath, meta); + return null; + } + String newChecksum = checkPath(defaultResources); + String newVersion = meta.dataVersion().orElse(null); + if (newChecksum.equals(oldChecksum) && Objects.equals(newVersion, oldVersion)) { + // The resources to extract have not changed, but the extracted resources have been modified + return null; + } else { + // The resources to extract differ from the saved checksum + String newExtractedChecksum = checkPath(outPath); + if (newExtractedChecksum.equals(oldChecksum)) { + // The calculated extracted checksum does not differ from the saved checksum + return newChecksum; + } + } + } else { + return checkPath(defaultResources); + } + } catch (IOException e) { + DefaultResources.LOGGER.error("Error checking compatibility of resources from {} targeted at {}", defaultResources, outPath, e); + } + couldNotUpdate(modId, outPath, meta); + return null; + } + + private static @Nullable String dataVersion(Path path) throws IOException { + Path checksumPath = path.resolve(CHECK_FILE_PATH); + if (Files.exists(checksumPath)) { + var parts = Files.readString(checksumPath).split(":", 2); + if (parts.length == 2) { + return parts[1]; + } + } + return null; + } + + private static String checkPath(Path path) throws IOException { + StringBuilder newChecksum = new StringBuilder(); + try (var walk = Files.walk(path)) { + walk.sorted(Comparator.comparing(p -> path.relativize(p).toString())).forEach(p -> { + try { + if (!Files.isDirectory(p) && !(path.relativize(p).getNameCount() == 1 && p.endsWith(CHECK_FILE_PATH))) { + Checksum check = new Adler32(); + try (var is = Files.newInputStream(p)) { + byte[] buffer = new byte[BUFFER_SIZE]; + int length; + while ((length = is.read(buffer)) > 0) { + check.update(buffer, 0, length); } } + newChecksum.append(encode((int) check.getValue())); } + } catch (IOException e) { + DefaultResources.LOGGER.error("Error calculating checksum at {}", p, e); } - } catch (IOException | RuntimeException e) { - DefaultResources.LOGGER.error("Could not read meta file for mod {}", modId, e); - } + }); + } + return newChecksum.toString(); + } + + private static CharSequence encode(int i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < 4; j++) { + sb.append((char) (((i >> (j * 4)) & 0xF) + 97)); } + return sb; } - private static void copyResources(Path defaultResources, Path outPath) { + private static void copyResources(Path defaultResources, Path outPath, String checksum, @Nullable String dataVersion) { try (var walk = Files.walk(defaultResources)) { - walk.forEach(p -> { + walk.sorted(Comparator.comparing(p -> p.relativize(defaultResources).toString())).forEach(p -> { try { if (!Files.isDirectory(p)) { String rel = defaultResources.relativize(p).toString(); @@ -139,16 +282,24 @@ private static void copyResources(Path defaultResources, Path outPath) { Files.copy(p, newPath); } } catch (IOException e) { - DefaultResources.LOGGER.error(e); + DefaultResources.LOGGER.error("Error checking compatibility of resources from {} targeted at {}, for path {}", defaultResources, outPath, p, e); } }); + Path checksumPath = outPath.resolve(CHECK_FILE_PATH); + Files.writeString(checksumPath, checksum + (dataVersion == null ? "" : ":" + dataVersion)); } catch (IOException e) { - DefaultResources.LOGGER.error(e); + DefaultResources.LOGGER.error("Error checking compatibility of resources from {} targeted at {}", defaultResources, outPath, e); } } public static void cleanupExtraction() { Config.INSTANCE.get().save(); + for (var entry : OUTDATED_TARGETS.entrySet()) { + String oldVersion = MOD_TARGETS.getOrDefault(entry.getKey(), Optional.empty()).orElse(null); + String newVersion = entry.getValue().orElse(null); + String modId = entry.getKey(); + OUTDATED_RESOURCES_LISTENERS.getOrDefault(modId, List.of()).forEach(listener -> listener.resourcesOutdated(oldVersion, newVersion)); + } } @NotNull diff --git a/Common/src/main/java/dev/lukebemish/defaultresources/impl/ModMetaFile.java b/Common/src/main/java/dev/lukebemish/defaultresources/impl/ModMetaFile.java index 76d68c6..2df6c15 100644 --- a/Common/src/main/java/dev/lukebemish/defaultresources/impl/ModMetaFile.java +++ b/Common/src/main/java/dev/lukebemish/defaultresources/impl/ModMetaFile.java @@ -7,17 +7,15 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.lukebemish.defaultresources.api.ResourceProvider; import java.util.Optional; -public record ModMetaFile(Optional markerPath, String resourcesPath, boolean zip, boolean createsMarker, - boolean extractsByDefault) { +public record ModMetaFile(String resourcesPath, boolean zip, + boolean extract, Optional dataVersion) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.STRING.optionalFieldOf("marker_path").forGetter(ModMetaFile::markerPath), Codec.STRING.optionalFieldOf("resources_path", DefaultResources.MOD_ID).forGetter(ModMetaFile::resourcesPath), Codec.BOOL.optionalFieldOf("zip", true).forGetter(ModMetaFile::zip), - Codec.BOOL.optionalFieldOf("creates_marker", false).forGetter(ModMetaFile::createsMarker), - Codec.BOOL.optionalFieldOf("extracts_by_default", false).forGetter(ModMetaFile::extractsByDefault) + Codec.BOOL.optionalFieldOf("extract", true).forGetter(ModMetaFile::extract), + Codec.STRING.optionalFieldOf("data_version").forGetter(ModMetaFile::dataVersion) ).apply(instance, ModMetaFile::new)); } diff --git a/Forge/src/main/java/dev/lukebemish/defaultresources/impl/forge/DefaultResourcesForge.java b/Forge/src/main/java/dev/lukebemish/defaultresources/impl/forge/DefaultResourcesForge.java index ea6d87a..12167ed 100644 --- a/Forge/src/main/java/dev/lukebemish/defaultresources/impl/forge/DefaultResourcesForge.java +++ b/Forge/src/main/java/dev/lukebemish/defaultresources/impl/forge/DefaultResourcesForge.java @@ -31,7 +31,7 @@ public class DefaultResourcesForge { public DefaultResourcesForge() { - ResourceProvider.forceInitialization(); + ResourceProvider.instance(); IEventBus modbus = FMLJavaModLoadingContext.get().getModEventBus(); modbus.register(this); } diff --git a/Forge/src/main/java/dev/lukebemish/defaultresources/impl/forge/PlatformImpl.java b/Forge/src/main/java/dev/lukebemish/defaultresources/impl/forge/PlatformImpl.java index 4181d05..3f63a3b 100644 --- a/Forge/src/main/java/dev/lukebemish/defaultresources/impl/forge/PlatformImpl.java +++ b/Forge/src/main/java/dev/lukebemish/defaultresources/impl/forge/PlatformImpl.java @@ -41,8 +41,9 @@ public void extractResources() { } FMLLoader.getLoadingModList().getModFiles().stream().flatMap(f -> f.getMods().stream()) .filter(mod -> !(mod.getModId().equals("forge") || mod.getModId().equals("minecraft"))) + .parallel() .forEach(mod -> - DefaultResources.forMod(FMLPaths.CONFIGDIR.get(), mod.getOwningFile().getFile()::findResource, mod.getModId())); + DefaultResources.forMod(mod.getOwningFile().getFile()::findResource, mod.getModId())); } @Override diff --git a/Quilt/src/main/java/dev/lukebemish/defaultresources/impl/quilt/DefaultResourcesQuilt.java b/Quilt/src/main/java/dev/lukebemish/defaultresources/impl/quilt/DefaultResourcesQuilt.java index 2d7cf5d..010031a 100644 --- a/Quilt/src/main/java/dev/lukebemish/defaultresources/impl/quilt/DefaultResourcesQuilt.java +++ b/Quilt/src/main/java/dev/lukebemish/defaultresources/impl/quilt/DefaultResourcesQuilt.java @@ -22,7 +22,7 @@ public class DefaultResourcesQuilt implements ModInitializer { @Override public void onInitialize(ModContainer mod) { - ResourceProvider.forceInitialization(); + ResourceProvider.instance(); addPackResources(PackType.SERVER_DATA); } diff --git a/Quilt/src/main/java/dev/lukebemish/defaultresources/impl/quilt/PlatformImpl.java b/Quilt/src/main/java/dev/lukebemish/defaultresources/impl/quilt/PlatformImpl.java index f315d16..959846a 100644 --- a/Quilt/src/main/java/dev/lukebemish/defaultresources/impl/quilt/PlatformImpl.java +++ b/Quilt/src/main/java/dev/lukebemish/defaultresources/impl/quilt/PlatformImpl.java @@ -37,10 +37,10 @@ public void extractResources() { } catch (IOException e) { DefaultResources.LOGGER.error(e); } - QuiltLoader.getAllMods().forEach(mod -> { + QuiltLoader.getAllMods().parallelStream().forEach(mod -> { String modid = mod.metadata().id(); if (!modid.equals("minecraft")) { - DefaultResources.forMod(QuiltLoader.getConfigDir(), mod.rootPath().toAbsolutePath()::resolve, modid); + DefaultResources.forMod(mod.rootPath().toAbsolutePath()::resolve, modid); } }); } diff --git a/version.properties b/version.properties index a933109..287b064 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ #Mon Dec 04 16:44:59 UTC 2023 -version=2.1.0 +version=2.2.0