mirror of
https://github.com/LegacyModdingMC/UniMixins.git
synced 2026-10-26 14:34:00 +00:00
Add Mixingasm module
This commit is contained in:
1 parent
2e751a1937
commit
aa2a36e15a
16 files changed
+434
-2
No files matched your search
@@ -25,3 +25,17 @@ Modifications:
|
|||||||
- made reflection support Mixin 0.7.11
|
- made reflection support Mixin 0.7.11
|
||||||
|
|
||||||
License: LGPL3 (see LICENSE.LGPLv3)
|
License: LGPL3 (see LICENSE.LGPLv3)
|
||||||
|
|
||||||
|
|
||||||
|
## module-mixingasm
|
||||||
|
|
||||||
|
Source: https://github.com/makamys/Mixingasm/tree/v0.2.2
|
||||||
|
|
||||||
|
Classes used:
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
Modifications:
|
||||||
|
- removed update check in MixingasmMod
|
||||||
|
|
||||||
|
License: Unlicense (see LICENSE.UNLICENSE)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <http://unlicense.org/>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
archivesBaseName = archivesBaseName.replace("-1.7.10", "-all-1.7.10")
|
archivesBaseName = archivesBaseName.replace("-1.7.10", "-all-1.7.10")
|
||||||
|
|
||||||
// The order of these determines the order embedded core plugins get initialized in
|
// The order of these determines the order embedded core plugins get initialized in
|
||||||
ext.moduleNames = ["module-mixin", "module-compat", "module-spongemixins", "module-gtnhmixins"]
|
ext.moduleNames = ["module-mixin", "module-compat", "module-mixingam", "module-spongemixins", "module-gtnhmixins"]
|
||||||
|
|
||||||
def getModuleJars() {
|
def getModuleJars() {
|
||||||
return moduleNames.collect { it -> tasks.getByPath(":$it:jar").getOutputs().getFiles().getSingleFile() }
|
return moduleNames.collect { it -> tasks.getByPath(":$it:jar").getOutputs().getFiles().getSingleFile() }
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
name = 'sponge'
|
||||||
|
url = 'https://repo.spongepowered.org/maven/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile('org.spongepowered:mixin:0.7.11-SNAPSHOT')
|
||||||
|
}
|
||||||
|
|
||||||
|
archivesBaseName = archivesBaseName.replace("-1.7.10", "-spongemixins-1.7.10")
|
||||||
|
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes (
|
||||||
|
'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
|
||||||
|
'FMLCorePluginContainsFMLMod': true,
|
||||||
|
'ForceLoadAsMod': true,
|
||||||
|
'MixinConfigs': 'mixingasm.mixin.json',
|
||||||
|
'TweakOrder': 0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ext.extraVersion = "0.2.2"
|
||||||
|
version += "+" + ext.extraVersion
|
||||||
|
|
||||||
|
minecraft {
|
||||||
|
replace("@VERSION@", extraVersion)
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package makamys.mixingasm;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import net.minecraft.launchwrapper.Launch;
|
||||||
|
|
||||||
|
public class ConfigHelper {
|
||||||
|
|
||||||
|
private final String MODID;
|
||||||
|
private final Logger LOGGER;
|
||||||
|
|
||||||
|
public ConfigHelper(String modid) {
|
||||||
|
this.MODID = modid;
|
||||||
|
this.LOGGER = LogManager.getLogger(MODID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getDefaultConfigFilePath(Path relPath) throws IOException {
|
||||||
|
String resourceRelPath = Paths.get("assets/" + MODID + "/default_config/").resolve(relPath).toString().replace('\\', '/');
|
||||||
|
URL resourceURL = new Object() { }.getClass().getEnclosingClass().getClassLoader().getResource(resourceRelPath);
|
||||||
|
|
||||||
|
switch(resourceURL.getProtocol()) {
|
||||||
|
case "jar":
|
||||||
|
String urlString = resourceURL.getPath();
|
||||||
|
int lastExclamation = urlString.lastIndexOf('!');
|
||||||
|
String newURLString = urlString.substring(0, lastExclamation);
|
||||||
|
return FileSystems.newFileSystem(new File(URI.create(newURLString)).toPath(), null).getPath(resourceRelPath);
|
||||||
|
case "file":
|
||||||
|
return new File(URI.create(resourceURL.toString())).toPath();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyDefaultConfigFile(Path src, Path dest) throws IOException {
|
||||||
|
Files.createDirectories(getParentSafe(dest));
|
||||||
|
LOGGER.debug("Copying " + src + " -> " + dest);
|
||||||
|
Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean createDefaultConfigFileIfMissing(File configFile, boolean overwrite) {
|
||||||
|
Path configFolderPath = Paths.get(new File(Launch.minecraftHome, "config").getPath());
|
||||||
|
Path configFilePath = Paths.get(configFile.getPath());
|
||||||
|
|
||||||
|
Path relPath = configFolderPath.relativize(configFilePath);
|
||||||
|
|
||||||
|
if (configFilePath.startsWith(configFolderPath)) {
|
||||||
|
try {
|
||||||
|
Path defaultConfigPath = getDefaultConfigFilePath(relPath);
|
||||||
|
if(Files.isRegularFile(defaultConfigPath)) {
|
||||||
|
if(!configFile.exists() || overwrite) {
|
||||||
|
copyDefaultConfigFile(defaultConfigPath, configFile.toPath());
|
||||||
|
}
|
||||||
|
} else if(Files.isDirectory(defaultConfigPath)) {
|
||||||
|
Files.createDirectories(Paths.get(configFile.getPath()));
|
||||||
|
// create contents of directory as well
|
||||||
|
for(Object po : Files.walk(defaultConfigPath).toArray()) {
|
||||||
|
Path destPath = configFile.toPath().resolve(
|
||||||
|
defaultConfigPath.toAbsolutePath().relativize(((Path)po).toAbsolutePath()).toString());
|
||||||
|
if(Files.isRegularFile((Path)po)) {
|
||||||
|
if(!Files.exists(destPath) || overwrite) {
|
||||||
|
copyDefaultConfigFile((Path)po, destPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.error("Failed to create default config file for " + relPath.toString() + ": " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGGER.debug("Invalid argument for creating default config file: " + relPath.toString()
|
||||||
|
+ " (file is not in the config directory)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getParentSafe(Path p) {
|
||||||
|
if(p == null || p.getParent() == null) {
|
||||||
|
return Paths.get("");
|
||||||
|
} else {
|
||||||
|
return p.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package makamys.mixingasm;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.spongepowered.asm.lib.tree.ClassNode;
|
||||||
|
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||||
|
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||||
|
|
||||||
|
public class MixinConfigPlugin implements IMixinConfigPlugin {
|
||||||
|
|
||||||
|
public static final String MODID = "mixingasm";
|
||||||
|
public static final Logger LOGGER = LogManager.getLogger(MODID);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad(String mixinPackage) {
|
||||||
|
Mixingasm.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRefMapperConfig() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getMixins() {
|
||||||
|
return Arrays.asList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package makamys.mixingasm;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||||
|
|
||||||
|
import makamys.mixingasm.api.IMixinSafeTransformer;
|
||||||
|
import makamys.mixingasm.api.TransformerInclusions;
|
||||||
|
import net.minecraft.launchwrapper.IClassTransformer;
|
||||||
|
import net.minecraft.launchwrapper.Launch;
|
||||||
|
|
||||||
|
public class Mixingasm {
|
||||||
|
|
||||||
|
public static final String MODID = "mixingasm";
|
||||||
|
public static final Logger LOGGER = LogManager.getLogger(MODID);
|
||||||
|
|
||||||
|
public static void run() {
|
||||||
|
List<String> badTransformers = getBadTransformers();
|
||||||
|
LOGGER.debug("Excluding transformers: " + badTransformers);
|
||||||
|
for(String badTransformer : badTransformers) {
|
||||||
|
MixinEnvironment.getCurrentEnvironment().addTransformerExclusion(badTransformer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isValidClassPattern(String pattern) {
|
||||||
|
return !pattern.startsWith(":");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getBadTransformers() {
|
||||||
|
List<String> dynamicTransformerInclusionPatterns = TransformerInclusions.getTransformerInclusionList();
|
||||||
|
LOGGER.debug("Dynamic transformer inclusion pattern list: " + dynamicTransformerInclusionPatterns);
|
||||||
|
|
||||||
|
List<String> badTransformers = new ArrayList<>();
|
||||||
|
|
||||||
|
List<String> transformerInclusionPatterns = Stream.of(
|
||||||
|
readConfig("transformer_inclusion_list_default.txt").stream(),
|
||||||
|
readConfig("transformer_inclusion_list.txt").stream(),
|
||||||
|
dynamicTransformerInclusionPatterns.stream())
|
||||||
|
.flatMap(i -> i)
|
||||||
|
.filter(Mixingasm::isValidClassPattern)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<String> transformerExclusionPatterns =
|
||||||
|
readConfig("transformer_exclusion_list.txt").stream()
|
||||||
|
.filter(Mixingasm::isValidClassPattern)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
for(IClassTransformer trans : Launch.classLoader.getTransformers()) {
|
||||||
|
String name = trans.getClass().getCanonicalName();
|
||||||
|
boolean included = false;
|
||||||
|
if((included = (transformerInclusionPatterns.stream().anyMatch(p -> patternMatches(name, p)) || trans instanceof IMixinSafeTransformer))
|
||||||
|
&& transformerExclusionPatterns.stream().noneMatch(p -> patternMatches(name, p))) {
|
||||||
|
LOGGER.debug(" Trusting transformer " + name);
|
||||||
|
} else {
|
||||||
|
LOGGER.debug(" Not trusting transformer " + name + (included ? " (because it was excluded via the config)" : ""));
|
||||||
|
badTransformers.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return badTransformers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean patternMatches(String str, String patternStr) {
|
||||||
|
Pattern pattern = Pattern.compile(patternStr.replace(".", "\\.").replace("*", ".*"));
|
||||||
|
return pattern.matcher(str).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> readConfig(String name){
|
||||||
|
ConfigHelper helper = new ConfigHelper(MODID);
|
||||||
|
File listFile = new File(Launch.minecraftHome, "config/" + MODID + "/" + name);
|
||||||
|
|
||||||
|
listFile.getParentFile().mkdirs();
|
||||||
|
|
||||||
|
List<String> lines = listFile.exists() ? readConfigLines(listFile) : null;
|
||||||
|
boolean overwrite = lines != null && lines.contains(":replaceableFile");
|
||||||
|
|
||||||
|
if(lines == null || overwrite) {
|
||||||
|
helper.createDefaultConfigFileIfMissing(listFile, overwrite);
|
||||||
|
lines = readConfigLines(listFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> readConfigLines(File file){
|
||||||
|
try (FileReader fr = new FileReader(file)){
|
||||||
|
return IOUtils.readLines(fr).stream()
|
||||||
|
.map(l -> l.contains("#") ? l.substring(0, l.indexOf('#')) : l)
|
||||||
|
.map(l -> l.trim())
|
||||||
|
.filter(l -> !l.isEmpty())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
} catch(Exception e) {
|
||||||
|
System.out.println("Failed to read " + file);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return Arrays.asList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package makamys.mixingasm.api;
|
||||||
|
|
||||||
|
/** Implement this interface to signal that your transformer is "Mixin-safe", i.e. it does not cause issues when run by Mixin's preprocessor. This will be used as a hint that it shouldn't get excluded from the mixin preprocessor's transformer list by Mixingasm. */
|
||||||
|
|
||||||
|
public interface IMixinSafeTransformer {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package makamys.mixingasm.api;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.minecraft.launchwrapper.Launch;
|
||||||
|
|
||||||
|
public class TransformerInclusions {
|
||||||
|
|
||||||
|
private static final String INCLUSION_LIST_BLACKBOARD_KEY = "mixingasm.transformerInclusionList";
|
||||||
|
|
||||||
|
/** Returns Mixingasm's dynamic transformer inclusion list. Add transformer name patterns to this list if you want to spare them from being added to
|
||||||
|
* the mixin environment's transformer exclusion list, for example if you need to mix into a version of a class that has been transformed by them.
|
||||||
|
* <br><br>
|
||||||
|
* <b>Note:</b> this needs to be called before the DEFAULT phase.
|
||||||
|
*/
|
||||||
|
public static List<String> getTransformerInclusionList(){
|
||||||
|
List<String> list = (List<String>)Launch.blackboard.get(INCLUSION_LIST_BLACKBOARD_KEY);
|
||||||
|
if(list == null) {
|
||||||
|
Launch.blackboard.put(INCLUSION_LIST_BLACKBOARD_KEY, list = new ArrayList<String>());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package makamys.mixingasm.forge;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.Mod;
|
||||||
|
import makamys.mixingasm.Mixingasm;
|
||||||
|
|
||||||
|
@Mod(modid = Mixingasm.MODID, version = "@VERSION@")
|
||||||
|
public class MixingasmMod {
|
||||||
|
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
# Classes listed in this file will be considered "untrusted" by Mixingasm and
|
||||||
|
# added to the mixin environment's transformer exclusion list.
|
||||||
|
# See transformer_inclusion_list_default.txt for more info.
|
||||||
|
|
||||||
|
|
||||||
|
# --- Untrusted transformers ---
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
# Classes listed in this file will be considered "trusted" by Mixingasm and
|
||||||
|
# may be spared from the mixin environment's transformer exclusion list.
|
||||||
|
# See transformer_inclusion_list_default.txt for more info.
|
||||||
|
|
||||||
|
|
||||||
|
# --- Additional trusted transformers ---
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
:replaceableFile # This file will get OVERWRITTEN at launch, nullifying any changes made to it, unless you delete this line. It is recommended to edit transformer_inclusion_list.txt and transformer_exclusion_list.txt instead, though.
|
||||||
|
|
||||||
|
# Mixingasm adds all transformers it doesn't "trust" to be Mixin-safe to the
|
||||||
|
# mixin environment's transformer exclusion list, avoiding running them an
|
||||||
|
# additional time when processing mixins at startup.
|
||||||
|
|
||||||
|
# This fixes issues that can arise from non-Mixin-aware transformers being run
|
||||||
|
# in that way. For example, some transformers break when run more than once.
|
||||||
|
|
||||||
|
# A transformer is considered "trusted" if one of these are true...
|
||||||
|
# * It's matched by a pattern in this file
|
||||||
|
# * It's matched by a pattern in transformer_inclusion_list.txt
|
||||||
|
# * It was added to Mixingasm's dynamic inclusion list by a mod using the API at startup
|
||||||
|
# * The transformer implements IMixinSafeTransformer
|
||||||
|
# AND the transformer is not matched by a pattern in transformer_exclusion_list.txt
|
||||||
|
|
||||||
|
# '*' can be used as a wildcard character.
|
||||||
|
|
||||||
|
|
||||||
|
# --- Trusted transformers ---
|
||||||
|
|
||||||
|
cpw.mods.fml.common.asm.*
|
||||||
|
net.minecraftforge.*
|
||||||
|
codechicken.core.asm.*
|
||||||
|
codechicken.lib.asm.*
|
||||||
|
org.spongepowered.asm.*
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"modid": "spongemixins",
|
||||||
|
"name": "UniMixins: Mixingasm",
|
||||||
|
"description": "UniMixins module that implements Mixingasm (version: ${extraVersion}).",
|
||||||
|
"version": "${extraVersion}",
|
||||||
|
"mcversion": "${minecraft_version}",
|
||||||
|
"url": "",
|
||||||
|
"updateUrl": "",
|
||||||
|
"authorList": [
|
||||||
|
"makamys"
|
||||||
|
],
|
||||||
|
"credits": "",
|
||||||
|
"logoFile": "",
|
||||||
|
"screenshots": []
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.6",
|
||||||
|
"package": "makamys.mixingasm.mixin",
|
||||||
|
"compatibilityLevel": "JAVA_8",
|
||||||
|
"mixins": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"plugin": "makamys.mixingasm.MixinConfigPlugin"
|
||||||
|
}
|
||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
include 'module-mixin'
|
include 'module-mixin'
|
||||||
|
include 'module-compat'
|
||||||
|
include 'module-mixingasm'
|
||||||
include 'module-spongemixins'
|
include 'module-spongemixins'
|
||||||
include 'module-gtnhmixins'
|
include 'module-gtnhmixins'
|
||||||
include 'module-all'
|
include 'module-all'
|
||||||
include 'module-compat'
|
|
||||||
Reference in new issue
Block a user