diff --git a/src/main/java/gregtech/api/modules/IGregTechModule.java b/src/main/java/gregtech/api/modules/IGregTechModule.java index 75208d8dce9..d8c156c8bf9 100644 --- a/src/main/java/gregtech/api/modules/IGregTechModule.java +++ b/src/main/java/gregtech/api/modules/IGregTechModule.java @@ -21,7 +21,7 @@ public interface IGregTechModule { /** * What other modules this module depends on. *

- * e.g. new ResourceLocation("gregtech", "foo_module") represents a dependency on the module + * e.g. {@code new ResourceLocation("gregtech", "foo_module")} represents a dependency on the module * "foo_module" in the container "gregtech" */ @NotNull @@ -29,25 +29,25 @@ default Set getDependencyUids() { return Collections.emptySet(); } - default void construction(FMLConstructionEvent event) {} + default void construction(@NotNull FMLConstructionEvent event) {} - default void preInit(FMLPreInitializationEvent event) {} + default void preInit(@NotNull FMLPreInitializationEvent event) {} - default void init(FMLInitializationEvent event) {} + default void init(@NotNull FMLInitializationEvent event) {} - default void postInit(FMLPostInitializationEvent event) {} + default void postInit(@NotNull FMLPostInitializationEvent event) {} - default void loadComplete(FMLLoadCompleteEvent event) {} + default void loadComplete(@NotNull FMLLoadCompleteEvent event) {} - default void serverAboutToStart(FMLServerAboutToStartEvent event) {} + default void serverAboutToStart(@NotNull FMLServerAboutToStartEvent event) {} - default void serverStarting(FMLServerStartingEvent event) {} + default void serverStarting(@NotNull FMLServerStartingEvent event) {} - default void serverStarted(FMLServerStartedEvent event) {} + default void serverStarted(@NotNull FMLServerStartedEvent event) {} - default void serverStopping(FMLServerStoppingEvent event) {} + default void serverStopping(@NotNull FMLServerStoppingEvent event) {} - default void serverStopped(FMLServerStoppedEvent event) {} + default void serverStopped(@NotNull FMLServerStoppedEvent event) {} /** * Register packets using GregTech's packet handling API here. @@ -87,7 +87,11 @@ default void registerPackets() {} return Collections.emptyList(); } - default boolean processIMC(FMLInterModComms.IMCMessage message) { + /** + * @param message the message to process + * @return if the message was processed, stopping all other modules from processing it + */ + default boolean processIMC(@NotNull FMLInterModComms.IMCMessage message) { return false; } diff --git a/src/main/java/gregtech/api/modules/IModuleManager.java b/src/main/java/gregtech/api/modules/IModuleManager.java index e6a04f62075..a2dd5048cd7 100644 --- a/src/main/java/gregtech/api/modules/IModuleManager.java +++ b/src/main/java/gregtech/api/modules/IModuleManager.java @@ -4,23 +4,28 @@ import net.minecraft.util.ResourceLocation; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + public interface IModuleManager { - default boolean isModuleEnabled(String containerID, String moduleID) { + default boolean isModuleEnabled(@NotNull String containerID, @NotNull String moduleID) { return isModuleEnabled(new ResourceLocation(containerID, moduleID)); } - default boolean isModuleEnabled(String moduleID) { + default boolean isModuleEnabled(@NotNull String moduleID) { return isModuleEnabled(GTUtility.gregtechId(moduleID)); } - boolean isModuleEnabled(ResourceLocation id); + boolean isModuleEnabled(@NotNull ResourceLocation id); - void registerContainer(IModuleContainer container); + void registerContainer(@NotNull IModuleContainer container); + @Nullable IModuleContainer getLoadedContainer(); + @NotNull ModuleStage getStage(); - boolean hasPassedStage(ModuleStage stage); + boolean hasPassedStage(@NotNull ModuleStage stage); } diff --git a/src/main/java/gregtech/modules/ModuleManager.java b/src/main/java/gregtech/modules/ModuleManager.java index 768d44e95b6..b65ff4938ff 100644 --- a/src/main/java/gregtech/modules/ModuleManager.java +++ b/src/main/java/gregtech/modules/ModuleManager.java @@ -12,29 +12,35 @@ import net.minecraftforge.fml.common.event.*; import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; +import it.unimi.dsi.fastutil.objects.Object2ReferenceLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; +import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; import java.io.File; +import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.stream.Collectors; -public class ModuleManager implements IModuleManager { +public final class ModuleManager implements IModuleManager { private static final ModuleManager INSTANCE = new ModuleManager(); private static final String MODULE_CFG_FILE_NAME = "modules.cfg"; private static final String MODULE_CFG_CATEGORY_NAME = "modules"; private static File configFolder; - private Map containers = new LinkedHashMap<>(); - private final Map sortedModules = new LinkedHashMap<>(); - private final Set loadedModules = new LinkedHashSet<>(); + private Map containers = new Object2ReferenceLinkedOpenHashMap<>(); + private final Map sortedModules = new Object2ReferenceLinkedOpenHashMap<>(); + private final Set loadedModules = new ReferenceLinkedOpenHashSet<>(); - private IModuleContainer currentContainer; + private @Nullable IModuleContainer currentContainer; private ModuleStage currentStage = ModuleStage.C_SETUP; - private final Logger logger = LogManager.getLogger("GregTech Module Loader"); + private static final Logger logger = LogManager.getLogger("GregTech Module Loader"); private Configuration config; private ModuleManager() {} @@ -44,11 +50,11 @@ public static ModuleManager getInstance() { } @Override - public boolean isModuleEnabled(ResourceLocation id) { + public boolean isModuleEnabled(@NotNull ResourceLocation id) { return sortedModules.containsKey(id); } - public boolean isModuleEnabled(IGregTechModule module) { + private boolean isModuleEnabled(@NotNull IGregTechModule module) { GregTechModule annotation = module.getClass().getAnnotation(GregTechModule.class); String comment = getComment(module); Property prop = getConfiguration().get(MODULE_CFG_CATEGORY_NAME, @@ -57,22 +63,22 @@ public boolean isModuleEnabled(IGregTechModule module) { } @Override - public IModuleContainer getLoadedContainer() { + public @Nullable IModuleContainer getLoadedContainer() { return currentContainer; } @Override - public ModuleStage getStage() { + public @NotNull ModuleStage getStage() { return currentStage; } @Override - public boolean hasPassedStage(ModuleStage stage) { + public boolean hasPassedStage(@NotNull ModuleStage stage) { return currentStage.ordinal() > stage.ordinal(); } @Override - public void registerContainer(IModuleContainer container) { + public void registerContainer(@NotNull IModuleContainer container) { if (currentStage != ModuleStage.C_SETUP) { logger.error("Failed to register module container {}, as module loading has already begun", container); return; @@ -81,14 +87,21 @@ public void registerContainer(IModuleContainer container) { containers.put(container.getID(), container); } - public void setup(ASMDataTable asmDataTable, File configDirectory) { - // find and register all containers registered with the @ModuleContainer annotation, then sort them by container - // name + /** + * Set up the Module Manager + * + * @param asmDataTable the data table containing all of the Module Container and Module classes + * @param configDirectory the directory containing the GT config directory + */ + public void setup(@NotNull ASMDataTable asmDataTable, @NotNull File configDirectory) { + // find and register all containers registered with the @ModuleContainer annotation discoverContainers(asmDataTable); + // then sort them by container name containers = containers.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .collect(Collectors.toMap( - Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new)); + Map.Entry::getKey, Map.Entry::getValue, + (a, b) -> a, Object2ReferenceLinkedOpenHashMap::new)); currentStage = ModuleStage.M_SETUP; configFolder = new File(configDirectory, GTValues.MODID); @@ -108,9 +121,10 @@ public void setup(ASMDataTable asmDataTable, File configDirectory) { MinecraftForge.ORE_GEN_BUS.register(clazz); } } + currentContainer = null; } - public void onConstruction(FMLConstructionEvent event) { + public void onConstruction(@NotNull FMLConstructionEvent event) { currentStage = ModuleStage.CONSTRUCTION; for (IGregTechModule module : loadedModules) { currentContainer = containers.get(getContainerID(module)); @@ -118,9 +132,10 @@ public void onConstruction(FMLConstructionEvent event) { module.construction(event); module.getLogger().debug("Construction complete"); } + currentContainer = null; } - public void onPreInit(FMLPreInitializationEvent event) { + public void onPreInit(@NotNull FMLPreInitializationEvent event) { currentStage = ModuleStage.PRE_INIT; // Separate loops for strict ordering for (IGregTechModule module : loadedModules) { @@ -134,9 +149,10 @@ public void onPreInit(FMLPreInitializationEvent event) { module.preInit(event); module.getLogger().debug("Pre-init complete"); } + currentContainer = null; } - public void onInit(FMLInitializationEvent event) { + public void onInit(@NotNull FMLInitializationEvent event) { currentStage = ModuleStage.INIT; for (IGregTechModule module : loadedModules) { currentContainer = containers.get(getContainerID(module)); @@ -144,9 +160,10 @@ public void onInit(FMLInitializationEvent event) { module.init(event); module.getLogger().debug("Init complete"); } + currentContainer = null; } - public void onPostInit(FMLPostInitializationEvent event) { + public void onPostInit(@NotNull FMLPostInitializationEvent event) { currentStage = ModuleStage.POST_INIT; for (IGregTechModule module : loadedModules) { currentContainer = containers.get(getContainerID(module)); @@ -154,9 +171,10 @@ public void onPostInit(FMLPostInitializationEvent event) { module.postInit(event); module.getLogger().debug("Post-init complete"); } + currentContainer = null; } - public void onLoadComplete(FMLLoadCompleteEvent event) { + public void onLoadComplete(@NotNull FMLLoadCompleteEvent event) { currentStage = ModuleStage.FINISHED; for (IGregTechModule module : loadedModules) { currentContainer = containers.get(getContainerID(module)); @@ -164,9 +182,10 @@ public void onLoadComplete(FMLLoadCompleteEvent event) { module.loadComplete(event); module.getLogger().debug("Load-complete complete"); } + currentContainer = null; } - public void onServerAboutToStart(FMLServerAboutToStartEvent event) { + public void onServerAboutToStart(@NotNull FMLServerAboutToStartEvent event) { currentStage = ModuleStage.SERVER_ABOUT_TO_START; for (IGregTechModule module : loadedModules) { currentContainer = containers.get(getContainerID(module)); @@ -174,9 +193,10 @@ public void onServerAboutToStart(FMLServerAboutToStartEvent event) { module.serverAboutToStart(event); module.getLogger().debug("Server-about-to-start complete"); } + currentContainer = null; } - public void onServerStarting(FMLServerStartingEvent event) { + public void onServerStarting(@NotNull FMLServerStartingEvent event) { currentStage = ModuleStage.SERVER_STARTING; for (IGregTechModule module : loadedModules) { currentContainer = containers.get(getContainerID(module)); @@ -184,9 +204,10 @@ public void onServerStarting(FMLServerStartingEvent event) { module.serverStarting(event); module.getLogger().debug("Server-starting complete"); } + currentContainer = null; } - public void onServerStarted(FMLServerStartedEvent event) { + public void onServerStarted(@NotNull FMLServerStartedEvent event) { currentStage = ModuleStage.SERVER_STARTED; for (IGregTechModule module : loadedModules) { currentContainer = containers.get(getContainerID(module)); @@ -194,23 +215,31 @@ public void onServerStarted(FMLServerStartedEvent event) { module.serverStarted(event); module.getLogger().debug("Server-started complete"); } + currentContainer = null; } - public void onServerStopping(FMLServerStoppingEvent event) { + public void onServerStopping(@NotNull FMLServerStoppingEvent event) { for (IGregTechModule module : loadedModules) { currentContainer = containers.get(getContainerID(module)); module.serverStopping(event); } + currentContainer = null; } - public void onServerStopped(FMLServerStoppedEvent event) { + public void onServerStopped(@NotNull FMLServerStoppedEvent event) { for (IGregTechModule module : loadedModules) { currentContainer = containers.get(getContainerID(module)); module.serverStopped(event); } + currentContainer = null; } - public void processIMC(ImmutableList messages) { + /** + * Forward incoming IMC messages to each loaded module + * + * @param messages the messages to forward + */ + public void processIMC(@NotNull @Unmodifiable List messages) { for (FMLInterModComms.IMCMessage message : messages) { for (IGregTechModule module : loadedModules) { if (module.processIMC(message)) { @@ -220,11 +249,16 @@ public void processIMC(ImmutableList messages) { } } - private void configureModules(Map> modules) { + /** + * Configure the modules according to the module Configuration + * + * @param modules the modules to configure + */ + private void configureModules(@NotNull Map> modules) { Locale locale = Locale.getDefault(); Locale.setDefault(Locale.ENGLISH); - Set toLoad = new LinkedHashSet<>(); - Set modulesToLoad = new LinkedHashSet<>(); + Set toLoad = new ObjectLinkedOpenHashSet<>(); + Set modulesToLoad = new ReferenceLinkedOpenHashSet<>(); Configuration config = getConfiguration(); config.load(); config.addCustomCategoryComment(MODULE_CFG_CATEGORY_NAME, @@ -303,7 +337,11 @@ private void configureModules(Map> modules) { Locale.setDefault(locale); } - private static IGregTechModule getCoreModule(List modules) { + /** + * @param modules the list of modules possibly containing a Core Module + * @return the first found Core Module found + */ + private static @Nullable IGregTechModule getCoreModule(@NotNull Iterable modules) { for (IGregTechModule module : modules) { GregTechModule annotation = module.getClass().getAnnotation(GregTechModule.class); if (annotation.coreModule()) { @@ -313,14 +351,22 @@ private static IGregTechModule getCoreModule(List modules) { return null; } - private static String getContainerID(IGregTechModule module) { + /** + * @param module the module to get the container ID for + * @return the container ID + */ + private static @NotNull String getContainerID(@NotNull IGregTechModule module) { GregTechModule annotation = module.getClass().getAnnotation(GregTechModule.class); return annotation.containerID(); } - private Map> getModules(ASMDataTable table) { + /** + * @param table the ASM Data Table containing the module data + * @return a map of Container ID to list of associated modules sorted by Module ID + */ + private static @NotNull Map> getModules(@NotNull ASMDataTable table) { List instances = getInstances(table); - Map> modules = new LinkedHashMap<>(); + Map> modules = new Object2ReferenceLinkedOpenHashMap<>(); for (IGregTechModule module : instances) { GregTechModule info = module.getClass().getAnnotation(GregTechModule.class); modules.computeIfAbsent(info.containerID(), k -> new ArrayList<>()).add(module); @@ -328,45 +374,70 @@ private Map> getModules(ASMDataTable table) { return modules; } + /** + * @param table the ASM Data Table containing the module data + * @return all IGregTechModule instances in sorted order by Container and Module ID + */ @SuppressWarnings("unchecked") - private List getInstances(ASMDataTable table) { + private static @NotNull List getInstances(@NotNull ASMDataTable table) { Set dataSet = table.getAll(GregTechModule.class.getCanonicalName()); List instances = new ArrayList<>(); for (ASMDataTable.ASMData data : dataSet) { String moduleID = (String) data.getAnnotationInfo().get("moduleID"); - List modDependencies = (ArrayList) data.getAnnotationInfo().get("modDependencies"); + List modDependencies = (List) data.getAnnotationInfo().get("modDependencies"); if (modDependencies == null || modDependencies.stream().allMatch(Loader::isModLoaded)) { try { Class clazz = Class.forName(data.getClassName()); - instances.add((IGregTechModule) clazz.newInstance()); - } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { - logger.error("Could not initialize module " + moduleID, e); + if (IGregTechModule.class.isAssignableFrom(clazz)) { + instances.add((IGregTechModule) clazz.getConstructor().newInstance()); + } else { + logger.error("Module of class {} with id {} is not an instanceof IGregTechModule", + clazz.getName(), moduleID); + } + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | + NoSuchMethodException | InvocationTargetException e) { + logger.error("Could not initialize module {}", moduleID, e); } } else { logger.info("Module {} is missing at least one of mod dependencies: {}, skipping loading...", moduleID, modDependencies); } } - return instances.stream().sorted((m1, m2) -> { - GregTechModule m1a = m1.getClass().getAnnotation(GregTechModule.class); - GregTechModule m2a = m2.getClass().getAnnotation(GregTechModule.class); - return (m1a.containerID() + ":" + m1a.moduleID()).compareTo(m2a.containerID() + ":" + m2a.moduleID()); - }).collect(Collectors.toCollection(ArrayList::new)); + return instances.stream() + .sorted(Comparator.comparing((m) -> m.getClass() + .getAnnotation(GregTechModule.class), + Comparator.comparing(GregTechModule::containerID) + .thenComparing(GregTechModule::moduleID))) + .collect(Collectors.toList()); } - private void discoverContainers(ASMDataTable table) { + /** + * Discovers ModuleContainers and registers them + * + * @param table the table containing the ModuleContainer data + */ + private void discoverContainers(@NotNull ASMDataTable table) { Set dataSet = table.getAll(ModuleContainer.class.getCanonicalName()); for (ASMDataTable.ASMData data : dataSet) { try { Class clazz = Class.forName(data.getClassName()); - registerContainer((IModuleContainer) clazz.newInstance()); - } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { - logger.error("Could not initialize module container " + data.getClassName(), e); + if (IGregTechModule.class.isAssignableFrom(clazz)) { + registerContainer((IModuleContainer) clazz.getConstructor().newInstance()); + } else { + logger.error("Module Container Class {} is not an instanceof IModuleContainer", clazz.getName()); + } + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | + InvocationTargetException e) { + logger.error("Could not initialize module container {}", data.getClassName(), e); } } } - private String getComment(IGregTechModule module) { + /** + * @param module the module to get the comment for + * @return the comment for the module's configuration + */ + private static String getComment(@NotNull IGregTechModule module) { GregTechModule annotation = module.getClass().getAnnotation(GregTechModule.class); String comment = annotation.description(); @@ -399,7 +470,10 @@ private String getComment(IGregTechModule module) { return comment; } - private Configuration getConfiguration() { + /** + * @return the module configuration instance + */ + private @NotNull Configuration getConfiguration() { if (config == null) { config = new Configuration(new File(configFolder, MODULE_CFG_FILE_NAME)); }