Merge pull request #2726 from ThatGamerBlue/scriptvm

mixins: make scriptvm more reliable
This commit is contained in:
Lucwousin
2020-06-28 17:17:20 +02:00
committed by GitHub
3 changed files with 66 additions and 130 deletions

View File

@@ -29,13 +29,13 @@ import org.ajoberstar.grgit.Grgit
buildscript { buildscript {
repositories { repositories {
gradlePluginPortal() gradlePluginPortal()
mavenLocal() maven(url = "https://repo.openosrs.com/repository/maven/")
maven(url = "https://raw.githubusercontent.com/open-osrs/hosting/master") maven(url = "https://raw.githubusercontent.com/open-osrs/hosting/master")
} }
dependencies { dependencies {
classpath("org.ajoberstar.grgit:grgit-core:4.0.2") classpath("org.ajoberstar.grgit:grgit-core:4.0.2")
classpath("com.github.ben-manes:gradle-versions-plugin:0.28.0") classpath("com.github.ben-manes:gradle-versions-plugin:0.28.0")
classpath("com.openosrs:injector-plugin:1.1.3") classpath("com.openosrs:injector-plugin:1.1.4")
} }
} }

View File

@@ -24,22 +24,22 @@
*/ */
package net.runelite.api.events; package net.runelite.api.events;
import lombok.Data; import lombok.Value;
import net.runelite.api.ScriptEvent; import net.runelite.api.ScriptEvent;
/** /**
* An event that is fired before the designated script is ran * An event that is fired before the designated script is ran
*/ */
@Data @Value
public class ScriptPreFired implements Event public class ScriptPreFired implements Event
{ {
/** /**
* The script id of the invoked script * The script id of the invoked script
*/ */
private final int scriptId; int scriptId;
/** /**
* The input of the script invoke, this will be null unless it is the root script * The input of the script invoke, this will be null unless it is the root script
*/ */
private ScriptEvent scriptEvent; ScriptEvent scriptEvent;
} }

View File

@@ -25,14 +25,9 @@
*/ */
package net.runelite.mixins; package net.runelite.mixins;
import java.util.ArrayList;
import java.util.Stack;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import net.runelite.api.Client; import net.runelite.api.Client;
import static net.runelite.api.Opcodes.INVOKE;
import static net.runelite.api.Opcodes.RETURN;
import static net.runelite.api.Opcodes.RUNELITE_EXECUTE;
import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.events.ScriptPostFired; import net.runelite.api.events.ScriptPostFired;
import net.runelite.api.events.ScriptPreFired; import net.runelite.api.events.ScriptPreFired;
@@ -45,6 +40,7 @@ import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.rs.api.RSClient; import net.runelite.rs.api.RSClient;
import net.runelite.rs.api.RSScript; import net.runelite.rs.api.RSScript;
import net.runelite.rs.api.RSScriptEvent; import net.runelite.rs.api.RSScriptEvent;
import static net.runelite.api.Opcodes.*;
@Mixin(RSClient.class) @Mixin(RSClient.class)
public abstract class ScriptVMMixin implements RSClient public abstract class ScriptVMMixin implements RSClient
@@ -52,7 +48,6 @@ public abstract class ScriptVMMixin implements RSClient
@Shadow("client") @Shadow("client")
private static Client client; private static Client client;
// This field is set by the ScriptVM raw injector
@Inject @Inject
private static RSScript currentScript; private static RSScript currentScript;
@@ -60,81 +55,70 @@ public abstract class ScriptVMMixin implements RSClient
@Inject @Inject
private static int currentScriptPC; private static int currentScriptPC;
// Call is injected by the raw injector
@Inject @Inject
private static ScriptPostFired deferredEvent = null; static void setCurrentScript(RSScript script)
{
@Inject currentScript = script;
private static Stack<Integer> scriptIds = new Stack<>(); }
// Call is injected into runScript by the ScriptVM raw injector // Call is injected into runScript by the ScriptVM raw injector
@Inject @Inject
static boolean vmExecuteOpcode(int opcode) static boolean vmExecuteOpcode(int opcode)
{ {
if (deferredEvent != null) switch (opcode)
{ {
client.getCallbacks().post(ScriptPostFired.class, deferredEvent); case RUNELITE_EXECUTE:
deferredEvent = null; assert currentScript.getInstructions()[currentScriptPC] == RUNELITE_EXECUTE;
}
if (opcode == RUNELITE_EXECUTE)
{
assert currentScript.getInstructions()[currentScriptPC] == RUNELITE_EXECUTE;
int stringStackSize = client.getStringStackSize();
String stringOp = client.getStringStack()[--stringStackSize];
client.setStringStackSize(stringStackSize);
if ("debug".equals(stringOp))
{
int intStackSize = client.getIntStackSize();
String fmt = client.getStringStack()[--stringStackSize];
StringBuffer out = new StringBuffer();
Matcher m = Pattern.compile("%(.)").matcher(fmt);
for (; m.find(); )
{
m.appendReplacement(out, "");
switch (m.group(1).charAt(0))
{
case 'i':
case 'd':
out.append(client.getIntStack()[--intStackSize]);
break;
case 's':
out.append(client.getStringStack()[--stringStackSize]);
break;
default:
out.append(m.group(0)).append("=unknown");
}
}
m.appendTail(out);
client.getLogger().debug(out.toString());
int stringStackSize = client.getStringStackSize();
String stringOp = client.getStringStack()[--stringStackSize];
client.setStringStackSize(stringStackSize); client.setStringStackSize(stringStackSize);
client.setIntStackSize(intStackSize);
return true;
}
ScriptCallbackEvent event = new ScriptCallbackEvent(); if ("debug".equals(stringOp))
event.setScript(currentScript); {
event.setEventName(stringOp); int intStackSize = client.getIntStackSize();
client.getCallbacks().post(ScriptCallbackEvent.class, event);
return true; String fmt = client.getStringStack()[--stringStackSize];
} StringBuffer out = new StringBuffer();
else if (opcode == INVOKE) Matcher m = Pattern.compile("%(.)").matcher(fmt);
{ while (m.find())
int id = currentScript.getIntOperands()[currentScriptPC]; {
scriptIds.push(id); m.appendReplacement(out, "");
ScriptPreFired event = new ScriptPreFired(id); switch (m.group(1).charAt(0))
event.setScriptEvent(null); {
client.getCallbacks().post(ScriptPreFired.class, event); case 'i':
} case 'd':
else if (opcode == RETURN) out.append(client.getIntStack()[--intStackSize]);
{ break;
if (scriptIds.size() > 1) // let the runScript method handle the final script case 's':
{ out.append(client.getStringStack()[--stringStackSize]);
deferredEvent = new ScriptPostFired(scriptIds.pop()); // fire the event when we've left the script break;
} default:
out.append(m.group(0)).append("=unknown");
}
}
m.appendTail(out);
client.getLogger().debug(out.toString());
client.setStringStackSize(stringStackSize);
client.setIntStackSize(intStackSize);
return true;
}
ScriptCallbackEvent event = new ScriptCallbackEvent();
event.setScript(currentScript);
event.setEventName(stringOp);
client.getCallbacks().post(ScriptCallbackEvent.class, event);
return true;
case INVOKE:
int scriptId = currentScript.getIntOperands()[currentScriptPC];
client.getCallbacks().post(ScriptPreFired.class, new ScriptPreFired(scriptId, null));
return false;
case RETURN:
client.getCallbacks().post(ScriptPostFired.class, new ScriptPostFired((int) currentScript.getHash()));
return false;
} }
return false; return false;
} }
@@ -149,20 +133,14 @@ public abstract class ScriptVMMixin implements RSClient
static void rl$runScript(RSScriptEvent event, int maxExecutionTime) static void rl$runScript(RSScriptEvent event, int maxExecutionTime)
{ {
Object[] arguments = event.getArguments(); Object[] arguments = event.getArguments();
if (arguments != null && arguments.length > 0 && arguments[0] instanceof JavaScriptCallback) assert arguments != null && arguments.length > 0;
if (arguments[0] instanceof JavaScriptCallback)
{ {
try try
{ {
ScriptPreFired preFired = new ScriptPreFired(-1);
preFired.setScriptEvent(event);
client.getCallbacks().post(ScriptPreFired.class, preFired);
((JavaScriptCallback) arguments[0]).run(event); ((JavaScriptCallback) arguments[0]).run(event);
ScriptPostFired postFired = new ScriptPostFired(-1);
client.getCallbacks().post(ScriptPostFired.class, postFired);
} }
catch (Exception e) // wont catch assertions catch (Exception e)
{ {
client.getLogger().error("Error in JavaScriptCallback", e); client.getLogger().error("Error in JavaScriptCallback", e);
} }
@@ -171,34 +149,13 @@ public abstract class ScriptVMMixin implements RSClient
{ {
try try
{ {
try final ScriptPreFired pre = new ScriptPreFired((int) arguments[0], event);
{ client.getCallbacks().post(ScriptPreFired.class, pre);
scriptIds.push((Integer) event.getArguments()[0]); // this is safe because it will always be the script id
ScriptPreFired preFired = new ScriptPreFired(scriptIds.peek()); // peek doesn't remove the top item
preFired.setScriptEvent(event);
client.getCallbacks().post(ScriptPreFired.class, preFired);
}
catch (ClassCastException ignored)
{
}
rs$runScript(event, maxExecutionTime); rs$runScript(event, maxExecutionTime);
if (!scriptIds.empty())
{
ScriptPostFired postFired = new ScriptPostFired(scriptIds.pop()); // hopefully the stack should be dry at this point
assert scriptIds.empty() : "Script ID stack should be empty! Contains: " + getAllScriptIds();
client.getCallbacks().post(ScriptPostFired.class, postFired);
}
} }
finally finally
{ {
currentScript = null; currentScript = null;
while (!scriptIds.empty())
{
scriptIds.pop(); // make sure the stack is empty, something disastrous happened
}
} }
} }
} }
@@ -214,25 +171,4 @@ public abstract class ScriptVMMixin implements RSClient
se.setArguments(args); se.setArguments(args);
runScript(se, 5000000); runScript(se, 5000000);
} }
@Inject
private static String getAllScriptIds()
{
ArrayList<Integer> ids = new ArrayList<>(scriptIds);
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Object item : ids)
{
if (first)
{
first = false;
}
else
{
sb.append(", ");
}
sb.append(item);
}
return sb.toString();
}
} }