All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.legendofdragoon.modloader.ModManager Maven / Gradle / Ivy

There is a newer version: 3.0.4
Show newest version
package org.legendofdragoon.modloader;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.reflections.Reflections;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

public class ModManager {
  private static final Logger LOGGER = LogManager.getFormatterLogger(ModManager.class);

  private final Map allModUrls = new HashMap<>();
  private final Map> allModClasses = new HashMap<>();
  private final Set allModIds = Collections.unmodifiableSet(this.allModClasses.keySet());

  private final List loadedModUrls = new ArrayList<>();
  private ClassLoader loadedModClassLoader;
  private final Map loadedModInstances = new HashMap<>();
  private final Collection unmodifiableLoadedModInstances = Collections.unmodifiableCollection(this.loadedModInstances.values());

  public ModManager(final Consumer access) {
    access.accept(new Access());
  }

  public Set getAllModIds() {
    return this.allModIds;
  }

  public boolean isLoaded(final String modId) {
    return this.loadedModInstances.containsKey(modId);
  }

  public boolean isReady(final String modId) {
    return this.loadedModInstances.containsKey(modId) && this.loadedModInstances.get(modId).state.isReady();
  }

  public Collection getLoadedMods() {
    return this.unmodifiableLoadedModInstances;
  }

  public class Access {
    private Access() { }

    public void findMods() throws IOException {
      final Path modsDir = Path.of("./mods");
      Files.createDirectories(modsDir);

      LOGGER.info("Scanning for mods...");
      final Collection urlList = new ArrayList<>();

      try(final DirectoryStream jars = Files.newDirectoryStream(modsDir, "*.jar")) {
        for(final Path jar : jars) {
          urlList.add(jar.toUri().toURL());
        }
      }

      final ClassLoader allModClassLoader = new URLClassLoader(urlList.toArray(URL[]::new), ModManager.class.getClassLoader());
      ModManager.this.allModUrls.clear();

      final Reflections reflections = new Reflections(
        new ConfigurationBuilder()
          .addUrls(this.getClass().getClassLoader().getResource(""))
          .addClassLoaders(this.getClass().getClassLoader()).addUrls(ClasspathHelper.forPackage("legend"))
          .addClassLoaders(allModClassLoader).addUrls(urlList)
      );

      final Set> modClasses = reflections.getTypesAnnotatedWith(Mod.class);

      for(final Class modClass : modClasses) {
        final Mod modAnnotation = modClass.getDeclaredAnnotation(Mod.class);

        if(ModManager.this.allModClasses.containsKey(modAnnotation.id())) {
          LOGGER.error("Duplicate mod ID %s! Skipping.", modAnnotation.id());
          continue;
        }

        LOGGER.info("Found mod: %s", modAnnotation.id());
        ModManager.this.allModClasses.put(modAnnotation.id(), modClass);
        ModManager.this.allModUrls.put(modAnnotation.id(), modClass.getProtectionDomain().getCodeSource().getLocation());
      }
    }

    /**
     * Load all found mods
     */
    public void loadMods() {
      this.loadMods(ModManager.this.allModIds);
    }

    /**
     * Load all mods in the list and return a list of missing mods
     */
    public Set loadMods(final Set modIds) {
      ModManager.this.loadedModUrls.clear();

      final Set missingModIds = new HashSet<>();
      for(final String modId : modIds) {
        if(ModManager.this.allModClasses.containsKey(modId)) {
          this.instantiateMod(modId);
        } else {
          missingModIds.add(modId);
        }
      }

      ModManager.this.loadedModClassLoader = new URLClassLoader(ModManager.this.loadedModUrls.toArray(URL[]::new), ModManager.class.getClassLoader());
      return missingModIds;
    }

    private void instantiateMod(final String modId) {
      try {
        final ModContainer modContainer = new ModContainer(modId, ModManager.this.allModClasses.get(modId).getDeclaredConstructor().newInstance());
        ModManager.this.loadedModInstances.put(modId, modContainer);
        ModManager.this.loadedModUrls.add(ModManager.this.allModUrls.get(modId));
        LOGGER.info("Loaded mod: %s", modId);
      } catch(final InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
        LOGGER.warn("FAILED TO LOAD MOD: %s", modId);
        LOGGER.warn("Exception:", ex);
      }
    }

    public void loadingComplete() {
      for(final ModContainer container : ModManager.this.loadedModInstances.values()) {
        container.state = ModState.READY;
      }
    }

    public void reset() {
      ModManager.this.loadedModUrls.clear();
      ModManager.this.loadedModClassLoader = null;
      ModManager.this.loadedModInstances.clear();
    }
  }

  public ConfigurationBuilder addModsToReflectionsConfig(final ConfigurationBuilder builder) {
    return builder
      .addUrls(this.getClass().getClassLoader().getResource("")) // Find mods in the current project (finds CoreMod in SC, mod in SCDK)
      .addClassLoaders(this.getClass().getClassLoader()).addUrls(ClasspathHelper.forPackage("legend")) // Finds CoreMod in SCDK
      .addClassLoaders(this.loadedModClassLoader).addUrls(this.loadedModUrls); // Finds mods in mods folder
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy