Created
May 26, 2018 22:16
-
-
Save Joedobo27/1165b3b7a36e5bdffa5095d806966c1a to your computer and use it in GitHub Desktop.
TimeUnitTickedTriggerMod
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.joedobo27.tuttm; | |
import com.wurmonline.server.behaviours.Action; | |
import java.util.WeakHashMap; | |
@SuppressWarnings("unused") | |
public class ActionTime { | |
private int lastWholeUnitTime; | |
private final int incrementTriggerMilliseconds; | |
private static WeakHashMap<Action, ActionTime> performers = new WeakHashMap<>(); | |
private ActionTime(Action action, int incrementTriggerMilliseconds){ | |
this.incrementTriggerMilliseconds = incrementTriggerMilliseconds; | |
performers.put(action, this); | |
} | |
public static ActionTime getActionTime(Action action, int incrementTriggerMilliseconds) { | |
if (!performers.containsKey(action)) | |
return new ActionTime(action, incrementTriggerMilliseconds); | |
return performers.get(action); | |
} | |
public boolean unitTimeJustTicked(float counterSeconds){ | |
final int SECONDS_TO_MILLISECONDS = 100; | |
int unitTime = (int)(Math.floor((counterSeconds * SECONDS_TO_MILLISECONDS) / (this.incrementTriggerMilliseconds))); | |
if (unitTime != this.lastWholeUnitTime){ | |
synchronized (this) { | |
this.lastWholeUnitTime = unitTime; | |
} | |
return true; | |
} | |
return false; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.joedobo27.tuttm; | |
import javassist.*; | |
import javassist.bytecode.*; | |
import javassist.expr.ExprEditor; | |
import javassist.expr.MethodCall; | |
import org.gotti.wurmunlimited.modloader.classhooks.HookManager; | |
import org.gotti.wurmunlimited.modloader.interfaces.Initable; | |
import org.gotti.wurmunlimited.modloader.interfaces.WurmServerMod; | |
import java.nio.ByteBuffer; | |
import java.util.Objects; | |
import java.util.logging.Logger; | |
import java.util.stream.IntStream; | |
public class TimeUnitTickedTriggerMod implements WurmServerMod, Initable { | |
private static final Logger logger = Logger.getLogger(TimeUnitTickedTriggerMod.class.getName()); | |
@Override | |
public void init() { | |
modifyItemBehaviourAction(); | |
} | |
private void modifyItemBehaviourAction(){ | |
/* | |
5226: aload_1 | |
5227: invokevirtual #676 // Method com/wurmonline/server/behaviours/Action.justTickedSecond:()Z | |
5230: ifeq 5246 | |
5233: aload_3 | |
5234: aload_2 | |
5235: iload 9 | |
5237: i2s | |
5238: aload_1 | |
5239: invokevirtual #677 // Method com/wurmonline/server/behaviours/Action.getPower:()F | |
5242: invokevirtual #678 // Method com/wurmonline/server/items/Item.repair:(Lcom/wurmonline/server/creatures/Creature;SF)D | |
*/ | |
try { | |
ClassPool classPool = HookManager.getInstance().getClassPool(); | |
CtClass ctClassItemBehaviour = classPool.get("com.wurmonline.server.behaviours.ItemBehaviour"); | |
ctClassItemBehaviour.getClassFile().compact(); | |
Bytecode find = new Bytecode(ctClassItemBehaviour.getClassFile().getConstPool()); | |
find.addAload(1); | |
find.addInvokevirtual("com/wurmonline/server/behaviours/Action", "justTickedSecond", "()Z"); | |
codeBranching(find, Opcode.IFEQ, 16); | |
find.addAload(3); | |
find.addAload(2); | |
find.addIload(9); | |
find.addOpcode(Opcode.I2S); | |
find.addAload(1); | |
find.addInvokevirtual("com/wurmonline/server/behaviours/Action", "getPower", "()F"); | |
find.addInvokevirtual("com/wurmonline/server/items/Item", "repair", "(Lcom/wurmonline/server/creatures/Creature;SF)D"); | |
CtMethod ctMethodAction = ctClassItemBehaviour.getMethod("action", | |
Descriptor.ofMethod(CtPrimitiveType.booleanType, new CtClass[]{ | |
classPool.get("com.wurmonline.server.behaviours.Action"), | |
classPool.get("com.wurmonline.server.creatures.Creature"), | |
classPool.get("com.wurmonline.server.items.Item"), | |
CtPrimitiveType.shortType, CtPrimitiveType.floatType | |
})); | |
int tableLineNumber = byteArrayToLineNumber(find.get(), ctMethodAction, 19); | |
ctMethodAction.instrument(new ExprEditor() { | |
@Override public void edit(MethodCall methodCall) throws CannotCompileException { | |
if (Objects.equals("justTickedSecond", methodCall.getMethodName()) && | |
methodCall.getLineNumber() == tableLineNumber) { | |
methodCall.replace("{" + | |
"com.joedobo27.tuttm.ActionTime actionTime = " + | |
"com.joedobo27.tuttm.ActionTime#getActionTime(act, 100);" + | |
"if (actionTime.unitTimeJustTicked(counter))" + | |
" $_ = true;" + | |
"else" + | |
" $_ = false;" + | |
"}"); | |
} | |
} | |
}); | |
}catch (NotFoundException | BadBytecode | CannotCompileException e){ | |
logger.warning(e.getMessage()); | |
} | |
} | |
@SuppressWarnings("SameParameterValue") | |
private int byteArrayToLineNumber(byte[] bytesSeek, CtMethod ctMethod, int byteArraySize) | |
throws BadBytecode, RuntimeException { | |
// Using bytesSeek iterate through the ctMethod's bytecode looking for a matching byte array sized to byteArraySize | |
int bytecodeIndex = -1; | |
CodeIterator codeIterator = ctMethod.getMethodInfo().getCodeAttribute().iterator(); | |
codeIterator.begin(); | |
long find = byteArrayToLong(bytesSeek); | |
while (codeIterator.hasNext() && codeIterator.lookAhead() + byteArraySize < codeIterator.getCodeLength()) { | |
int index = codeIterator.next(); | |
byte[] bytesFound = new byte[byteArraySize]; | |
for(int i=0;i<byteArraySize;i++){ | |
bytesFound[i] = (byte)codeIterator.byteAt(index + i); | |
} | |
long found = byteArrayToLong(bytesFound); | |
if (found == find) { | |
bytecodeIndex = index; | |
} | |
} | |
if (bytecodeIndex == -1) | |
throw new RuntimeException("no bytecode match found."); | |
// Get the line number table entry for the bytecodeIndex. | |
LineNumberAttribute lineNumberAttribute = (LineNumberAttribute) ctMethod.getMethodInfo().getCodeAttribute() | |
.getAttribute(LineNumberAttribute.tag); | |
int lineNumber = lineNumberAttribute.toLineNumber(bytecodeIndex); | |
int lineNumberTableOrdinal = IntStream.range(0, lineNumberAttribute.tableLength()) | |
.filter(value -> Objects.equals(lineNumberAttribute.lineNumber(value), lineNumber)) | |
.findFirst() | |
.orElseThrow(RuntimeException::new); | |
return lineNumberAttribute.lineNumber(lineNumberTableOrdinal); | |
} | |
private static long byteArrayToLong(byte[] bytesOriginal) { | |
if (bytesOriginal.length < 8) { | |
byte[] bytesLongPadded = new byte[8]; | |
System.arraycopy(bytesOriginal, 0, bytesLongPadded, 8 - bytesOriginal.length, | |
bytesOriginal.length); | |
return ByteBuffer.wrap(bytesLongPadded).getLong(); | |
} | |
else | |
return ByteBuffer.wrap(bytesOriginal).getLong(); | |
} | |
@SuppressWarnings("SameParameterValue") | |
private static void codeBranching(Bytecode bytecode, int opcode, int branchCount){ | |
bytecode.addOpcode(opcode); | |
bytecode.add((branchCount >>> 8) & 0xFF, branchCount & 0xFF); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment