From 51943f0beaa17c306fcc3f3313ef89503d847aa5 Mon Sep 17 00:00:00 2001 From: Dogboy21 Date: Sat, 29 Jul 2023 21:06:38 +0200 Subject: [PATCH] Overhauled Project Structure and add Java Agent/ModLauncher Support (#2) * feat: split project into subprojects - still wip also added support for ModLauncher (Forge 1.13+) and JVM agents * feat: change compile target to Java 7 to also support older minecraft versions * chore(actions): bump jdk version to 16 since it's the required version for modlauncher * chore: merge manifests of subprojects in bundleJar task * chore: compile modlauncher module with Java 8 to fix startup in 1.13-1.16 * fix: do not throw exception on attempted reinitialization * feat: prevent double-patching of mods when the agent is active * fix(modlauncher): add class locator so patched mods can find the patched OIS correctly * feat: add more patches to default config --------- Co-authored-by: Dogboy21 --- .github/workflows/build.yml | 4 +- agent/build.gradle | 12 + .../agent/SIBTransformer.java | 24 ++ .../agent/SerializationIsBadAgent.java | 15 ++ build.gradle | 64 ++--- core/build.gradle | 21 ++ .../ClassFilteringObjectInputStream.java | 11 +- .../serializationisbad/core/Patches.java | 52 ++-- .../core/SerializationIsBad.java | 80 ++++++ .../core}/config/PatchModule.java | 2 +- .../core}/config/SIBConfig.java | 2 +- gradle.properties | 7 +- gradle/wrapper/gradle-wrapper.jar | Bin 54708 -> 54413 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- legacyforge/build.gradle | 15 ++ .../mods/fml/relauncher/FMLInjectionData.java | 0 .../fml/relauncher/IFMLLoadingPlugin.java | 0 .../legacyforge/SIBTransformer.java | 19 ++ .../SerializationIsBadCoreMod.java | 55 +++++ .../fml/relauncher/FMLInjectionData.java | 21 ++ .../fml/relauncher/IFMLLoadingPlugin.java | 2 - modlauncher/build.gradle | 6 + .../modlauncher/SIBTransformer.java | 38 +++ ...rializationIsBadTransformationService.java | 76 ++++++ ...ods.modlauncher.api.ITransformationService | 1 + serializationisbad.json | 232 +++++++++++++++++- settings.gradle | 1 + .../io/dogboy/serializationisbad/Patches.java | 12 - .../SerializationIsBad.java | 77 ------ 29 files changed, 679 insertions(+), 172 deletions(-) create mode 100644 agent/build.gradle create mode 100644 agent/src/main/java/io/dogboy/serializationisbad/agent/SIBTransformer.java create mode 100644 agent/src/main/java/io/dogboy/serializationisbad/agent/SerializationIsBadAgent.java create mode 100644 core/build.gradle rename {src/main/java/io/dogboy/serializationisbad => core/src/main/java/io/dogboy/serializationisbad/core}/ClassFilteringObjectInputStream.java (78%) rename src/main/java/io/dogboy/serializationisbad/SIBTransformer.java => core/src/main/java/io/dogboy/serializationisbad/core/Patches.java (56%) create mode 100644 core/src/main/java/io/dogboy/serializationisbad/core/SerializationIsBad.java rename {src/main/java/io/dogboy/serializationisbad => core/src/main/java/io/dogboy/serializationisbad/core}/config/PatchModule.java (95%) rename {src/main/java/io/dogboy/serializationisbad => core/src/main/java/io/dogboy/serializationisbad/core}/config/SIBConfig.java (96%) create mode 100644 legacyforge/build.gradle rename {src => legacyforge/src}/main/java/cpw/mods/fml/relauncher/FMLInjectionData.java (100%) rename {src => legacyforge/src}/main/java/cpw/mods/fml/relauncher/IFMLLoadingPlugin.java (100%) create mode 100644 legacyforge/src/main/java/io/dogboy/serializationisbad/legacyforge/SIBTransformer.java create mode 100644 legacyforge/src/main/java/io/dogboy/serializationisbad/legacyforge/SerializationIsBadCoreMod.java create mode 100644 legacyforge/src/main/java/net/minecraftforge/fml/relauncher/FMLInjectionData.java rename {src => legacyforge/src}/main/java/net/minecraftforge/fml/relauncher/IFMLLoadingPlugin.java (98%) create mode 100644 modlauncher/build.gradle create mode 100644 modlauncher/src/main/java/io/dogboy/serializationisbad/modlauncher/SIBTransformer.java create mode 100644 modlauncher/src/main/java/io/dogboy/serializationisbad/modlauncher/SerializationIsBadTransformationService.java create mode 100644 modlauncher/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService create mode 100644 settings.gradle delete mode 100644 src/main/java/io/dogboy/serializationisbad/Patches.java delete mode 100644 src/main/java/io/dogboy/serializationisbad/SerializationIsBad.java diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 76948fe..c4a8a65 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,10 +8,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 8 + - name: Set up JDK 16 uses: actions/setup-java@v3 with: - java-version: '8' + java-version: '16' distribution: 'temurin' - name: Validate Gradle wrapper uses: gradle/wrapper-validation-action@v1 diff --git a/agent/build.gradle b/agent/build.gradle new file mode 100644 index 0000000..9e20533 --- /dev/null +++ b/agent/build.gradle @@ -0,0 +1,12 @@ +dependencies { + implementation project(':core') +} + +jar { + manifest { + attributes([ + "Premain-Class": "io.dogboy.serializationisbad.agent.SerializationIsBadAgent", + "Can-Redefine-Classes": "true", + ]) + } +} diff --git a/agent/src/main/java/io/dogboy/serializationisbad/agent/SIBTransformer.java b/agent/src/main/java/io/dogboy/serializationisbad/agent/SIBTransformer.java new file mode 100644 index 0000000..fd1e571 --- /dev/null +++ b/agent/src/main/java/io/dogboy/serializationisbad/agent/SIBTransformer.java @@ -0,0 +1,24 @@ +package io.dogboy.serializationisbad.agent; + +import io.dogboy.serializationisbad.core.Patches; +import io.dogboy.serializationisbad.core.SerializationIsBad; +import org.objectweb.asm.tree.ClassNode; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.security.ProtectionDomain; + +public class SIBTransformer implements ClassFileTransformer { + @Override + public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { + String classNameDots = className.replace('/', '.'); + + if (Patches.getPatchModuleForClass(classNameDots) == null) return classfileBuffer; + + SerializationIsBad.logger.info("Applying patches to " + classNameDots); + + ClassNode classNode = Patches.readClassNode(classfileBuffer); + Patches.applyPatches(classNameDots, classNode); + return Patches.writeClassNode(classNode); + } +} diff --git a/agent/src/main/java/io/dogboy/serializationisbad/agent/SerializationIsBadAgent.java b/agent/src/main/java/io/dogboy/serializationisbad/agent/SerializationIsBadAgent.java new file mode 100644 index 0000000..3d37f54 --- /dev/null +++ b/agent/src/main/java/io/dogboy/serializationisbad/agent/SerializationIsBadAgent.java @@ -0,0 +1,15 @@ +package io.dogboy.serializationisbad.agent; + +import io.dogboy.serializationisbad.core.SerializationIsBad; + +import java.io.File; +import java.lang.instrument.Instrumentation; + +public class SerializationIsBadAgent { + + public static void premain(String agentArgs, Instrumentation inst) { + SerializationIsBad.init(new File(".")); + inst.addTransformer(new SIBTransformer()); + } + +} diff --git a/build.gradle b/build.gradle index c7b7149..034dfb4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,60 +1,32 @@ -buildscript { - repositories { - maven { url = 'https://maven.minecraftforge.net/' } - mavenCentral() - } - dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:3.+' - } +plugins { + id 'java' } - -apply plugin: 'net.minecraftforge.gradle' -apply plugin: 'eclipse' -apply plugin: 'maven-publish' - -version = '1.1.1' -group = 'io.dogboy.serializationisbad' -archivesBaseName = 'serializationisbad' - -sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' -minecraft { - mappings channel: 'snapshot', version: '20171003-1.12' +subprojects { + apply plugin: 'java' - runs { - client { - workingDirectory project.file('run') + sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.7' - property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP' - property 'forge.logging.console.level', 'debug' + repositories { + maven { + url = 'https://maven.minecraftforge.net' } - - server { - property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP' - property 'forge.logging.console.level', 'debug' + maven { + url = 'https://libraries.minecraft.net' } } } -dependencies { - minecraft 'net.minecraftforge:forge:1.12.2-14.23.5.2860' -} - -jar { - exclude("cpw/mods/fml/relauncher/FMLInjectionData.class") +task bundleJar(type: Jar, dependsOn: subprojects.assemble) { + archivesBaseName = rootProject.name + from project(':core').configurations.archives.allArtifacts.files.collect { zipTree(it) } + from project(':legacyforge').configurations.archives.allArtifacts.files.collect { zipTree(it) } + from project(':modlauncher').configurations.archives.allArtifacts.files.collect { zipTree(it) } + from project(':agent').configurations.archives.allArtifacts.files.collect { zipTree(it) } manifest { - attributes([ - "Specification-Title": project.name, - "Specification-Vendor": "dogboy21", - "Specification-Version": "1", - "Implementation-Title": project.name, - "Implementation-Version": "${version}", - "Implementation-Vendor" :"dogboy21", - "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), - "FMLCorePlugin": "io.dogboy.serializationisbad.SerializationIsBad", - ]) + from subprojects.collect{ it.jar.manifest } } } -jar.finalizedBy('reobfJar') +build.dependsOn bundleJar diff --git a/core/build.gradle b/core/build.gradle new file mode 100644 index 0000000..b68ad01 --- /dev/null +++ b/core/build.gradle @@ -0,0 +1,21 @@ +apply plugin: 'java-library' + +dependencies { + implementation 'com.google.code.gson:gson:2.10.1' + api 'org.apache.logging.log4j:log4j-api:2.20.0' + api 'org.ow2.asm:asm-tree:9.5' +} + +jar { + manifest { + attributes([ + "Specification-Title": rootProject.name, + "Specification-Vendor": "dogboy21", + "Specification-Version": "1", + "Implementation-Title": rootProject.name, + "Implementation-Version": "${rootProject.version}", + "Implementation-Vendor" : "dogboy21", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + ]) + } +} diff --git a/src/main/java/io/dogboy/serializationisbad/ClassFilteringObjectInputStream.java b/core/src/main/java/io/dogboy/serializationisbad/core/ClassFilteringObjectInputStream.java similarity index 78% rename from src/main/java/io/dogboy/serializationisbad/ClassFilteringObjectInputStream.java rename to core/src/main/java/io/dogboy/serializationisbad/core/ClassFilteringObjectInputStream.java index 6573ae3..d86ccbc 100644 --- a/src/main/java/io/dogboy/serializationisbad/ClassFilteringObjectInputStream.java +++ b/core/src/main/java/io/dogboy/serializationisbad/core/ClassFilteringObjectInputStream.java @@ -1,6 +1,6 @@ -package io.dogboy.serializationisbad; +package io.dogboy.serializationisbad.core; -import io.dogboy.serializationisbad.config.PatchModule; +import io.dogboy.serializationisbad.core.config.PatchModule; import java.io.IOException; import java.io.InputStream; @@ -24,12 +24,12 @@ private boolean isClassAllowed(String className) { className = className.substring(1, className.length() - 1); } - if (SerializationIsBad.config.getClassAllowlist().contains(className) + if (SerializationIsBad.getInstance().getConfig().getClassAllowlist().contains(className) || this.patchModule.getClassAllowlist().contains(className)) { return true; } - Set allowedPackages = new HashSet<>(SerializationIsBad.config.getPackageAllowlist()); + Set allowedPackages = new HashSet<>(SerializationIsBad.getInstance().getConfig().getPackageAllowlist()); allowedPackages.addAll(this.patchModule.getPackageAllowlist()); for (String allowedPackage : allowedPackages) { @@ -47,7 +47,8 @@ protected Class resolveClass(ObjectStreamClass desc) throws IOException, Clas if (!this.isClassAllowed(desc.getName())) { SerializationIsBad.logger.warn("Tried to resolve class " + desc.getName() + ", which is not allowed to be deserialized"); - if (SerializationIsBad.config.isExecuteBlocking()) throw new ClassNotFoundException("Class " + desc.getName() + " is not allowed to be deserialized"); + if (SerializationIsBad.getInstance().getConfig().isExecuteBlocking()) + throw new ClassNotFoundException("Class " + desc.getName() + " is not allowed to be deserialized"); } return super.resolveClass(desc); diff --git a/src/main/java/io/dogboy/serializationisbad/SIBTransformer.java b/core/src/main/java/io/dogboy/serializationisbad/core/Patches.java similarity index 56% rename from src/main/java/io/dogboy/serializationisbad/SIBTransformer.java rename to core/src/main/java/io/dogboy/serializationisbad/core/Patches.java index 9f92182..29490ae 100644 --- a/src/main/java/io/dogboy/serializationisbad/SIBTransformer.java +++ b/core/src/main/java/io/dogboy/serializationisbad/core/Patches.java @@ -1,6 +1,6 @@ -package io.dogboy.serializationisbad; +package io.dogboy.serializationisbad.core; -import net.minecraft.launchwrapper.IClassTransformer; +import io.dogboy.serializationisbad.core.config.PatchModule; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; @@ -12,46 +12,58 @@ import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.TypeInsnNode; -public class SIBTransformer implements IClassTransformer { - @Override - public byte[] transform(String name, String transformedName, byte[] basicClass) { - if (Patches.getPatchModuleForClass(transformedName) == null) return basicClass; +public class Patches { - SerializationIsBad.logger.info("Applying patches to " + transformedName); + public static PatchModule getPatchModuleForClass(String className) { + for (PatchModule patchModule : SerializationIsBad.getInstance().getConfig().getPatchModules()) { + if (patchModule.getClassesToPatch().contains(className)) { + return patchModule; + } + } + + return null; + } + public static ClassNode readClassNode(byte[] classBytecode) { ClassNode classNode = new ClassNode(); - ClassReader classReader = new ClassReader(basicClass); + ClassReader classReader = new ClassReader(classBytecode); classReader.accept(classNode, 0); + return classNode; + } + public static byte[] writeClassNode(ClassNode classNode) { + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); + classNode.accept(writer); + return writer.toByteArray(); + } + + public static void applyPatches(String className, ClassNode classNode) { for (MethodNode methodNode : classNode.methods) { InsnList instructions = methodNode.instructions; for (int i = 0; i < instructions.size(); i++) { AbstractInsnNode instruction = instructions.get(i); if (instruction.getOpcode() == Opcodes.NEW && instruction instanceof TypeInsnNode && "java/io/ObjectInputStream".equals(((TypeInsnNode) instruction).desc)) { - ((TypeInsnNode) instruction).desc = "io/dogboy/serializationisbad/ClassFilteringObjectInputStream"; + ((TypeInsnNode) instruction).desc = "io/dogboy/serializationisbad/core/ClassFilteringObjectInputStream"; - SerializationIsBad.logger.info("(1/2) Redirecting ObjectInputStream to ClassFilteringObjectInputStream in method " + methodNode.name); + SerializationIsBad.logger.info(" (1/2) Redirecting ObjectInputStream to ClassFilteringObjectInputStream in method " + methodNode.name); } else if (instruction.getOpcode() == Opcodes.INVOKESPECIAL && instruction instanceof MethodInsnNode && "java/io/ObjectInputStream".equals(((MethodInsnNode) instruction).owner) && "".equals(((MethodInsnNode) instruction).name)) { - ((MethodInsnNode) instruction).owner = "io/dogboy/serializationisbad/ClassFilteringObjectInputStream"; - ((MethodInsnNode) instruction).desc = "(Ljava/io/InputStream;Lio/dogboy/serializationisbad/config/PatchModule;)V"; + ((MethodInsnNode) instruction).owner = "io/dogboy/serializationisbad/core/ClassFilteringObjectInputStream"; + ((MethodInsnNode) instruction).desc = "(Ljava/io/InputStream;Lio/dogboy/serializationisbad/core/config/PatchModule;)V"; InsnList additionalInstructions = new InsnList(); - additionalInstructions.add(new LdcInsnNode(transformedName)); - additionalInstructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "io/dogboy/serializationisbad/Patches", - "getPatchModuleForClass", "(Ljava/lang/String;)Lio/dogboy/serializationisbad/config/PatchModule;", false)); + additionalInstructions.add(new LdcInsnNode(className)); + additionalInstructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "io/dogboy/serializationisbad/core/Patches", + "getPatchModuleForClass", "(Ljava/lang/String;)Lio/dogboy/serializationisbad/core/config/PatchModule;", false)); instructions.insertBefore(instruction, additionalInstructions); - SerializationIsBad.logger.info("(2/2) Redirecting ObjectInputStream to ClassFilteringObjectInputStream in method " + methodNode.name); + SerializationIsBad.logger.info(" (2/2) Redirecting ObjectInputStream to ClassFilteringObjectInputStream in method " + methodNode.name); } } } - - ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); - classNode.accept(writer); - return writer.toByteArray(); } + } diff --git a/core/src/main/java/io/dogboy/serializationisbad/core/SerializationIsBad.java b/core/src/main/java/io/dogboy/serializationisbad/core/SerializationIsBad.java new file mode 100644 index 0000000..2ffe639 --- /dev/null +++ b/core/src/main/java/io/dogboy/serializationisbad/core/SerializationIsBad.java @@ -0,0 +1,80 @@ +package io.dogboy.serializationisbad.core; + +import com.google.gson.Gson; +import io.dogboy.serializationisbad.core.config.SIBConfig; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +public class SerializationIsBad { + public static final Logger logger = LogManager.getLogger(SerializationIsBad.class); + private static SerializationIsBad instance; + private static boolean agentActive = false; + + public static SerializationIsBad getInstance() { + return SerializationIsBad.instance; + } + + public static void init(File minecraftDir) { + if (SerializationIsBad.instance != null) { + SerializationIsBad.logger.warn("Attempted to initialize SerializationIsBad twice, skipping"); + return; + } + + String implementationType = SerializationIsBad.getImplementationType(); + if (implementationType.equals("agent")) { + SerializationIsBad.agentActive = true; + } + SerializationIsBad.logger.info("Initializing SerializationIsBad, implementation type: " + implementationType); + SerializationIsBad.instance = new SerializationIsBad(minecraftDir); + } + + private final SIBConfig config; + + private SerializationIsBad(File minecraftDir) { + this.config = SerializationIsBad.readConfig(minecraftDir); + + SerializationIsBad.logger.info("Loaded config file"); + SerializationIsBad.logger.info(" Blocking Enabled: " + this.config.isExecuteBlocking()); + SerializationIsBad.logger.info(" Loaded Patch Modules: " + this.config.getPatchModules().size()); + } + + public SIBConfig getConfig() { + return this.config; + } + + private static SIBConfig readConfig(File minecraftDir) { + File configFile = new File(new File(minecraftDir, "config"), "serializationisbad.json"); + + if (configFile.isFile()) { + Gson gson = new Gson(); + try (FileInputStream fileInputStream = new FileInputStream(configFile)) { + return gson.fromJson(new InputStreamReader(fileInputStream, StandardCharsets.UTF_8), + SIBConfig.class); + } catch (Exception e) { + SerializationIsBad.logger.error("Failed to load config file", e); + } + } + + return new SIBConfig(); + } + + private static String getImplementationType() { + for (StackTraceElement stackTraceElement : Thread.currentThread().getStackTrace()) { + if (stackTraceElement.getClassName().startsWith("io.dogboy.serializationisbad.") + && !stackTraceElement.getClassName().startsWith("io.dogboy.serializationisbad.core.")) { + return stackTraceElement.getClassName().split("[.]")[3]; + } + } + + return "unknown"; + } + + public static boolean isAgentActive() { + return SerializationIsBad.agentActive; + } +} diff --git a/src/main/java/io/dogboy/serializationisbad/config/PatchModule.java b/core/src/main/java/io/dogboy/serializationisbad/core/config/PatchModule.java similarity index 95% rename from src/main/java/io/dogboy/serializationisbad/config/PatchModule.java rename to core/src/main/java/io/dogboy/serializationisbad/core/config/PatchModule.java index 1fcc311..f5500b2 100644 --- a/src/main/java/io/dogboy/serializationisbad/config/PatchModule.java +++ b/core/src/main/java/io/dogboy/serializationisbad/core/config/PatchModule.java @@ -1,4 +1,4 @@ -package io.dogboy.serializationisbad.config; +package io.dogboy.serializationisbad.core.config; import java.util.HashSet; import java.util.Set; diff --git a/src/main/java/io/dogboy/serializationisbad/config/SIBConfig.java b/core/src/main/java/io/dogboy/serializationisbad/core/config/SIBConfig.java similarity index 96% rename from src/main/java/io/dogboy/serializationisbad/config/SIBConfig.java rename to core/src/main/java/io/dogboy/serializationisbad/core/config/SIBConfig.java index 2b891db..d29f338 100644 --- a/src/main/java/io/dogboy/serializationisbad/config/SIBConfig.java +++ b/core/src/main/java/io/dogboy/serializationisbad/core/config/SIBConfig.java @@ -1,4 +1,4 @@ -package io.dogboy.serializationisbad.config; +package io.dogboy.serializationisbad.core.config; import java.util.ArrayList; import java.util.HashSet; diff --git a/gradle.properties b/gradle.properties index 878bf1f..eee28fe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,3 @@ -# Sets default memory used for gradle commands. Can be overridden by user or command line properties. -# This is required to provide enough memory for the Minecraft decompilation process. -org.gradle.jvmargs=-Xmx3G -org.gradle.daemon=false \ No newline at end of file +group=io.dogboy.serializationisbad +name=serializationisbad +version=1.2 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7a3265ee94c0ab25cf079ac8ccdf87f41d455d42..0d4a9516871afd710a9d84d89e31ba77745607bd 100644 GIT binary patch delta 22872 zcmZ6yV|!+8*R&g(9ox2T+qQGYPKRe~+qUhbW7|&0b~?top7-0m{=(d9))-^2I$Em1 zV=KWExFFs()4UV;w4rq#T<%>!K@xBqTTB>W4I_ii#GpYyu9FEF>4Bc=S{UDoC(hv$ zkfOR3Y6S}RArtfF#OMikC|UEI9x&`A;?y>dmW;{pTrY8+H+L0|wR1KMeKr>#k?}SF z3Nh-wh~1!3 zdI1&>Kpqe$mz-`X3dE(0o8d0pH^*mL$E7@sI1E9+e{(UTmi|FX{Jn@?dS6O2!V;(E zoHLqEtb~pUp-CvawL9iM%4`ZG3C<_?zy~7_Z5e3Fe#9+F9qY zuAp6410$1_&x{;~<%7kA2J_QTg&Rz}X5s%n?ez5usmjE7;60k9CO-pC<3rqjI=}8t zw{qbu5P2h)!7K0l_1u3{Gl1Qd?;x$G1}8+k-m*k$IpfJsan&QbWYB*p-^mrXujsJ) zH7joCcXAxVdX-r=yW_A2f!}(e8I{NRdT{-cW-lGVUVk=*gH6f00mam8W?r1*fgW#P z>@D+N&SsYMU;-pajzuhAHCiI=Lm&~Q9EkiYa0EKf7y2_0c>?LJ2S2OI0WHBAZk z7A1NKcaoRUL~_i6zj{9^F8^;iZ{E-&uYXv+?Q3Wp)}0*UpT+y4^*4^VL>TZg`*+ij z5Hn+gTl$$Vs6Q)rNpXAo7Kne=?x8hzAL~;l|6M+@U884_)ATAjnld%&=B-Z z0TusI4~HofpYi&!e^PC1=HF+<{ecM)fnCiSQADukIx#f4E}Ezhtf8+2_j+9UN+8sk zWB;5?R9@e0A?IMAgjaN?rdcru>^>pmg3%G@jNT`(05})@>g=NI!^@lbHcA8;P&D3# zLs|9^#o^kivRB4NMb~UFplvGa`Ym`OfVv|fr!EKEe1>kn=0eR2J$Mpx6ljwJ;cYf5umGC(>Fkd%FeX!+s_2#lG}3Rqx#E zQq$XT>N6DO;D_+)_@evO9fe_jyWEXM_KX%T~nn%99w>yIT()#f^I8608=pD$r@{HAEyRi zmn}3o`dx?PvYaUgCf#`{Lpy)qva0GDmewuZnJslvn?Sw}^)d3!q6U!!6{%*2RWq0) zSnq?jxu@lWG;-WxyNo#$Xz=jQdjw0ohH3Nfkq57gcxO6@!iowhZ}4p6?f0f}9Q`~KxtvVrM*(rp( zGfoBBmx7c{dg*GkQTzxj!690y_U$&2DXtzU>wK*gV~O-@Uw~ z`^xvRqD(#_CH`a7_c7C+^A9-d*%!a|{ru!A&Ylm%Daa>(rhgK68tp6?f2$@GHQH;dGS2j%W@k2ojEoz z{$wi}e7JdaL9+hlH+h^)GKI!LD@be1c%(ahMp$Kr^vO9^z+`D9_DHIJsZ(qIDGk4o zCOyo&z9IEt^kxLJ{hk=f^kJCdqxtP^P=bP=AM{z*bP+`0P?0tiS(!69@6o9TIOwK{y`DKx+4A z;M$?AYn6?Lus%8!{OuCUME7e!ZT9D=ePseTCtemoS|6;19FuQ7068t3l(A^*6D z25(SLr}B#BV=J*fKJlbnI4SUwp%`n^V(wX3Ko>?Iy)a*gA+8!GB}N;%A|y45?O~4` z=M^S{57_pU&(SI2xd9{6M)4hn{BQ~9=wX)*)=H9$zyx}YqqaeJCE&2_eKai=4_C{6 zbG@U^YwLY8);0q!8|Y!(-EvWZd0XSqC{MQAnFDY$AkAxO>1fr7E^`wCrcGcexOrSq zF13_}Z0PI&Y!WLBd$*Rmy4{<*Ch~?Af#i*RgbLQEWtk4}c$Z6$gQa@ST{a}9H3GCL zR^9g6`Q1?2Wfubb*0g}DMJ+s)UQYbgKk`|0x8?q5P^hcNGET3h-Rt5yLt^Q8pO1Bp^}OLxUoT^$Taz29phO}Kflt7ZJ9_i zzLs}!hEj2AZxCv2dmH+~A!kMlq9z4aMnhJjYjFz}d!E~+ffsMLs!&|awWf*p3)a|r zm-$PwiVKW4+0{?4o@%IU;ksiQk43(MZFVE@&DuTL4)_{zt*KJDOp4#M@ zPUgc4e81>=x|=O|wRE141}Ie_f~iIF7zQA=Hl!{VwUxssMW*1a+VjUR`gHwkD3!Xx z0WJrktHXB=k!WeuTV}bUkn4qS&ETyZW{k7Vqqkt^I?u>vzDr{vl%Ja>(a7xBoPk!* zHy(KJ#NbSnfZ!Ce=WJS6d}9 z{lzkmbNyK%H*BhluTo|zHK?EcuPV`wqu#7L{+;2 zNUrwm1?qJ_MRXHnT83n;w0?p6ib|4_A9c%8@Jhd?J?SpqN7z2STf=0>XJ*Ucq8dCc zofJgY#3KwwC1!3g+i}E0Vli$a#1pP9#*eYo9Jy&0eH?q{qD$?=8tM2TeFKPyL!|2R zadkma(}=Wna7mvWz^yrMTsVr1ItI%E+Ar`tp3IopxCk_{o^Aoki+b|B7N^rK2Z*cR z1}DGVrW;%YZkr?&!sfw9S`MCIk1+*sbm5g0FH1Tp#B3AitN00Y^`N+z(q^!72Tzt} zs=Wl@p*!hO8K;!Cb~+c5NLrs-T2p5jJa|1(>dV$NCRkGqi_$gTG5iV^Q7_bh=jBgq zsZeJ)se)2#6>b;?wTs118L}U5Hq3&xaB7@>ilu&TwX*r;y>Ldw52s%BlOM0Tp)XVM zm+IeOM)ePfz5;QlXmlJ_eJCGAlST(jVxjtos51<P{elkE@ow7}RuamxolLpEw(;p440q|`qHo>jhBdkixIv_LiX4z1&lvoUT+ zC@x3o5eBj{t7eX+=STZc(aum&YX@f+2kNBx|}(C2ff;s2Mr3+oYim~ zB}%&qsW9%u(cw>o_=^j>^Pg8;yR^&L!JlibxBU&1FOT4+4bU2bOOqZ}`OyqLgZrm*uVzjDO_!d+&TA<$iYsF;z{UIEY z<{PZ@r{}8-{z@>Qd6m7@a3YtMsF0|7d+dvrD0Nru4yTt+8mR?{ckc!P=bJZr?pp+A z1#`vP>gnG>mnMvQpwsocaT46OR@qDP*y@cZG+*lt67-P#WfPvuJ$-2-_74QS$`kQ9lj>z&Ok#9H-VuAhD0dAjYGiM?_ujKC(01Y z0aoY6ZD%8%LKkVSfghSZS^tO!_ownf@3U=E=r56}L%Z5xq5TPS6g%K|9+Wc4#!37w326WORNs#SvaUGo%m8OXBP@rDcg**K?)X6s>kPz~ z1?tP3-(7yEm_)lU&ZEic*H0HF14dh}-SW5uSM)^onH#TdMKpT{SlI&HH>`qt^SjD$ zY+BVcXM2I`4}kM%+1kt6AxTc8E+@Eieg};;5pLKu5@hVz30TN-zd<@khau&?RVKo) z??(fNol+{VlZ?Mf#|f=>kua3)b+q;&l9e|RmNNFipwMM~(z+0iGL`(m7^1BHE2@4K zP1Bjba?&a?#dN#A9no&O1|Ik@OpVR@1m&sAvHHM#KmJPY#yW?oFHKUiv5dl2z7*CZ z#6}1BbRnbg+}>-vNtg>gsje}Nd{VB7qXibgRUoW@(*}&0`OftclNg*LQgMN*%n&aqi_2?^VunRLFEEElwxB;9B`aap!Woo{>kLKpZF41K0uloClq~zBxeud}e z>8Pc_g@`eN35SjL6bFlp6xej$6C ziSHMQ*>k}X=k_fN1g5WPF0ErgMm|MXHqbv`O~R zKkM=&aV!k>nsc*|AcDINPS z^ePp8xlf7jg4<3X!0vBe!Y3fc8nv_)=6b4S#QrFrX zbo?I-_4kV3^YqmK{!KaJ=Ti2xHc;HHebHD;6~38LW(17F=1Q~^<}=?-5j<-uP4Rm= z$#j(fFcg*RoU;0EMbK$qLyyAOd*)E8m)BMow%T`sRJr!-ZQ#q0t6JRqB~ZP2VQU@ht3 zf?CQ1VD>0yGdc>{_Qt@59UuJ5hXz+ff-)-MTRvJ*h$OUhPYwAg2Dd`(PuOy*;Hf(Se~1_HU^S_I(9f>Xj3~AWYaiOrQA0 zq=AfFd%j~_=54v+ze$nfP$K3*&4Evk0sp+aW;6j58Tp7JA0$A3K}|+6bwiWtjRcM6 zz`Q2cxF?7oFOORQpQ1}DZa^@8seeTMuImt|l*SK7Sbv16ySyaj?|H!d!4&C>C_Ns!i{)sawpzZMDQ5G5k}>*Bd_ z^L>gl{4fWJfJygdQ=ns+0)W}RP{x`lz?~>Wf^e|#fmxGZ6dtvfBVtV>VnI{<++*E_ z>dF&?z72>B(uqXAr;jn53;cxLUJy+rE`0%`v)So)QFctLkR z6A`ya$6w+QxA?t6Wr?En?&OFCTJIK%TdrX@ccp;3YF;KjM4^v2<-WHhbA@)=7@Pct zR*Cx^t5_7}9rEDI4>Z z8I2(8=T!ChNZc;`;m_XWNDXe^1t^Mkf1tp7ku)7a&Kw8L9Z3XwW6i&z7y@iamq{Dw zMyr7NOFk$*2n9?No52EB3ruHzdPk)?t_-C{nCH7mA{yl- zyNoiuP`prNB_7<$yIuj5(UceuWN-%W)KDRPY{Jg{d<&fBb@^Q%=dAcQBJ~KFB1S?K zkQb{WxYq%@aI|wo{Ql9*3KIo;H;`X*L}5V5^uNZeB)KwaUx*;TKkj#sH(rqWzITVN zKG>1EAPaZ!-x2Bm>G6j9hugiz7d-jQzr%#@5DA}?b=(*UUn-5~{=mrsMN{t@?H3Rm z#_+o`{A0h~);ZwW{h{0Ah6?XVzQFC^^(+{?(Wv>xh7_KZI6+D`omL@H@c)zp!!UuF z2%AA~@%?)f1zg7R{i^)C(UXOpJ-;x-wtn3bu$L7r)?s-N@PAA7n{t_M;2KtaL2n9) zUH?c9JYb>Q|3DZ1WQ9URSW3``6+zphMxVzkODHzzJa=A!8dHk>^Kq5HKMfZVRE%B^ z;3#TP)r>Qn64G{hwF_FZ3nQ_D{n7(w6^j1kwMBxl9FI@u5fgvsKN+1HSB`3F9?N;V zP}TCn<1em%TAlH^G~X_B{vdxjdb=v{61-lK4*9jC-SpLH3QEzCx5}gA%U1F@ zz2>=r(9GbOo58+P{D2rO;K$7Kx+)d$fSMH>bp75Lm3ZPf_ygYTC#_C}*mAuDIs^#< z!i59^LYd5xN{}2gg8wyh%w)&e{AIo5f(2IsYrm{3tSds@Zo?#POD7Qm zU@Z$?lFyLuh=_W*WcoY8bvy#?1Y0wy%b4$bXZ0$!d@Em;EJK;dNaSsqKlT0W`TYG1 z`2PG@+XLwdxHVl5gv4c8uP4!N2`0ix1Q)j53jtWQ^ly$GM%P z9-deEr;C}!SYxZwgLSxulAKB%Wjcl~CWEjR+D;MHgrFxW5oDKUw?m&s;ihP)=2)>2 zRg1TwGeej$>+*XPOUp#hJSfN?iRD^NexkbTi|B#Ez zwd|59)y7^ITM7)d;;&D2O}j}*On@1bi#Q+Aw~%S&NhW4UG8$l|D}~%KOk>_2pKF2J z2r4~6WXNWFy6Wg`D#CnB_EyPu4&wGT^Qtum@T{yp{312;R%wrykuLMyr?Q(-O-HbI zGd2|cy*+J9su7Y|S3?h2%~?kanaQ!4u1;e-xiKAfs|TVje)LB3ArL5UwMdiLFVTgU@AHO*2UT27DF=B9m|&kM0Kqm@ z!R`8McURFJE7K8XJjEAe3RVqr6kp%93NJTwKjI+%HPt~Wmf)69Ejm;G6KcTMnajeU zE6-h!czl9TM1)m`gmw-ts+Zs{PfN#<7*M=Qat5jmFkERgNFjim9=8R(I7^K}!wC+j z1jRdPiE5d#nETfJMf06pTq&UX$2yX1M*%nkfnVXU zysPNo8|MsZ^jB{GBQ>c0J~qfv;%sM5R-NEJv(#31)?=9YkL1=y2A8`gKfmYwCm@ab zwGJp9ad87b&cBwr`@%{YRISeKE>OE;y6m*@w(Mz`nq*zY3z<-yMOAvEepiF?V7zo< zz0;(!Rm0f$f>5iC^2-vFlH5)y_9xvegngaG7v&R|fD18LJp=_3uMd~*1QBuWKK~Wz zTQ!21qy=&{ycuOpOw@3NeK*j==oy*VV(8HT0=5TcqEzim%`U_p zqxnKFj8>X|hS`6bTfc)z+>FZ6kx(C89?)o!>j<}JH~sZ>xO8rWLZ}})NW}c|^L9zH zu$ge&0j(0p7nv_k<-HJWf7A6k3ZcY5hX%;QmdN=N_PoS4l8eP6?{6NO&I|iOuEzob^FYM*qWoV7z)Cgg^0Qv5Y?AV_76VIGWE0LMDos^v5d z=@o1wBC2R5PD%LtC5mSX+)a^BSzyJCP-~-BKDJu>{H24?bFgPpC4^@h!?{eL7w5iJ zn8c`nq|3@LXKP3Jmm)jl#yW!e`aW(KeEi=i+{X!neB_&6N5PvWMa5s2caft2n-szVgy8fv!RWdf))_s{$lYo z?AI#K1^tmWTHFQsd(Tan&<&HX7ICT?0?$s-T@hsH6Qx%boX2vlJk}$IiP{$kk1x@F zTRyJ3*ML?IV?FoMjI;I;k>1~Fu91n~vN+DpM{cjEzXGQi9$eCWQJSEg3wXpmK@Q+Z z>NrC)?i~&t=fw{(9u#oZ2j5i7W_My|rxbD|n7Dj!k4DP}tl!x6op}9MAs6&R$su8g zMr(cFLoy9VII~)*8iI`q!~uroy7c)rNrdes9}R1H@Qq2&dC@$&j=Za%fBr8xNzR+W zO>TGJhFeGC-R1`e0hxqN7PiJn4hZ~}EHH};9MQZ~L{mZeo^5MrU{FS(-9@DfG+f6N z?nRcPKEeku&9N%NVmhf$M6N&14l!ojl9o~j6V>=KE1-E$LtDCcs-rY$>iC~ z%*@Rc5CQ_fpojx4U~3by0g-an!SsoC0+g0)*yFcA; ze=ysA_YN}hGz|XDo{^T} zY!d8tMx|hy^j;}BQahq|R-8fdSY>7zN^8nNZ41;7QWEh;>HLkX9xP_ET7N!Lm$2Sw zs#$IKv`Zq*V-7HR6cz08`3>969n$XHNB+t>j8b)qADqJGG2O#kd>;j?xq5T zm(!TKucoN6Ei0tz-Drj}>4YV^wrU8l@NkuRV5kWYE&-wnn_pm4yL~WL=)uM!F61Xp zS5#@(G0SNQ>WW#Qa%Q(1TZ(#s6?Tu6&ho+p`pOJpeUl`K23yLHtNR}ztZq6+1iY*hx`39lHiNEZ>3HyK=w6wVpWJ`c&2glElNjDLnj}xW?oiQ4CvgFCl zIz-xeDsC4bm=@Gu$wKDtB>xnM^n0>fnhMQ3gE(`DW|c8-Nj>g@mS{M5g6!Fl%aQ`G zlsB3@k7S{t&7-ZYaNHW{MeQ~`sIK(gjuheA7vvnAZjhY8APJ;kJojOpQofs_b>L$2 z(XTT$_A>C^OgPm*83_Y5qOpm&EnQ9cY-S};TAOj3S)iOl&(JLjQVsQY$U9}8(zHa0 zR3Ng>#KDUHD%C5^eS!XOJo)?MaI*X=j%JQ( zn4#asTeVYr9(5pf*ZnH`D^(m#D1d14w}Y0Hg&y1}`?r_51pm$Noa(=sx z7%qvub~3v8t26PX%vj;2uE08*>;RzMAH}5-mde< zApGH7U9l@6OdCnT_8UD>wW>Mntn80V=dJFzy%|)(Wma3K*Wu)~y_q zDN6?SWdO#XiBbe4RFTm)tjy2Ju2BJSu{gN|!V9rX(pSk<1^K&Q(({w2XC~S6>zFR) zcn)1;@oK`vGW5l#>|vs3V6lsGGQM_=stM_L5+Q{x*l<9dS%?_a9Aun$0HlP}4PR1r z*f$Prrpa76S-&g@J-dNkzdlULB4iSJ64QZ8gdx&@?zZw8mEteGa)huYlC_bj-ioPX zoAlUpw~*kO?`!^<;(#A;O*G*|mSj>jBC{{sV6Tch^gCf;#rPN^7rkJKr;?dj*w6~V zpPnKl3{ISGO+O#uoAn(CWm)hA^}i}rE8{#*Ch^k+T9=3rGlhWA$|%DxxBrT?PvNYv z=Q2^yN;a@+uKW)D4*7=XTSC$uZQJ8rV)`QbLe0(V#uH->M2utmaxt3qyWyGhd){t- zssGRR5ruJ)EpmnzS!`HY2el8zClVUy$)e>z^+z${l!nbi%{2N2Q%MeK$687BPdi8s zDFu^(EHUnJJuxVp+;I5qI8fKOPm*qNCZvwKE9xt%?Y6pAs!M9Nmc4bjTV6Z!4%M1s z%`PE^tB5;&(@uVyc;A$RERLRkUD&b_7PC5QS#nUqVl8`XbUc?`V(ANebX07!t4gE2 zrl?`N5cV~$3ju&Nw`vot?Bg^gsN*sd>s4K#f;~HX5QA4z%(I&~6{7lcfxz&&ApB4R z0NsA230}!=hn>667~6TwfnK*|Vr|v!JMq0YtEy6xL!*UZr{biGfo*rW+ZPS`kBvsC zo5ATFQ6;)!P~JM5HBLhoN*OCfC+t$vtE9K+^g;dU0M!omVBuO z+$m71572O2(oQ6u4?OPf%eGd#cZ?aL-sZPc4-x)>x&`+CP&gb=8=@9MYh>)2&|{+? z=Sup&ino^kS%YTp7I78&Crkeg@7ws>6#6^g`erD6nGnm?4iwxD71V=}DA7J;6(-*K z0nti)CCv_j=um&^au0hD978eXbK(FV!jt*ogHe>Ck&K73SF-P=N8yE?dtnA;g?>~A z3C#`%)&rKuBXl>q8tF?ST|2ukj7A>Ko+Ya=5gF8uBn*PUFXu?c6jK$5Lax*0-T_uC zz3eK*&{n=$;52lS%glnch&05abiroT^7o8FGMf9=Hy%DNKAKzXix%LSls8m9@vGEd z_1D}n{lOEhqDuTR`5r)Cg${+oQeb9eKau2}&{pTg4ZhqWLotLw@ZbgFRqiBPLRh>J#{gQ`~bIVQz! zOg7SORlZ_;lQ8w5M?w#?0Yf@wH#Jcdh^46~XRltTGkFg0%WrRmKq?NY!3jnXG?xCpS8tVrs2rvjV2spyr)OcB~S;%FN(ZbBP2;jn6>)eJPffF4}J!ZS@G)cEz;12mt zN>x7i@=*09a}7%;eP`T#PQEzft_$$|=i2t3zM}$!9fZLP&iMPS_BS6vzw$f`=2#ix zk?l>8>;S(!rgICc-jjU#?xN(V@etmLdsOf~N^fX{PRnKpreYFe-=sUmcy4rv4a)1D zra>*^gwD-mk`s|CkC z-2e1V@2!)GAmBx#vs^LcV-9)u;Ep+rx#%lZydz}816RcvMlKL$8oFch$_kgHBy^rK zDGDoj7G?^vWzsl7Dku4@9VZqNO4(|IuGs359K}IuNRV6U*ax}2#Lvh#d|N#N#ce+j zRkzfRsmj^PdPEzTq?_x<8yuO};}cqcS&Y_9Zi$oBq>Q;guwM-HBW{b6zJ%B~jBpT< ziU<+w=~nuSS4sZBmy`nm{CKJ3f$EN9g&!Rq9 zYlC?lxXzhXcKet*N1Fa%|En`ceV#viP#_=;s30H$|FI?VWabb;;Q9ZNrJr8T>kzm8 zU)0*lF^h|wJMcj;nsO4AEuaiR80{oDO9O6Yv+|okbhI~o5h*K>68a+ca*%7m9r!>0 z-OjI46)&^$ixhz^zE4xxFE`y=A3HvKeiOGpzK;X}k}*)qkQPvgTVOC`v?Q1T$h2yN zM%fg_(RxTFV+Bq-%&XE&xbf=SB!(rzvIGBDSG<1m>!&o?QoX*6>bYNy%2F1O=Z_6RxvX&Xayi4?5=)k-SG7maOl z80=JJq}im!rW;WxpXs7>oRAjPAvj}6j78{(#&rtxWWzcMScy@Kgt8(XXNG}-5>&H(*US}s>F8VB<_20t8 za7x0ohlZGN+c(0DAY3@@@;)>4Xlaw$VHm8N3na>ZCi$wFEgr;G-up%u_G^^ZLPv%xg;6fm zr08twbR~EmM(|$2=sr^Yqb8lQa0d zO`gCM^U~(I+r-rfXPUUl-$Y~NzQl)O`04kkNV41{hi1N{hiXWYu2QTfw(8Y@Yt0HH zjuo2AAB23lSJ6?K(ZA?@IRU+Tf~wiWQLQDdc>r9haf|kJO81H#8tg}qrl~A&P zxuKeTwYH)Kmb@H__FSIsaOy{WvpwAb4o0}}G{ypO2BUP%Es3?)$?(Cva%nfpNwtuT z4Vij7okML3bTDMAIdznr?KMLJH$1 zJ8#pqHIwqRNILYK&)*3X>3Rxe8{HCzrHwkW{_|4l`UD_ZR{Nj|l( zLWehumkCSI=N-;#m!vXt$e_~HGb*|FE55Msl~g2m z1mpoq-LG&J#<&7q{1My(oiH%qYP@C*WDd?p?&Ceai%Zy=J41o>`CY95>!Ba$ry!g&e$I-*yn8`vdrrKegtdS{TIo7shOe&hSv$i9|L|eg*X*uc73w^vVZ8T zxli#EA4?C3Daod2pcBMD1Hwo}ra9q^Hl;c$*#nnL+JAKo)^DN=M3AdY^v~r9?+D~_ zeN|||v@GKLsdLc?IEp)fSu<(#N4SAR4cw{*d@)UjKi|hyG(@G$G4}>}2h|Y`V#B&# zpL;?8Tyz*gPVfOgQlycy=4Y7ef#)abx6_>s z<`9{Pz&nEAszNLms*Pml7}# zD3F)`Mk4?J8;8pCc);9Ld!;31^sygLyq#4H9O{U8`Jg6fbA&{ag2>r$Y^p3{<^e6N z)#I*HNx_fG@554=g# za3+&cGX`sJ zIG9y&HCUHPYj({Y_Ie{xOV&;*)w+WSpJj0(n$(k36!f=*Vmw>O#O(tp)!ZDDR?;|i z*PLzgZ&8jLZmg>AZJoUXrKL}I%qP9A)H@5OX_vZ1gv7T~-r6gwxAa}!sx;?NKOGiH zl)ZlmBqlV0d-no{Eu9ruYXUM3be#%e)amZ2dyVM`!EtB8b{-;bfzNASDP*ho&e*go zEPK#9F1G|_Y{HwU_LTABu{LujoCh5;BO z>#EVL9@CbZ|AzWe7BAYYg~xEfEn@Jr2MNH_oi_S`tds}DtENDoVP@AXOt$I#zCeWJ z-mL*0w)C$!u`R0TC6j z3lq!$CF$sSwp1(Vh$DC*)Ez1g(G%iFYOfpPfZp&~^4Y}=ZR0ps@_}^pv;}vRgb6D7<4EFj1b(@MP9Tq{a zV*sU`Jhud9LW>zS=r#UcJ=A&_(^1ryp3e)1{n`3CTYFR}^tO z|Ip}6&@Zqw2Q?O{&LiCpi2hg2!S5)Qng87oRsV_k|8_gt{7TET-sHoR;gvn>M4Pv-G4CRBls zQ?4kpNy#kQXRm3o(w>3~NVhD; z`F;2G>YDGPqJ?V@T=O5^+YU2KmrnAmemPLIOl=!ezSo~)0)c956KLNgVJ7rOyN66( z3nz-r+V`Qy>}zmJ*hTv67DIkAxRIAv9kY%>M~RlVh(++Xuh!YRBlgi^6HkSw zr9pA%i@b_vGGok&=N*C07*o6srV=AnaU3;l^r5tl{QndhEnw&BTWm$LOx%>Ut9DE? z_y|w;-{$+DfQDKw^K}gQPGJbmKQ3vsH~-ORUf)*N=-ic9z1`aZ#gJ>>A!bMRn?KKx z^)B7^-xirwU+R7r?5>9O*?H-Dx&_gE?lINW=}&LK&ZaSe>}$c+WLJwJ=ShfO5_fM; zNoHCteChJzMKyA`&}ZuqN}sSER+1v~rO|1@jDbF4py~7t**i-GaAd zp*2_qNo3W<%r~@#cS&7A+_k(}#d62?NZbR49sb%7b!BCl8@om{zY*@JYPF-ENjkNv zC6Dklu|UyKoqxD!M$YSCI!PoNWrA74CKBtB?_QcG#{K;Nu9M|aS>Ql2UebR(zyC8} z|C`kX6;AB>Nna7?Nw^qRVU#GCS?e@UbC*V{{h_R%lbJZ5cGRd5nML7_mN`cX4VsQ=?ty$HexfC(WIb~ zb*e^sGl~BEaqZMbN-?W5ol_v^3m5i*mwUPhEzbw#kZ|bMiAQszRhMIoKs37;M!BVS zci7a3pt|fg-wlho2a?~-Iz_p&bw}832SR0&eYV2{EB_q%u<3~NrS1;LGlN>Sjy{>) zP{WHv##-2Aro#;@^fBd{E+51F3GOIH$L+3a%&b1S$Q30@-v*Ql83Ljf4 z%o4}Lp!%J${nFc*Y+Su>rlZ-@c-6O8tvJo1hOHnpo?cD`%RaI<-)h_ddS+hl*nR6R zyT~CGNU{9XS^7ou?Cr+D8KGOrtI%%QcU1IdMzDVh;SPs`PBGrTlq& z{&Q(8a*8e4)fxxI<4s@T?!JT^H1Cc-U2Sis%;EPn*$hFCP<(yQ5bXkYcEJ9v{_I?}49v)wCi15Izbpv)LWC%)Jk-@ScEHz6} zZ*avV*x57OafGy$=p|4eo+*)TyESrz`05GEQNQQUb|)Vkwx zS?88ym8uZe(@#J~rIITI;wF|VDT2l)ej)y^JLi(QwIoxgIzs>ph02?AQ$s)z5#3@$ zQ~opTod3!mY{{ru%*lr7NWf2hbK90TQh;SsSg2xMG@jZ}i~NIk|ET;QrVI=7)&V&R;l zMBHs+^U-g`mT6{#Gyb(q8V6?bYX2QeY<*{bANTf73+UU|G}~xJ|JZIkekXB!s^o<> zG}bfQCX;T2(0YM6q$);av22f83?Xan*nl;It*XvAjepq+H!Bj+?ffv;#6VmnFjho9 z@AD9bbfC_$!UdZ?HP8bsEAF$c{s0vmdskoD%25!52mbgmhZ)1<1m^*3fCb@BcLr(5 z*TN+3@5&&>iYG=@?MJuBjQ?>&VZ4WW1_la&mdV+;sdUB|FO&(&ql?Z68bP$SDK?+c z`$*m4>b3Bg8vSKQBrKHXsxvqmZ5~SslSJ;os}a!NcC1bU0jSn%CF$5nJZhg~CDk<) z0j=`78`*{<*`@Dr58mstYqMdPd*;D6pBTllr0&|=K_Vr@DE3FAFmjQ5Vk(MeDJ!Xy z#M^ZU-Ci}Nz}roa>O_!`XFXIx zg-dg^WIt>#2%N;$X8l8!sX$P4OGoK1y2CH38}gN6)q*ej5q5tV8_0&|LzVL`&QK;x z=-urw9UNJX^3r0B;Y0I?^6u5(50_&Tj7Y+X6}%=*2pevbW*Jv-l^=Q-ACi4dM#ZMg zf-@&Pl9Cg6=Rg)8k~m7ReQhjx@Z6OqXvs+Sj=~<)TPBRrqq-3+SN75o$lQ-7PHTb10ysVBK8#+Hm;PcM-K5H! zH@q`v!l4Ko*N4(?yNc-4^*aCJ<0Oblg^Zs(-b z$b0+@u0ml%agOTe)J+<28zm&H;Qxs^lj3XrRwxgydGe0ph`K+tvzY0FcIxaESAaqO zBqqi8`5FRhcd1(Y+$wVx$<~)^HwzmNzxJKdX}P7pWlMy8{wctL@>E|!Q;{R`J5&nM zO?1i4|JpngxS=d<^8D1CPnb*T>$X$sgd|82{r|c+>!_%@c7an$DL@8>;#J!kLVKF{83J?E@-p2FEBj;}Oa z#qJny0TeUD^6|v#7BDz7cm}Qb(E67N#I*}l;UyI}QB3`YnEJ>~S&(+jI4>GP6OW+J zUXhhuz(>XJRm|)1es<}{q`?&+yleIaQVDb;ig$*tl?x%?(o)|NPVC;gFP|z(5)KfxO`~s{uLqWfwp-zz=E^dsT{bI+n=}(y z`(F8CCgypgKQ;2Ai82)QOMcGMD;CV~UMBf@$RNt1Dj}1+7v_HqlH~u&PD3;l?l6&` z(MF{2{N=+>+|yJVx<|EzkPi#W`qmS-rZ3>quXyR_i3Vw|Tzmta*=o1G0bvTM&!N-r zNrm|xXZo@&*Rj(y3$)A$9C^HVd&;qrJl6g7UD`9H)DM4-2{(~ni(ZOfpkKu{uD-!w zk+_{p6BxP~jKT^q%4ATwG$ceIZggdpoY4s1(a?Tt$fT@iWD4}%euB1q=)YbWEvZ94 zRIHd=i#@FFwd-kJc(Y@45cIV|_ZXTMrqqt_52$Zezjd6jO(8n-aiaK2Xqlri+)1b^ zAsAq4kf1DkY1O2(O`P=6#X%T2Gnh%x-=VDg)f-rzp1V!lUl?8MXr3v9lBueP=R7hs z&;MGC@zWpVFY0wWF5k)0t;Z=9ke6#wnUM4hqxiRwldV_EAo0P{R|k0cBtBupOyMMgeOF{m77%?5p<1r z=-nVyyqWtU3l>4v3SD`0^)fVOpg0>UJ3?N3F2%%r$5il~dTW~Mg%$STd&;lv&*!L1 ze97&puZdRfX8cm=ihkkJD64HKsykfd|cWPZ&N zRa~oeNZw?A>7~*UY5{qV@2X*2=@0vZ`}a3V=kcpWm|(g;HV3GM)OMk6R1eXeUW$g_ zq;01GM|!%;S^tws$q3~(vNnJ~QZ?N5KTX#E@lM)QwB@G9Knurti>ymVVUn1^BjA%o zBgg1qSb7fXYkl#|j9Ef^Pq(;cu7R{(ji^tINV?p@lk^XL^_fAqa3h4l>eg^7SJF#wa=_b|V?xxmZ z=X7&Y7P-9UDwME1e&UG_?d_mFWuX>YnvMw*v$_e3^zw=h`o7j7a!>nOCCUgMy`~xS zd1EJ$n?|8Y$TWn9&gZhMQ0mQxjlCT*Q95_aF6AgTOC~Y8kA-2x7{+ks03q2^Zl(BL z_~6KgKv#5?&JR%2q8`pJUMLDi%U6Gl6Kw+LR2%nj-U$6%^2wtdrx$8@+43F+bgE`+!@r3JY77vt zBIbRv#=UKWTREtdJ@F4366PWdrUUy3b7{-N$ z6`m*E8Cr^)d|F3BHn=d8jP;JOG(??steLx^d^$WS#9SVB{^)!v$)T8ysFvYWnZYT9 zFJ4-C1!`}0W;14>r9W!03YdK>~7Q;934_CmW$c=zE4(!v=A-zgAP z!;5Ua1DZ2k=)h*C#1@C~_`C?NMlDJ>m8+d4bfzlzw24&QzJunR2=R@F5NEo(#CHG9 zA@b!ZW#GX0m>pSVb@%aQo~be@xjCoL2H#m~@U31rDZlA)nUijZX-?x5VTGaOt0|p% zYM^k3s*RMur{roINmfI_{xWrir13#}frjsbPOW?L4RRUxe2cn2Ldjh75(*B2XYKYS zp$-U9>(P+kahcMrm6|JT9A>f2`&p^+W9ck47l&50hFZj#8TOLZ9e8$pM)a3y+84|o z60stGj3%5@6imFEEtv(Ht$W-&2YO^HW`%$o$`ZOgI~+p}gp%r(A|nR5D%%omRaJOJ z%-wQquj|+Gv{rM>9_1^T8~4?TtX8XpLlV!h3o%meR~r^GII6~JHuDw_Jb7oCOD9>1pIJ9HftcQ7vDP*WymKq~5&GP4uX&Ur ztctg~Q&&}cw8dZcjT*j&@4evx0_rQQ)tOXiGPYsHrm*Q{;Nh2TmhS$hLcau4XS~)p zT)c-~Yt=Mx9z#@uRL_1oaz?7d0S-zvSI z2Cyz5KqBrEL_) z3Y4@*cU2~aBA;;3@Mtuc$7EDn8xpW?=L>DOW_=ho2|b_avyZgmQDKPZAoPthhP3Q? zSXJ{(wb96A9uGQvwTuOz*RGMGgVvbQ(?243R`?~xb_mUqPibhf`)nn-iN>IWdZffk z{tfHK6`HRsKsk~^?!oGN;!uLpLCAnjM1p=adB)Q`Tf~icb);48A z!F(iv+T3caSxOX+x!UintFI@&&2?=%;CjM0G%w5GIHu6Ymbo4^-B5K-O5lcN?nTLI z7a@*oQ3K(ebA+~4ud`z_eY$F7oj5me1L&Ad59$L4dXd&rI3m0hVOn_gU>Pc^RtFiI z-tA4?gKBMk6{bE{1X>f5sRmw!{^|3(oYA|0vm={5hemqW-c&YW*bK$2l`}IidQ(tl z)yR%7aDmCyENP4Y8kY16CyDQt;aPUkOsm*xI=)~5z)kvycSFvxmie0UrqXLR`mVbo zzilr14&3c7x1JkQ(h12Ji7uC(i0(K8)2LWV?OMs_xH(iBzSuwoTLBlvm0*}8zfe4V z!&QSbrfdRdKVBWKgLoH@tGXQId34n&_0$2OWXY?ECN5V{GS@RLs!AcNSW-PTTc6(O zATZxVIgl~NJZt9QfHTtc1ilfGqbdY|yS0Zig+Pz8!GL|L9G zfC=v~gqhhY1t_tv$z0zuVgrdYvzkRs{kJ+LsJo~|!__fAat_~rGMnu<3gDwnu24MT zyN#!hVtWTwWS_6!I!kR%;PYp@4yE4JxIEG!*$fonRvJN8xDk1t0GXe|PhK~BNf7ol zYAP`=FY+vXVM@PmcuB$fdN|j6cVoqAf!e3)Qsj!6r{L#v*ROpm-dE))aXtC2Y|USI z>m-~mlN|!Mi%4d8Tg2IEmyZ?u(8zEqg!N?MI;uX87ROk?93oiiWZ z4gPj;mqq!ExBRO7tz$x)pPrTYh5_Ev8DC$Nwrs;RlMr51 zE-b1rqvE!{-PIyIYLw4L9ZeQd{g$y1g>)vC4GN@;^GUOv6i!`>_p}X3MJ06^-&`e2 z=$oJpySlW+Erj3#GsPEwftR>a>ds&kLXS06^nji}esoZ_uSWUO#)m2Tje&M7H29A0 ziDJ?tK1WJUYn?`_vtA<9${PlFAg6uLn>UuNF6WQ>Xcw*d<6OPi=_wLE|3Z9Gc(!t? z1x=-;x=#_>UBB0U;Q=0&_r=y1J!c7kEwFpOn)T9IZ4sz}uWl*$>@g=b_Jy6DDdkwX zs5E9VN|w$4(p!)z4ZjlE&p&xp)^%WYP>|t#u&WtTuA0-)e-_iRbnv3oK4u~1D~L}L z{u6}Bn>!vFWTpva_C-MX7SkPFVO$d!01?3w`l!Vo z^r3bC3uuSB`&FB``@12WN6TFo9(v9rpkNBj&C!hV3UdPB(=2@q#2oSdK|E@G!a$pN ziah~K^vfN_(-1#_!X?^^r-o0kxuGm=+Q}KrI~?kK!S2pJK~=%TU7@PDmf|h$JXP*W z%;Orya64eC^|*((j7li}&wGmocP0Hcdg7P{|B0Yz&=-}!_1NR+fVY=ECN3jl;Q@jd z*YZpQ=^~?3Ps9}Z%ciYmxn$g(FR}Vr3z{kn3F1QQ90|adB89IB@bI#J(m`?jd}HfB zJpvVO!b$aXY(JDaY8)DHW`n&xx{yro&oW%*NQ-h+i+vso zxlnoAKwb5Y3!Rp$h^BLmJx+;6I6dtRNqRDGazF)2sd+1Dir@L|0?xDf3No?`TAMne zd5tH+iLSz@C@!mP+QHvDZ117uWUFL3Ug(EzwA2EDib-g4rkQeE8Cs#EjGHrz`9|f{ z44lFhN>_C3%{y@%_LU3%i6X&uBJJ1tm3l1^KJKUq{lz!2O#vL6u63%vKksjr*NMMw zj}R2(REoRvW6ja^SqQqjZF+WoLIT)2K$WkVer0JY&^XbRIqo*+TUcdSvlLO_nP=#f8{#WF{8Y#5&omUrH7T{> z@}a)l3;vx16z`V_H#NaV)J7Fayi47QHfDHnEy5J#^@Hdv>_R}Od+^OR{Y?BS)^1Z` zpi1}l3$6wEPy>;U8gQaf&!CZm#U~|e`vSyfQVeIJ*HoiZ*>9#j_d^ARQ*xXdirze6+rEuz5Pz>jU^;t;jF)~Q$k4A!!(v;l zPu~ipHuQDQ;@#^2r5dRb?QJ6u*7*2S&Ss1Fh<@CT8Dj)=J}6xsTsM2iIc#-a5TPo* zPxoykV-w8gIh@b-4x_m=U>=a|rLkeNl=C*|bY@7t1D8fAe~+1d-be44FlIo=_8{gfJytW@ZC$fy#8eQbl18w)bUM5FC{0h^yr%u;uUv6 ze4btkw+~a$Ri@qUr^aHs@mi;$B2lz`a&BN&NZYwjODSj*X7 z5&6rJ$LoT5+sUkL=Q#tLg^-gkLzN*7cqc!iJOl_f*JlIqec*#N18W8nMj(UxclV#I zlG#ReKgy)C;|}X~F9SCrn}SVWXsfWkoDuuH#TQYBGF-2K+ba{|Mdg~oNrhR05!u<> zVmX)hQeTQ7Q%ZGPy#ny~(~t4FWj+xKS7P7Sq($wAvF6AJ4~Lc}<9OQj+=c@~4Gwy( z<(1*q@_XKpC-k+UzDxC`vfc+Sg-K?Yt(DW{v$)6t>i2P6HCQK~Qdzdv`Pw^wV9|)N zjNE^LzBv~fUfH(hsxj{@KQu4N;|E*U>UMk|`9`-bGqRn3rq_q-c?J2Y+Sa8hd&|T% z!nWq#w=>u>SybxqS+;{HD|S*ooe!jwKh^A%qQ{8c}{ zHcQ)MG|uDj$dZ08zr-4(uSRryc()WYcMNF!1I6InOezz1qLm%H2+yq_mTPTsz;L!H zx&%lR)b~G5m%mTbO64{wFHNXr4?wZ|6yc-0H^YQ(XYhE5Ip=%F@HOaS$Zl4-DTw=? zCINCxc^^XKksfztHuXnOU6eK_)BCZ9wfXgjB`|;Ww!KdP{vplJTNSFg=8aUt?V5xk zTc`}>TvT4>FLRsmmWP=B62e@;pq7^S}qtDvc!|2%qp zuaf6ZZT80~{o?yhje#wDrgc=QI(ko-JE3H#LpVgcjkm2bYMm*zGJPG@@e0y3QM~># zG6CXE+QT8>6S^L^@MGJJq^DR=cYUa>22jrU>k=TQ&=7IMsC%~kTMR)^19*yyz(7%! z!^Co9GPI+Y%Jzam0AaWMX?-)m~G6yUF(bjZZof3HwJ7-~NWvPB&}EU2FV7A8%A z_wVGW^uuJCG#<>Zj~&fe4tdJ}Q2J&+J9OwR)!RyRxl_@@wI4_TGwg*>XrUtp~FgGxnjs)r~3|J>z&*%{vl z@T)Hq%w~)L@K^HbzZ6n(_kRFi_yb@=NbrC2a{iN+^B0H+djOS3I37KuJHS4Vkf2pY z!g!;^9#%d?kddb=Mdm<&{#uzEgM`>%3SSBS*$c))I8JGl6s^o*jA|E1}BJIsHQ{V{pR HKcoKx#<@Jp delta 23104 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 data) { } + + @Override + public String getAccessTransformerClass() { + return null; + } + + private static File getMinecraftHome() { + try { + return (File) net.minecraftforge.fml.relauncher.FMLInjectionData.data()[6]; + } catch (Throwable e) { + return (File) cpw.mods.fml.relauncher.FMLInjectionData.data()[6]; + } + } + +} diff --git a/legacyforge/src/main/java/net/minecraftforge/fml/relauncher/FMLInjectionData.java b/legacyforge/src/main/java/net/minecraftforge/fml/relauncher/FMLInjectionData.java new file mode 100644 index 0000000..1536e73 --- /dev/null +++ b/legacyforge/src/main/java/net/minecraftforge/fml/relauncher/FMLInjectionData.java @@ -0,0 +1,21 @@ +/* + * Forge Mod Loader + * Copyright (c) 2012-2013 cpw. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser Public License v2.1 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Contributors: + * cpw - implementation + */ + +package net.minecraftforge.fml.relauncher; + + +public class FMLInjectionData +{ + public static Object[] data() { + throw new RuntimeException("Dummy implementation"); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/fml/relauncher/IFMLLoadingPlugin.java b/legacyforge/src/main/java/net/minecraftforge/fml/relauncher/IFMLLoadingPlugin.java similarity index 98% rename from src/main/java/net/minecraftforge/fml/relauncher/IFMLLoadingPlugin.java rename to legacyforge/src/main/java/net/minecraftforge/fml/relauncher/IFMLLoadingPlugin.java index 40df6a8..69bfb62 100644 --- a/src/main/java/net/minecraftforge/fml/relauncher/IFMLLoadingPlugin.java +++ b/legacyforge/src/main/java/net/minecraftforge/fml/relauncher/IFMLLoadingPlugin.java @@ -19,7 +19,6 @@ package net.minecraftforge.fml.relauncher; -import javax.annotation.Nullable; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -61,7 +60,6 @@ public interface IFMLLoadingPlugin * minecraft class loading * TODO: implement crash ;) */ - @Nullable String getSetupClass(); /** diff --git a/modlauncher/build.gradle b/modlauncher/build.gradle new file mode 100644 index 0000000..5db3ab6 --- /dev/null +++ b/modlauncher/build.gradle @@ -0,0 +1,6 @@ +sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' + +dependencies { + implementation project(':core') + implementation 'cpw.mods:modlauncher:8.0.0' +} diff --git a/modlauncher/src/main/java/io/dogboy/serializationisbad/modlauncher/SIBTransformer.java b/modlauncher/src/main/java/io/dogboy/serializationisbad/modlauncher/SIBTransformer.java new file mode 100644 index 0000000..14437db --- /dev/null +++ b/modlauncher/src/main/java/io/dogboy/serializationisbad/modlauncher/SIBTransformer.java @@ -0,0 +1,38 @@ +package io.dogboy.serializationisbad.modlauncher; + +import cpw.mods.modlauncher.api.ITransformer; +import cpw.mods.modlauncher.api.ITransformerVotingContext; +import cpw.mods.modlauncher.api.TransformerVoteResult; +import io.dogboy.serializationisbad.core.Patches; +import io.dogboy.serializationisbad.core.config.PatchModule; +import org.objectweb.asm.tree.ClassNode; + +import java.util.Set; +import java.util.stream.Collectors; + +public class SIBTransformer implements ITransformer { + private final PatchModule patchModule; + + public SIBTransformer(PatchModule patchModule) { + this.patchModule = patchModule; + } + + @Override + public ClassNode transform(ClassNode input, ITransformerVotingContext context) { + Patches.applyPatches(input.name, input); + return input; + } + + @Override + public TransformerVoteResult castVote(ITransformerVotingContext context) { + return TransformerVoteResult.YES; + } + + @Override + public Set targets() { + return this.patchModule.getClassesToPatch().stream() + .map(Target::targetClass) + .collect(Collectors.toSet()); + } + +} diff --git a/modlauncher/src/main/java/io/dogboy/serializationisbad/modlauncher/SerializationIsBadTransformationService.java b/modlauncher/src/main/java/io/dogboy/serializationisbad/modlauncher/SerializationIsBadTransformationService.java new file mode 100644 index 0000000..0bac2f2 --- /dev/null +++ b/modlauncher/src/main/java/io/dogboy/serializationisbad/modlauncher/SerializationIsBadTransformationService.java @@ -0,0 +1,76 @@ +package io.dogboy.serializationisbad.modlauncher; + +import cpw.mods.modlauncher.api.IEnvironment; +import cpw.mods.modlauncher.api.ITransformationService; +import cpw.mods.modlauncher.api.ITransformer; +import cpw.mods.modlauncher.api.IncompatibleEnvironmentException; +import io.dogboy.serializationisbad.core.SerializationIsBad; + +import java.net.URL; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class SerializationIsBadTransformationService implements ITransformationService { + + @Override + public String name() { + return "serializationisbad"; + } + + @Override + public void initialize(IEnvironment environment) { + if (SerializationIsBad.isAgentActive()) return; + + Path minecraftDir = environment.getProperty(IEnvironment.Keys.GAMEDIR.get()) + .orElseThrow(() -> new RuntimeException("No game path found")); + + SerializationIsBad.init(minecraftDir.toFile()); + } + + @Override + public void onLoad(IEnvironment env, Set otherServices) throws IncompatibleEnvironmentException { } + + @Override + public List transformers() { + if (SerializationIsBad.isAgentActive()) return Collections.emptyList(); + + return SerializationIsBad.getInstance().getConfig().getPatchModules().stream() + .map(SIBTransformer::new) + .collect(Collectors.toList()); + } + + @Override + public void beginScanning(IEnvironment environment) { } + + @Override + public Map.Entry, Supplier>>> additionalClassesLocator() { + if (SerializationIsBad.isAgentActive()) return null; + + return new Map.Entry, Supplier>>>() { + @Override + public Set getKey() { + return new HashSet<>(Arrays.asList("io.dogboy.serializationisbad.")); + } + + @Override + public Supplier>> getValue() { + return () -> str -> Optional.ofNullable(SerializationIsBadTransformationService.class.getResource("/" + str)); + } + + @Override + public Supplier>> setValue(Supplier>> value) { + throw new UnsupportedOperationException(); + } + }; + } + +} diff --git a/modlauncher/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService b/modlauncher/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService new file mode 100644 index 0000000..72a6291 --- /dev/null +++ b/modlauncher/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService @@ -0,0 +1 @@ +io.dogboy.serializationisbad.modlauncher.SerializationIsBadTransformationService \ No newline at end of file diff --git a/serializationisbad.json b/serializationisbad.json index 09d1388..a696b31 100644 --- a/serializationisbad.json +++ b/serializationisbad.json @@ -3,7 +3,8 @@ "patchModules": [ { "classesToPatch": [ - "net.bdew.lib.network.SerializedMessageCodec" + "net.bdew.lib.network.SerializedMessageCodec", + "net.bdew.lib.network.NetChannel" ], "classAllowlist": [], "packageAllowlist": [ @@ -66,6 +67,24 @@ "immersive_armors" ] }, + { + "classesToPatch": [ + "immersive_aircraft.cobalt.network.Message" + ], + "classAllowlist": [], + "packageAllowlist": [ + "immersive_aircraft" + ] + }, + { + "classesToPatch": [ + "immersive_paintings.cobalt.network.Message" + ], + "classAllowlist": [], + "packageAllowlist": [ + "immersive_paintings" + ] + }, { "classesToPatch": [ "gcewing.projectblue.BaseNBTChannel$NBTCodec" @@ -110,6 +129,217 @@ "packageAllowlist": [ "com.mattdahepic" ] + }, + { + "classesToPatch": [ + "com.iconmaster.aec.network.TransferConfigsPacket" + ], + "classAllowlist": [], + "packageAllowlist": [ + "com.iconmaster.aec" + ] + }, + { + "classesToPatch": [ + "net.tslat.aoa3.common.packet.leaderboard.PacketLeaderboardStats", + "net.tslat.aoa3.common.packet.leaderboard.PacketIndividualLeaderboardStats" + ], + "classAllowlist": [], + "packageAllowlist": [ + "net.tslat.aoa3" + ] + }, + { + "classesToPatch": [ + "pl.asie.lib.util.Base64" + ], + "classAllowlist": [], + "packageAllowlist": [ + "pl.asie" + ] + }, + { + "classesToPatch": [ + "arrowsplus.core.forge.PacketHandler" + ], + "classAllowlist": [], + "packageAllowlist": [ + "arrowsplus" + ] + }, + { + "classesToPatch": [ + "hellfirepvp.astralsorcery.common.network.packet.server.PktSyncConfig" + ], + "classAllowlist": [], + "packageAllowlist": [ + "hellfirepvp.astralsorcery" + ] + }, + { + "classesToPatch": [ + "mal.carbonization.network.MultiblockFurnaceMessageServer" + ], + "classAllowlist": [], + "packageAllowlist": [ + "mal.carbonization" + ] + }, + { + "classesToPatch": [ + "p455w0rd.capes.packet.PacketClientAddFriend", + "p455w0rd.capes.packet.PacketClientRemoveFriend", + "p455w0rd.capes.packet.PacketClientUpdateFriend", + "p455w0rd.capes.packet.PacketServerFriendSync", + "p455w0rd.capes.packet.PacketServerPlayerSync", + "p455w0rd.capes.packet.PacketServerTextureURLSync" + ], + "classAllowlist": [], + "packageAllowlist": [ + "p455w0rd.capes" + ] + }, + { + "classesToPatch": [ + "gcewing.sg.base.ForgeNBTNetworking$BasePacketHandler", + "gcewing.sg.BaseNBTChannel$NBTCodec", + "gcewing.sg.BaseNBTChannel$BasePacketHandler" + ], + "classAllowlist": [], + "packageAllowlist": [ + "gcewing.sg" + ] + }, + { + "classesToPatch": [ + "com.zerren.extrafirma.core.network.PacketHandler" + ], + "classAllowlist": [], + "packageAllowlist": [ + "com.zerren.extrafirma" + ] + }, + { + "classesToPatch": [ + "journeymap.common.network.impl.Message" + ], + "classAllowlist": [], + "packageAllowlist": [ + "journeymap" + ] + }, + { + "classesToPatch": [ + "com.polipo.bookshelf.net.MyMessage" + ], + "classAllowlist": [], + "packageAllowlist": [ + "com.polipo.bookshelf" + ] + }, + { + "classesToPatch": [ + "mca.PacketHandler", + "mca.core.forge.PacketHandler" + ], + "classAllowlist": [], + "packageAllowlist": [ + "mca" + ] + }, + { + "classesToPatch": [ + "logisticspipes.network.abstractpackets.GenericPacket", + "logisticspipes.network.packets.debuggui.DebugInfoUpdate", + "logisticspipes.network.packets.debuggui.DebugTargetResponse", + "logisticspipes.network.packets.debuggui.DebugTypePacket", + "logisticspipes.network.packets.routingdebug.RoutingUpdateTargetResponse" + ], + "classAllowlist": [], + "packageAllowlist": [ + "logisticspipes" + ] + }, + { + "classesToPatch": [ + "aeronicamc.mods.mxtune.network.NetworkSerializedHelper" + ], + "classAllowlist": [], + "packageAllowlist": [ + "aeronicamc.mods.mxtune" + ] + }, + { + "classesToPatch": [ + "p455w0rd.p455w0rdsthings.network.PacketConfigSync" + ], + "classAllowlist": [], + "packageAllowlist": [ + "p455w0rd.p455w0rdsthings" + ] + }, + { + "classesToPatch": [ + "radixcore.modules.RadixNettyIO" + ], + "classAllowlist": [], + "packageAllowlist": [ + "radixcore" + ] + }, + { + "classesToPatch": [ + "net.smart.moving.SmartMovingPacketStream" + ], + "classAllowlist": [], + "packageAllowlist": [ + "net.smart.moving" + ] + }, + { + "classesToPatch": [ + "svenhjol.strange.scrolls.message.ClientQuestList" + ], + "classAllowlist": [], + "packageAllowlist": [ + "svenhjol.strange" + ] + }, + { + "classesToPatch": [ + "com.supermartijn642.configlib.ConfigSyncPacket" + ], + "classAllowlist": [], + "packageAllowlist": [ + "com.supermartijn642" + ] + }, + { + "classesToPatch": [ + "vazkii.tinkerer.common.network.PacketManager" + ], + "classAllowlist": [], + "packageAllowlist": [ + "vazkii.tinkerer" + ] + }, + { + "classesToPatch": [ + "p455w0rd.tanaddons.network.PacketConfigSync" + ], + "classAllowlist": [], + "packageAllowlist": [ + "p455w0rd.tanaddons" + ] + }, + { + "classesToPatch": [ + "tterrag.core.common.config.PacketConfigSync" + ], + "classAllowlist": [], + "packageAllowlist": [ + "tterrag" + ] } ], "classAllowlist": [ diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..3500300 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include 'core', 'legacyforge', 'modlauncher', 'agent' diff --git a/src/main/java/io/dogboy/serializationisbad/Patches.java b/src/main/java/io/dogboy/serializationisbad/Patches.java deleted file mode 100644 index 9509c2e..0000000 --- a/src/main/java/io/dogboy/serializationisbad/Patches.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.dogboy.serializationisbad; - -import io.dogboy.serializationisbad.config.PatchModule; - -public class Patches { - public static PatchModule getPatchModuleForClass(String className) { - return SerializationIsBad.config.getPatchModules().stream() - .filter(patchModule -> patchModule.getClassesToPatch().contains(className)) - .findFirst() - .orElse(null); - } -} diff --git a/src/main/java/io/dogboy/serializationisbad/SerializationIsBad.java b/src/main/java/io/dogboy/serializationisbad/SerializationIsBad.java deleted file mode 100644 index 8a8813c..0000000 --- a/src/main/java/io/dogboy/serializationisbad/SerializationIsBad.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.dogboy.serializationisbad; - -import com.google.gson.Gson; -import io.dogboy.serializationisbad.config.SIBConfig; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.annotation.Nullable; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.Map; - -@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.Name(SerializationIsBad.modId) -@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions("io.dogboy.serializationisbad") -@cpw.mods.fml.relauncher.IFMLLoadingPlugin.Name(SerializationIsBad.modId) -@cpw.mods.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions("io.dogboy.serializationisbad") -public class SerializationIsBad implements net.minecraftforge.fml.relauncher.IFMLLoadingPlugin, cpw.mods.fml.relauncher.IFMLLoadingPlugin { - public static final String modId = "serializationisbad"; - static final Logger logger = LogManager.getLogger(SerializationIsBad.class); - static SIBConfig config = new SIBConfig(); - - public SerializationIsBad() { - File minecraftHome = SerializationIsBad.getMinecraftHome(); - File configFile = new File(new File(minecraftHome, "config"), "serializationisbad.json"); - - if (configFile.isFile()) { - Gson gson = new Gson(); - try (FileInputStream fileInputStream = new FileInputStream(configFile)) { - SerializationIsBad.config = gson.fromJson(new InputStreamReader(fileInputStream, StandardCharsets.UTF_8), - SIBConfig.class); - - SerializationIsBad.logger.info("Loaded config file"); - SerializationIsBad.logger.info(" Blocking Enabled: " + SerializationIsBad.config.isExecuteBlocking()); - SerializationIsBad.logger.info(" Loaded Patch Modules: " + SerializationIsBad.config.getPatchModules().size()); - } catch (Exception e) { - SerializationIsBad.logger.error("Failed to load config file", e); - } - } - } - - @Override - public String[] getASMTransformerClass() { - return new String[]{ SIBTransformer.class.getCanonicalName() }; - } - - @Override - public String getModContainerClass() { - return null; - } - - @Nullable - @Override - public String getSetupClass() { - return null; - } - - @Override - public void injectData(Map data) { - - } - - @Override - public String getAccessTransformerClass() { - return null; - } - - private static File getMinecraftHome() { - try { - return (File) net.minecraftforge.fml.relauncher.FMLInjectionData.data()[6]; - } catch (Throwable e) { - return (File) cpw.mods.fml.relauncher.FMLInjectionData.data()[6]; - } - } - -}