Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added transformer for onUsingTick in EntityPlayer #414

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ public enum AsmTransformers {
"Removes the logging of GL errors from OptiFine/Shadersmod",
() -> TweaksConfig.removeOptifineGLErrors,
Side.CLIENT,
"com.mitchej123.hodgepodge.asm.transformers.optifine.GLErrorLoggingTransformer");
"com.mitchej123.hodgepodge.asm.transformers.optifine.GLErrorLoggingTransformer"),
FIX_ON_USING_TICK(
"Changes EntityPlayer.onUpdate to use ItemStack.areItemStacksEqual instead of ==.",
() -> FixesConfig.fixOnUsingTick,
Side.BOTH,
"com.mitchej123.hodgepodge.asm.transformers.mc.EntityPlayerTransformer"
);
// spotless:on

private final Supplier<Boolean> applyIf;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.mitchej123.hodgepodge.asm.transformers.mc;

import net.minecraft.launchwrapper.IClassTransformer;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;

public class EntityPlayerTransformer implements IClassTransformer {

private static final Logger LOGGER = LogManager.getLogger("EntityPlayerTransformer");

@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;

if (NameContext.Obf.entityPlayer.equals(name)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can check transformedName directly, it won't be obfuscated

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested it in the full pack and it looks like this isn't the case. I had to use the obfuscated name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can do "net.minecraft.entity.player.EntityPlayer".equals(transformedName) even when in obfuscated environement

final ClassReader cr = new ClassReader(basicClass);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write the class reader code once for both classes

final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
transformClassNode(NameContext.Obf, cn);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);

LOGGER.info("Transformed {}", name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need to log that you are transforming a class, however you should use a try catch and if it returns an exception, you should log it and return the untransformed class

return cw.toByteArray();
}

if (NameContext.Deobf.entityPlayer.equals(name)) {
final ClassReader cr = new ClassReader(basicClass);
final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
transformClassNode(NameContext.Deobf, cn);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't do that to check the obfuscation state, check it from the injectData method in the IFMLLoadingPlugin implementation

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);

LOGGER.info("Transformed {}", name);
return cw.toByteArray();
}

return basicClass;
}

public void transformClassNode(NameContext names, ClassNode cn) {
LOGGER.info("Found EntityPlayer (class = {}, name context = {})", names.entityPlayer, names.name());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't need to log


for (var method : cn.methods) {
if (method.name.equals(names.onUpdate)) {
LOGGER.info("Found EntityPlayer.onUpdate ({})", names.onUpdate);

var cursor = method.instructions.getFirst();

while (cursor != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't you use the built in iterator or a for loop over instructions.toArray()

if (cursor instanceof FieldInsnNode field && field.getOpcode() == Opcodes.GETFIELD
&& field.name.equals(names.itemInUse)) {
if (cursor.getNext() instanceof JumpInsnNode jump && jump.getOpcode() == Opcodes.IF_ACMPNE) {
LOGGER.info("Found injection point");

method.instructions.insertBefore(
jump,
new MethodInsnNode(
Opcodes.INVOKESTATIC,
names.itemStack,
names.areItemStacksEqual,
String.format("(L%s;L%s;)Z", names.itemStack, names.itemStack),
false));

((JumpInsnNode) jump).setOpcode(Opcodes.IFEQ);
return;
}
}

cursor = cursor.getNext();
}

LOGGER.warn("Could not find injection point.");
}
}
}

private static enum NameContext {

Deobf("net.minecraft.entity.player.EntityPlayer", "onUpdate", "net/minecraft/item/ItemStack",
"areItemStacksEqual", "itemInUse"),
Obf("yz", "h", "add", "b", "f");

public final String entityPlayer, onUpdate, itemStack, areItemStacksEqual, itemInUse;

private NameContext(String entityPlayer, String onUpdate, String itemStack, String areItemStacksEqual,
String itemInUse) {
this.entityPlayer = entityPlayer;
this.onUpdate = onUpdate;
this.itemStack = itemStack;
this.areItemStacksEqual = areItemStacksEqual;
this.itemInUse = itemInUse;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,11 @@ public class FixesConfig {
@Config.RequiresMcRestart
public static boolean fixCaseCommands;

@Config.Comment("Fix onUsingTick not getting called on the server")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public static boolean fixOnUsingTick;

// affecting multiple mods

@Config.Comment("Remove old/stale/outdated update checks.")
Expand Down
Loading