mmb.engine.mods.ModLoader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of multimachinebuilder Show documentation
Show all versions of multimachinebuilder Show documentation
Dependency for the MultiMachineBuilder, a voxel game about building an industrial empire in a finite world.
THIS RELEASE IS NOT PLAYABLE. To play the game, donwload from >ITCH.IO LINK HERE< or >GH releases link here<
The newest version!
/**
*
*/
package mmb.engine.mods;
import java.io.*;
import java.util.function.*;
import org.apache.commons.compress.archivers.jar.JarArchiveEntry;
import org.apache.commons.compress.archivers.jar.JarArchiveInputStream;
import org.apache.commons.io.IOUtils;
import io.github.micwan88.helperclass4j.ByteClassLoader;
import mmb.NN;
import mmb.engine.MMBUtils;
import mmb.engine.debug.*;
import mmb.engine.files.AdvancedFile;
/**
* A class which loads a single file
* @author oskar
*/
public class ModLoader {
/** The file to load*/
public final AdvancedFile file;
/** This mod loader's loaded modfile */
@NN public final Modfile modfile;
/** The path reference to the mod */
@NN public final String originalPath;
/** The display name of this mod loader */
@NN public final String name;
@NN private Thread runningOn;
/**
* Waits until mod finishes loading
* @throws InterruptedException when loading stops abruptly
*/
public void untilLoad() throws InterruptedException{
runningOn.join();
}
/**
* Can use optional handler, for this purpose see load(Path, Consumer)
* Load addon from path
* @param source mod file
* @return new loader
*/
public static ModLoader load(AdvancedFile source) {
return new ModLoader(source);
}
/**
* Uses optional handler, for function without handler see load(Path)
* Load addon from path
* @param source mod file
* @param handler optional handler
* @return new loader
*/
public static ModLoader load(AdvancedFile source, Consumer handler) {
return new ModLoader(source, handler);
}
/**
* Creates a mod-loader
* @param a
* @param originalPath
*/
protected ModLoader(AdvancedFile source) {
this(source, MMBUtils.doNothing());
}
protected ModLoader(AdvancedFile source, Consumer handler) {
super();
this.originalPath = source.name();
this.file = source;
modfile = new Modfile(originalPath, file);
String name0 = originalPath;
if(!modfile.isOnline()) name0 = AdvancedFile.dirName(file.name())[1];
name = name0;
runningOn = new Thread(() -> {init(); Mods.files.add(modfile); handler.accept(modfile);});
runningOn.start();
}
/** Loads the file */
private void init() {
//Part -1: initialize
String debugName = AdvancedFile.dirName(file.name())[1];
Debugger debug = new Debugger("MOD - "+ debugName);
//Part 0: pre-download checks
if(file.isDirectory()) {
debug.printl("The mod is a directory, aborting");
modfile.state = ModfileState.DIRECTORY;
return;
}
//Part 1: download
byte[] data = null;
try(InputStream in = file.getInputStream()) {
debug.printl("Opening a modfile: " + file.name());
if(!file.exists()) {
debug.printl(file.name()+" does not exist.");
modfile.state = ModfileState.NOEXIST;
return;
}
data = IOUtils.toByteArray(in); //take in byte array and load data
}catch(IllegalArgumentException e) {
if("Cannot read more than 2 147 483 647 into a byte array".equals(e.getMessage())) {
debug.stacktraceError(e, "The mod is too big to load");
modfile.state = ModfileState.BLOATED;
}else {
debug.stacktraceError(e, "Make sure that \"" + name + "\" is not mistyped.");
modfile.state = ModfileState.NOEXIST;
}
}catch(IOException e) {
if(modfile.isOnline()) debug.stacktraceError(e, "Failed to download the file " + file.name());
else debug.stacktraceError(e, "Failed to read the file " + file.name());
modfile.state = ModfileState.NOEXIST;
}catch(Exception e) {
debug.stacktraceError(e, "Couldn't read " + name + " for unknown reasons");
modfile.state = ModfileState.NOEXIST;
}catch(OutOfMemoryError e) {
debug.stacktraceError(e, "The mod is too big to load");
modfile.state = ModfileState.BLOATED;
}
//Part 1a: check for empty byte arrays
if(data == null || data.length == 0) {
modfile.state = ModfileState.EMPTY;
return;
}
//Part 2: classpath load the file
try{
bcl.loadJarDataInBytes(data);
}catch (ClassFormatError e) {
debug.stacktraceError(e, "Invalid class file");
modfile.state = ModfileState.DEAD;
} catch(NoClassDefFoundError e) {
debug.stacktraceError(e, "Missing dependencies");
modfile.state = ModfileState.DEAD;
} catch (IOException e) {
debug.printl("Couldn't open zip or jar file " + file.name());
debug.stacktrace(e);
modfile.state = ModfileState.BROKEN;
} catch (Exception e) {
debug.stacktraceError(e, "Couldn't process classfile(s). Please check if class file exists and works.");
modfile.state = ModfileState.DEAD;
}
if(modfile.state != ModfileState.MEDIA) return; //The file is broken
//Part 3: find contents
try(JarArchiveInputStream jis = new JarArchiveInputStream(new ByteArrayInputStream(data))) {
while(true) {
JarArchiveEntry je = jis.getNextJarEntry();
if(je == null) break; //No more entries, break
if(modfile.state != ModfileState.DEAD && je.getName().endsWith(".class")) modfile.state = ModfileState.API;
debug.printl("entry: "+je.getName());
if(je.getName().endsWith(".class"))
modfile.addClassName(je.getName());
modfile.hasValidData = true;
}
}catch(Exception e) {
debug.printl("Couldn't open zip or jar file " + file.name());
debug.stacktrace(e);
modfile.state = ModfileState.BROKEN;
return;
}
//Part 3A: mark empty mods
if(!modfile.hasValidData) {
modfile.state = ModfileState.EMPTY;
}
}
/** The class loader used to load mods */
public static final ByteClassLoader bcl = new ByteClassLoader(ClassLoader.getSystemClassLoader());
}