seppuku/src/main/java/me/rigamortis/seppuku/impl/patch/PlayerControllerMPPatch.java

396 lines
18 KiB
Java

package me.rigamortis.seppuku.impl.patch;
import me.rigamortis.seppuku.Seppuku;
import me.rigamortis.seppuku.api.event.player.*;
import me.rigamortis.seppuku.api.patch.ClassPatch;
import me.rigamortis.seppuku.api.patch.MethodPatch;
import me.rigamortis.seppuku.impl.management.PatchManager;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import team.stiff.pomelo.EventManager;
import static org.objectweb.asm.Opcodes.*;
/**
* Author Seth
* 4/6/2019 @ 6:01 PM.
*/
public final class PlayerControllerMPPatch extends ClassPatch {
public PlayerControllerMPPatch() {
super("net.minecraft.client.multiplayer.PlayerControllerMP", "bsa");
}
/**
* This is called when we finish mining a block
*
* @param methodNode
* @param env
*/
@MethodPatch(
mcpName = "onPlayerDestroyBlock",
notchName = "a",
mcpDesc = "(Lnet/minecraft/util/math/BlockPos;)Z",
notchDesc = "(Let;)Z")
public void onPlayerDestroyBlock(MethodNode methodNode, PatchManager.Environment env) {
//create a list of instructions and add the needed instructions to call our hook function
final InsnList insnList = new InsnList();
//add ALOAD to pass the BlockPos into our hook function
insnList.add(new VarInsnNode(ALOAD, 1));
//call our hook function
insnList.add(new MethodInsnNode(INVOKESTATIC, Type.getInternalName(this.getClass()), "onPlayerDestroyBlockHook", env == PatchManager.Environment.IDE ? "(Lnet/minecraft/util/math/BlockPos;)Z" : "(Let;)Z", false));
//add a label to jump to
final LabelNode jmp = new LabelNode();
//add if equals and pass the label
insnList.add(new JumpInsnNode(IFEQ, jmp));
//add a 0 or false
insnList.add(new InsnNode(ICONST_0));
//add return so the rest of the function doesn't get called
insnList.add(new InsnNode(IRETURN));
//add our label
insnList.add(jmp);
//insert the list of instructions at the top of the function
methodNode.instructions.insert(insnList);
}
/**
* Our onPlayerDestroyBlock hook used to get block coordinates
* of what we just broke
*
* @param pos
* @return
*/
public static boolean onPlayerDestroyBlockHook(BlockPos pos) {
//dispatch our event and pass the BlockPos
final EventDestroyBlock event = new EventDestroyBlock(pos);
Seppuku.INSTANCE.getEventManager().dispatchEvent(event);
return event.isCanceled();
}
/**
* This is where minecraft starts mining
*
* @param methodNode
* @param env
*/
@MethodPatch(
mcpName = "clickBlock",
notchName = "a",
mcpDesc = "(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/EnumFacing;)Z",
notchDesc = "(Let;Lfa;)Z")
public void clickBlock(MethodNode methodNode, PatchManager.Environment env) {
//create a list of instructions and add the needed instructions to call our hook function
final InsnList insnList = new InsnList();
//add ALOAD to pass the BlockPos into our hook function
insnList.add(new VarInsnNode(ALOAD, 1));
//add ALOAD to pass the EnumFacing into our hook function
insnList.add(new VarInsnNode(ALOAD, 2));
//call our hook function
insnList.add(new MethodInsnNode(INVOKESTATIC, Type.getInternalName(this.getClass()), "clickBlockHook", env == PatchManager.Environment.IDE ? "(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/EnumFacing;)Z" : "(Let;Lfa;)Z", false));
//add a label to jump to
final LabelNode jmp = new LabelNode();
//add if equals and pass the label
insnList.add(new JumpInsnNode(IFEQ, jmp));
//add a 0 or false
insnList.add(new InsnNode(ICONST_0));
//add return so the rest of the function doesn't get called
insnList.add(new InsnNode(IRETURN));
//add our label
insnList.add(jmp);
//insert the list of instructions at the top of the function
methodNode.instructions.insert(insnList);
}
/**
* Our clickBlock hook used to detect when we first
* click on a block
*
* @param pos
* @param face
* @return
*/
public static boolean clickBlockHook(BlockPos pos, EnumFacing face) {
//dispatch our event and pass the BlockPos and EnumFacing
final EventClickBlock event = new EventClickBlock(pos, face);
Seppuku.INSTANCE.getEventManager().dispatchEvent(event);
return event.isCanceled();
}
/**
* This is where minecraft handles abort destroying blocks
* and resetting break progress
*
* @param methodNode
* @param env
*/
@MethodPatch(
mcpName = "resetBlockRemoving",
notchName = "c",
mcpDesc = "()V")
public void resetBlockRemoving(MethodNode methodNode, PatchManager.Environment env) {
//create a list of instructions and add the needed instructions to call our hook function
final InsnList insnList = new InsnList();
insnList.add(new MethodInsnNode(INVOKESTATIC, Type.getInternalName(this.getClass()), "resetBlockRemovingHook", "()Z", false));
//add a label to jump to
final LabelNode jmp = new LabelNode();
//add if equals and pass the label
insnList.add(new JumpInsnNode(IFEQ, jmp));
//add return so the rest of the function doesn't get called
insnList.add(new InsnNode(RETURN));
//add our label
insnList.add(jmp);
//insert the list of instructions at the top of the function
methodNode.instructions.insert(insnList);
}
/**
* Our resetBlockRemoving used to detect when we stop mining
* It is cancellable so we can save break progress
*
* @return
*/
public static boolean resetBlockRemovingHook() {
//dispatch the event
final EventResetBlockRemoving event = new EventResetBlockRemoving();
Seppuku.INSTANCE.getEventManager().dispatchEvent(event);
return event.isCanceled();
}
/**
* This is where minecraft handles breaking blocks and calculates
* block hit delay/current damage
*
* @param methodNode
* @param env
*/
@MethodPatch(
mcpName = "onPlayerDamageBlock",
notchName = "b",
mcpDesc = "(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/EnumFacing;)Z",
notchDesc = "(Let;Lfa;)Z")
public void onPlayerDamageBlock(MethodNode methodNode, PatchManager.Environment env) {
//create a list of instructions and add the needed instructions to call our hook function
final InsnList insnList = new InsnList();
//add ALOAD to pass the BlockPos into our hook function
insnList.add(new VarInsnNode(ALOAD, 1));
//add ALOAD to pass the EnumFacing into our hook function
insnList.add(new VarInsnNode(ALOAD, 2));
//call our hook function
insnList.add(new MethodInsnNode(INVOKESTATIC, Type.getInternalName(this.getClass()), "onPlayerDamageBlockHook", env == PatchManager.Environment.IDE ? "(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/EnumFacing;)Z" : "(Let;Lfa;)Z", false));
//add a label to jump to
final LabelNode jmp = new LabelNode();
//add if equals and pass the label
insnList.add(new JumpInsnNode(IFEQ, jmp));
//add a 0 or false
insnList.add(new InsnNode(ICONST_0));
//add return so the rest of the function doesn't get called
insnList.add(new InsnNode(IRETURN));
//add our label
insnList.add(jmp);
//insert the list of instructions at the top of the function
methodNode.instructions.insert(insnList);
}
/**
* Our onPlayerDamageBlock hook used to detect if we are
* currently mining a block
*
* @param pos
* @param face
* @return
*/
public static boolean onPlayerDamageBlockHook(BlockPos pos, EnumFacing face) {
//dispatch the event
final EventPlayerDamageBlock event = new EventPlayerDamageBlock(pos, face);
Seppuku.INSTANCE.getEventManager().dispatchEvent(event);
return event.isCanceled();
}
/**
* This is where minecraft handles right clicking
* on blocks
*
* @param methodNode
* @param env
*/
@MethodPatch(
mcpName = "processRightClickBlock",
notchName = "a",
mcpDesc = "(Lnet/minecraft/client/entity/EntityPlayerSP;Lnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/EnumFacing;Lnet/minecraft/util/math/Vec3d;Lnet/minecraft/util/EnumHand;)Lnet/minecraft/util/EnumActionResult;",
notchDesc = "(Lbud;Lbsb;Let;Lfa;Lbhe;Lub;)Lud;")
public void processRightClickBlock(MethodNode methodNode, PatchManager.Environment env) {
//create a list of instructions
final InsnList insnList = new InsnList();
//create a new instance of "EventRightClickBlock" and dupe the top val on the stack
insnList.add(new TypeInsnNode(NEW, Type.getInternalName(EventRightClickBlock.class)));
insnList.add(new InsnNode(DUP));
//add ALOAD to pass BlockPos into our call
insnList.add(new VarInsnNode(ALOAD, 3));
//add ALOAD to pass EnumFacing into our call
insnList.add(new VarInsnNode(ALOAD, 4));
//add ALOAD to pass Vec3d into our call
insnList.add(new VarInsnNode(ALOAD, 5));
//add ALOAD to pass EnumHand into our call
insnList.add(new VarInsnNode(ALOAD, 6));
//call EventRightClickBlock's constructor
insnList.add(new MethodInsnNode(INVOKESPECIAL, Type.getInternalName(EventRightClickBlock.class), "<init>", env == PatchManager.Environment.IDE ? "(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/EnumFacing;Lnet/minecraft/util/math/Vec3d;Lnet/minecraft/util/EnumHand;)V" : "(Let;Lfa;Lbhe;Lub;)V", false));
//store a reference to the instance in the local vars
insnList.add(new VarInsnNode(ASTORE, 19));
//get "Seppuku.INSTNANCE"
insnList.add(new FieldInsnNode(GETSTATIC, Type.getInternalName(Seppuku.class), "INSTANCE", "Lme/rigamortis/seppuku/Seppuku;"));
//call "getEventManager"
insnList.add(new MethodInsnNode(INVOKEVIRTUAL, Type.getInternalName(Seppuku.class), "getEventManager", "()Lteam/stiff/pomelo/EventManager;", false));
//add ALOAD to get our event we stored
insnList.add(new VarInsnNode(ALOAD, 19));
//call "dispatchEvent" and pass the event
insnList.add(new MethodInsnNode(INVOKEINTERFACE, Type.getInternalName(EventManager.class), "dispatchEvent", "(Ljava/lang/Object;)Ljava/lang/Object;", true));
//remove the top val on the stack because we duped it
insnList.add(new InsnNode(POP));
//add ALOAD to get our event
insnList.add(new VarInsnNode(ALOAD, 19));
//call "isCanceled"
insnList.add(new MethodInsnNode(INVOKEVIRTUAL, Type.getInternalName(EventRightClickBlock.class), "isCanceled", "()Z", false));
//add a label to jump to
final LabelNode jmp = new LabelNode();
//add "if equals"
insnList.add(new JumpInsnNode(IFEQ, jmp));
//return "EnumActionResult.FAIL"
insnList.add(new FieldInsnNode(GETSTATIC, env == PatchManager.Environment.IDE ? "net/minecraft/util/EnumActionResult" : "ud", env == PatchManager.Environment.IDE ? "FAIL" : "c", env == PatchManager.Environment.IDE ? "Lnet/minecraft/util/EnumActionResult;" : "Lud;"));
//add ARETURN because we are returning an object...
insnList.add(new InsnNode(ARETURN));
//add our label
insnList.add(jmp);
//insert the list of instructions at the top
methodNode.instructions.insert(insnList);
}
@MethodPatch(
mcpName = "processRightClick",
notchName = "a",
mcpDesc = "(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/world/World;Lnet/minecraft/util/EnumHand;)Lnet/minecraft/util/EnumActionResult;",
notchDesc = "(Laed;Lamu;Lub;)Lud;")
public void processRightClick(MethodNode methodNode, PatchManager.Environment env) {
//create a list of instructions
final InsnList insnList = new InsnList();
//create a new instance of "EventRightClick" and dupe the top val on the stack
insnList.add(new TypeInsnNode(NEW, Type.getInternalName(EventRightClick.class)));
insnList.add(new InsnNode(DUP));
//add ALOAD to pass EnumHand into our call
insnList.add(new VarInsnNode(ALOAD, 3));
//call EventRightClick's constructor
insnList.add(new MethodInsnNode(INVOKESPECIAL, Type.getInternalName(EventRightClick.class), "<init>", env == PatchManager.Environment.IDE ? "(Lnet/minecraft/util/EnumHand;)V" : "(Lub;)V", false));
//store a reference to the instance in the local vars
insnList.add(new VarInsnNode(ASTORE, 10));
//get "Seppuku.INSTNANCE"
insnList.add(new FieldInsnNode(GETSTATIC, Type.getInternalName(Seppuku.class), "INSTANCE", "Lme/rigamortis/seppuku/Seppuku;"));
//call "getEventManager"
insnList.add(new MethodInsnNode(INVOKEVIRTUAL, Type.getInternalName(Seppuku.class), "getEventManager", "()Lteam/stiff/pomelo/EventManager;", false));
//add ALOAD to get our event we stored
insnList.add(new VarInsnNode(ALOAD, 10));
//call "dispatchEvent" and pass the event
insnList.add(new MethodInsnNode(INVOKEINTERFACE, Type.getInternalName(EventManager.class), "dispatchEvent", "(Ljava/lang/Object;)Ljava/lang/Object;", true));
//remove the top val on the stack because we duped it
insnList.add(new InsnNode(POP));
//add ALOAD to get our event
insnList.add(new VarInsnNode(ALOAD, 10));
//call "isCanceled"
insnList.add(new MethodInsnNode(INVOKEVIRTUAL, Type.getInternalName(EventRightClick.class), "isCanceled", "()Z", false));
//add a label to jump to
final LabelNode jmp = new LabelNode();
//add "if equals"
insnList.add(new JumpInsnNode(IFEQ, jmp));
//return "EnumActionResult.FAIL"
insnList.add(new FieldInsnNode(GETSTATIC, env == PatchManager.Environment.IDE ? "net/minecraft/util/EnumActionResult" : "ud", env == PatchManager.Environment.IDE ? "FAIL" : "c", env == PatchManager.Environment.IDE ? "Lnet/minecraft/util/EnumActionResult;" : "Lud;"));
//add ARETURN because we are returning an object...
insnList.add(new InsnNode(ARETURN));
//add our label
insnList.add(jmp);
//insert the list of instructions at the top
methodNode.instructions.insert(insnList);
}
@MethodPatch(
mcpName = "isHittingPosition",
notchName = "b",
mcpDesc = "(Lnet/minecraft/util/math/BlockPos;)Z",
notchDesc = "(Let;)Z")
public void isHittingPosition(MethodNode methodNode, PatchManager.Environment env) {
//create a list of instructions and add the needed instructions to call our hook function
final InsnList insnList = new InsnList();
//add ALOAD to pass the BlockPos into our call
insnList.add(new VarInsnNode(ALOAD, 1));
//call our hook function
insnList.add(new MethodInsnNode(INVOKESTATIC, Type.getInternalName(this.getClass()), "isHittingPositionHook", env == PatchManager.Environment.IDE ? "(Lnet/minecraft/util/math/BlockPos;)Z" : "(Let;)Z", false));
//add a label to jump to
final LabelNode jmp = new LabelNode();
//add if equals and pass the label
insnList.add(new JumpInsnNode(IFEQ, jmp));
//add a 0 or false
insnList.add(new InsnNode(ICONST_0));
//add return so the rest of the function doesn't get called
insnList.add(new InsnNode(IRETURN));
//add our label
insnList.add(jmp);
//insert the list of instructions at the top of the function
methodNode.instructions.insert(insnList);
}
public static boolean isHittingPositionHook(BlockPos pos) {
final EventHittingPosition event = new EventHittingPosition(pos);
Seppuku.INSTANCE.getEventManager().dispatchEvent(event);
return event.isCanceled();
}
@MethodPatch(
mcpName = "getIsHittingBlock",
notchName = "m",
mcpDesc = "()Z")
public void getIsHittingBlock(MethodNode methodNode, PatchManager.Environment env) {
//create a list of instructions
final InsnList insnList = new InsnList();
//call our hook function
insnList.add(new MethodInsnNode(INVOKESTATIC, Type.getInternalName(this.getClass()), "getIsHittingBlockHook", "()Z", false));
//create a label to jump to
final LabelNode jmp = new LabelNode();
//add "if equals"
insnList.add(new JumpInsnNode(IFEQ, jmp));
//add 0 or false
insnList.add(new InsnNode(ICONST_0));
//return so the rest of the function doesnt get called
insnList.add(new InsnNode(IRETURN));
//add our label
insnList.add(jmp);
//insert the list of instructs at the top of the function
methodNode.instructions.insert(insnList);
}
/**
* Our getIsHittingBlockHook hook used to override block-hitting hand activity
*
* @return true if the event is cancelled
*/
public static boolean getIsHittingBlockHook() {
//dispatch our event
final EventHittingBlock event = new EventHittingBlock();
Seppuku.INSTANCE.getEventManager().dispatchEvent(event);
return event.isCanceled();
}
}