diff --git a/src/main/java/me/melontini/andromeda/base/Bootstrap.java b/src/main/java/me/melontini/andromeda/base/Bootstrap.java index 692a730c..1bcb4e73 100644 --- a/src/main/java/me/melontini/andromeda/base/Bootstrap.java +++ b/src/main/java/me/melontini/andromeda/base/Bootstrap.java @@ -6,13 +6,13 @@ import lombok.CustomLog; import me.melontini.andromeda.base.config.Config; import me.melontini.andromeda.common.Andromeda; import me.melontini.andromeda.common.client.AndromedaClient; -import me.melontini.andromeda.util.AndromedaLog; import me.melontini.andromeda.util.ClassPath; import me.melontini.andromeda.util.CommonValues; import me.melontini.andromeda.util.Debug; import me.melontini.andromeda.util.exceptions.AndromedaException; import me.melontini.andromeda.util.mixin.AndromedaMixins; import me.melontini.dark_matter.api.base.util.EntrypointRunner; +import me.melontini.dark_matter.api.base.util.classes.ThrowingRunnable; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.loader.api.FabricLoader; @@ -23,8 +23,8 @@ import org.spongepowered.asm.mixin.Mixins; import java.io.IOException; import java.nio.file.Files; -import java.nio.file.Path; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; @CustomLog public class Bootstrap { @@ -55,33 +55,28 @@ public class Bootstrap { } public static void onPreLaunch() { - Config.load(); LOGGER.info("Andromeda({}) on {}({})", CommonValues.version(), CommonValues.platform(), CommonValues.platform().version()); - JsonObject oldCfg = null; - var oldCfgPath = FabricLoader.getInstance().getConfigDir().resolve("andromeda.json"); - if (Files.exists(oldCfgPath)) { - try { - oldCfg = JsonParser.parseReader(Files.newBufferedReader(oldCfgPath)).getAsJsonObject(); - } catch (IOException e) { - AndromedaLog.error("Couldn't read pre-1.0.0 config!"); - } - } - if (CommonValues.platform() == CommonValues.Platform.CONNECTOR) { LOGGER.warn("Andromeda may not work on Connector! (If #557 is open on Connector's GitHub)"); } - Path newCfg = FabricLoader.getInstance().getConfigDir().resolve("andromeda.json"); - if (Files.exists(newCfg) && !Files.exists(CommonValues.configPath())) { - try { - Files.createDirectories(CommonValues.configPath().getParent()); - Files.move(newCfg, CommonValues.configPath()); - } catch (IOException e) { - AndromedaLog.error("Couldn't rename old m-tweaks config!", e); + AtomicReference oldCfg = new AtomicReference<>(); + var oldCfgPath = FabricLoader.getInstance().getConfigDir().resolve("andromeda.json"); + if (Files.exists(oldCfgPath)) { + if (!Files.exists(CommonValues.configPath())) { + wrapIO(() -> { + oldCfg.set(JsonParser.parseReader(Files.newBufferedReader(oldCfgPath)).getAsJsonObject()); + Files.createDirectories(CommonValues.configPath().getParent()); + Files.move(oldCfgPath, CommonValues.configPath()); + }, "Couldn't rename pre-1.0.0 config!"); + } else { + wrapIO(() -> Files.delete(oldCfgPath), "Couldn't delete pre-1.0.0 config!"); } } + Config.load(); + List> list = new ArrayList<>(Arrays.asList(ServiceLoader.load(Module.class) .stream().map(ServiceLoader.Provider::get).toArray(Module[]::new))); @@ -95,7 +90,7 @@ public class Bootstrap { ModuleManager m; try { - m = new ModuleManager(list, oldCfg); + m = new ModuleManager(list, oldCfg.get()); } catch (Throwable t) { throw new AndromedaException("Failed to initialize ModuleManager!!!", t); } @@ -108,6 +103,14 @@ public class Bootstrap { for (Module module : m.loaded()) { module.onPreLaunch(); } } + static void wrapIO(ThrowingRunnable runnable, String msg) { + try { + runnable.run(); + } catch (IOException e) { + LOGGER.error(msg, e); + } + } + public static ClassPath getModuleClassPath() { return AndromedaMixins.getClassPath(); } diff --git a/src/main/java/me/melontini/andromeda/base/ModuleManager.java b/src/main/java/me/melontini/andromeda/base/ModuleManager.java index 6745842e..767e66f0 100644 --- a/src/main/java/me/melontini/andromeda/base/ModuleManager.java +++ b/src/main/java/me/melontini/andromeda/base/ModuleManager.java @@ -38,6 +38,8 @@ public class ModuleManager { final Map> mixinConfigs = new HashMap<>(); ModuleManager(List> discovered, @Nullable JsonObject oldCfg) { + if (Bootstrap.INSTANCE != null) + throw new IllegalStateException("ModuleManager already initialized!"); Bootstrap.INSTANCE = this; Set ids = new HashSet<>(); @@ -149,7 +151,7 @@ public class ModuleManager { public void cleanConfigs(Path root) { Set paths = collectPaths(root.getParent()); if (Files.exists(root)) { - Utilities.runUnchecked(() -> Files.walkFileTree(root, new SimpleFileVisitor<>() { + Bootstrap.wrapIO(() -> Files.walkFileTree(root, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (!paths.contains(file)) { @@ -158,7 +160,7 @@ public class ModuleManager { } return super.visitFile(file, attrs); } - })); + }), "Failed to clean up configs!"); } } -- 2.51.2 From 85a9548852e779322d04e37c8b92f539414ceb6e Mon Sep 17 00:00:00 2001 From: melontini <104443436+melontini@users.noreply.github.com> Date: Sun, 24 Dec 2023 12:31:37 +0700 Subject: [PATCH 2/9] Create configs for loaded modules only. --- .../me/melontini/andromeda/base/ModuleManager.java | 14 +++++++------- .../me/melontini/andromeda/common/Andromeda.java | 7 +++++-- .../andromeda/common/config/DataConfigs.java | 4 ++-- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/java/me/melontini/andromeda/base/ModuleManager.java b/src/main/java/me/melontini/andromeda/base/ModuleManager.java index 767e66f0..83dc633d 100644 --- a/src/main/java/me/melontini/andromeda/base/ModuleManager.java +++ b/src/main/java/me/melontini/andromeda/base/ModuleManager.java @@ -81,7 +81,7 @@ public class ModuleManager { this.moduleNames = sorted.stream().filter(Module::enabled) .collect(ImmutableMap.toImmutableMap(m -> m.meta().id(), m -> m)); - cleanConfigs(FabricLoader.getInstance().getConfigDir().resolve("andromeda")); + cleanConfigs(FabricLoader.getInstance().getConfigDir().resolve("andromeda"), sorted); } private void fixScopes(List> list) { @@ -148,15 +148,15 @@ public class ModuleManager { return !Object.class.equals(m.getSuperclass()) ? getConfigClass(m.getSuperclass()) : BasicConfig.class; } - public void cleanConfigs(Path root) { - Set paths = collectPaths(root.getParent()); + public void cleanConfigs(Path root, Collection> modules) { + Set paths = collectPaths(root.getParent(), modules); if (Files.exists(root)) { Bootstrap.wrapIO(() -> Files.walkFileTree(root, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (!paths.contains(file)) { + if (file.toString().endsWith(".json") && !Files.isHidden(file) && !paths.contains(file)) { Files.delete(file); - LOGGER.info("Removed {} as it doesn't belong to any module!", file); + LOGGER.info("Removed {} as it doesn't belong to any module!", FabricLoader.getInstance().getGameDir().relativize(file)); } return super.visitFile(file, attrs); } @@ -164,13 +164,13 @@ public class ModuleManager { } } - private Set collectPaths(Path root) { + private Set collectPaths(Path root, Collection> modules) { Set paths = new HashSet<>(); paths.add(root.resolve("andromeda/mod.json")); paths.add(root.resolve("andromeda/debug.json")); - all().forEach(module -> paths.add(module.manager().resolve(root))); + modules.forEach(module -> paths.add(module.manager().resolve(root))); return paths; } diff --git a/src/main/java/me/melontini/andromeda/common/Andromeda.java b/src/main/java/me/melontini/andromeda/common/Andromeda.java index b9540b66..cc8165d1 100644 --- a/src/main/java/me/melontini/andromeda/common/Andromeda.java +++ b/src/main/java/me/melontini/andromeda/common/Andromeda.java @@ -2,6 +2,7 @@ package me.melontini.andromeda.common;//common between modules, not environments import me.melontini.andromeda.base.Environment; import me.melontini.andromeda.base.ModuleManager; +import me.melontini.andromeda.base.config.BasicConfig; import me.melontini.andromeda.base.config.Config; import me.melontini.andromeda.common.config.DataConfigs; import me.melontini.andromeda.common.registries.Common; @@ -45,8 +46,10 @@ public class Andromeda { }); ServerLifecycleEvents.SERVER_STARTED.register(server -> { - server.getWorlds().forEach(world -> ModuleManager.get().cleanConfigs(server.session.getWorldDirectory(world.getRegistryKey()).resolve("world_config/andromeda"))); - ModuleManager.get().cleanConfigs(server.session.getDirectory(WorldSavePath.ROOT).resolve("config/andromeda")); + var list = ModuleManager.get().loaded().stream().filter(module -> module.config().scope == BasicConfig.Scope.DIMENSION).toList(); + server.getWorlds().forEach(world -> ModuleManager.get().cleanConfigs(server.session.getWorldDirectory(world.getRegistryKey()).resolve("world_config/andromeda"), list)); + ModuleManager.get().cleanConfigs(server.session.getDirectory(WorldSavePath.ROOT).resolve("config/andromeda"), + ModuleManager.get().loaded().stream().filter(module -> module.config().scope == BasicConfig.Scope.WORLD).toList()); }); ServerLifecycleEvents.END_DATA_PACK_RELOAD.register((server, resourceManager, success) -> { diff --git a/src/main/java/me/melontini/andromeda/common/config/DataConfigs.java b/src/main/java/me/melontini/andromeda/common/config/DataConfigs.java index f70d586e..7fe3c7e9 100644 --- a/src/main/java/me/melontini/andromeda/common/config/DataConfigs.java +++ b/src/main/java/me/melontini/andromeda/common/config/DataConfigs.java @@ -83,7 +83,7 @@ public class DataConfigs extends JsonDataLoader { ScopedConfigs.get(world); Set> futures = new HashSet<>(); - for (Module module : ModuleManager.get().all()) { + for (Module module : ModuleManager.get().loaded()) { if (module.meta().environment() == Environment.CLIENT) continue; //Those are always GLOBAL. switch (module.config().scope) { @@ -104,7 +104,7 @@ public class DataConfigs extends JsonDataLoader { server.getWorlds().forEach(ScopedConfigs::get); Set> futures = new HashSet<>(); - for (Module module : ModuleManager.get().all()) { + for (Module module : ModuleManager.get().loaded()) { if (module.meta().environment() == Environment.CLIENT) continue; //Those are always GLOBAL. switch (module.config().scope) { -- 2.51.2 From bf14d5def8e98f323c75c77101516e4d6ee3f0bf Mon Sep 17 00:00:00 2001 From: melontini <104443436+melontini@users.noreply.github.com> Date: Sun, 24 Dec 2023 13:10:13 +0700 Subject: [PATCH 3/9] Submit config tasks to workers. --- .../andromeda/common/config/DataConfigs.java | 38 ++++++++++--------- .../common/config/ScopedConfigs.java | 6 ++- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/main/java/me/melontini/andromeda/common/config/DataConfigs.java b/src/main/java/me/melontini/andromeda/common/config/DataConfigs.java index 7fe3c7e9..72d2d3ed 100644 --- a/src/main/java/me/melontini/andromeda/common/config/DataConfigs.java +++ b/src/main/java/me/melontini/andromeda/common/config/DataConfigs.java @@ -15,13 +15,13 @@ import net.minecraft.resource.ResourceManager; import net.minecraft.server.MinecraftServer; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; +import net.minecraft.util.Util; import net.minecraft.util.profiler.Profiler; import net.minecraft.world.World; import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.Executor; public class DataConfigs extends JsonDataLoader { @@ -36,7 +36,7 @@ public class DataConfigs extends JsonDataLoader { return CompletableFuture.supplyAsync(() -> { Map, Set, ? extends BasicConfig>>>>> configs = new HashMap<>(); data.forEach((id, object) -> { - var m = ModuleManager.get().getModule(id.getPath()).orElseThrow(() -> new IllegalStateException("Invalid module path '%s'!".formatted(id.getPath()))); + var m = ModuleManager.get().getModule(id.getPath()).orElseThrow(() -> new IllegalStateException("Invalid module path '%s'! The module must be enabled!".formatted(id.getPath()))); if (m.config().scope != BasicConfig.Scope.DIMENSION) throw new IllegalStateException("Invalid module scope `%s` for '%s'! Must be '%s'".formatted(m.config().scope, m.meta().id(), BasicConfig.Scope.DIMENSION)); @@ -44,17 +44,21 @@ public class DataConfigs extends JsonDataLoader { object.entrySet().forEach(entry -> { var map = configs.computeIfAbsent(Identifier.tryParse(entry.getKey()), string -> new HashMap<>()); map.computeIfAbsent(m, module -> new LinkedHashSet<>()).add(CompletableFuture.supplyAsync(() -> { - var instance = this.gson.fromJson(entry.getValue(), cls); - Set config = new HashSet<>(); - entry.getValue().getAsJsonObject().entrySet().forEach(entry2 -> { - try { - config.add(cls.getField(entry2.getKey())); - } catch (NoSuchFieldException e) { - throw new CompletionException("Failed to load config data for module '%s'".formatted(m.meta().id()), e); - } - }); - return Tuple.of(config, instance); - }, executor)); + try { + var instance = this.gson.fromJson(entry.getValue(), cls); + Set config = new HashSet<>(); + entry.getValue().getAsJsonObject().entrySet().forEach(entry2 -> { + try { + config.add(cls.getField(entry2.getKey())); + } catch (NoSuchFieldException e) { + throw new RuntimeException("Failed to load config data for module '%s'".formatted(m.meta().id()), e); + } + }); + return Tuple.of(config, instance); + } catch (Exception e) { + throw new RuntimeException("Failed to load config data for module '%s'".formatted(m.meta().id()), e); + } + }, Util.getMainWorkerExecutor())); }); }); return CompletableFuture.allOf(configs.values().stream().flatMap(map -> map.values().stream()) @@ -90,9 +94,9 @@ public class DataConfigs extends JsonDataLoader { case WORLD -> futures.add(CompletableFuture.runAsync(() -> { if (world.getRegistryKey().equals(World.OVERWORLD)) ScopedConfigs.prepareForWorld(world, module, ScopedConfigs.getPath(world, module)); - })); + }, Util.getMainWorkerExecutor())); case DIMENSION -> - CompletableFuture.runAsync(() -> ScopedConfigs.prepareForWorld(world, module, ScopedConfigs.getPath(world, module))); + CompletableFuture.runAsync(() -> ScopedConfigs.prepareForWorld(world, module, ScopedConfigs.getPath(world, module)), Util.getMainWorkerExecutor()); } } CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); @@ -111,12 +115,12 @@ public class DataConfigs extends JsonDataLoader { case WORLD -> futures.add(CompletableFuture.runAsync(() -> { ServerWorld world = server.getWorld(World.OVERWORLD); ScopedConfigs.prepareForWorld(world, module, ScopedConfigs.getPath(world, module)); - })); + }, Util.getMainWorkerExecutor())); case DIMENSION -> CompletableFuture.runAsync(() -> { for (ServerWorld world : server.getWorlds()) { ScopedConfigs.prepareForWorld(world, module, ScopedConfigs.getPath(world, module)); } - }); + }, Util.getMainWorkerExecutor()); } } CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); diff --git a/src/main/java/me/melontini/andromeda/common/config/ScopedConfigs.java b/src/main/java/me/melontini/andromeda/common/config/ScopedConfigs.java index e0f76a49..45e771d3 100644 --- a/src/main/java/me/melontini/andromeda/common/config/ScopedConfigs.java +++ b/src/main/java/me/melontini/andromeda/common/config/ScopedConfigs.java @@ -118,8 +118,10 @@ public class ScopedConfigs { return (T) configs.get(module); } - public synchronized void addConfig(Module module, BasicConfig config) { - configs.put(module, config); + public void addConfig(Module module, BasicConfig config) { + synchronized (this.configs) { + this.configs.put(module, config); + } } @Override -- 2.51.2 From 3186a97987509df4c01f889a35ff5e0f68580087 Mon Sep 17 00:00:00 2001 From: melontini <104443436+melontini@users.noreply.github.com> Date: Sun, 24 Dec 2023 13:35:26 +0700 Subject: [PATCH 4/9] some fields can be final. --- .../me/melontini/andromeda/util/ClassPath.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/me/melontini/andromeda/util/ClassPath.java b/src/main/java/me/melontini/andromeda/util/ClassPath.java index 2dd3a642..e4590cd5 100644 --- a/src/main/java/me/melontini/andromeda/util/ClassPath.java +++ b/src/main/java/me/melontini/andromeda/util/ClassPath.java @@ -13,8 +13,8 @@ import java.util.*; public final class ClassPath { - Set scanned = new HashSet<>(); - Set infos = new TreeSet<>(Comparator.comparing(info -> info.name)); + private final Set scanned = new HashSet<>(); + private final Set infos = new TreeSet<>(Comparator.comparing(info -> info.name)); private ClassPath() { @@ -48,7 +48,7 @@ public final class ClassPath { String s = pckg.replace('/', '.'); Set set = new TreeSet<>(Comparator.comparing(info -> info.name)); - for (Info info : infos) { + for (Info info : this.infos) { if (info.getName().startsWith(s)) set.add(info); } @@ -60,13 +60,17 @@ public final class ClassPath { Path path = Utilities.supplyUnchecked(() -> Path.of(url.toURI())); - if (scanned.contains(path)) return; + if (this.scanned.contains(path)) return; if (Files.isDirectory(path)) { scan(path); } else { scanJar(path); } + + synchronized (this.scanned) { + this.scanned.add(path); + } } @SneakyThrows @@ -90,7 +94,9 @@ public final class ClassPath { String name = path.relativize(file).toString(); if (name.endsWith(".class") && !name.contains("$")) { String clsName = name.substring(0, name.length() - ".class".length()).replace('\\', '.').replace('/', '.'); - ClassPath.this.infos.add(new Info(file, clsName)); + synchronized (ClassPath.this.infos) { + ClassPath.this.infos.add(new Info(file, clsName)); + } } return FileVisitResult.CONTINUE; } -- 2.51.2 From fb742a8ffe5ea0d89ad78d49137f6c53117b0d99 Mon Sep 17 00:00:00 2001 From: melontini <104443436+melontini@users.noreply.github.com> Date: Sun, 24 Dec 2023 14:05:44 +0700 Subject: [PATCH 5/9] Remove Connector notice. --- src/main/java/me/melontini/andromeda/base/Bootstrap.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/me/melontini/andromeda/base/Bootstrap.java b/src/main/java/me/melontini/andromeda/base/Bootstrap.java index 1bcb4e73..2f69982c 100644 --- a/src/main/java/me/melontini/andromeda/base/Bootstrap.java +++ b/src/main/java/me/melontini/andromeda/base/Bootstrap.java @@ -57,10 +57,6 @@ public class Bootstrap { public static void onPreLaunch() { LOGGER.info("Andromeda({}) on {}({})", CommonValues.version(), CommonValues.platform(), CommonValues.platform().version()); - if (CommonValues.platform() == CommonValues.Platform.CONNECTOR) { - LOGGER.warn("Andromeda may not work on Connector! (If #557 is open on Connector's GitHub)"); - } - AtomicReference oldCfg = new AtomicReference<>(); var oldCfgPath = FabricLoader.getInstance().getConfigDir().resolve("andromeda.json"); if (Files.exists(oldCfgPath)) { -- 2.51.2 From 1d641e5bdae38a2da4301ae05acba6dc94d343c7 Mon Sep 17 00:00:00 2001 From: melontini <104443436+melontini@users.noreply.github.com> Date: Sun, 24 Dec 2023 14:16:12 +0700 Subject: [PATCH 6/9] Allow skipping version tests. --- .../java/me/melontini/andromeda/base/Bootstrap.java | 4 ++-- .../gui/item_frame_tooltips/ItemFrameTooltips.java | 13 ++++++------- .../modules/gui/name_tooltips/NameTooltips.java | 13 ++++++------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/main/java/me/melontini/andromeda/base/Bootstrap.java b/src/main/java/me/melontini/andromeda/base/Bootstrap.java index 2f69982c..08fe30a3 100644 --- a/src/main/java/me/melontini/andromeda/base/Bootstrap.java +++ b/src/main/java/me/melontini/andromeda/base/Bootstrap.java @@ -111,9 +111,9 @@ public class Bootstrap { return AndromedaMixins.getClassPath(); } - public static boolean testModVersion(String modId, String predicate) { + public static boolean testModVersion(Module m, String modId, String predicate) { Optional mod = FabricLoader.getInstance().getModContainer(modId); - if (mod.isPresent()) { + if (mod.isPresent() && !Debug.skipIntegration(m.meta().id(), modId)) { try { VersionPredicate version = VersionPredicate.parse(predicate); return version.test(mod.get().getMetadata().getVersion()); diff --git a/src/main/java/me/melontini/andromeda/modules/gui/item_frame_tooltips/ItemFrameTooltips.java b/src/main/java/me/melontini/andromeda/modules/gui/item_frame_tooltips/ItemFrameTooltips.java index 194fc6f1..aa8c847d 100644 --- a/src/main/java/me/melontini/andromeda/modules/gui/item_frame_tooltips/ItemFrameTooltips.java +++ b/src/main/java/me/melontini/andromeda/modules/gui/item_frame_tooltips/ItemFrameTooltips.java @@ -9,27 +9,26 @@ import me.melontini.andromeda.common.client.config.FeatureBlockade; import me.melontini.dark_matter.api.base.config.ConfigManager; import me.melontini.dark_matter.api.minecraft.util.TextUtil; +import java.util.function.BooleanSupplier; + import static me.melontini.andromeda.base.Bootstrap.testModVersion; @OldConfigKey("itemFrameTooltips") @ModuleInfo(name = "item_frame_tooltips", category = "gui", environment = Environment.CLIENT) public class ItemFrameTooltips extends Module { + private final BooleanSupplier iceberg = () -> testModVersion(this, "minecraft", ">=1.20") && testModVersion(this, "iceberg", "<1.1.13"); + @Override public void onConfig(ConfigManager manager) { manager.onLoad(config -> { - if (testModVersion("minecraft", ">=1.20") && - testModVersion("iceberg", "<1.1.13")) { - config.enabled = false; - } + if (iceberg.getAsBoolean()) config.enabled = false; }); } @Override public void collectBlockades() { - FeatureBlockade.get().explain(this, "enabled", () -> - testModVersion("minecraft", ">=1.20") && - testModVersion("iceberg", "<1.1.13"), + FeatureBlockade.get().explain(this, "enabled", iceberg, TextUtil.translatable("andromeda.config.option_manager.reason.andromeda.iceberg")); } } diff --git a/src/main/java/me/melontini/andromeda/modules/gui/name_tooltips/NameTooltips.java b/src/main/java/me/melontini/andromeda/modules/gui/name_tooltips/NameTooltips.java index 54c8d4d4..a4edfff0 100644 --- a/src/main/java/me/melontini/andromeda/modules/gui/name_tooltips/NameTooltips.java +++ b/src/main/java/me/melontini/andromeda/modules/gui/name_tooltips/NameTooltips.java @@ -9,27 +9,26 @@ import me.melontini.andromeda.common.client.config.FeatureBlockade; import me.melontini.dark_matter.api.base.config.ConfigManager; import me.melontini.dark_matter.api.minecraft.util.TextUtil; +import java.util.function.BooleanSupplier; + import static me.melontini.andromeda.base.Bootstrap.testModVersion; @OldConfigKey("tooltipNotName") @ModuleInfo(name = "name_tooltips", category = "gui", environment = Environment.CLIENT) public class NameTooltips extends Module { + private final BooleanSupplier iceberg = () -> testModVersion(this, "minecraft", ">=1.20") && testModVersion(this, "iceberg", "<1.1.13"); + @Override public void onConfig(ConfigManager manager) { manager.onLoad(config -> { - if (testModVersion("minecraft", ">=1.20") && - testModVersion("iceberg", "<1.1.13")) { - config.enabled = false; - } + if (iceberg.getAsBoolean()) config.enabled = false; }); } @Override public void collectBlockades() { - FeatureBlockade.get().explain(this, "enabled", () -> - testModVersion("minecraft", ">=1.20") && - testModVersion("iceberg", "<1.1.13"), + FeatureBlockade.get().explain(this, "enabled", iceberg, TextUtil.translatable("andromeda.config.option_manager.reason.andromeda.iceberg")); } } -- 2.51.2 From 75479095ca4237f77d1c12c5b669b5449283d9fc Mon Sep 17 00:00:00 2001 From: melontini <104443436+melontini@users.noreply.github.com> Date: Sun, 24 Dec 2023 14:58:41 +0700 Subject: [PATCH 7/9] Simplify keepers --- .../andromeda/common/registries/Common.java | 3 +-- .../andromeda/common/registries/Keeper.java | 25 +++++++++++++------ .../better_fletching_table/Content.java | 2 +- .../modules/blocks/incubator/Content.java | 4 +-- .../modules/entities/boats/BoatEntities.java | 8 +++--- .../entities/minecarts/MinecartEntities.java | 6 ++--- .../modules/items/infinite_totem/Content.java | 2 +- .../modules/items/lockpick/Content.java | 2 +- .../mechanics/throwable_items/Content.java | 2 +- .../modules/misc/unknown/Content.java | 5 ++-- 10 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/main/java/me/melontini/andromeda/common/registries/Common.java b/src/main/java/me/melontini/andromeda/common/registries/Common.java index ce668923..31740559 100644 --- a/src/main/java/me/melontini/andromeda/common/registries/Common.java +++ b/src/main/java/me/melontini/andromeda/common/registries/Common.java @@ -54,8 +54,7 @@ public class Common { } public static > Keeper start(Supplier supplier) { - R builder = supplier.get(); - return new Keeper<>(() -> builder::build); + return new Keeper<>(() -> supplier.get().build()); } public static Identifier id(String path) { diff --git a/src/main/java/me/melontini/andromeda/common/registries/Keeper.java b/src/main/java/me/melontini/andromeda/common/registries/Keeper.java index 8c3d29d6..ac194edd 100644 --- a/src/main/java/me/melontini/andromeda/common/registries/Keeper.java +++ b/src/main/java/me/melontini/andromeda/common/registries/Keeper.java @@ -12,17 +12,19 @@ import java.util.function.Supplier; public class Keeper { - private Supplier> supplier; + private volatile Callable supplier; + private volatile boolean initialized; + private T value; private Field field; private final Set> consumers = new HashSet<>(); - public Keeper(Supplier> supplier) { + public Keeper(Callable supplier) { this.supplier = MakeSure.notNull(supplier); } - public static Keeper of(Supplier> supplier) { + public static Keeper of(Callable supplier) { return new Keeper<>(supplier); } @@ -48,11 +50,18 @@ public class Keeper { } void init(@Nullable Field f) throws Exception { - this.value = this.supplier.get().call(); - this.supplier = null; - this.field = f; - - if (isPresent()) this.consumers.forEach(consumer -> consumer.accept(get())); + if (!initialized) { + synchronized (this) { + if (!initialized) { + this.value = supplier.call(); + this.supplier = null; + this.field = f; + this.initialized = true; + + if (isPresent()) this.consumers.forEach(c -> c.accept(this.value)); + } + } + } } public T get() { diff --git a/src/main/java/me/melontini/andromeda/modules/blocks/better_fletching_table/Content.java b/src/main/java/me/melontini/andromeda/modules/blocks/better_fletching_table/Content.java index a79c54ed..d81bf007 100644 --- a/src/main/java/me/melontini/andromeda/modules/blocks/better_fletching_table/Content.java +++ b/src/main/java/me/melontini/andromeda/modules/blocks/better_fletching_table/Content.java @@ -20,7 +20,7 @@ import static me.melontini.andromeda.common.registries.Common.id; public class Content { - public static final Keeper> FLETCHING = Keeper.of(() -> () -> + public static final Keeper> FLETCHING = Keeper.of(() -> RegistryUtil.createScreenHandler(id("fletching"), () -> FletchingScreenHandler::new)); public static void init(BetterFletchingTable module) { diff --git a/src/main/java/me/melontini/andromeda/modules/blocks/incubator/Content.java b/src/main/java/me/melontini/andromeda/modules/blocks/incubator/Content.java index 6b069267..ec487fa3 100644 --- a/src/main/java/me/melontini/andromeda/modules/blocks/incubator/Content.java +++ b/src/main/java/me/melontini/andromeda/modules/blocks/incubator/Content.java @@ -24,8 +24,8 @@ public class Content { .blockEntity((block, id) -> ContentBuilder.BlockEntityBuilder.create(id, IncubatorBlockEntity::new, block))) .afterInit(item -> AndromedaItemGroup.accept(acceptor -> acceptor.item(ModuleManager.quick(Incubator.class), item))); - public static final Keeper INCUBATOR = Keeper.of(() -> () -> asItem(INCUBATOR_BLOCK.get())); + public static final Keeper INCUBATOR = Keeper.of(() -> asItem(INCUBATOR_BLOCK.get())); - public static final Keeper> INCUBATOR_BLOCK_ENTITY = Keeper.of(() -> () -> + public static final Keeper> INCUBATOR_BLOCK_ENTITY = Keeper.of(() -> INCUBATOR_BLOCK.get() == null ? null : RegistryUtil.asBlockEntity(INCUBATOR_BLOCK.get())); } diff --git a/src/main/java/me/melontini/andromeda/modules/entities/boats/BoatEntities.java b/src/main/java/me/melontini/andromeda/modules/entities/boats/BoatEntities.java index d8f4bfe8..77952b1e 100644 --- a/src/main/java/me/melontini/andromeda/modules/entities/boats/BoatEntities.java +++ b/src/main/java/me/melontini/andromeda/modules/entities/boats/BoatEntities.java @@ -23,25 +23,25 @@ public class BoatEntities { private static Boats MODULE; - public static final Keeper> BOAT_WITH_TNT = Keeper.of(() -> () -> + public static final Keeper> BOAT_WITH_TNT = Keeper.of(() -> RegistryUtil.createEntityType(() -> MODULE.config().isTNTBoatOn, id("tnt_boat"), FabricEntityTypeBuilder.create(SpawnGroup.MISC, TNTBoatEntity::new) .dimensions(new EntityDimensions(1.375F, 0.5625F, true)))); - public static final Keeper> BOAT_WITH_FURNACE = Keeper.of(() -> () -> + public static final Keeper> BOAT_WITH_FURNACE = Keeper.of(() -> RegistryUtil.createEntityType(() -> MODULE.config().isFurnaceBoatOn, id("furnace_boat"), FabricEntityTypeBuilder.create(SpawnGroup.MISC, FurnaceBoatEntity::new) .dimensions(new EntityDimensions(1.375F, 0.5625F, true)))); - public static final Keeper> BOAT_WITH_JUKEBOX = Keeper.of(() -> () -> + public static final Keeper> BOAT_WITH_JUKEBOX = Keeper.of(() -> RegistryUtil.createEntityType(() -> MODULE.config().isJukeboxBoatOn, id("jukebox_boat"), FabricEntityTypeBuilder.create(SpawnGroup.MISC, JukeboxBoatEntity::new) .dimensions(new EntityDimensions(1.375F, 0.5625F, true)))); - public static final Keeper> BOAT_WITH_HOPPER = Keeper.of(() -> () -> + public static final Keeper> BOAT_WITH_HOPPER = Keeper.of(() -> RegistryUtil.createEntityType(() -> MODULE.config().isHopperBoatOn, id("hopper_boat"), FabricEntityTypeBuilder.create(SpawnGroup.MISC, HopperBoatEntity::new) diff --git a/src/main/java/me/melontini/andromeda/modules/entities/minecarts/MinecartEntities.java b/src/main/java/me/melontini/andromeda/modules/entities/minecarts/MinecartEntities.java index f5fa3421..bb344370 100644 --- a/src/main/java/me/melontini/andromeda/modules/entities/minecarts/MinecartEntities.java +++ b/src/main/java/me/melontini/andromeda/modules/entities/minecarts/MinecartEntities.java @@ -16,19 +16,19 @@ public class MinecartEntities { private static Minecarts MODULE; - public static final Keeper> ANVIL_MINECART_ENTITY = Keeper.of(() -> () -> + public static final Keeper> ANVIL_MINECART_ENTITY = Keeper.of(() -> RegistryUtil.createEntityType(() -> MODULE.config().isAnvilMinecartOn, id("anvil_minecart"), FabricEntityTypeBuilder.create(SpawnGroup.MISC, AnvilMinecartEntity::new) .dimensions(new EntityDimensions(0.98F, 0.7F, true)))); - public static final Keeper> NOTEBLOCK_MINECART_ENTITY = Keeper.of(() -> () -> + public static final Keeper> NOTEBLOCK_MINECART_ENTITY = Keeper.of(() -> RegistryUtil.createEntityType(() -> MODULE.config().isNoteBlockMinecartOn, id("note_block_minecart"), FabricEntityTypeBuilder.create(SpawnGroup.MISC, NoteBlockMinecartEntity::new) .dimensions(new EntityDimensions(0.98F, 0.7F, true)))); - public static final Keeper> JUKEBOX_MINECART_ENTITY = Keeper.of(() -> () -> + public static final Keeper> JUKEBOX_MINECART_ENTITY = Keeper.of(() -> RegistryUtil.createEntityType(() -> MODULE.config().isJukeboxMinecartOn, id("jukebox_minecart"), FabricEntityTypeBuilder.create(SpawnGroup.MISC, JukeboxMinecartEntity::new) diff --git a/src/main/java/me/melontini/andromeda/modules/items/infinite_totem/Content.java b/src/main/java/me/melontini/andromeda/modules/items/infinite_totem/Content.java index 040fecdd..ba67f9f2 100644 --- a/src/main/java/me/melontini/andromeda/modules/items/infinite_totem/Content.java +++ b/src/main/java/me/melontini/andromeda/modules/items/infinite_totem/Content.java @@ -22,6 +22,6 @@ public class Content { .itemGroup(CommonItemGroups.combat())) .afterInit(item -> AndromedaItemGroup.accept(acceptor -> acceptor.item(ModuleManager.quick(InfiniteTotem.class), item))); - public static Keeper KNOCKOFF_TOTEM_PARTICLE = Keeper.of(() -> () -> + public static Keeper KNOCKOFF_TOTEM_PARTICLE = Keeper.of(() -> RegistryUtil.create(id("knockoff_totem_particles"), "particle_type", FabricParticleTypes::simple)); } diff --git a/src/main/java/me/melontini/andromeda/modules/items/lockpick/Content.java b/src/main/java/me/melontini/andromeda/modules/items/lockpick/Content.java index 52b4e243..df89b2ef 100644 --- a/src/main/java/me/melontini/andromeda/modules/items/lockpick/Content.java +++ b/src/main/java/me/melontini/andromeda/modules/items/lockpick/Content.java @@ -21,7 +21,7 @@ public class Content { .itemGroup(CommonItemGroups.tools())) .afterInit(item -> AndromedaItemGroup.accept(acceptor -> acceptor.item(ModuleManager.quick(Lockpick.class), item))); - public static final Keeper> MERCHANT_INVENTORY = Keeper.of(() -> () -> + public static final Keeper> MERCHANT_INVENTORY = Keeper.of(() -> RegistryUtil.createScreenHandler(() -> MODULE.config().villagerInventory, id("merchant_inventory"), () -> MerchantInventoryScreenHandler::new)); } diff --git a/src/main/java/me/melontini/andromeda/modules/mechanics/throwable_items/Content.java b/src/main/java/me/melontini/andromeda/modules/mechanics/throwable_items/Content.java index e2738ee0..37573e4d 100644 --- a/src/main/java/me/melontini/andromeda/modules/mechanics/throwable_items/Content.java +++ b/src/main/java/me/melontini/andromeda/modules/mechanics/throwable_items/Content.java @@ -36,7 +36,7 @@ import static me.melontini.andromeda.util.CommonValues.MODID; public class Content { - public static final Keeper> FLYING_ITEM = Keeper.of(() -> () -> + public static final Keeper> FLYING_ITEM = Keeper.of(() -> RegistryUtil.createEntityType(id("flying_item"), FabricEntityTypeBuilder.create(SpawnGroup.MISC, FlyingItemEntity::new) .dimensions(new EntityDimensions(0.25F, 0.25F, true)).trackRangeChunks(4).trackedUpdateRate(10))); diff --git a/src/main/java/me/melontini/andromeda/modules/misc/unknown/Content.java b/src/main/java/me/melontini/andromeda/modules/misc/unknown/Content.java index 0a3b287b..76fb7cb5 100644 --- a/src/main/java/me/melontini/andromeda/modules/misc/unknown/Content.java +++ b/src/main/java/me/melontini/andromeda/modules/misc/unknown/Content.java @@ -15,8 +15,9 @@ import static me.melontini.dark_matter.api.content.RegistryUtil.asItem; public class Content { - public static final Keeper ROSE_OF_THE_VALLEY_BLOCK = start(() -> ContentBuilder.BlockBuilder.create(id("rose_of_the_valley"), () -> new FlowerBlock(StatusEffects.REGENERATION, 12, AbstractBlock.Settings.copy(Blocks.LILY_OF_THE_VALLEY))) + public static final Keeper ROSE_OF_THE_VALLEY_BLOCK = start(() -> + ContentBuilder.BlockBuilder.create(id("rose_of_the_valley"), () -> new FlowerBlock(StatusEffects.REGENERATION, 12, AbstractBlock.Settings.copy(Blocks.LILY_OF_THE_VALLEY))) .item((block, id) -> ContentBuilder.ItemBuilder.create(id, () -> new RoseOfTheValley(block, new FabricItemSettings().rarity(Rarity.UNCOMMON))))); - public static final Keeper ROSE_OF_THE_VALLEY = Keeper.of(() -> () -> asItem(ROSE_OF_THE_VALLEY_BLOCK.orThrow())); + public static final Keeper ROSE_OF_THE_VALLEY = Keeper.of(() -> asItem(ROSE_OF_THE_VALLEY_BLOCK.orThrow())); } -- 2.51.2 From 27cefa16b29d6a3dc016da696d765083df4c06f1 Mon Sep 17 00:00:00 2001 From: melontini <104443436+melontini@users.noreply.github.com> Date: Sun, 24 Dec 2023 15:52:14 +0700 Subject: [PATCH 8/9] Try threaded module setup --- .../melontini/andromeda/base/Bootstrap.java | 24 ++++++++++++++-- .../me/melontini/andromeda/base/Module.java | 6 ++-- .../andromeda/base/ModuleManager.java | 13 ++++++--- .../andromeda/modules/ModuleDiscovery.java | 28 +++++++++++++------ 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/main/java/me/melontini/andromeda/base/Bootstrap.java b/src/main/java/me/melontini/andromeda/base/Bootstrap.java index 08fe30a3..64bfeefe 100644 --- a/src/main/java/me/melontini/andromeda/base/Bootstrap.java +++ b/src/main/java/me/melontini/andromeda/base/Bootstrap.java @@ -12,6 +12,7 @@ import me.melontini.andromeda.util.Debug; import me.melontini.andromeda.util.exceptions.AndromedaException; import me.melontini.andromeda.util.mixin.AndromedaMixins; import me.melontini.dark_matter.api.base.util.EntrypointRunner; +import me.melontini.dark_matter.api.base.util.MathStuff; import me.melontini.dark_matter.api.base.util.classes.ThrowingRunnable; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -23,7 +24,12 @@ import org.spongepowered.asm.mixin.Mixins; import java.io.IOException; import java.nio.file.Files; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.ServiceLoader; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicReference; @CustomLog @@ -31,6 +37,8 @@ public class Bootstrap { static ModuleManager INSTANCE; + private static ExecutorService SERVICE; + @Environment(EnvType.CLIENT) public static void onClient() { for (Module module : ModuleManager.get().loaded()) { module.onClient(); } @@ -55,6 +63,9 @@ public class Bootstrap { } public static void onPreLaunch() { + int i = MathStuff.clamp(Runtime.getRuntime().availableProcessors() - 1, 1, 4); + SERVICE = new ForkJoinPool(i, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); + LOGGER.info("Andromeda({}) on {}({})", CommonValues.version(), CommonValues.platform(), CommonValues.platform().version()); AtomicReference oldCfg = new AtomicReference<>(); @@ -73,8 +84,8 @@ public class Bootstrap { Config.load(); - List> list = new ArrayList<>(Arrays.asList(ServiceLoader.load(Module.class) - .stream().map(ServiceLoader.Provider::get).toArray(Module[]::new))); + List> list = new ArrayList<>(40); + ServiceLoader.load(Module.class).stream().map(ServiceLoader.Provider::get).forEach(list::add); EntrypointRunner.run("andromeda:modules", ModuleManager.ModuleSupplier.class, s -> list.addAll(s.get())); @@ -97,6 +108,9 @@ public class Bootstrap { FabricLoader.getInstance().getObjectShare().put("andromeda:module_manager", m); for (Module module : m.loaded()) { module.onPreLaunch(); } + + SERVICE.shutdownNow().forEach(Runnable::run); + SERVICE = null; } static void wrapIO(ThrowingRunnable runnable, String msg) { @@ -111,6 +125,10 @@ public class Bootstrap { return AndromedaMixins.getClassPath(); } + public static ExecutorService getPreLaunchService() { + return SERVICE; + } + public static boolean testModVersion(Module m, String modId, String predicate) { Optional mod = FabricLoader.getInstance().getModContainer(modId); if (mod.isPresent() && !Debug.skipIntegration(m.meta().id(), modId)) { diff --git a/src/main/java/me/melontini/andromeda/base/Module.java b/src/main/java/me/melontini/andromeda/base/Module.java index 15c2c81b..285a01aa 100644 --- a/src/main/java/me/melontini/andromeda/base/Module.java +++ b/src/main/java/me/melontini/andromeda/base/Module.java @@ -20,9 +20,9 @@ public abstract class Module { return new Metadata(info1.name(), info1.category(), info1.environment()); }); - ConfigManager manager; - T config; - T defaultConfig; + volatile ConfigManager manager; + volatile T config; + volatile T defaultConfig; public void onClient() { initClasses("client.Client"); diff --git a/src/main/java/me/melontini/andromeda/base/ModuleManager.java b/src/main/java/me/melontini/andromeda/base/ModuleManager.java index 83dc633d..0500f6cc 100644 --- a/src/main/java/me/melontini/andromeda/base/ModuleManager.java +++ b/src/main/java/me/melontini/andromeda/base/ModuleManager.java @@ -23,6 +23,7 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.*; +import java.util.concurrent.CompletableFuture; @CustomLog public class ModuleManager { @@ -60,10 +61,12 @@ public class ModuleManager { this.setUpConfigs(sorted); - sorted.forEach(module -> { + Set> futures = new HashSet<>(); + sorted.forEach(module -> futures.add(CompletableFuture.runAsync(() -> { module.config = Utilities.cast(module.manager.load(FabricLoader.getInstance().getConfigDir())); module.defaultConfig = Utilities.cast(module.manager.createDefault()); - }); + }))); + CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); sorted.forEach(Module::postConfig); @@ -114,7 +117,8 @@ public class ModuleManager { } private void setUpConfigs(List> list) { - list.forEach(m -> { + Set> futures = new HashSet<>(); + list.forEach(m -> futures.add(CompletableFuture.runAsync(() -> { var config = ConfigManager.of(getConfigClass(m.getClass()), "andromeda/" + m.meta().id()); config.onLoad(config1 -> { if (Config.get().sideOnlyMode) { @@ -134,7 +138,8 @@ public class ModuleManager { config.exceptionHandler((e, stage) -> LOGGER.error("Failed to %s config for module: %s".formatted(stage.toString().toLowerCase(), m.meta().id()), e)); m.manager = Utilities.cast(config); m.onConfig(Utilities.cast(config)); - }); + }))); + CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); } public Class getConfigClass(Class m) { diff --git a/src/main/java/me/melontini/andromeda/modules/ModuleDiscovery.java b/src/main/java/me/melontini/andromeda/modules/ModuleDiscovery.java index 0f72ba18..2cf29c6b 100644 --- a/src/main/java/me/melontini/andromeda/modules/ModuleDiscovery.java +++ b/src/main/java/me/melontini/andromeda/modules/ModuleDiscovery.java @@ -10,23 +10,35 @@ import org.objectweb.asm.ClassReader; import org.objectweb.asm.tree.ClassNode; import org.spongepowered.asm.util.Annotations; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; public class ModuleDiscovery implements ModuleManager.ModuleSupplier { @Override public List> get() { Bootstrap.getModuleClassPath().addUrl(ModuleDiscovery.class.getProtectionDomain().getCodeSource().getLocation()); - return Bootstrap.getModuleClassPath().getTopLevelRecursive("me.melontini.andromeda.modules") + + List>> futures = new ArrayList<>(); + Bootstrap.getModuleClassPath().getTopLevelRecursive("me.melontini.andromeda.modules") .stream().filter(ci -> !ci.packageName().endsWith("mixin") && !ci.packageName().endsWith("client")) - .map(bs -> Utilities.supplyUnchecked(bs::readAllBytes)) - .map(bytes -> { + .forEach(info -> futures.add(CompletableFuture.supplyAsync(() -> { + byte[] bytes = Utilities.supplyUnchecked(info::readAllBytes); + ClassReader reader = new ClassReader(bytes); ClassNode node = new ClassNode(); reader.accept(node, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); - return node; - }).filter(node -> Annotations.getVisible(node, ModuleInfo.class) != null) - .map(node -> Utilities.supplyUnchecked(() -> Class.forName(node.name.replace('/', '.')))) - .map(c -> Utilities.supplyUnchecked(() -> (Module) Reflect.setAccessible(Reflect.findConstructor(c).orElseThrow(() -> new IllegalStateException("Module has no no-args ctx!"))) - .newInstance())).toList(); + + if (Annotations.getVisible(node, ModuleInfo.class) != null) { + var c = Utilities.supplyUnchecked(() -> Class.forName(node.name.replace('/', '.'))); + return Utilities.supplyUnchecked(() -> (Module) Reflect.setAccessible(Reflect.findConstructor(c).orElseThrow(() -> new IllegalStateException("Module has no no-args ctx!"))) + .newInstance()); + } + return null; + }, Bootstrap.getPreLaunchService()))); + return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)) + .handle((unused, throwable) -> futures).join().stream() + .map(CompletableFuture::join).filter(Objects::nonNull).toList(); } } -- 2.51.2 From c589cf1953e0f4fa10f69f1805435aeb55149451 Mon Sep 17 00:00:00 2001 From: melontini <104443436+melontini@users.noreply.github.com> Date: Sun, 24 Dec 2023 16:00:59 +0700 Subject: [PATCH 9/9] Bump version and CHANGELOG.md --- CHANGELOG.md | 13 ++++--------- gradle.properties | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86068a96..7a5525a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,8 @@ ## Alpha warning! -* New module: Moist Control! - * Allows you to control farmland hydration range. As simple as that! -* Major change: Scoped configs! - * I recommend checking out the wiki for more info, as there's quite a lot. - -*** - -* Added legacy config (0.9.2) migration! -* Added Additional Additions support to Useful Fletching Table. +* Fixed legacy config migration when both config versions are present. +* Scoped config are no longer created or stored for disabled modules. +* Module setup and Scoped Configs setup should be a bit faster now. +* Removed Connector notice. * Updated Chinese translation. Courtesy of [Rad586](https://github.com/Rad586). \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 9acb2d5e..ab54df51 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ minecraft_version=1.19.2 yarn_mappings=1.19.2+build.28 loader_version=0.15.0 # Mod Properties -mod_version=1.4.0-1.19.2 +mod_version=1.4.1-1.19.2 maven_group=me.melontini archives_base_name=andromeda # Dependencies