aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
author zaaarf <me@zaaarf.foo>2024-01-10 03:23:24 +0100
committer zaaarf <me@zaaarf.foo>2024-01-10 03:23:24 +0100
commit067b2e537deac2d053b0c6b1cfb22e7f191830c7 (patch)
tree2caaf4c4d0a70d45745c9c177fefc1a6e49da43e /src/main
parent63cfbaa56ccf67da7b65cf058a68a4b843af5748 (diff)
feat: basic implementation of macro system/Bindings
Co-authored-by: alemi <me@alemi.dev>
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/com/badlogic/gdx/utils/SharedLibraryLoadRuntimeException.java32
-rw-r--r--src/main/java/com/badlogic/gdx/utils/SharedLibraryLoader.java355
-rw-r--r--src/main/java/ftbsc/bscv/Boscovicino.java17
-rw-r--r--src/main/java/ftbsc/bscv/system/Bindings.java89
-rw-r--r--src/main/java/ftbsc/bscv/system/Macros.java50
-rw-r--r--src/main/java/ftbsc/bscv/system/ModManager.java10
6 files changed, 548 insertions, 5 deletions
diff --git a/src/main/java/com/badlogic/gdx/utils/SharedLibraryLoadRuntimeException.java b/src/main/java/com/badlogic/gdx/utils/SharedLibraryLoadRuntimeException.java
new file mode 100644
index 0000000..6af5474
--- /dev/null
+++ b/src/main/java/com/badlogic/gdx/utils/SharedLibraryLoadRuntimeException.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright 2011 See AUTHORS file.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.badlogic.gdx.utils;
+
+public class SharedLibraryLoadRuntimeException extends RuntimeException {
+ private static final long serialVersionUID = 8263101105331379889L;
+
+ public SharedLibraryLoadRuntimeException(String message) {
+ super(message);
+ }
+
+ public SharedLibraryLoadRuntimeException(Throwable t) {
+ super(t);
+ }
+
+ public SharedLibraryLoadRuntimeException(String message, Throwable t) {
+ super(message, t);
+ }
+}
diff --git a/src/main/java/com/badlogic/gdx/utils/SharedLibraryLoader.java b/src/main/java/com/badlogic/gdx/utils/SharedLibraryLoader.java
new file mode 100644
index 0000000..554b862
--- /dev/null
+++ b/src/main/java/com/badlogic/gdx/utils/SharedLibraryLoader.java
@@ -0,0 +1,355 @@
+/*******************************************************************************
+ * Copyright 2011 See AUTHORS file.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+package com.badlogic.gdx.utils;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.UUID;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/** Loads shared libraries from a natives jar file (desktop) or arm folders (Android). For desktop projects, have the natives jar
+ * in the classpath, for Android projects put the shared libraries in the libs/armeabi and libs/armeabi-v7a folders.
+ * @author mzechner
+ * @author Nathan Sweet */
+public class SharedLibraryLoader {
+ static public boolean isWindows = System.getProperty("os.name").contains("Windows");
+ static public boolean isLinux = System.getProperty("os.name").contains("Linux");
+ static public boolean isMac = System.getProperty("os.name").contains("Mac");
+ static public boolean isIos = false;
+ static public boolean isAndroid = false;
+ static public boolean isARM = System.getProperty("os.arch").startsWith("arm") || System.getProperty("os.arch").startsWith("aarch64");
+ static public boolean is64Bit = System.getProperty("os.arch").contains("64") || System.getProperty("os.arch").startsWith("armv8");
+
+ static {
+ boolean isMOEiOS = System.getProperty("moe.platform.name") != null;
+ String vm = System.getProperty("java.runtime.name");
+ if (vm != null && vm.contains("Android Runtime")) {
+ isAndroid = true;
+ isWindows = false;
+ isLinux = false;
+ isMac = false;
+ is64Bit = false;
+ }
+ if (isMOEiOS || (!isAndroid && !isWindows && !isLinux && !isMac)) {
+ isIos = true;
+ isAndroid = false;
+ isWindows = false;
+ isLinux = false;
+ isMac = false;
+ is64Bit = false;
+ }
+ }
+
+ static private final HashSet<String> loadedLibraries = new HashSet<>();
+ static private final Random random = new Random();
+
+ private String nativesJar;
+
+ public SharedLibraryLoader () {
+ }
+
+ static String randomUUID () {
+ return new UUID(random.nextLong(), random.nextLong()).toString();
+ }
+
+ /** Fetches the natives from the given natives jar file. Used for testing a shared lib on the fly.
+ * @param nativesJar */
+ public SharedLibraryLoader (String nativesJar) {
+ this.nativesJar = nativesJar;
+ }
+
+ /** Returns a CRC of the remaining bytes in the stream. */
+ public String crc (InputStream input) {
+ if (input == null) throw new IllegalArgumentException("input cannot be null.");
+ CRC32 crc = new CRC32();
+ byte[] buffer = new byte[4096];
+ try {
+ while (true) {
+ int length = input.read(buffer);
+ if (length == -1) break;
+ crc.update(buffer, 0, length);
+ }
+ } catch (Exception ex) {
+ } finally {
+ closeQuietly(input);
+ }
+ return Long.toString(crc.getValue(), 16);
+ }
+
+ /** Maps a platform independent library name to a platform dependent name. */
+ public String mapLibraryName (String libraryName) {
+ if (isWindows) return libraryName + (is64Bit ? "64.dll" : ".dll");
+ if (isLinux) return "lib" + libraryName + (isARM ? "arm" : "") + (is64Bit ? "64.so" : ".so");
+ if (isMac) return "lib" + libraryName + (isARM ? "arm" : "") + (is64Bit ? "64.dylib" : ".dylib");
+ return libraryName;
+ }
+
+ /** Loads a shared library for the platform the application is running on.
+ * @param libraryName The platform independent library name. If not contain a prefix (eg lib) or suffix (eg .dll). */
+ public void load (String libraryName) {
+ // in case of iOS, things have been linked statically to the executable, bail out.
+ if (isIos) return;
+
+ synchronized (SharedLibraryLoader.class) {
+ if (isLoaded(libraryName)) return;
+ String platformName = mapLibraryName(libraryName);
+ try {
+ if (isAndroid)
+ System.loadLibrary(platformName);
+ else
+ loadFile(platformName);
+ setLoaded(libraryName);
+ } catch (Throwable ex) {
+ throw new SharedLibraryLoadRuntimeException("Couldn't load shared library '" + platformName + "' for target: "
+ + (isAndroid ? "Android" : (System.getProperty("os.name") + (isARM ? ", ARM" : "") + (is64Bit ? ", 64-bit" : ", 32-bit"))),
+ ex);
+ }
+ }
+ }
+
+ private InputStream readFile (String path) {
+ if (nativesJar == null) {
+ InputStream input = SharedLibraryLoader.class.getResourceAsStream("/" + path);
+ if (input == null) throw new SharedLibraryLoadRuntimeException("Unable to read file for extraction: " + path);
+ return input;
+ }
+
+ // Read from JAR.
+ try {
+ ZipFile file = new ZipFile(nativesJar);
+ ZipEntry entry = file.getEntry(path);
+ if (entry == null) throw new SharedLibraryLoadRuntimeException("Couldn't find '" + path + "' in JAR: " + nativesJar);
+ return file.getInputStream(entry);
+ } catch (IOException ex) {
+ throw new SharedLibraryLoadRuntimeException("Error reading '" + path + "' in JAR: " + nativesJar, ex);
+ }
+ }
+
+ /** Extracts the specified file to the specified directory if it does not already exist or the CRC does not match. If file
+ * extraction fails and the file exists at java.library.path, that file is returned.
+ * @param sourcePath The file to extract from the classpath or JAR.
+ * @param dirName The name of the subdirectory where the file will be extracted. If null, the file's CRC will be used.
+ * @return The extracted file. */
+ public File extractFile (String sourcePath, String dirName) throws IOException {
+ try {
+ String sourceCrc = crc(readFile(sourcePath));
+ if (dirName == null) dirName = sourceCrc;
+
+ File extractedFile = getExtractedFile(dirName, new File(sourcePath).getName());
+ if (extractedFile == null) {
+ extractedFile = getExtractedFile(randomUUID(), new File(sourcePath).getName());
+ if (extractedFile == null) throw new SharedLibraryLoadRuntimeException(
+ "Unable to find writable path to extract file. Is the user home directory writable?");
+ }
+ return extractFile(sourcePath, sourceCrc, extractedFile);
+ } catch (RuntimeException ex) {
+ // Fallback to file at java.library.path location, eg for applets.
+ File file = new File(System.getProperty("java.library.path"), sourcePath);
+ if (file.exists()) return file;
+ throw ex;
+ }
+ }
+
+ /** Extracts the specified file into the temp directory if it does not already exist or the CRC does not match. If file
+ * extraction fails and the file exists at java.library.path, that file is returned.
+ * @param sourcePath The file to extract from the classpath or JAR.
+ * @param dir The location where the extracted file will be written. */
+ public void extractFileTo (String sourcePath, File dir) throws IOException {
+ extractFile(sourcePath, crc(readFile(sourcePath)), new File(dir, new File(sourcePath).getName()));
+ }
+
+ /** Returns a path to a file that can be written. Tries multiple locations and verifies writing succeeds.
+ * @return null if a writable path could not be found. */
+ private File getExtractedFile (String dirName, String fileName) {
+ // Temp directory with username in path.
+ File idealFile = new File(
+ System.getProperty("java.io.tmpdir") + "/libgdx" + System.getProperty("user.name") + "/" + dirName, fileName);
+ if (canWrite(idealFile)) return idealFile;
+
+ // System provided temp directory.
+ try {
+ File file = File.createTempFile(dirName, null);
+ if (file.delete()) {
+ file = new File(file, fileName);
+ if (canWrite(file)) return file;
+ }
+ } catch (IOException ignored) {
+ }
+
+ // User home.
+ File file = new File(System.getProperty("user.home") + "/.libgdx/" + dirName, fileName);
+ if (canWrite(file)) return file;
+
+ // Relative directory.
+ file = new File(".temp/" + dirName, fileName);
+ if (canWrite(file)) return file;
+
+ // We are running in the OS X sandbox.
+ if (System.getenv("APP_SANDBOX_CONTAINER_ID") != null) return idealFile;
+
+ return null;
+ }
+
+ /** Returns true if the parent directories of the file can be created and the file can be written. */
+ private boolean canWrite (File file) {
+ File parent = file.getParentFile();
+ File testFile;
+ if (file.exists()) {
+ if (!file.canWrite() || !canExecute(file)) return false;
+ // Don't overwrite existing file just to check if we can write to directory.
+ testFile = new File(parent, randomUUID().toString());
+ } else {
+ parent.mkdirs();
+ if (!parent.isDirectory()) return false;
+ testFile = file;
+ }
+ try {
+ new FileOutputStream(testFile).close();
+ if (!canExecute(testFile)) return false;
+ return true;
+ } catch (Throwable ex) {
+ return false;
+ } finally {
+ testFile.delete();
+ }
+ }
+
+ private boolean canExecute (File file) {
+ try {
+ Method canExecute = File.class.getMethod("canExecute");
+ if ((Boolean)canExecute.invoke(file)) return true;
+
+ Method setExecutable = File.class.getMethod("setExecutable", boolean.class, boolean.class);
+ setExecutable.invoke(file, true, false);
+
+ return (Boolean)canExecute.invoke(file);
+ } catch (Exception ignored) {
+ }
+ return false;
+ }
+
+ private File extractFile (String sourcePath, String sourceCrc, File extractedFile) throws IOException {
+ String extractedCrc = null;
+ if (extractedFile.exists()) {
+ try {
+ extractedCrc = crc(new FileInputStream(extractedFile));
+ } catch (FileNotFoundException ignored) {
+ }
+ }
+
+ // If file doesn't exist or the CRC doesn't match, extract it to the temp dir.
+ if (extractedCrc == null || !extractedCrc.equals(sourceCrc)) {
+ InputStream input = null;
+ FileOutputStream output = null;
+ try {
+ input = readFile(sourcePath);
+ extractedFile.getParentFile().mkdirs();
+ output = new FileOutputStream(extractedFile);
+ byte[] buffer = new byte[4096];
+ while (true) {
+ int length = input.read(buffer);
+ if (length == -1) break;
+ output.write(buffer, 0, length);
+ }
+ } catch (IOException ex) {
+ throw new SharedLibraryLoadRuntimeException("Error extracting file: " + sourcePath + "\nTo: " + extractedFile.getAbsolutePath(),
+ ex);
+ } finally {
+ closeQuietly(input);
+ closeQuietly(output);
+ }
+ }
+
+ return extractedFile;
+ }
+
+ /** Extracts the source file and calls System.load. Attemps to extract and load from multiple locations. Throws runtime
+ * exception if all fail. */
+ private void loadFile (String sourcePath) {
+ String sourceCrc = crc(readFile(sourcePath));
+
+ String fileName = new File(sourcePath).getName();
+
+ // Temp directory with username in path.
+ File file = new File(System.getProperty("java.io.tmpdir") + "/libgdx" + System.getProperty("user.name") + "/" + sourceCrc,
+ fileName);
+ Throwable ex = loadFile(sourcePath, sourceCrc, file);
+ if (ex == null) return;
+
+ // System provided temp directory.
+ try {
+ file = File.createTempFile(sourceCrc, null);
+ if (file.delete() && loadFile(sourcePath, sourceCrc, file) == null) return;
+ } catch (Throwable ignored) {
+ }
+
+ // User home.
+ file = new File(System.getProperty("user.home") + "/.libgdx/" + sourceCrc, fileName);
+ if (loadFile(sourcePath, sourceCrc, file) == null) return;
+
+ // Relative directory.
+ file = new File(".temp/" + sourceCrc, fileName);
+ if (loadFile(sourcePath, sourceCrc, file) == null) return;
+
+ // Fallback to java.library.path location, eg for applets.
+ file = new File(System.getProperty("java.library.path"), sourcePath);
+ if (file.exists()) {
+ System.load(file.getAbsolutePath());
+ return;
+ }
+
+ throw new SharedLibraryLoadRuntimeException(ex);
+ }
+
+ /** @return null if the file was extracted and loaded. */
+ private Throwable loadFile (String sourcePath, String sourceCrc, File extractedFile) {
+ try {
+ System.load(extractFile(sourcePath, sourceCrc, extractedFile).getAbsolutePath());
+ return null;
+ } catch (Throwable ex) {
+ return ex;
+ }
+ }
+
+ /** Sets the library as loaded, for when application code wants to handle libary loading itself. */
+ static public synchronized void setLoaded (String libraryName) {
+ loadedLibraries.add(libraryName);
+ }
+
+ static public synchronized boolean isLoaded (String libraryName) {
+ return loadedLibraries.contains(libraryName);
+ }
+
+ public static void closeQuietly (Closeable c) {
+ if (c != null) {
+ try {
+ c.close();
+ } catch (Throwable ignored) {
+ }
+ }
+ }
+}
diff --git a/src/main/java/ftbsc/bscv/Boscovicino.java b/src/main/java/ftbsc/bscv/Boscovicino.java
index 8d6ab67..cf03753 100644
--- a/src/main/java/ftbsc/bscv/Boscovicino.java
+++ b/src/main/java/ftbsc/bscv/Boscovicino.java
@@ -6,9 +6,7 @@ import com.mojang.brigadier.tree.CommandNode;
import ftbsc.bscv.api.IModule;
import ftbsc.bscv.patches.CommandsPatch.CommandsBuiltEvent;
-import ftbsc.bscv.system.Friends;
-import ftbsc.bscv.system.ModManager;
-import ftbsc.bscv.system.Ruler;
+import ftbsc.bscv.system.*;
import net.minecraft.client.gui.screen.IngameMenuScreen;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.command.CommandSource;
@@ -27,6 +25,7 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import java.io.IOException;
@Mod("bscv")
public class Boscovicino implements ICommons {
@@ -34,7 +33,7 @@ public class Boscovicino implements ICommons {
public static final Logger LOGGER = LogManager.getLogger();
- public static ModManager modManager; //todo this should not be static
+ public static ModManager modManager; //todo rename
private final CommandDispatcher<CommandSource> dispatcher = new CommandDispatcher<>();
@@ -46,7 +45,10 @@ public class Boscovicino implements ICommons {
@SuppressWarnings("unused") // it just needs to exist to be used by player
private static Ruler ruler;
- public Boscovicino() {
+ public static Bindings bindings;
+ public static Macros macros;
+
+ public Boscovicino() throws IOException {
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onSetupComplete);
ForgeConfigSpec.Builder cfg = new ForgeConfigSpec.Builder();
@@ -59,6 +61,10 @@ public class Boscovicino implements ICommons {
Boscovicino.ruler = new Ruler();
+ ForgeConfigSpec.Builder bindingSpec = new ForgeConfigSpec.Builder();
+ Boscovicino.bindings = new Bindings(bindingSpec);
+ Boscovicino.macros = new Macros();
+
Boscovicino.spec = cfg.build();
ForgeConfigSpec.Builder friendSpec = new ForgeConfigSpec.Builder();
@@ -67,6 +73,7 @@ public class Boscovicino implements ICommons {
// register config handler
ModLoadingContext.get().registerConfig(Type.CLIENT, spec, "bscv.toml");
ModLoadingContext.get().registerConfig(Type.CLIENT, friendSpec.build(), "friends.toml");
+ ModLoadingContext.get().registerConfig(Type.CLIENT, bindingSpec.build(), "bindings.toml");
// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);
diff --git a/src/main/java/ftbsc/bscv/system/Bindings.java b/src/main/java/ftbsc/bscv/system/Bindings.java
new file mode 100644
index 0000000..7081d42
--- /dev/null
+++ b/src/main/java/ftbsc/bscv/system/Bindings.java
@@ -0,0 +1,89 @@
+package ftbsc.bscv.system;
+
+import ftbsc.bscv.Boscovicino;
+import ftbsc.bscv.api.IModule;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.settings.KeyBinding;
+import net.minecraft.client.util.InputMappings;
+import net.minecraftforge.client.event.InputEvent;
+import net.minecraftforge.common.ForgeConfigSpec;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.eventbus.api.SubscribeEvent;
+import net.minecraftforge.fml.client.registry.ClientRegistry;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+
+public class Bindings {
+ public static final int UNBOUND = InputMappings.UNKNOWN.getValue();
+
+ private final HashMap<KeyBinding, Runnable> executorMap;
+
+ private ForgeConfigSpec.ConfigValue<?> store;
+
+ public Bindings(ForgeConfigSpec.Builder spec) {
+ MinecraftForge.EVENT_BUS.register(this);
+ this.executorMap = new HashMap<>();
+ this.store = spec
+ //.comment("actual friend list")
+ .define("lol", () -> null, (u) -> {
+ System.out.println(u.getClass().getName());
+ return true;
+ });
+ }
+
+ public KeyBinding registerBinding(String name, int key, IModule module) {
+ KeyBinding kb = this.createBinding(name, key);
+ this.executorMap.put(kb, module::toggle);
+ return kb;
+ }
+
+ public KeyBinding registerBinding(String name, int key, String macroFileName) {
+ KeyBinding kb = this.createBinding(name, key);
+ this.executorMap.put(kb, () -> {
+ Boscovicino.macros.execute(macroFileName);
+ });
+ return kb;
+ }
+
+ private KeyBinding createBinding(String name, int key) {
+ KeyBinding kb = new KeyBinding(getBindName(name), UNBOUND, getCategory());
+ InputMappings.Input in = 0 <= key && key <= 7
+ ? InputMappings.Type.MOUSE.getOrCreate(key)
+ : InputMappings.Type.KEYSYM.getOrCreate(key);
+ kb.setKey(in);
+ ClientRegistry.registerKeyBinding(kb);
+ return kb;
+ }
+
+ @SubscribeEvent
+ public void onKeyPress(InputEvent.KeyInputEvent event) {
+ this.onKeyPress(event.getKey());
+ }
+
+ @SubscribeEvent
+ public void onKeyPress(InputEvent.MouseInputEvent event) {
+ this.onKeyPress(event.getButton());
+ }
+
+ //TODO on keybind change event
+ private void onKeyPress(int key) {
+ long windowId = Minecraft.getInstance().getWindow().getWindow();
+ for(KeyBinding kb : this.executorMap.keySet()) {
+ if(kb.getKey().getValue() == key) {
+ if(InputMappings.isKeyDown(windowId, key)) this.executorMap.get(kb).run();
+ return;
+ }
+ }
+ }
+
+ private static String getBindName(String name) {
+ return String.format("key.%s.%s", Boscovicino.MOD_ID, name);
+ }
+
+ private static String getCategory() {
+ return String.format("key.category.%s", Boscovicino.MOD_ID);
+ }
+}
diff --git a/src/main/java/ftbsc/bscv/system/Macros.java b/src/main/java/ftbsc/bscv/system/Macros.java
new file mode 100644
index 0000000..ca8d56f
--- /dev/null
+++ b/src/main/java/ftbsc/bscv/system/Macros.java
@@ -0,0 +1,50 @@
+package ftbsc.bscv.system;
+
+import ftbsc.bscv.Boscovicino;
+import party.iroiro.luajava.Lua;
+import party.iroiro.luajava.luajit.LuaJit;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+
+public class Macros {
+ private final Lua lua;
+ private final HashMap<String, String> macroCache;
+
+ public Macros() throws IOException {
+ this.lua = new LuaJit();
+ this.lua.pushJavaClass(Boscovicino.class); //TODO use instance
+ this.lua.setGlobal("BSCV");
+ this.macroCache = new HashMap<>();
+
+ // load macros
+ Path path = Paths.get("macros/");
+ if(!Files.isDirectory(path)) {
+ Files.createDirectories(path);
+ }
+
+ //TODO lazy load setting
+ try(DirectoryStream<Path> stream = Files.newDirectoryStream(path, (p) -> p.toFile().isFile())) {
+ for(Path macro : stream) {
+ String name = macro.getFileName().toString();
+ if(name.endsWith(".lua")) {
+ String code = String.join("\n", Files.readAllLines(macro));
+ this.macroCache.put(name, code);
+ Boscovicino.bindings.registerBinding(name, Bindings.UNBOUND, name);
+ }
+ }
+ }
+ }
+
+ public boolean execute(String macroFilePath) {
+ String code = this.macroCache.get(macroFilePath);
+ if(code == null) return false;
+
+ this.lua.execute(code);
+ return true;
+ }
+}
diff --git a/src/main/java/ftbsc/bscv/system/ModManager.java b/src/main/java/ftbsc/bscv/system/ModManager.java
index 9420d44..9dea139 100644
--- a/src/main/java/ftbsc/bscv/system/ModManager.java
+++ b/src/main/java/ftbsc/bscv/system/ModManager.java
@@ -39,6 +39,16 @@ public class ModManager {
return null;
}
+ @Nullable
+ public IModule get(String name) {
+ for (IModule m : this.mods) {
+ if (m.getName().equals(name)) {
+ return m;
+ }
+ }
+ return null;
+ }
+
public void load() {
for (ILoadable module : ServiceLoader.load(ILoadable.class)) {
if(module instanceof IModule) {