Skip to content

Instantly share code, notes, and snippets.

@Joedobo27
Created May 26, 2018 22:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Joedobo27/1165b3b7a36e5bdffa5095d806966c1a to your computer and use it in GitHub Desktop.
Save Joedobo27/1165b3b7a36e5bdffa5095d806966c1a to your computer and use it in GitHub Desktop.
TimeUnitTickedTriggerMod
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;
}
}
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