Search in sources :

Example 1 with CharsetLoadConfigEvent

use of pl.asie.charset.lib.config.CharsetLoadConfigEvent in project Charset by CharsetMC.

the class ModuleLoader method readDataTable.

@SuppressWarnings("unchecked")
private void readDataTable(ASMDataTable table) {
    Multimap<String, String> unmetDependencies = HashMultimap.create();
    Set<String> enabledModules = new HashSet<>();
    Set<String> compatModules = new HashSet<>();
    Map<String, ASMDataTable.ASMData> moduleData = new HashMap<>();
    Property baseProfileProp = ModCharset.configGeneral.get("general", "profile", "DEFAULT");
    baseProfileProp.setValidValues(new String[] { "DEFAULT", "STABLE", "TESTING", "EXPERIMENTAL" });
    baseProfileProp.setLanguageKey("config.charset.profile.name");
    baseProfileProp.setRequiresMcRestart(true);
    ModuleProfile profile, defaultProfile;
    if (ModCharset.INDEV) {
        defaultProfile = ModuleProfile.INDEV;
    } else if (ModCharset.defaultOptions.containsKey("profile")) {
        defaultProfile = getProfileFromString(ModCharset.defaultOptions.get("profile"));
    } else {
        defaultProfile = ModuleProfile.STABLE;
    }
    baseProfileProp.setComment("Set the base profile for Charset.\nThis will give you a default set of modules based on stability.\nAllowed values: DEFAULT, STABLE, TESTING, EXPERIMENTAL (DEFAULT means " + defaultProfile.name() + ")\nFor fine-grained configuration, check modules.cfg!");
    if ("DEFAULT".equals(baseProfileProp.getString().toUpperCase())) {
        profile = defaultProfile;
    } else {
        profile = getProfileFromString(baseProfileProp.getString());
    }
    ModCharset.profile = profile;
    ModCharset.logger.info("Charset profile is " + ModCharset.profile);
    ConfigCategory category = ModCharset.configModules.getCategory("overrides");
    category.setComment("Overrides can have one of three values: DEFAULT, ENABLE, DISABLE\nDEFAULT will enable the module based on your profile settings and dependency availability.");
    category = ModCharset.configModules.getCategory("categories");
    category.setComment("This section allows you to disable certain categories of content, based on a tag system.");
    boolean configDirty = false;
    Map<String, Boolean> categoryMap = new HashMap<>();
    // Initialize categories
    for (ASMDataTable.ASMData data : table.getAll(CharsetModule.class.getName())) {
        Map<String, Object> info = data.getAnnotationInfo();
        List<String> tags = (List<String>) info.getOrDefault("categories", Collections.emptyList());
        for (String s : tags) {
            if (!categoryMap.containsKey(s)) {
                Property prop = ModCharset.configModules.get("categories", s, !CATEGORIES_OFF_BY_DEFAULT.contains(s));
                prop.setRequiresMcRestart(true);
                categoryMap.put(s, prop.getBoolean());
            }
        }
    }
    for (ASMDataTable.ASMData data : table.getAll(CharsetModule.class.getName())) {
        Map<String, Object> info = data.getAnnotationInfo();
        String name = (String) info.get("name");
        String desc = (String) info.get("description");
        if (desc == null)
            desc = "";
        ModuleProfile modProfile = ModuleProfile.valueOf(((ModAnnotation.EnumHolder) info.get("profile")).getValue());
        Boolean isDefault = (Boolean) info.getOrDefault("isDefault", true);
        Boolean compat = modProfile == ModuleProfile.COMPAT;
        Boolean clientOnly = (Boolean) info.getOrDefault("isClientOnly", false);
        Boolean serverOnly = (Boolean) info.getOrDefault("isServerOnly", false);
        List<String> tags = (List<String>) info.getOrDefault("categories", Collections.emptyList());
        String moduleGuiClass = (String) info.getOrDefault("moduleConfigGui", "");
        if (moduleGuiClass.length() > 0) {
            moduleGuiClasses.put(name, moduleGuiClass);
        }
        moduleData.put(name, data);
        ThreeState override = ThreeState.MAYBE;
        if ((Boolean) info.getOrDefault("isVisible", true)) {
            if (modProfile == ModuleProfile.INDEV && profile != ModuleProfile.INDEV) {
                override = ThreeState.NO;
            } else {
                if (compat) {
                    Property prop = ModCharset.configModules.get("compat", name, isDefault);
                    prop.setRequiresMcRestart(true);
                    if (!prop.getBoolean())
                        override = ThreeState.NO;
                } else {
                    Property prop = ModCharset.configModules.get("overrides", name, "DEFAULT");
                    prop.setValidValues(new String[] { "DEFAULT", "ENABLE", "DISABLE" });
                    prop.setRequiresMcRestart(true);
                    if (desc.length() > 0)
                        desc += " ";
                    desc += "[Profile: " + modProfile.name().toUpperCase() + "";
                    if (!isDefault) {
                        desc += ", off by default!";
                    }
                    desc += "]";
                    if (!desc.equals(prop.getComment())) {
                        prop.setComment(desc);
                        configDirty = true;
                    }
                    if (prop.getString().toUpperCase().startsWith("ENABLE")) {
                        override = ThreeState.YES;
                    } else if (prop.getString().toUpperCase().startsWith("DISABLE")) {
                        override = ThreeState.NO;
                    } else if (!"DEFAULT".equals(prop.getString().toUpperCase())) {
                        ModCharset.logger.warn("Invalid value for '" + name + "' override: '" + prop.getString() + ";");
                    }
                }
            }
        }
        if (clientOnly && !FMLCommonHandler.instance().getSide().isClient()) {
            continue;
        }
        if (serverOnly && !FMLCommonHandler.instance().getSide().isServer()) {
            continue;
        }
        if (compat) {
            compatModules.add(name);
        }
        if (override == ThreeState.MAYBE && isDefault) {
            List<String> antideps = (List<String>) info.get("antidependencies");
            if (antideps != null) {
                for (String dep : antideps) {
                    if (isDepPresent(dep, enabledModules)) {
                        ModCharset.logger.info("Antidependency " + dep + " is present - disabling otherwise not forced module " + name + ".");
                        isDefault = false;
                        break;
                    }
                }
            }
            for (String s : tags) {
                if (!categoryMap.get(s)) {
                    ModCharset.logger.info("Category " + s + " is disabled - disabling otherwise not forced module " + name + ".");
                    isDefault = false;
                }
            }
        }
        if (!compat && modProfile != ModuleProfile.FORCED && modProfile.ordinal() > profile.ordinal()) {
            isDefault = false;
        }
        EnableInformation enableInfo = new EnableInformation(isDefault, override);
        if (enableInfo.isEnabled()) {
            enabledModules.add(name);
            enableInfoMap.put(name, enableInfo);
            if (!"lib".equals(name))
                dependencies.put(name, "lib");
            List<String> deps = (List<String>) info.get("dependencies");
            if (deps != null) {
                dependencies.putAll(name, deps);
            }
        }
    }
    if (ModCharset.configGeneral.hasChanged()) {
        ModCharset.configGeneral.save();
    }
    if (ModCharset.configModules.hasChanged() || configDirty) {
        ModCharset.configModules.save();
        configDirty = false;
    }
    int removedCount = 1;
    while (removedCount > 0) {
        removedCount = 0;
        for (String name : enabledModules) {
            if (dependencies.containsKey(name)) {
                for (String dep : dependencies.get(name)) {
                    boolean optional = false;
                    if (dep.startsWith("optional:")) {
                        optional = true;
                        dep = dep.substring("optional:".length());
                    }
                    boolean met = optional || isDepPresent(dep, enabledModules);
                    if (!met) {
                        enableInfoMap.get(name).dependenciesMet = false;
                        unmetDependencies.put(name, dep);
                        break;
                    }
                }
            }
        }
        Iterator<String> unmetDepKey = unmetDependencies.keySet().iterator();
        while (unmetDepKey.hasNext()) {
            String depMod = unmetDepKey.next();
            EnableInformation enableInfo = enableInfoMap.get(depMod);
            if (!enableInfo.isEnabled()) {
                if (!compatModules.contains(depMod)) {
                    ModCharset.logger.info("Module " + depMod + " requires " + joinerComma.join(unmetDependencies.get(depMod)) + ", but is not force-enabled. You can ignore this - it is not an error, just information.");
                }
                removedCount++;
                enabledModules.remove(depMod);
                unmetDepKey.remove();
            }
        }
    }
    for (String name : enabledModules) {
        if (ModCharset.INDEV) {
            ModCharset.logger.info("Instantiating module " + name);
        }
        ASMDataTable.ASMData data = moduleData.get(name);
        try {
            Object o = getClass(data).newInstance();
            loadedModules.put(name, o);
            loadedModulesByClass.put(data.getClassName(), o);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    if (unmetDependencies.size() > 0) {
        List<String> depStrings = new ArrayList<>(unmetDependencies.size());
        for (String depMod : unmetDependencies.keys()) {
            depStrings.add(depMod + "<-[" + joinerComma.join(unmetDependencies.get(depMod)) + "]");
        }
        throw new RuntimeException("The following mandatory dependencies were not met: " + joinerComma.join(depStrings));
    }
    iterateModules(table, Mod.EventHandler.class.getName(), (data, instance) -> {
        String methodName = data.getObjectName().substring(0, data.getObjectName().indexOf('('));
        String methodDesc = data.getObjectName().substring(methodName.length());
        MethodType methodType = MethodType.fromMethodDescriptorString(methodDesc, classLoader);
        if (ModCharset.INDEV) {
            if (methodType.parameterCount() != 1) {
                throw new RuntimeException("Invalid parameter count " + methodType.parameterCount() + " for EventHandler in " + instance.getClass() + "!");
            }
        }
        try {
            MethodHandle methodHandle = MethodHandles.lookup().findVirtual(getClass(data), methodName, methodType);
            List<Pair<String, MethodHandle>> list = loaderHandles.computeIfAbsent(methodType.parameterType(0), k -> new ArrayList<>());
            list.sort(Comparator.comparing(Pair::getLeft));
            list.add(Pair.of(loadedModules.inverse().get(instance), methodHandle));
        } catch (NoSuchMethodException e) {
        // method has been annotated away, ignore
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    });
    iterateModules(table, CharsetModule.Instance.class.getName(), (data, instance) -> {
        try {
            String instString = (String) data.getAnnotationInfo().get("value");
            if (instString == null || instString.equals("")) {
                getField(data).set(instance, instance);
            } else {
                Object inst2 = loadedModules.get(instString);
                getField(data).set(instance, inst2);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    });
    iterateModules(table, CharsetModule.PacketRegistry.class.getName(), (data, instance) -> {
        String id = (String) data.getAnnotationInfo().get("value");
        if (id == null)
            id = loadedModules.inverse().get(instance);
        try {
            String channelName = "chrs:" + id.substring(id.lastIndexOf('.') + 1);
            getField(data).set(instance, new PacketRegistry(channelName));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    });
    iterateModules(table, CharsetModule.Configuration.class.getName(), (data, instance) -> {
        String id = (String) data.getAnnotationInfo().get("value");
        if (id == null)
            id = loadedModules.inverse().get(instance);
        try {
            Configuration config = new Configuration(ModCharset.getModuleConfigFile(id));
            getField(data).set(instance, config);
            moduleConfigs.put(id, config);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    });
    Side side = FMLCommonHandler.instance().getSide();
    for (ASMDataTable.ASMData data : table.getAll(CharsetModule.SidedProxy.class.getName())) {
        String clientSide = (String) data.getAnnotationInfo().get("clientSide");
        String serverSide = (String) data.getAnnotationInfo().get("serverSide");
        try {
            Field f = getField(data);
            f.set(null, Class.forName(side == Side.CLIENT ? clientSide : serverSide).newInstance());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    List<String> sortedModules = new ArrayList<>();
    Set<String> remainingModules = Sets.newHashSet(enabledModules);
    while (!remainingModules.isEmpty()) {
        Iterator<String> remainingIterator = remainingModules.iterator();
        boolean added = false;
        while (remainingIterator.hasNext()) {
            String s = remainingIterator.next();
            boolean canAdd = true;
            for (String dep : dependencies.get(s)) {
                if (!dep.startsWith("mod:") && !dep.startsWith("optional:") && !sortedModules.contains(dep)) {
                    canAdd = false;
                    break;
                }
            }
            if (canAdd) {
                added = true;
                sortedModules.add(s);
                remainingIterator.remove();
            }
        }
        if (!added) {
            throw new RuntimeException("Cyclic dependency within Charset modules! Report!");
        }
    }
    for (List<Pair<String, MethodHandle>> list : loaderHandles.values()) {
        list.sort(Comparator.comparingInt(a -> sortedModules.indexOf(a.getKey())));
    }
    for (String s : sortedModules) {
        MinecraftForge.EVENT_BUS.register(loadedModules.get(s));
    }
    for (ASMDataTable.ASMData data : table.getAll(CharsetCompatAnnotation.class.getName())) {
        String id = (String) data.getAnnotationInfo().get("value");
        try {
            addClassNames(table, Class.forName(data.getClassName()), id);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    passEvent(new CharsetLoadConfigEvent(true));
    if (ModCharset.configModules.hasChanged() || configDirty) {
        ModCharset.configModules.save();
        configDirty = false;
    }
}
Also used : CharsetMCMPAddon(pl.asie.charset.lib.modcompat.mcmultipart.CharsetMCMPAddon) MethodHandle(java.lang.invoke.MethodHandle) java.util(java.util) Loader(net.minecraftforge.fml.common.Loader) ModCharset(pl.asie.charset.ModCharset) CharsetChiselsAndBitsPlugin(pl.asie.charset.lib.modcompat.chiselsandbits.CharsetChiselsAndBitsPlugin) FMLCommonHandler(net.minecraftforge.fml.common.FMLCommonHandler) ASMDataTable(net.minecraftforge.fml.common.discovery.ASMDataTable) Pair(org.apache.commons.lang3.tuple.Pair) Side(net.minecraftforge.fml.relauncher.Side) BiConsumer(java.util.function.BiConsumer) CharsetJEIPlugin(pl.asie.charset.lib.modcompat.jei.CharsetJEIPlugin) Mod(net.minecraftforge.fml.common.Mod) com.google.common.collect(com.google.common.collect) FMLEvent(net.minecraftforge.fml.common.event.FMLEvent) PacketRegistry(pl.asie.charset.lib.network.PacketRegistry) ThreeState(pl.asie.charset.lib.utils.ThreeState) CharsetLoadConfigEvent(pl.asie.charset.lib.config.CharsetLoadConfigEvent) MethodHandles(java.lang.invoke.MethodHandles) Field(java.lang.reflect.Field) ModAnnotation(net.minecraftforge.fml.common.discovery.asm.ModAnnotation) MinecraftForge(net.minecraftforge.common.MinecraftForge) MethodType(java.lang.invoke.MethodType) Configuration(net.minecraftforge.common.config.Configuration) Property(net.minecraftforge.common.config.Property) ConfigCategory(net.minecraftforge.common.config.ConfigCategory) Joiner(com.google.common.base.Joiner) Configuration(net.minecraftforge.common.config.Configuration) Side(net.minecraftforge.fml.relauncher.Side) ThreeState(pl.asie.charset.lib.utils.ThreeState) Field(java.lang.reflect.Field) ASMDataTable(net.minecraftforge.fml.common.discovery.ASMDataTable) ConfigCategory(net.minecraftforge.common.config.ConfigCategory) ModAnnotation(net.minecraftforge.fml.common.discovery.asm.ModAnnotation) Property(net.minecraftforge.common.config.Property) Pair(org.apache.commons.lang3.tuple.Pair) MethodType(java.lang.invoke.MethodType) PacketRegistry(pl.asie.charset.lib.network.PacketRegistry) CharsetLoadConfigEvent(pl.asie.charset.lib.config.CharsetLoadConfigEvent) MethodHandle(java.lang.invoke.MethodHandle)

Aggregations

Joiner (com.google.common.base.Joiner)1 com.google.common.collect (com.google.common.collect)1 MethodHandle (java.lang.invoke.MethodHandle)1 MethodHandles (java.lang.invoke.MethodHandles)1 MethodType (java.lang.invoke.MethodType)1 Field (java.lang.reflect.Field)1 java.util (java.util)1 BiConsumer (java.util.function.BiConsumer)1 MinecraftForge (net.minecraftforge.common.MinecraftForge)1 ConfigCategory (net.minecraftforge.common.config.ConfigCategory)1 Configuration (net.minecraftforge.common.config.Configuration)1 Property (net.minecraftforge.common.config.Property)1 FMLCommonHandler (net.minecraftforge.fml.common.FMLCommonHandler)1 Loader (net.minecraftforge.fml.common.Loader)1 Mod (net.minecraftforge.fml.common.Mod)1 ASMDataTable (net.minecraftforge.fml.common.discovery.ASMDataTable)1 ModAnnotation (net.minecraftforge.fml.common.discovery.asm.ModAnnotation)1 FMLEvent (net.minecraftforge.fml.common.event.FMLEvent)1 Side (net.minecraftforge.fml.relauncher.Side)1 Pair (org.apache.commons.lang3.tuple.Pair)1