diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..2b91bc8d5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "ASMHelper"] + path = ASMHelper + url = https://github.com/squeek502/ASMHelper.git + branch = 1.12.x diff --git a/ASMHelper b/ASMHelper new file mode 160000 index 000000000..6f5aeda4c --- /dev/null +++ b/ASMHelper @@ -0,0 +1 @@ +Subproject commit 6f5aeda4cc92c2bcd6cf173444386c955b58dffe diff --git a/Readme.txt b/Readme.txt index c039a21b0..366a7a2b9 100644 --- a/Readme.txt +++ b/Readme.txt @@ -18,8 +18,12 @@ Step 3: run the command: "./gradlew eclipse" Step 4: Import project Step 5: create a system variable called "JAVA_MC_HOME" with the location of the JDK 1.8.0_251 (This is needed for gradle to work correctly) + And make sure it is used in the build.gradle file. -Step 6: make sure the eclipse has the JDK 1.8.0_251 installed. (This is needed so that eclipse can run minecraft) +Step 6: In the eclipse run configuration add "-Dfml.coreMods.load=backsun.lod.LodMain" + This lets forge know that you have a core mod you want to run. + +Step 7: make sure the eclipse has the JDK 1.8.0_251 installed. (This is needed so that eclipse can run minecraft) Other commands: diff --git a/_tools/ASM/MCPMappingViewer_1.0.1.jar b/_tools/ASM/MCPMappingViewer_1.0.1.jar new file mode 100644 index 000000000..7a8eecf36 Binary files /dev/null and b/_tools/ASM/MCPMappingViewer_1.0.1.jar differ diff --git a/_tools/ASM/readme.txt b/_tools/ASM/readme.txt new file mode 100644 index 000000000..824c2d222 --- /dev/null +++ b/_tools/ASM/readme.txt @@ -0,0 +1 @@ +these are tools needed for looking at obfuscated minecraft code. \ No newline at end of file diff --git a/_tools/OptiFine_1.12.2_HD_U_F5.jar b/_tools/optifine/OptiFine_1.12.2_HD_U_F5.jar similarity index 100% rename from _tools/OptiFine_1.12.2_HD_U_F5.jar rename to _tools/optifine/OptiFine_1.12.2_HD_U_F5.jar diff --git a/_tools/OptiFine_1.12.2_HD_U_F5_deobfuscated.jar b/_tools/optifine/OptiFine_1.12.2_HD_U_F5_deobfuscated.jar similarity index 100% rename from _tools/OptiFine_1.12.2_HD_U_F5_deobfuscated.jar rename to _tools/optifine/OptiFine_1.12.2_HD_U_F5_deobfuscated.jar diff --git a/_tools/java_decompiler-gui-1.6.6.jar b/_tools/optifine/java_decompiler-gui-1.6.6.jar similarity index 100% rename from _tools/java_decompiler-gui-1.6.6.jar rename to _tools/optifine/java_decompiler-gui-1.6.6.jar diff --git a/_tools/readme.txt b/_tools/optifine/readme.txt similarity index 100% rename from _tools/readme.txt rename to _tools/optifine/readme.txt diff --git a/_tools/simple deob.bat b/_tools/optifine/simple deob.bat similarity index 100% rename from _tools/simple deob.bat rename to _tools/optifine/simple deob.bat diff --git a/_tools/simpledeobf-0.6.jar b/_tools/optifine/simpledeobf-0.6.jar similarity index 100% rename from _tools/simpledeobf-0.6.jar rename to _tools/optifine/simpledeobf-0.6.jar diff --git a/build.gradle b/build.gradle index 62315ca85..d86ae8806 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,14 @@ minecraft { // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. } +jar { + manifest { + attributes 'FMLCorePlugin': 'backsun.lod.LodMain' + //attributes 'FMLCorePluginContainsMod': 'true' + attributes 'FMLCorePluginContainsFMLMod': 'true' + } +} + dependencies { // you may put jars on which you depend on in ./libs // or you may define them like so.. diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..c6d8d0152 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ":ASMHelper" \ No newline at end of file diff --git a/src/main/java/backsun/lod/LodCore.java b/src/main/java/backsun/lod/LodCore.java new file mode 100644 index 000000000..50bac22b3 --- /dev/null +++ b/src/main/java/backsun/lod/LodCore.java @@ -0,0 +1,8 @@ +package backsun.lod; + +// -Dfml.coreMods.load=backsun.lod.LodCore + +public class LodCore +{ + +} diff --git a/src/main/java/backsun/lod/Main.java b/src/main/java/backsun/lod/LodMain.java similarity index 58% rename from src/main/java/backsun/lod/Main.java rename to src/main/java/backsun/lod/LodMain.java index e3d6e435f..8941e48ce 100644 --- a/src/main/java/backsun/lod/Main.java +++ b/src/main/java/backsun/lod/LodMain.java @@ -1,5 +1,8 @@ package backsun.lod; +import java.util.Map; + +import backsun.lod.asm.RenderGlobalClassTransformer; import backsun.lod.proxy.ClientProxy; import backsun.lod.proxy.CommonProxy; import backsun.lod.util.Reference; @@ -12,17 +15,20 @@ import net.minecraftforge.fml.common.SidedProxy; import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; /** * * @author James Seibel - * @version 08-31-2020 + * @version 02-07-2021 */ +@IFMLLoadingPlugin.MCVersion("1.12.2") +@IFMLLoadingPlugin.TransformerExclusions({"backsun.lod.asm"}) @Mod(modid = Reference.MOD_ID, name = Reference.NAME, version = Reference.VERSION) -public class Main +public class LodMain implements IFMLLoadingPlugin { @Instance - public static Main instance; + public static LodMain instance; @SidedProxy(clientSide = Reference.CLIENT_PROXY_CLASS, serverSide = Reference.COMMON_PROXY_CLASS) public static CommonProxy common_proxy; @@ -39,7 +45,6 @@ public class Main { MinecraftForge.EVENT_BUS.register(common_proxy); client_proxy = new ClientProxy(); - } @EventHandler @@ -47,4 +52,39 @@ public class Main { } + + + + + // required for IFMLLoadingPlugin + + @Override + public String[] getASMTransformerClass() + { + return new String[] { RenderGlobalClassTransformer.class.getName() }; + } + + @Override + public String getModContainerClass() + { + // unneeded since we are also running a normal mod + return null; + } + + @Override + public String getSetupClass() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void injectData(Map data) { + // TODO Auto-generated method stub + } + + @Override + public String getAccessTransformerClass() { + // TODO Auto-generated method stub + return null; + } } diff --git a/src/main/java/backsun/lod/asm/RenderGlobalClassTransformer.java b/src/main/java/backsun/lod/asm/RenderGlobalClassTransformer.java new file mode 100644 index 000000000..00e09aade --- /dev/null +++ b/src/main/java/backsun/lod/asm/RenderGlobalClassTransformer.java @@ -0,0 +1,130 @@ +package backsun.lod.asm; + +import java.util.Arrays; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.VarInsnNode; + +import backsun.lod.util.RenderGlobalHook; +import net.minecraft.launchwrapper.IClassTransformer; + +public class RenderGlobalClassTransformer implements IClassTransformer +{ + private static final String[] classesBeingTransformed = { "net.minecraft.client.renderer.RenderGlobal" }; + + @Override + public byte[] transform(String name, String transformedName, byte[] classBeingTransformed) + { + int index = Arrays.asList(classesBeingTransformed).indexOf(transformedName); + + // do we wan't to transform this class? + if (index != -1) + { + // yes, transform this class + boolean isObfuscated = !name.equals(transformedName); + return transformClass(index, classBeingTransformed, isObfuscated); + } + else + { + // no, just skip this class + return classBeingTransformed; + } + } + + private static byte[] transformClass(int index, byte[] classBeingTransformed, boolean isObfuscated) + { + try + { + // convert the byte code into readable ASM code + ClassNode classNode = new ClassNode(); + ClassReader classReader = new ClassReader(classBeingTransformed); + classReader.accept(classNode, 0); + + transformRenderGlobal(classNode, isObfuscated); + + // convert back into byte code + ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + classNode.accept(classWriter); + return classWriter.toByteArray(); + } + catch(Exception e) + { + e.printStackTrace(); + } + + return classBeingTransformed; + } + + + + private static void transformRenderGlobal(ClassNode classNode, boolean isObfuscated) + { + final String methodName = isObfuscated ? "a" : "renderBlockLayer"; + final String methodDesc = isObfuscated ? + "(Lamk;)V" : + "(Lnet/minecraft/util/BlockRenderLayer;)V"; + + for (MethodNode method : classNode.methods) + { + if (method.name.equals(methodName) && method.desc.equals(methodDesc)) + { + AbstractInsnNode firstLoadNode = null; + AbstractInsnNode firstReturnNode = null; + for (AbstractInsnNode instruction : method.instructions.toArray()) + { + if (firstLoadNode == null && instruction.getOpcode() == Opcodes.ALOAD) + { + // look for the first time the RenderGlobal (self) + // variable is loaded, IE the first line of code + // in the unedited method + if (((VarInsnNode) instruction).var == 0) + { + firstLoadNode = instruction; + } + } + + if (instruction.getOpcode() == Opcodes.RETURN) + { + // look for the first (and only) return statement + // IE the last line of code in the unedited method + firstReturnNode = instruction; + break; + } + } + + if (firstLoadNode != null && firstReturnNode != null) + { + // add the startRenderingStencil method to the beginning of the method + InsnList toInsert = new InsnList(); + toInsert.add(new VarInsnNode(Opcodes.ALOAD, 1)); // BlockRenderLayer variable + toInsert.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(RenderGlobalHook.class), RenderGlobalHook.START_STENCIL_METHOD_NAME, isObfuscated ? "(Lamk;)V" : "(Lnet/minecraft/util/BlockRenderLayer;)V", false)); + toInsert.add(new LabelNode()); + method.instructions.insertBefore(firstLoadNode, toInsert); + + + // add the endRenderingStencil method to the end of the method + toInsert = new InsnList(); + toInsert.add(new VarInsnNode(Opcodes.ALOAD, 1)); // BlockRenderLayer variable + toInsert.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(RenderGlobalHook.class), RenderGlobalHook.END_STENCIL_METHOD_NAME, isObfuscated ? "(Lamk;)V" : "(Lnet/minecraft/util/BlockRenderLayer;)V", false)); + toInsert.add(new LabelNode()); + method.instructions.insertBefore(firstReturnNode, toInsert); + } + else + { + System.out.println("Something went wrong transforming RenderGlobal!"); + } + + + } + } + } +} diff --git a/src/main/java/backsun/lod/proxy/ClientProxy.java b/src/main/java/backsun/lod/proxy/ClientProxy.java index f087eea9c..99f32a10a 100644 --- a/src/main/java/backsun/lod/proxy/ClientProxy.java +++ b/src/main/java/backsun/lod/proxy/ClientProxy.java @@ -20,8 +20,6 @@ import net.minecraftforge.client.event.RenderWorldLastEvent; import net.minecraftforge.event.terraingen.PopulateChunkEvent; import net.minecraftforge.event.world.ChunkEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; -import net.minecraftforge.fml.common.gameevent.TickEvent.RenderTickEvent; /** * This is used by the client. @@ -49,34 +47,15 @@ public class ClientProxy extends CommonProxy // render event // //==============// - @SubscribeEvent - public void renderTick(RenderTickEvent event) - { - if (event.phase == Phase.START) - { - startRenderingStencil(); - } - } - public static void startRenderingStencil() - { - GL11.glClearStencil(0); - GL11.glStencilMask(0xFF); //255 - GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT); - - GL11.glEnable(GL11.GL_STENCIL_TEST); - GL11.glStencilFunc(GL11.GL_ALWAYS, 1, 0x11111111); // the 2 numbers here don't matter since GL_ALWAYS is being used - GL11.glStencilMask(0b11111111); - GL11.glStencilOp(GL11.GL_KEEP, // this doesn't mater since GL_ALWAYS is being used - GL11.GL_KEEP, // stencil test passes - GL11.GL_INCR); // stencil + depth pass - //GL11.glStencilOp(GL11.GL_INCR, GL11.GL_INCR, GL11.GL_INCR); - } - public static void endRenderingStencil() - { - GL11.glStencilOp(GL11.GL_KEEP, // this doesn't mater since GL_ALWAYS is being used - GL11.GL_KEEP, // stencil test passes - GL11.GL_KEEP); // stencil + depth pass - } +// @SubscribeEvent +// public void renderTick(RenderTickEvent event) +// { +// if (event.phase == Phase.START) +// { +// RenderGlobalHook.startRenderingStencil(null); +// } +// } + @@ -85,9 +64,8 @@ public class ClientProxy extends CommonProxy @SubscribeEvent public void renderWorldLast(RenderWorldLastEvent event) { - endRenderingStencil(); +// RenderGlobalHook.endRenderingStencil(null); GL11.glStencilFunc(GL11.GL_EQUAL, 0, 0xFF); - // GEQUAL 1, once EQUAL 1 doens't render on mountains renderLods(event.getPartialTicks()); diff --git a/src/main/java/backsun/lod/util/RenderGlobalHook.java b/src/main/java/backsun/lod/util/RenderGlobalHook.java new file mode 100644 index 000000000..73f3a8176 --- /dev/null +++ b/src/main/java/backsun/lod/util/RenderGlobalHook.java @@ -0,0 +1,65 @@ +package backsun.lod.util; + +import org.lwjgl.opengl.GL11; + +import net.minecraft.util.BlockRenderLayer; + +/** + * + * @author James Seibel + * @version 02-07-2021 + */ +public class RenderGlobalHook +{ + /** + * this variable should be the same as the method name below. + * It is used when transforming the RenderGlobal class' + * renderBlockLayer method. + */ + public static final String START_STENCIL_METHOD_NAME = "startRenderingStencil"; + + /** + * Start drawing to the stencil + *

+ * called in the order:
+ * BlockRenderLayer.SOLID
+ * BlockRenderLayer.CUTOUT_MIPPED
+ * BlockRenderLayer.CUTOUT
+ * BlockRenderLayer.TRANSLUCENT
+ */ + public static void startRenderingStencil(BlockRenderLayer blockLayerIn) + { + // solid is the first layer rendered + // clear the buffer so we can start fresh. + // if this isn't cleared first we will have overlap with the fog + // outside the world + if (blockLayerIn == BlockRenderLayer.SOLID) + { + GL11.glClearStencil(0); + GL11.glStencilMask(0xFF); //255 + GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT); + } + + GL11.glEnable(GL11.GL_STENCIL_TEST); + GL11.glStencilFunc(GL11.GL_ALWAYS, 1, 0x11111111); // the 2 numbers here don't matter since GL_ALWAYS is being used + GL11.glStencilMask(0b11111111); + GL11.glStencilOp(GL11.GL_KEEP, // this doesn't mater since GL_ALWAYS is being used + GL11.GL_KEEP, // stencil test passes + GL11.GL_REPLACE); // stencil + depth pass + } + + + /** + * this variable should be the same as the method name below. + * It is used when transforming the RenderGlobal class' + * renderBlockLayer method. + */ + public static final String END_STENCIL_METHOD_NAME = "endRenderingStencil"; + + public static void endRenderingStencil(BlockRenderLayer blockLayerIn) + { + GL11.glStencilOp(GL11.GL_KEEP, // this doesn't mater since GL_ALWAYS is being used + GL11.GL_KEEP, // stencil test passes + GL11.GL_KEEP); // stencil + depth pass + } +}