Merge pull request #3055 from Owain94/upstream-2910

This commit is contained in:
Owain van Brakel
2021-10-29 04:50:09 +02:00
committed by GitHub
72 changed files with 2014 additions and 909 deletions

View File

@@ -25,9 +25,9 @@
object ProjectVersions {
const val launcherVersion = "2.2.0"
const val rlVersion = "1.7.27"
const val rlVersion = "1.8.0"
const val openosrsVersion = "4.12.2"
const val openosrsVersion = "4.13.0"
const val rsversion = 200
const val cacheversion = 165

View File

@@ -28,9 +28,8 @@ package net.runelite.cache.script;
import java.util.HashMap;
import java.util.Map;
import static net.runelite.cache.script.Opcodes.*;
public class Instructions
public class Instructions implements Opcodes
{
private final Map<Integer, Instruction> instructions = new HashMap<>();
private final Map<String, Instruction> instructionsByName = new HashMap<>();

File diff suppressed because it is too large Load Diff

View File

@@ -22,12 +22,9 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.script;
package net.runelite.cache.script;
import static net.runelite.api.Opcodes.RUNELITE_EXECUTE;
import net.runelite.cache.script.Instructions;
public class RuneLiteInstructions extends Instructions
public class RuneLiteInstructions extends Instructions implements RuneLiteOpcodes
{
@Override
public void init()

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,25 +22,12 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.api;
package net.runelite.cache.script;
/**
* Utility class containing ASM opcodes used by the RuneLite client.
*/
public class Opcodes
public interface RuneLiteOpcodes extends Opcodes
{
/**
* opcode used to return from scripts.
*/
public static final int RETURN = 21;
/**
* opcode used to invoke scripts.
*/
public static final int INVOKE = 40;
/**
* RuneLite execution opcode used to inject scripts.
*/
public static final int RUNELITE_EXECUTE = 6599;
int RUNELITE_EXECUTE = 6599;
}

View File

@@ -56,6 +56,13 @@ public class GetField extends Instruction implements GetFieldInstruction
this.field = field;
}
public GetField(Instructions instructions, net.runelite.asm.Field field)
{
super(instructions, InstructionType.GETFIELD);
this.field = field.getPoolField();
this.myField = field;
}
@Override
public String toString()
{

View File

@@ -56,6 +56,13 @@ public class GetStatic extends Instruction implements GetFieldInstruction
this.field = field;
}
public GetStatic(Instructions instructions, net.runelite.asm.Field field)
{
super(instructions, InstructionType.GETSTATIC);
this.field = field.getPoolField();
this.myField = field;
}
@Override
public String toString()
{

View File

@@ -64,6 +64,13 @@ public class InvokeSpecial extends Instruction implements InvokeInstruction
this.method = method;
}
public InvokeSpecial(Instructions instructions, net.runelite.asm.Method method)
{
super(instructions, InstructionType.INVOKESPECIAL);
this.method = method.getPoolMethod();
this.myMethod = method;
}
@Override
public void accept(MethodVisitor visitor)
{

View File

@@ -64,6 +64,13 @@ public class InvokeStatic extends Instruction implements InvokeInstruction
this.method = method;
}
public InvokeStatic(Instructions instructions, net.runelite.asm.Method method)
{
super(instructions, InstructionType.INVOKESTATIC);
this.method = method.getPoolMethod();
this.myMethod = method;
}
@Override
public void accept(MethodVisitor visitor)
{

View File

@@ -66,6 +66,13 @@ public class InvokeVirtual extends Instruction implements InvokeInstruction
this.method = method;
}
public InvokeVirtual(Instructions instructions, net.runelite.asm.Method method)
{
super(instructions, InstructionType.INVOKEVIRTUAL);
this.method = method.getPoolMethod();
this.myMethod = method;
}
@Override
public void accept(MethodVisitor visitor)
{

View File

@@ -56,6 +56,13 @@ public class New extends Instruction implements TypeInstruction
this.clazz = clazz;
}
public New(Instructions instructions, net.runelite.asm.ClassFile classFile)
{
super(instructions, InstructionType.NEW);
this.clazz = classFile.getPoolClass();
this.myClass = classFile;
}
@Override
public void accept(MethodVisitor visitor)
{

View File

@@ -53,6 +53,12 @@ public class PutField extends Instruction implements SetFieldInstruction
super(instructions, type);
}
public PutField(Instructions instructions, Field field)
{
super(instructions, InstructionType.PUTFIELD);
this.field = field;
}
public PutField(Instructions instructions, net.runelite.asm.Field field)
{
super(instructions, InstructionType.PUTFIELD);

View File

@@ -52,6 +52,12 @@ public class PutStatic extends Instruction implements SetFieldInstruction
super(instructions, type);
}
public PutStatic(Instructions instructions, Field field)
{
super(instructions, InstructionType.PUTSTATIC);
this.field = field;
}
public PutStatic(Instructions instructions, net.runelite.asm.Field field)
{
super(instructions, InstructionType.PUTSTATIC);

View File

@@ -32,7 +32,7 @@ public class Field
{
private final Class clazz;
private final String name;
private final Type type;
private Type type;
public Field(Class clazz, String name, Type type)
{
@@ -102,4 +102,9 @@ public class Field
{
return type;
}
public void setType(Type type)
{
this.type = type;
}
}

View File

@@ -31,7 +31,7 @@ public class Method
{
private final Class clazz;
private final String name;
private final Signature type;
private Signature type;
public Method(Class clazz, String name, Signature type)
{
@@ -101,4 +101,9 @@ public class Method
{
return type;
}
public void setType(Signature type)
{
this.type = type;
}
}

View File

@@ -32,7 +32,6 @@ dependencies {
compileOnly(group = "org.projectlombok", name = "lombok", version = ProjectVersions.lombokVersion)
implementation(project(":runelite-api"))
implementation(group = "com.google.code.gson", name = "gson", version = "2.8.5")
implementation(group = "com.google.guava", name = "guava", version = "30.1.1-jre")
implementation(group = "com.squareup.okhttp3", name = "okhttp", version = "4.9.1")

View File

@@ -8,6 +8,8 @@
package com.openosrs.injector;
import com.openosrs.injector.injection.InjectData;
import static com.openosrs.injector.rsapi.RSApi.API_BASE;
import static com.openosrs.injector.rsapi.RSApi.RL_API_BASE;
import com.openosrs.injector.rsapi.RSApiClass;
import com.openosrs.injector.rsapi.RSApiMethod;
import java.util.List;
@@ -42,8 +44,6 @@ import net.runelite.asm.signature.Signature;
import net.runelite.deob.DeobAnnotations;
import net.runelite.deob.deobfuscators.arithmetic.DMath;
import org.jetbrains.annotations.Nullable;
import static com.openosrs.injector.rsapi.RSApi.API_BASE;
import static com.openosrs.injector.rsapi.RSApi.RL_API_BASE;
public interface InjectUtil
{
@@ -552,4 +552,27 @@ public interface InjectUtil
{
injectObfuscatedGetter(DMath.modInverse(getter), instrs, into);
}
private static List<Type> findArgs(final String str, final List<Type> ret, final int from, final int to)
{
if (from >= to)
{
return ret;
}
int i = from;
while (str.charAt(i) == '[')
{
++i;
}
if (str.charAt(i) == 'L')
{
i = str.indexOf(';', i);
}
ret.add(new Type(str.substring(from, ++i)));
return findArgs(str, ret, i, to);
}
}

View File

@@ -22,6 +22,8 @@ import com.openosrs.injector.injectors.raw.GraphicsObject;
import com.openosrs.injector.injectors.raw.Occluder;
import com.openosrs.injector.injectors.raw.RasterizerAlpha;
import com.openosrs.injector.injectors.raw.RenderDraw;
import com.openosrs.injector.injectors.raw.CopyRuneLiteClasses;
import com.openosrs.injector.injectors.raw.RuneLiteIterableHashTable;
import com.openosrs.injector.injectors.raw.RuneliteObject;
import com.openosrs.injector.injectors.raw.ScriptVM;
import com.openosrs.injector.rsapi.RSApi;
@@ -111,6 +113,12 @@ public class Injector extends InjectData implements InjectTaskHandler
inject(new CreateAnnotations(this));
inject(new GraphicsObject(this));
inject(new CopyRuneLiteClasses(this));
inject(new RuneLiteIterableHashTable(this));
inject(new RuneliteObject(this));
inject(new InterfaceInjector(this));
@@ -142,8 +150,6 @@ public class Injector extends InjectData implements InjectTaskHandler
inject(new AddPlayerToMenu(this));
inject(new GraphicsObject(this));
validate(new InjectorValidator(this));
transform(new SourceChanger(this));

View File

@@ -0,0 +1,276 @@
/*
* Copyright (c) 2021, Owain van Brakel <https://github.com/Owain94>
* All rights reserved.
*
* This code is licensed under GPL3, see the complete license in
* the LICENSE file in the root directory of this submodule.
*/
package com.openosrs.injector.injectors.raw;
import com.openosrs.injector.InjectUtil;
import com.openosrs.injector.injection.InjectData;
import com.openosrs.injector.injectors.AbstractInjector;
import java.util.List;
import java.util.ListIterator;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Field;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instructions.GetField;
import net.runelite.asm.attributes.code.instructions.GetStatic;
import net.runelite.asm.attributes.code.instructions.InvokeSpecial;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.attributes.code.instructions.InvokeVirtual;
import net.runelite.asm.attributes.code.instructions.New;
import net.runelite.asm.attributes.code.instructions.PutField;
import net.runelite.asm.attributes.code.instructions.PutStatic;
import net.runelite.asm.pool.Class;
import net.runelite.asm.signature.Signature;
import org.objectweb.asm.Opcodes;
public class CopyRuneLiteClasses extends AbstractInjector
{
private static final List<String> RUNELITE_OBJECTS = List.of(
"RuneLiteObject",
"RuneLiteIterableHashTable"
);
public CopyRuneLiteClasses(InjectData inject)
{
super(inject);
}
public void inject()
{
for (String className : RUNELITE_OBJECTS)
{
ClassFile runeliteObjectVanilla = inject.vanilla.findClass(className);
final ClassFile runeLiteObjectDeob = inject.getDeobfuscated()
.findClass(className);
if (runeliteObjectVanilla == null)
{
runeliteObjectVanilla = new ClassFile(inject.vanilla);
runeliteObjectVanilla.setVersion(Opcodes.V1_8);
runeliteObjectVanilla.setName(className);
runeliteObjectVanilla.setAccess(runeLiteObjectDeob.getAccess());
if (runeLiteObjectDeob.getParentClass() != null)
{
ClassFile deobClass = inject.getDeobfuscated().findClass(runeLiteObjectDeob.getParentClass().getName());
if (deobClass != null)
{
runeliteObjectVanilla.setParentClass(inject.toVanilla(deobClass).getPoolClass());
}
else
{
runeliteObjectVanilla.setParentClass(runeLiteObjectDeob.getParentClass());
}
}
inject.toVanilla.put(runeLiteObjectDeob, runeliteObjectVanilla);
for (Class interfaze : runeLiteObjectDeob.getInterfaces())
{
runeliteObjectVanilla.getInterfaces().addInterface(interfaze);
}
for (Field field : runeLiteObjectDeob.getFields())
{
field.setType(InjectUtil.deobToVanilla(inject, field.getType()));
runeliteObjectVanilla.addField(field);
}
for (Method method : runeLiteObjectDeob.getMethods())
{
transformMethod(method);
runeliteObjectVanilla.addMethod(method);
}
inject.vanilla.addClass(runeliteObjectVanilla);
}
}
}
private void transformMethod(Method method)
{
method.setDescriptor(getObfuscatedSignature(method.getDescriptor()));
Code code = method.getCode();
if (code != null)
{
Instructions ins = code.getInstructions();
for (ListIterator<Instruction> iterator = ins.listIterator(); iterator.hasNext(); )
{
Instruction i = iterator.next();
if (i instanceof PutField)
{
net.runelite.asm.pool.Field field = ((PutField) i).getField();
Field vanilla = findField(field);
if (vanilla != null)
{
iterator.set(new PutField(ins, vanilla));
}
else
{
field.setType(getObfuscatedSignature(field.getType()));
iterator.set(new PutField(ins, field));
}
}
else if (i instanceof GetField)
{
net.runelite.asm.pool.Field field = ((GetField) i).getField();
Field vanilla = findField(field);
if (vanilla != null)
{
iterator.set(new GetField(ins, vanilla));
}
else
{
field.setType(getObfuscatedSignature(field.getType()));
iterator.set(new GetField(ins, field));
}
}
else if (i instanceof PutStatic)
{
net.runelite.asm.pool.Field field = ((PutStatic) i).getField();
Field vanilla = findField(field);
if (vanilla != null)
{
iterator.set(new PutStatic(ins, vanilla));
}
else
{
field.setType(getObfuscatedSignature(field.getType()));
iterator.set(new PutStatic(ins, field));
}
}
else if (i instanceof GetStatic)
{
net.runelite.asm.pool.Field field = ((GetStatic) i).getField();
Field vanilla = findField(field);
if (vanilla != null)
{
iterator.set(new GetStatic(ins, vanilla));
}
else
{
field.setType(getObfuscatedSignature(field.getType()));
iterator.set(new GetStatic(ins, field));
}
}
else if (i instanceof InvokeSpecial)
{
net.runelite.asm.pool.Method meth = ((InvokeSpecial) i).getMethod();
Method vanilla = findMethod(meth, true);
if (vanilla != null)
{
iterator.set(new InvokeSpecial(ins, vanilla));
}
else
{
meth.setType(getObfuscatedSignature(meth.getType()));
iterator.set(new InvokeSpecial(ins, meth));
}
}
else if (i instanceof InvokeStatic)
{
net.runelite.asm.pool.Method meth = ((InvokeStatic) i).getMethod();
Method vanilla = findMethod(meth, false);
if (vanilla != null)
{
iterator.set(new InvokeStatic(ins, vanilla));
}
else
{
meth.setType(getObfuscatedSignature(meth.getType()));
iterator.set(new InvokeStatic(ins, meth));
}
}
else if (i instanceof InvokeVirtual)
{
net.runelite.asm.pool.Method meth = ((InvokeVirtual) i).getMethod();
Method vanilla = findMethod(meth, true);
if (vanilla != null)
{
iterator.set(new InvokeVirtual(ins, vanilla));
}
else
{
meth.setType(getObfuscatedSignature(meth.getType()));
iterator.set(new InvokeVirtual(ins, meth));
}
}
else if (i instanceof New)
{
Class clazz = ((New) i).getNewClass();
ClassFile deobClass = inject.getDeobfuscated().findClass(clazz.getName());
if (deobClass != null)
{
iterator.set(new New(ins, inject.toVanilla(deobClass)));
}
}
}
}
}
private Signature getObfuscatedSignature(Signature signature)
{
Signature.Builder builder = new Signature.Builder();
for (int j = 0; j < signature.size(); ++j)
{
Type type = signature.getTypeOfArg(j);
builder.addArgument(InjectUtil.deobToVanilla(inject, type));
}
builder.setReturnType(InjectUtil.deobToVanilla(inject, signature.getReturnValue()));
return builder.build();
}
private Type getObfuscatedSignature(Type type)
{
return InjectUtil.deobToVanilla(inject, type);
}
private Method findMethod(net.runelite.asm.pool.Method meth, boolean notStatic)
{
try
{
return InjectUtil.findMethod(inject, meth.getName(), meth.getClazz().getName(), meth.getType()::equals, notStatic, false);
}
catch (Exception e)
{
return null;
}
}
private Field findField(net.runelite.asm.pool.Field field)
{
try
{
return InjectUtil.findField(inject, field.getName(), field.getClazz().getName());
}
catch (Exception e)
{
return null;
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2021, Owain van Brakel <https://github.com/Owain94>
* All rights reserved.
*
* This code is licensed under GPL3, see the complete license in
* the LICENSE file in the root directory of this submodule.
*/
package com.openosrs.injector.injectors.raw;
import com.openosrs.injector.injection.InjectData;
import com.openosrs.injector.injectors.AbstractInjector;
import java.util.List;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.InstructionType;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instructions.ALoad;
import net.runelite.asm.attributes.code.instructions.Dup;
import net.runelite.asm.attributes.code.instructions.InvokeSpecial;
import net.runelite.asm.attributes.code.instructions.New;
import net.runelite.asm.attributes.code.instructions.Return;
import net.runelite.asm.signature.Signature;
public class RuneLiteIterableHashTable extends AbstractInjector
{
private static final String RUNELITE_ITERABLE_HASHTABLE = "RuneLiteIterableHashTable";
public RuneLiteIterableHashTable(InjectData inject)
{
super(inject);
}
public void inject()
{
ClassFile runeliteIterableHashTableVanilla = inject.vanilla.findClass(RUNELITE_ITERABLE_HASHTABLE);
final ClassFile nodeHashTableVanilla = inject.toVanilla(
inject.getDeobfuscated()
.findClass("NodeHashTable")
);
Method copy = new Method(nodeHashTableVanilla, "iterator", new Signature("()Ljava/util/Iterator;"));
copy.setPublic();
final Code code = new Code(copy);
code.setMaxStack(3);
copy.setCode(code);
nodeHashTableVanilla.addMethod(copy);
final Instructions instructions = code.getInstructions();
final List<Instruction> ins = instructions.getInstructions();
ins.add(new New(instructions, runeliteIterableHashTableVanilla.getPoolClass()));
ins.add(new Dup(instructions));
ins.add(new ALoad(instructions, 0));
ins.add(new InvokeSpecial(instructions, new net.runelite.asm.pool.Method(runeliteIterableHashTableVanilla.getPoolClass(), "<init>", new Signature("(L" + nodeHashTableVanilla.getName() + ";)V"))));
ins.add(new Return(instructions, InstructionType.ARETURN));
}
}

View File

@@ -16,14 +16,11 @@ import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.InstructionType;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instructions.ALoad;
import net.runelite.asm.attributes.code.instructions.Dup;
import net.runelite.asm.attributes.code.instructions.InvokeSpecial;
import net.runelite.asm.attributes.code.instructions.New;
import net.runelite.asm.attributes.code.instructions.Return;
import net.runelite.asm.attributes.code.instructions.VReturn;
import net.runelite.asm.signature.Signature;
import org.objectweb.asm.Opcodes;
public class RuneliteObject extends AbstractInjector
{
@@ -38,66 +35,25 @@ public class RuneliteObject extends AbstractInjector
{
ClassFile runeliteObjectVanilla = inject.vanilla.findClass(RUNELITE_OBJECT);
final ClassFile graphicsObjectVanilla = inject.toVanilla(
inject.getDeobfuscated()
.findClass("GraphicsObject")
);
graphicsObjectVanilla.clearFinal();
final ClassFile clientVanilla = inject.toVanilla(
inject.getDeobfuscated()
.findClass("Client")
);
final ClassFile runeLiteObjectDeob = inject.getDeobfuscated()
.findClass(RUNELITE_OBJECT);
Method copy = new Method(clientVanilla, "createRuneLiteObject", new Signature("()Lnet/runelite/api/RuneLiteObject;"));
copy.setPublic();
if (runeliteObjectVanilla == null)
{
runeliteObjectVanilla = new ClassFile(inject.vanilla);
runeliteObjectVanilla.setName(RUNELITE_OBJECT);
runeliteObjectVanilla.setParentClass(graphicsObjectVanilla.getPoolClass());
runeliteObjectVanilla.setAccess(Opcodes.ACC_PUBLIC);
runeliteObjectVanilla.setVersion(Opcodes.V1_8);
inject.vanilla.addClass(runeliteObjectVanilla);
final Code code = new Code(copy);
code.setMaxStack(2);
copy.setCode(code);
clientVanilla.addMethod(copy);
inject.toVanilla.put(runeLiteObjectDeob, runeliteObjectVanilla);
}
final Instructions instructions = code.getInstructions();
final List<Instruction> ins = instructions.getInstructions();
{
Method initRuneliteObject = new Method(runeliteObjectVanilla, "<init>", new Signature("()V"));
initRuneliteObject.setPublic();
final Code code = new Code(initRuneliteObject);
code.setMaxStack(1);
initRuneliteObject.setCode(code);
runeliteObjectVanilla.addMethod(initRuneliteObject);
final Instructions instructions = code.getInstructions();
final List<Instruction> ins = instructions.getInstructions();
ins.add(new ALoad(instructions, 0));
ins.add(new InvokeSpecial(instructions, new net.runelite.asm.pool.Method(graphicsObjectVanilla.getPoolClass(), "<init>", new Signature("()V"))));
ins.add(new VReturn(instructions));
}
{
Method copy = new Method(clientVanilla, "createRuneLiteObject", new Signature("()Lnet/runelite/api/RuneLiteObject;"));
copy.setPublic();
final Code code = new Code(copy);
code.setMaxStack(2);
copy.setCode(code);
clientVanilla.addMethod(copy);
final Instructions instructions = code.getInstructions();
final List<Instruction> ins = instructions.getInstructions();
ins.add(new New(instructions, runeliteObjectVanilla.getPoolClass()));
ins.add(new Dup(instructions));
ins.add(new InvokeSpecial(instructions, new net.runelite.asm.pool.Method(runeliteObjectVanilla.getPoolClass(), "<init>", new Signature("()V"))));
ins.add(new Return(instructions, InstructionType.ARETURN));
}
ins.add(new New(instructions, runeliteObjectVanilla.getPoolClass()));
ins.add(new Dup(instructions));
ins.add(new InvokeSpecial(instructions, new net.runelite.asm.pool.Method(runeliteObjectVanilla.getPoolClass(), "<init>", new Signature("()V"))));
ins.add(new Return(instructions, InstructionType.ARETURN));
}
}

View File

@@ -30,6 +30,8 @@ dependencies {
compileOnly(group = "org.projectlombok", name = "lombok", version = ProjectVersions.lombokVersion)
api(project(":cache"))
implementation(group = "com.google.code.findbugs", name = "jsr305", version = "3.0.2")
implementation(group = "com.google.guava", name = "guava", version = "30.1.1-jre")
implementation(group = "org.apache.commons", name = "commons-text", version = "1.9")

View File

@@ -2337,4 +2337,6 @@ public interface Client extends GameEngine
*/
@Nullable
ClanSettings getClanSettings(int clanId);
void setUnlockedFps(boolean unlock);
}

View File

@@ -6,7 +6,7 @@ import java.util.Collection;
* A data structure that uses a hash function to compute an index into an
* array of buckets from which node objects can be quickly obtained.
*/
public interface HashTable<T extends Node>
public interface HashTable<T extends Node> extends Iterable<T>
{
/**
* Gets a node by its hash value.

View File

@@ -85,11 +85,18 @@ public enum InventoryID
* Theater of Blood reward chest inventory (Raids 2)
*/
THEATRE_OF_BLOOD_CHEST(612),
/**
* Seed vault located inside the Farming Guild
*/
SEED_VAULT(626);
SEED_VAULT(626),
/**
* Group ironman shared storage
*/
GROUP_STORAGE(659),
/**
* Player inventory when accessing group ironman shared storage
*/
GROUP_STORAGE_INV(660);
private final int id;

View File

@@ -39,6 +39,11 @@ public class ClanChannelChanged
*/
@Nullable
private final ClanChannel clanChannel;
/**
* The clan id, or -1 for guest clan
* @see net.runelite.api.clan.ClanID
*/
private int clanId;
/**
* Whether or not this was the guest clan channel
*/

View File

@@ -53,9 +53,14 @@ public interface Callbacks
void postDeferred(Object event);
/**
* Called each client cycle.
* Called each tick
*/
void clientMainLoop();
void tick();
/**
* Called each frame
*/
void frame();
/**
* Called after receiving update NPCs packet from server.

View File

@@ -180,7 +180,7 @@ public class Hooks implements Callbacks
}
@Override
public void clientMainLoop()
public void tick()
{
if (shouldProcessGameTick)
{
@@ -194,8 +194,6 @@ public class Hooks implements Callbacks
client.setTickCount(tick + 1);
}
eventBus.post(BEFORE_RENDER);
clientThread.invoke();
long now = System.nanoTime();
@@ -225,6 +223,12 @@ public class Hooks implements Callbacks
}
}
@Override
public void frame()
{
eventBus.post(BEFORE_RENDER);
}
/**
* When the world map opens it loads about ~100mb of data into memory, which
* represents about half of the total memory allocated by the client.

View File

@@ -304,7 +304,10 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener
// input. We prevent having a tag tab open while also performing a normal search, so if a tag tab
// is active here it must mean we have placed the bank into search mode. See onScriptPostFired().
TagTab activeTab = tabInterface.getActiveTab();
String search = activeTab != null ? TAG_SEARCH + activeTab.getTag() : searchfilter;
// Shared storage uses the bankmain filter scripts too. Allow using tag searches in it but don't
// apply the tag search from the active tab.
final boolean bankOpen = client.getItemContainer(InventoryID.BANK) != null;
String search = activeTab != null && bankOpen ? TAG_SEARCH + activeTab.getTag() : searchfilter;
if (search.isEmpty())
{
@@ -487,8 +490,9 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener
if (event.getScriptId() == ScriptID.BANKMAIN_SEARCHING)
{
// The return value of bankmain_searching is on the stack. If we have a tag tab active
// make it return true to put the bank in a searching state.
if (tabInterface.getActiveTab() != null || tabInterface.isTagTabActive())
// and are in the bank, make it return true to put the bank in a searching state.
boolean bankOpen = client.getItemContainer(InventoryID.BANK) != null;
if (bankOpen && (tabInterface.getActiveTab() != null || tabInterface.isTagTabActive()))
{
client.getIntStack()[client.getIntStackSize() - 1] = 1; // true
}

View File

@@ -61,18 +61,18 @@ public class BarrowsBrotherSlainOverlay extends OverlayPanel
@Override
public Dimension render(Graphics2D graphics)
{
// Do not display overlay if potential is null/hidden
final Widget potential = client.getWidget(WidgetInfo.BARROWS_POTENTIAL);
if (potential == null || potential.isHidden())
final Widget barrowsBrothers = client.getWidget(WidgetInfo.BARROWS_BROTHERS);
if (barrowsBrothers == null)
{
return null;
}
// Hide original overlay
final Widget barrowsBrothers = client.getWidget(WidgetInfo.BARROWS_BROTHERS);
if (barrowsBrothers != null)
// Hide original brother and potential overlays
barrowsBrothers.setHidden(true);
final Widget potential = client.getWidget(WidgetInfo.BARROWS_POTENTIAL);
if (potential != null)
{
barrowsBrothers.setHidden(true);
potential.setHidden(true);
}

View File

@@ -364,6 +364,7 @@ public class CameraPlugin extends Plugin implements KeyListener, MouseListener
public void onClientTick(ClientTick event)
{
menuHasEntries = hasMenuEntries(client.getMenuEntries());
sliderTooltip = null;
}
@Subscribe
@@ -416,7 +417,6 @@ public class CameraPlugin extends Plugin implements KeyListener, MouseListener
if (sliderTooltip != null)
{
tooltipManager.add(sliderTooltip);
sliderTooltip = null;
}
}

View File

@@ -1875,6 +1875,8 @@ public class ChatCommandsPlugin extends Plugin
case "dusk":
case "dawn":
case "gargs":
case "ggs":
case "gg":
return "Grotesque Guardians";
case "crazy arch":

View File

@@ -46,7 +46,7 @@ public interface FpsConfig extends Config
@Range(
min = 1,
max = 50
max = 360
)
@ConfigItem(
keyName = "maxFps",
@@ -72,7 +72,7 @@ public interface FpsConfig extends Config
@Range(
min = 1,
max = 50
max = 360
)
@ConfigItem(
keyName = "maxFpsUnfocused",

View File

@@ -78,6 +78,7 @@ import net.runelite.api.hooks.DrawCallbacks;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginInstantiationException;
@@ -387,7 +388,10 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
}
this.gl = glContext.getGL().getGL4();
gl.setSwapInterval(0);
final boolean unlockFps = this.config.unlockFps();
client.setUnlockedFps(unlockFps);
gl.setSwapInterval(unlockFps ? 1 : 0);
if (log.isDebugEnabled())
{
@@ -459,6 +463,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
{
client.setGpu(false);
client.setDrawCallbacks(null);
client.setUnlockedFps(false);
invokeOnMainThread(() ->
{
@@ -529,6 +534,23 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
return configManager.getConfig(GpuPluginConfig.class);
}
@Subscribe
public void onConfigChanged(ConfigChanged configChanged)
{
if (configChanged.getGroup().equals(GpuPluginConfig.GROUP))
{
if (configChanged.getKey().equals("unlockFps"))
{
boolean unlockFps = Boolean.parseBoolean(configChanged.getNewValue());
clientThread.invokeLater(() ->
{
client.setUnlockedFps(unlockFps);
invokeOnMainThread(() -> gl.setSwapInterval(unlockFps ? 1 : 0));
});
}
}
}
private void initProgram() throws ShaderException
{
String versionHeader = OSType.getOSType() == OSType.Linux ? LINUX_VERSION_HEADER : WINDOWS_VERSION_HEADER;
@@ -776,6 +798,12 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
final Scene scene = client.getScene();
scene.setDrawDistance(getDrawDistance());
// Only reset the target buffer offset right before drawing the scene. That way if there are frames
// after this that don't involve a scene draw, like during LOADING/HOPPING/CONNECTION_LOST, we can
// still redraw the previous frame's scene to emulate the client behavior of not painting over the
// viewport buffer.
targetBufferOffset = 0;
invokeOnMainThread(() ->
{
// UBO. Only the first 32 bytes get modified here, the rest is the constant sin/cos table.
@@ -1029,21 +1057,9 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
private void drawFrame(int overlayColor)
{
if (jawtWindow.getAWTComponent() != client.getCanvas())
{
// We inject code in the game engine mixin to prevent the client from doing canvas replacement,
// so this should not ever be hit
log.warn("Canvas invalidated!");
shutDown();
startUp();
return;
}
if (client.getGameState() == GameState.LOADING || client.getGameState() == GameState.HOPPING)
{
// While the client is loading it doesn't draw
return;
}
// We inject code in the game engine mixin to prevent the client from doing canvas replacement,
// so this should not ever be tripped
assert jawtWindow.getAWTComponent() == client.getCanvas() : "canvas invalidated";
final int canvasHeight = client.getCanvasHeight();
final int canvasWidth = client.getCanvasWidth();
@@ -1105,7 +1121,8 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
// Draw 3d scene
final TextureProvider textureProvider = client.getTextureProvider();
if (textureProvider != null)
final GameState gameState = client.getGameState();
if (textureProvider != null && gameState.getState() >= GameState.LOADING.getState())
{
if (textureArrayId == -1)
{
@@ -1265,7 +1282,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
modelBufferSmall.clear();
modelBufferUnordered.clear();
targetBufferOffset = 0;
smallModels = largeModels = unorderedModels = 0;
tempOffset = 0;
tempUvOffset = 0;
@@ -1421,12 +1437,18 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
@Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged)
{
if (computeMode == ComputeMode.NONE || gameStateChanged.getGameState() != GameState.LOGGED_IN)
switch (gameStateChanged.getGameState())
{
return;
case LOGGED_IN:
if (computeMode != ComputeMode.NONE)
{
invokeOnMainThread(this::uploadScene);
}
break;
case LOGIN_SCREEN:
// Avoid drawing the last frame's buffer during LOADING after LOGIN_SCREEN
targetBufferOffset = 0;
}
invokeOnMainThread(this::uploadScene);
}
private void uploadScene()

View File

@@ -34,9 +34,11 @@ import net.runelite.client.plugins.gpu.config.AntiAliasingMode;
import net.runelite.client.plugins.gpu.config.ColorBlindMode;
import net.runelite.client.plugins.gpu.config.UIScalingMode;
@ConfigGroup("gpu")
@ConfigGroup(GpuPluginConfig.GROUP)
public interface GpuPluginConfig extends Config
{
String GROUP = "gpu";
@Range(
max = MAX_DISTANCE
)
@@ -146,4 +148,15 @@ public interface GpuPluginConfig extends Config
{
return false;
}
@ConfigItem(
keyName = "unlockFps",
name = "Unlock FPS",
description = "Removes the 50 FPS cap for camera movement",
position = 10
)
default boolean unlockFps()
{
return false;
}
}

View File

@@ -61,6 +61,7 @@ import net.runelite.api.VarPlayer;
import net.runelite.api.Varbits;
import net.runelite.api.events.AreaSoundEffectPlayed;
import net.runelite.api.events.BeforeRender;
import net.runelite.api.events.ClientTick;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.PostStructComposition;
import net.runelite.api.events.ScriptPreFired;
@@ -918,10 +919,15 @@ public class MusicPlugin extends Plugin
if (sliderTooltip != null)
{
tooltipManager.add(sliderTooltip);
sliderTooltip = null;
}
}
@Subscribe
public void onClientTick(ClientTick event)
{
sliderTooltip = null;
}
@Subscribe
public void onAreaSoundEffectPlayed(AreaSoundEffectPlayed areaSoundEffectPlayed)
{

View File

@@ -62,11 +62,7 @@ class PrayerDoseOverlay extends Overlay
private boolean trackTick = true;
@Setter(AccessLevel.PACKAGE)
private boolean hasPrayerRestore;
@Setter(AccessLevel.PACKAGE)
private int bonusPrayer;
@Setter(AccessLevel.PACKAGE)
private boolean hasHolyWrench;
private int restoreAmount;
@Inject
private PrayerDoseOverlay(final Client client, final TooltipManager tooltipManager, final PrayerPlugin plugin, final PrayerConfig config)
@@ -127,7 +123,7 @@ class PrayerDoseOverlay extends Overlay
tooltipManager.add(new Tooltip(sb.toString()));
}
if (!config.showPrayerDoseIndicator() || !hasPrayerRestore)
if (!config.showPrayerDoseIndicator() || restoreAmount == 0)
{
return null;
}
@@ -136,17 +132,7 @@ class PrayerDoseOverlay extends Overlay
final int maxPrayer = client.getRealSkillLevel(Skill.PRAYER);
final int prayerPointsMissing = maxPrayer - currentPrayer;
if (prayerPointsMissing <= 0)
{
return null;
}
final double dosePercentage = hasHolyWrench ? .27 : .25;
final int basePointsRestored = (int) Math.floor(maxPrayer * dosePercentage);
final int pointsRestored = basePointsRestored + 7 + bonusPrayer;
if (prayerPointsMissing < pointsRestored)
if (prayerPointsMissing <= 0 || prayerPointsMissing < restoreAmount)
{
return null;
}

View File

@@ -30,6 +30,7 @@ import java.time.Duration;
import java.time.Instant;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import javax.annotation.Nullable;
import javax.inject.Inject;
import lombok.AccessLevel;
import lombok.Getter;
@@ -39,7 +40,6 @@ import net.runelite.api.InventoryID;
import net.runelite.api.Item;
import net.runelite.api.ItemContainer;
import net.runelite.api.Prayer;
import net.runelite.client.events.ConfigChanged;
import net.runelite.api.Skill;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.ItemContainerChanged;
@@ -47,6 +47,7 @@ import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.plugins.Plugin;
@@ -141,26 +142,15 @@ public class PrayerPlugin extends Plugin
@Subscribe
public void onItemContainerChanged(final ItemContainerChanged event)
{
final ItemContainer container = event.getItemContainer();
final ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY);
final ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT);
if (container == inventory || container == equipment)
final int id = event.getContainerId();
if (id == InventoryID.INVENTORY.getId())
{
doseOverlay.setHasHolyWrench(false);
doseOverlay.setHasPrayerRestore(false);
doseOverlay.setBonusPrayer(0);
if (inventory != null)
{
checkContainerForPrayer(inventory.getItems());
}
if (equipment != null)
{
prayerBonus = checkContainerForPrayer(equipment.getItems());
}
updatePotionBonus(event.getItemContainer(),
client.getItemContainer(InventoryID.EQUIPMENT));
}
else if (id == InventoryID.EQUIPMENT.getId())
{
prayerBonus = totalPrayerBonus(event.getItemContainer().getItems());
}
}
@@ -222,26 +212,29 @@ public class PrayerPlugin extends Plugin
}
}
private int checkContainerForPrayer(Item[] items)
private int totalPrayerBonus(Item[] items)
{
if (items == null)
{
return 0;
}
int total = 0;
for (Item item : items)
{
ItemStats is = itemManager.getItemStats(item.getId(), false);
if (is != null && is.getEquipment() != null)
{
total += is.getEquipment().getPrayer();
}
}
return total;
}
private void updatePotionBonus(ItemContainer inventory, @Nullable ItemContainer equip)
{
boolean hasPrayerPotion = false;
boolean hasSuperRestore = false;
boolean hasSanfew = false;
boolean hasWrench = false;
for (Item item : items)
for (Item item : inventory.getItems())
{
if (item == null)
{
continue;
}
final PrayerRestoreType type = PrayerRestoreType.getType(item.getId());
if (type != null)
@@ -258,32 +251,45 @@ public class PrayerPlugin extends Plugin
hasSanfew = true;
break;
case HOLYWRENCH:
doseOverlay.setHasHolyWrench(true);
hasWrench = true;
break;
}
}
ItemStats is = itemManager.getItemStats(item.getId(), false);
if (is != null && is.getEquipment() != null)
{
total += is.getEquipment().getPrayer();
}
}
if (hasSanfew || hasSuperRestore || hasPrayerPotion)
// Some items providing the holy wrench bonus can also be worn
if (!hasWrench && equip != null)
{
doseOverlay.setHasPrayerRestore(true);
if (hasSanfew)
for (Item item : equip.getItems())
{
doseOverlay.setBonusPrayer(2);
}
else if (hasSuperRestore)
{
doseOverlay.setBonusPrayer(1);
final PrayerRestoreType type = PrayerRestoreType.getType(item.getId());
if (type == PrayerRestoreType.HOLYWRENCH)
{
hasWrench = true;
break;
}
}
}
return total;
// Prayer potion: floor(7 + 25% of base level) - 27% with holy wrench
// Super restore: floor(8 + 25% of base level) - 27% with holy wrench
// Sanfew serum: floor(4 + 30% of base level) - 32% with holy wrench
final int prayerLevel = client.getRealSkillLevel(Skill.PRAYER);
int restored = 0;
if (hasSanfew)
{
restored = Math.max(restored, 4 + (int) Math.floor(prayerLevel * (hasWrench ? .32 : .30)));
}
if (hasSuperRestore)
{
restored = Math.max(restored, 8 + (int) Math.floor(prayerLevel * (hasWrench ? .27 : .25)));
}
if (hasPrayerPotion)
{
restored = Math.max(restored, 7 + (int) Math.floor(prayerLevel * (hasWrench ? .27 : .25)));
}
doseOverlay.setRestoreAmount(restored);
}
double getTickProgress()

View File

@@ -30,7 +30,9 @@ import net.runelite.api.ItemID;
enum PrayerRestoreType
{
RESTOREPOT(ItemID.SUPER_RESTORE4, ItemID.SUPER_RESTORE3, ItemID.SUPER_RESTORE2, ItemID.SUPER_RESTORE1),
RESTOREPOT(ItemID.SUPER_RESTORE4, ItemID.SUPER_RESTORE3, ItemID.SUPER_RESTORE2, ItemID.SUPER_RESTORE1,
ItemID.BLIGHTED_SUPER_RESTORE4, ItemID.BLIGHTED_SUPER_RESTORE3, ItemID.BLIGHTED_SUPER_RESTORE2,
ItemID.BLIGHTED_SUPER_RESTORE1),
PRAYERPOT(ItemID.PRAYER_POTION4, ItemID.PRAYER_POTION3, ItemID.PRAYER_POTION2, ItemID.PRAYER_POTION1),
SANFEWPOT(ItemID.SANFEW_SERUM4, ItemID.SANFEW_SERUM3, ItemID.SANFEW_SERUM2, ItemID.SANFEW_SERUM1),
HOLYWRENCH(ItemID.PRAYER_CAPE, ItemID.PRAYER_CAPET, ItemID.MAX_CAPE,

View File

@@ -34,7 +34,6 @@ import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
@@ -62,7 +61,6 @@ import net.runelite.client.ui.components.IconTextField;
class SkillCalculator extends JPanel
{
private static final int MAX_XP = 200_000_000;
private static final DecimalFormat XP_FORMAT = new DecimalFormat("#.#");
private final UICalculatorInputArea uiInput;
private final Client client;
@@ -203,7 +201,7 @@ class SkillCalculator extends JPanel
int actionCount = 0;
int neededXP = targetXP - currentXP;
double xp = 0;
int xp = 0;
for (UIActionSlot slot : combinedActionSlots)
{
@@ -213,7 +211,8 @@ class SkillCalculator extends JPanel
if (neededXP > 0)
{
assert xp != 0;
actionCount = (int) Math.ceil(neededXP / xp);
neededXP *= 10;
actionCount = (neededXP - 1) / xp + 1;
}
combinedActionSlot.setText(formatXPActionString(xp, actionCount, "exp - "));
@@ -272,19 +271,15 @@ class SkillCalculator extends JPanel
private void adjustCheckboxes(JCheckBox target, SkillBonus bonus)
{
adjustXPBonus(0);
bonusCheckBoxes.forEach(otherSelectedCheckbox ->
for (JCheckBox otherSelectedCheckbox : bonusCheckBoxes)
{
if (otherSelectedCheckbox != target)
{
otherSelectedCheckbox.setSelected(false);
}
});
if (target.isSelected())
{
adjustXPBonus(bonus.getValue());
}
adjustXPBonus(target.isSelected() ? bonus.getValue() : 0f);
}
private void renderActionSlots()
@@ -347,11 +342,13 @@ class SkillCalculator extends JPanel
int actionCount = 0;
int neededXP = targetXP - currentXP;
SkillAction action = slot.getAction();
double xp = (action.isIgnoreBonus()) ? action.getXp() : action.getXp() * xpFactor;
final float bonus = action.isIgnoreBonus() ? 1f : xpFactor;
final int xp = Math.round(action.getXp() * bonus * 10f);
if (neededXP > 0)
{
actionCount = (int) Math.ceil(neededXP / xp);
neededXP *= 10;
actionCount = (neededXP - 1) / xp + 1;
}
slot.setText("Lvl. " + action.getLevel() + " (" + formatXPActionString(xp, actionCount, "exp) - "));
@@ -363,9 +360,11 @@ class SkillCalculator extends JPanel
updateCombinedAction();
}
private static String formatXPActionString(double xp, int actionCount, String expExpression)
private static String formatXPActionString(int xp, int actionCount, String expExpression)
{
return XP_FORMAT.format(xp) + expExpression + NumberFormat.getIntegerInstance().format(actionCount) + (actionCount == 1 ? " action" : " actions");
int integer = xp / 10;
int frac = xp % 10;
return (frac != 0 ? (integer + "." + frac) : integer) + expExpression + NumberFormat.getIntegerInstance().format(actionCount) + (actionCount == 1 ? " action" : " actions");
}
private void updateInputFields()

View File

@@ -85,7 +85,7 @@ class UIActionSlot extends JPanel
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private double value;
private int value;
UIActionSlot(SkillAction action, ClientThread clientThread, ItemManager itemManager, JLabel uiIcon)
{

View File

@@ -35,6 +35,7 @@ public enum FletchingAction implements ItemSkillAction
ARROW_SHAFT(ItemID.ARROW_SHAFT, 1, 0.33f),
HEADLESS_ARROW(ItemID.HEADLESS_ARROW, 1, 1),
BRONZE_ARROW(ItemID.BRONZE_ARROW, 1, 1.3f),
BRONZE_JAVELIN(ItemID.BRONZE_JAVELIN, 3, 1),
OGRE_ARROW(ItemID.OGRE_ARROW, 5, 1),
SHORTBOW_U(ItemID.SHORTBOW_U, 5, 5),
SHORTBOW(ItemID.SHORTBOW, 5, 5),
@@ -47,6 +48,7 @@ public enum FletchingAction implements ItemSkillAction
LONGBOW_U(ItemID.LONGBOW_U, 10, 10),
OPAL_BOLTS(ItemID.OPAL_BOLTS, 11, 1.6f),
IRON_ARROW(ItemID.IRON_ARROW, 15, 2.5f),
IRON_JAVELIN(ItemID.IRON_JAVELIN, 17, 2),
OAK_SHORTBOW_U(ItemID.OAK_SHORTBOW_U, 20, 16.5f),
OAK_SHORTBOW(ItemID.OAK_SHORTBOW, 20, 16.5f),
IRON_DART(ItemID.IRON_DART, 22, 3.8f),
@@ -57,6 +59,7 @@ public enum FletchingAction implements ItemSkillAction
OAK_LONGBOW(ItemID.OAK_LONGBOW, 25, 25),
OAK_SHIELD(ItemID.OAK_SHIELD, 27, 50),
STEEL_ARROW(ItemID.STEEL_ARROW, 30, 5),
STEEL_JAVELIN(ItemID.STEEL_JAVELIN, 32, 5),
KEBBIT_BOLTS(ItemID.KEBBIT_BOLTS, 32, 1),
WILLOW_SHORTBOW_U(ItemID.WILLOW_SHORTBOW_U, 35, 33.3f),
WILLOW_SHORTBOW(ItemID.WILLOW_SHORTBOW, 35, 33.3f),
@@ -77,6 +80,7 @@ public enum FletchingAction implements ItemSkillAction
TEAK_STOCK(ItemID.TEAK_STOCK, 46, 27),
STEEL_CROSSBOW_U(ItemID.STEEL_CROSSBOW_U, 46, 54),
STEEL_CROSSBOW(ItemID.STEEL_CROSSBOW, 46, 27),
MITHRIL_JAVELIN(ItemID.MITHRIL_JAVELIN, 47, 8),
MAPLE_SHORTBOW_U(ItemID.MAPLE_SHORTBOW_U, 50, 50),
MAPLE_SHORTBOW(ItemID.MAPLE_SHORTBOW, 50, 50),
BARBED_BOLTS(ItemID.BARBED_BOLTS, 51, 9.5f),
@@ -98,6 +102,7 @@ public enum FletchingAction implements ItemSkillAction
MAHOGANY_STOCK(ItemID.MAHOGANY_STOCK, 61, 41),
ADAMANT_CROSSBOW_U(ItemID.ADAMANT_CROSSBOW_U, 61, 82),
ADAMANT_CROSSBOW(ItemID.ADAMANT_CROSSBOW, 61, 41),
ADAMANT_JAVELIN(ItemID.ADAMANT_JAVELIN, 62, 10),
RUBY_BOLTS(ItemID.RUBY_BOLTS, 63, 6.3f),
DIAMOND_BOLTS(ItemID.DIAMOND_BOLTS, 65, 7),
YEW_SHORTBOW(ItemID.YEW_SHORTBOW, 65, 67.5f),
@@ -114,6 +119,7 @@ public enum FletchingAction implements ItemSkillAction
ONYX_BOLTS(ItemID.ONYX_BOLTS, 73, 9.4f),
RUNE_ARROW(ItemID.RUNE_ARROW, 75, 12.5f),
AMETHYST_BROAD_BOLTS(ItemID.AMETHYST_BROAD_BOLTS, 76, 10.6f),
RUNE_JAVELIN(ItemID.RUNE_JAVELIN, 77, 12.4f),
MAGIC_STOCK(ItemID.MAGIC_STOCK, 78, 70),
DRAGON_CROSSBOW_U(ItemID.DRAGON_CROSSBOW_U, 78, 135),
DRAGON_CROSSBOW(ItemID.DRAGON_CROSSBOW, 78, 70),
@@ -128,6 +134,7 @@ public enum FletchingAction implements ItemSkillAction
MAGIC_SHIELD(ItemID.MAGIC_SHIELD, 87, 183),
AMETHYST_DART(ItemID.AMETHYST_DART, 90, 21),
DRAGON_ARROW(ItemID.DRAGON_ARROW, 90, 15),
DRAGON_JAVELIN(ItemID.DRAGON_JAVELIN, 92, 15),
REDWOOD_SHIELD(ItemID.REDWOOD_SHIELD, 92, 216),
DRAGON_DART(ItemID.DRAGON_DART, 95, 25),
;

View File

@@ -86,4 +86,15 @@ public interface SpecialCounterConfig extends Config
{
return 0;
}
@ConfigItem(
position = 5,
keyName = "bulwarkThreshold",
name = "Dinh's Bulwark",
description = "Threshold for Dinh's Bulwark (0 to disable)"
)
default int bulwarkThreshold()
{
return 0;
}
}

View File

@@ -40,7 +40,7 @@ enum SpecialWeapon
BARRELCHEST_ANCHOR("Barrelchest Anchor", new int[]{ItemID.BARRELCHEST_ANCHOR}, true, (c) -> 0),
BONE_DAGGER("Bone Dagger", new int[]{ItemID.BONE_DAGGER, ItemID.BONE_DAGGER_P, ItemID.BONE_DAGGER_P_8876, ItemID.BONE_DAGGER_P_8878}, true, (c) -> 0),
DORGESHUUN_CROSSBOW("Dorgeshuun Crossbow", new int[]{ItemID.DORGESHUUN_CROSSBOW}, true, (c) -> 0),
BULWARK("Dinh's Bulwark", new int[]{ItemID.DINHS_BULWARK}, false, c -> 0);
BULWARK("Dinh's Bulwark", new int[]{ItemID.DINHS_BULWARK}, false, SpecialCounterConfig::bulwarkThreshold);
private final String name;
private final int[] itemID;

View File

@@ -45,6 +45,7 @@ import net.runelite.api.Player;
import net.runelite.api.SpriteID;
import net.runelite.api.clan.ClanChannel;
import net.runelite.api.clan.ClanChannelMember;
import net.runelite.api.clan.ClanID;
import net.runelite.api.events.ClanChannelChanged;
import net.runelite.api.events.ClanMemberJoined;
import net.runelite.api.events.ClanMemberLeft;
@@ -290,7 +291,7 @@ public class TeamPlugin extends Plugin
@Subscribe
public void onClanChannelChanged(ClanChannelChanged event)
{
if (!event.isGuest())
if (event.getClanId() == ClanID.CLAN)
{
removeClanChatCounter();
clanChatCount = 0;

View File

@@ -40,6 +40,7 @@ import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Varbits;
import net.runelite.api.WidgetNode;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.vars.Autoweed;
import net.runelite.api.widgets.WidgetModalMode;
@@ -101,11 +102,12 @@ public class FarmingTracker
boolean changed = false;
//Varbits don't get sent when a modal widget is open so just return
if (client.getComponentTable().getNodes()
.stream()
.anyMatch(widgetNode -> widgetNode.getModalMode() != WidgetModalMode.NON_MODAL))
for (WidgetNode widgetNode : client.getComponentTable())
{
return false;
if (widgetNode.getModalMode() != WidgetModalMode.NON_MODAL)
{
return false;
}
}
{

View File

@@ -28,6 +28,7 @@ import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.Units;
import net.runelite.client.plugins.woodcutting.config.ClueNestTier;
@ConfigGroup("woodcutting")
public interface WoodcuttingConfig extends Config
@@ -57,6 +58,17 @@ public interface WoodcuttingConfig extends Config
@ConfigItem(
position = 3,
keyName = "clueNestNotifyTier",
name = "Clue nest notification",
description = "Configures the clue tier from which to start notifying of a clue nest spawn"
)
default ClueNestTier clueNestNotifyTier()
{
return ClueNestTier.BEGINNER;
}
@ConfigItem(
position = 4,
keyName = "showWoodcuttingStats",
name = "Show session stats",
description = "Configures whether to display woodcutting session stats"
@@ -67,7 +79,7 @@ public interface WoodcuttingConfig extends Config
}
@ConfigItem(
position = 4,
position = 5,
keyName = "showRedwoods",
name = "Show Redwood trees",
description = "Configures whether to show a indicator for redwood trees"
@@ -78,7 +90,7 @@ public interface WoodcuttingConfig extends Config
}
@ConfigItem(
position = 5,
position = 6,
keyName = "showRespawnTimers",
name = "Show respawn timers",
description = "Configures whether to display the respawn timer overlay"

View File

@@ -51,6 +51,7 @@ import net.runelite.api.events.GameObjectDespawned;
import net.runelite.api.events.GameObjectSpawned;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.ItemSpawned;
import net.runelite.client.Notifier;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
@@ -58,6 +59,7 @@ import net.runelite.client.events.OverlayMenuClicked;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDependency;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.woodcutting.config.ClueNestTier;
import net.runelite.client.plugins.xptracker.XpTrackerPlugin;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.OverlayMenuEntry;
@@ -107,6 +109,7 @@ public class WoodcuttingPlugin extends Plugin
private final List<TreeRespawn> respawns = new ArrayList<>();
private boolean recentlyLoggedIn;
private int currentPlane;
private ClueNestTier clueTierSpawned;
@Provides
WoodcuttingConfig getConfig(ConfigManager configManager)
@@ -130,6 +133,7 @@ public class WoodcuttingPlugin extends Plugin
treeObjects.clear();
session = null;
axe = null;
clueTierSpawned = null;
}
@Subscribe
@@ -148,6 +152,7 @@ public class WoodcuttingPlugin extends Plugin
public void onGameTick(GameTick gameTick)
{
recentlyLoggedIn = false;
clueTierSpawned = null;
currentPlane = client.getPlane();
respawns.removeIf(TreeRespawn::isExpired);
@@ -190,11 +195,26 @@ public class WoodcuttingPlugin extends Plugin
if (event.getMessage().contains("A bird's nest falls out of the tree") && config.showNestNotification())
{
notifier.notify("A bird nest has spawned!");
if (clueTierSpawned == null || clueTierSpawned.ordinal() >= config.clueNestNotifyTier().ordinal())
{
notifier.notify("A bird nest has spawned!");
}
// Clear the clue tier that has previously spawned
clueTierSpawned = null;
}
}
}
@Subscribe
public void onItemSpawned(ItemSpawned itemSpawned)
{
if (clueTierSpawned == null)
{
// This will be set only if one of the clue nests has spawned. It will then be reset the next game tick.
clueTierSpawned = ClueNestTier.getTierFromItem(itemSpawned.getItem().getId());
}
}
@Subscribe
public void onGameObjectSpawned(final GameObjectSpawned event)
{

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2021, Tal <https://github.com/talsk>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.woodcutting.config;
import com.google.common.collect.ImmutableMap;
import net.runelite.api.ItemID;
public enum ClueNestTier
{
BEGINNER,
EASY,
MEDIUM,
HARD,
ELITE,
DISABLED;
private static final ImmutableMap<Integer, ClueNestTier> CLUE_NEST_ID_TO_TIER = new ImmutableMap.Builder<Integer, ClueNestTier>()
.put(ItemID.CLUE_NEST_ELITE, ClueNestTier.ELITE)
.put(ItemID.CLUE_NEST_HARD, ClueNestTier.HARD)
.put(ItemID.CLUE_NEST_MEDIUM, ClueNestTier.MEDIUM)
.put(ItemID.CLUE_NEST_EASY, ClueNestTier.EASY)
.put(ItemID.CLUE_NEST_BEGINNER, ClueNestTier.BEGINNER)
.build();
static public ClueNestTier getTierFromItem(int itemId)
{
return CLUE_NEST_ID_TO_TIER.get(itemId);
}
}

View File

@@ -266,7 +266,10 @@ public class ClientLoader implements Supplier<Applet>
private void updateVanilla(RSConfig config) throws IOException, VerificationException
{
Certificate[] jagexCertificateChain = getJagexCertificateChain();
Certificate[][] jagexCertificateChains = {
loadCertificateChain("jagex.crt"),
loadCertificateChain("jagex2021.crt")
};
// Get the mtime of the first thing in the vanilla cache
// we check this against what the server gives us to let us skip downloading and patching the whole thing
@@ -283,7 +286,7 @@ public class ClientLoader implements Supplier<Applet>
JarEntry je = vanillaCacheTest.getNextJarEntry();
if (je != null)
{
verifyJarEntry(je, jagexCertificateChain);
verifyJarEntry(je, jagexCertificateChains);
vanillaCacheMTime = je.getLastModifiedTime().toMillis();
}
else
@@ -362,7 +365,7 @@ public class ClientLoader implements Supplier<Applet>
}
networkJIS.skip(Long.MAX_VALUE);
verifyJarEntry(je, jagexCertificateChain);
verifyJarEntry(je, jagexCertificateChains);
long vanillaClientMTime = je.getLastModifiedTime().toMillis();
if (!vanillaCacheIsInvalid && vanillaClientMTime != vanillaCacheMTime)
{
@@ -379,7 +382,7 @@ public class ClientLoader implements Supplier<Applet>
{
// as with the request stream, its important to not early close vanilla too
JarInputStream vanillaCacheTest = new JarInputStream(Channels.newInputStream(vanilla));
verifyWholeJar(vanillaCacheTest, jagexCertificateChain);
verifyWholeJar(vanillaCacheTest, jagexCertificateChains);
}
catch (Exception e)
{
@@ -395,7 +398,7 @@ public class ClientLoader implements Supplier<Applet>
OutputStream out = Channels.newOutputStream(vanilla);
out.write(preRead.toByteArray());
copyStream.setOut(out);
verifyWholeJar(networkJIS, jagexCertificateChain);
verifyWholeJar(networkJIS, jagexCertificateChains);
copyStream.skip(Long.MAX_VALUE); // write the trailer to the file too
out.flush();
vanilla.truncate(vanilla.position());
@@ -612,9 +615,9 @@ public class ClientLoader implements Supplier<Applet>
return rs;
}
private static Certificate[] getJagexCertificateChain()
private static Certificate[] loadCertificateChain(String name)
{
try (InputStream in = ClientLoader.class.getResourceAsStream("jagex.crt"))
try (InputStream in = ClientLoader.class.getResourceAsStream(name))
{
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
@@ -626,28 +629,33 @@ public class ClientLoader implements Supplier<Applet>
}
}
private void verifyJarEntry(JarEntry je, Certificate[] certs) throws VerificationException
private void verifyJarEntry(JarEntry je, Certificate[][] chains) throws VerificationException
{
switch (je.getName())
if (je.getName().equals("META-INF/JAGEXLTD.SF") || je.getName().equals("META-INF/JAGEXLTD.RSA"))
{
case "META-INF/JAGEXLTD.SF":
case "META-INF/JAGEXLTD.RSA":
// You can't sign the signing files
return;
default:
if (!Arrays.equals(je.getCertificates(), certs))
{
throw new VerificationException("Unable to verify jar entry: " + je.getName());
}
// You can't sign the signing files
return;
}
// Jar entry must match one of the trusted certificate chains
Certificate[] entryCertificates = je.getCertificates();
for (Certificate[] chain : chains)
{
if (Arrays.equals(entryCertificates, chain))
{
return;
}
}
throw new VerificationException("Unable to verify jar entry: " + je.getName());
}
private void verifyWholeJar(JarInputStream jis, Certificate[] certs) throws IOException, VerificationException
private void verifyWholeJar(JarInputStream jis, Certificate[][] chains) throws IOException, VerificationException
{
for (JarEntry je; (je = jis.getNextJarEntry()) != null; )
{
jis.skip(Long.MAX_VALUE);
verifyJarEntry(je, certs);
verifyJarEntry(je, chains);
}
}
}

View File

@@ -50,7 +50,7 @@ public class WidgetOverlay extends Overlay
new WidgetOverlay(client, WidgetInfo.RAIDS_POINTS_INFOBOX, OverlayPosition.TOP_LEFT),
new WidgetOverlay(client, WidgetInfo.TOB_PARTY_INTERFACE, OverlayPosition.TOP_LEFT),
new WidgetOverlay(client, WidgetInfo.TOB_PARTY_STATS, OverlayPosition.TOP_LEFT),
new WidgetOverlay(client, WidgetInfo.GWD_KC, OverlayPosition.TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.GWD_KC, OverlayPosition.TOP_LEFT),
new WidgetOverlay(client, WidgetInfo.TITHE_FARM, OverlayPosition.TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.PEST_CONTROL_BOAT_INFO, OverlayPosition.TOP_LEFT),
new WidgetOverlay(client, WidgetInfo.PEST_CONTROL_KNIGHT_INFO_CONTAINER, OverlayPosition.TOP_LEFT),

View File

@@ -33,7 +33,6 @@ import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.Constants;
import net.runelite.api.GameState;
import net.runelite.api.InventoryID;
import net.runelite.api.ItemContainer;
import net.runelite.api.NPC;
import net.runelite.api.Node;
@@ -112,14 +111,9 @@ public class GameEventManager
eventBus.register(subscriber);
for (final InventoryID inventory : InventoryID.values())
for (final ItemContainer itemContainer : client.getItemContainers())
{
final ItemContainer itemContainer = client.getItemContainer(inventory);
if (itemContainer != null)
{
eventBus.post(new ItemContainerChanged(inventory.getId(), itemContainer));
}
eventBus.post(new ItemContainerChanged(itemContainer.getId(), itemContainer));
}
for (NPC npc : client.getCachedNPCs())

View File

@@ -0,0 +1,83 @@
-----BEGIN CERTIFICATE-----
MIIFXjCCBEagAwIBAgIQe8nov0sYXAw/4Qpi35irpzANBgkqhkiG9w0BAQsFADBM
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMuMSYwJAYDVQQDEx10
aGF3dGUgU0hBMjU2IENvZGUgU2lnbmluZyBDQTAeFw0yMTEwMjEwMDAwMDBaFw0y
MjEwMTkyMzU5NTlaMEkxCzAJBgNVBAYTAkdCMRIwEAYDVQQHEwlDYW1icmlkZ2Ux
EjAQBgNVBAoTCUphZ2V4IEx0ZDESMBAGA1UEAxMJSmFnZXggTHRkMIICIjANBgkq
hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArDT2DqhgDbCSihEH67vHsOokbTmLeuco
z8ApWGKgyZGdO73l0YmXzco/N681xCP8SFr1chfwc9H+mS9QB/3Tp51wqLjb7U9A
IYmQVcnJNw3glZrRzBlZJCKSbcMFUQ++0+WLzKZKY/ZW8xGsu6GsUdxLadOt90pr
Ak4SrgM02PU0RfmqyEs037ezuzyR0dMYOdTHM01l1h8M2GFD9IWrqwazDcdBUpKX
KSW1nFOmSaF7TtQTq6dIHBlXE5Y2Zob/XYCYotP/3yI3XL3QEWsjF/wkTRJr5WCC
MGgWRezNf5WaE+S431+cs70FEufD32iKsOjHhbddX9qFfHXgLaAfoozNiLCCRdS4
gaRMD6USF6T0MbWKpe43KtBukYXHZ37bW9etwEDTo23J0TBiFPHZPpKhaEo7BcZU
eS1vTH41Bf0Siv3RkP/r5b+mkLgiqINUr+GVrcGnbpDHsvCs5UBX9kLm4EIDwB5m
PlgJV85Ou5cFYvxZZyRW1IrKHXsKIWsheRTFBnij6sJr/PKZcTWBEeSxXpk6nGnE
mNRrNV+GEVwHBltC2ReCYcu2khXVcPF9qnfCfzEkHRUNFm8QkKwbLpeyqG4923Pa
+YcPK0wEJHm5t3Mv+1u8kL6SOWm37h5Z9HRLsd7N4JFXHglqVgy3BPm77oKG6XXj
WrSquVvZG1ECAwEAAaOCAT0wggE5MB0GA1UdDgQWBBQUS46z2oXY4BEoFq6JU6kM
wCHD6DAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDAYDVR0T
AQH/BAIwADA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRw
Oi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDov
L3RsLnN5bWNiLmNvbS90bC5jcmwwVwYIKwYBBQUHAQEESzBJMB8GCCsGAQUFBzAB
hhNodHRwOi8vdGwuc3ltY2QuY29tMCYGCCsGAQUFBzAChhpodHRwOi8vdGwuc3lt
Y2IuY29tL3RsLmNydDAfBgNVHSMEGDAWgBRXhptUuL6mKYrk9sLiExiJhc3ctzAN
BgkqhkiG9w0BAQsFAAOCAQEAlAYVi6THkvr28AN8ExBmTYJKkZV5bDEhc55V2Ncf
a0J/v4pzFEnZoPKQBoRcxIZtY/c1Pr7NorGt3bkmBA8tc4Ni59US9OeGE9D9XYw3
WTOD0Cl40u+bV5dTim2fU2iQy18CtZzJsn+oXa4KHcCPl4+zZ4OLX2kPyxcSBce4
9vzXbBg0CMj1bh3HVrB93r3pLFCJtYJutPKeZjQfapst6mQddGqIY4ghmOEhH/Sf
qEno2Q0WYd4KHuynGfjdcmcNkntdkBvzQ5yaL55zjpQIXhg0dnaQxF+NuBwX6mXz
R0sshmSJfKy9hr0IYCO422uSwB1cDZ3IZnk/nR9JpVRPRQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEmTCCA4GgAwIBAgIQcaC3NpXdsa/COyuaGO5UyzANBgkqhkiG9w0BAQsFADCB
qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTMxMjEwMDAwMDAwWhcNMjMx
MjA5MjM1OTU5WjBMMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu
MSYwJAYDVQQDEx10aGF3dGUgU0hBMjU2IENvZGUgU2lnbmluZyBDQTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJtVAkwXBenQZsP8KK3TwP7v4Ol+1B72
qhuRRv31Fu2YB1P6uocbfZ4fASerudJnyrcQJVP0476bkLjtI1xC72QlWOWIIhq+
9ceu9b6KsRERkxoiqXRpwXS2aIengzD5ZPGx4zg+9NbB/BL+c1cXNVeK3VCNA/hm
zcp2gxPI1w5xHeRjyboX+NG55IjSLCjIISANQbcL4i/CgOaIe1Nsw0RjgX9oR4wr
Ks9b9IxJYbpphf1rAHgFJmkTMIA4TvFaVcnFUNaqOIlHQ1z+TXOlScWTaf53lpqv
84wOV7oz2Q7GQtMDd8S7Oa2R+fP3llw6ZKbtJ1fB6EDzU/K+KTT+X/kCAwEAAaOC
ARcwggETMC8GCCsGAQUFBwEBBCMwITAfBggrBgEFBQcwAYYTaHR0cDovL3QyLnN5
bWNiLmNvbTASBgNVHRMBAf8ECDAGAQH/AgEAMDIGA1UdHwQrMCkwJ6AloCOGIWh0
dHA6Ly90MS5zeW1jYi5jb20vVGhhd3RlUENBLmNybDAdBgNVHSUEFjAUBggrBgEF
BQcDAgYIKwYBBQUHAwMwDgYDVR0PAQH/BAQDAgEGMCkGA1UdEQQiMCCkHjAcMRow
GAYDVQQDExFTeW1hbnRlY1BLSS0xLTU2ODAdBgNVHQ4EFgQUV4abVLi+pimK5PbC
4hMYiYXN3LcwHwYDVR0jBBgwFoAUe1tFz6/Oy3r9MZIaarbzRutXSFAwDQYJKoZI
hvcNAQELBQADggEBACQ79degNhPHQ/7wCYdo0ZgxbhLkPx4flntrTB6HnovFbKOx
DHtQktWBnLGPLCm37vmRBbmOQfEs9tBZLZjgueqAAUdAlbg9nQO9ebs1tq2cTCf2
Z0UQycW8h05Ve9KHu93cMO/G1GzMmTVtHOBg081ojylZS4mWCEbJjvx1T8XcCcxO
J4tEzQe8rATgtTOlh5/03XMMkeoSgW/jdfAetZNsRBfVPpfJvQcsVncfhd1G6L/e
LIGUo/flt6fBN591ylV3TV42KcqF2EVBcld1wHlb+jQQBm1kIEK3OsgfHUZkAl/G
R77wxDooVNr2Hk+aohlDpG9J+PxeQiAohItHIG4=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
jVaMaA==
-----END CERTIFICATE-----

View File

@@ -99,7 +99,6 @@ public class PrayerPluginTest
when(client.isPrayerActive(Prayer.PRESERVE)).thenReturn(true);
when(client.getBoostedSkillLevel(Skill.PRAYER)).thenReturn(99);
when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(itemContainer);
prayerPlugin.onItemContainerChanged(new ItemContainerChanged(InventoryID.EQUIPMENT.getId(), itemContainer));
@@ -114,7 +113,6 @@ public class PrayerPluginTest
when(client.isPrayerActive(Prayer.PRESERVE)).thenReturn(true);
when(client.getBoostedSkillLevel(Skill.PRAYER)).thenReturn(99);
when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(itemContainer);
prayerPlugin.onItemContainerChanged(new ItemContainerChanged(InventoryID.EQUIPMENT.getId(), itemContainer));
@@ -129,7 +127,6 @@ public class PrayerPluginTest
when(client.isPrayerActive(Prayer.PRESERVE)).thenReturn(true);
when(client.getBoostedSkillLevel(Skill.PRAYER)).thenReturn(99);
when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(itemContainer);
prayerPlugin.onItemContainerChanged(new ItemContainerChanged(InventoryID.EQUIPMENT.getId(), itemContainer));
@@ -145,7 +142,6 @@ public class PrayerPluginTest
when(client.isPrayerActive(Prayer.PRESERVE)).thenReturn(true);
when(client.getBoostedSkillLevel(Skill.PRAYER)).thenReturn(99);
when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(itemContainer);
prayerPlugin.onItemContainerChanged(new ItemContainerChanged(InventoryID.EQUIPMENT.getId(), itemContainer));

View File

@@ -0,0 +1,252 @@
/*
* Copyright (c) 2020, Jordan Zomerlei <https://github.com/JZomerlei>
* Copyright (c) 2019, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.woodcutting;
import com.google.inject.Guice;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import javax.inject.Inject;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.ItemID;
import net.runelite.api.Tile;
import net.runelite.api.TileItem;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.ItemSpawned;
import net.runelite.client.Notifier;
import net.runelite.client.plugins.woodcutting.config.ClueNestTier;
import net.runelite.client.ui.overlay.OverlayManager;
import static org.junit.Assert.assertNotNull;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class WoodcuttingPluginTest
{
private static final String BIRDS_NEST_MESSAGE = "A bird's nest falls out of the tree.";
@Inject
WoodcuttingPlugin woodcuttingPlugin;
@Mock
@Bind
WoodcuttingConfig woodcuttingConfig;
@Mock
@Bind
Notifier notifier;
@Mock
@Bind
Client client;
@Mock
@Bind
WoodcuttingOverlay woodcuttingOverlay;
@Mock
@Bind
WoodcuttingTreesOverlay woodcuttingTreesOverlay;
@Mock
@Bind
OverlayManager overlayManager;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
}
@Test
public void testLogs()
{
ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You get some logs.", "", 0);
woodcuttingPlugin.onChatMessage(chatMessage);
assertNotNull(woodcuttingPlugin.getSession());
}
@Test
public void testOakLogs()
{
ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You get some oak logs.", "", 0);
woodcuttingPlugin.onChatMessage(chatMessage);
assertNotNull(woodcuttingPlugin.getSession());
}
@Test
public void testArcticLogs()
{
ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You get an arctic log.", "", 0);
woodcuttingPlugin.onChatMessage(chatMessage);
assertNotNull(woodcuttingPlugin.getSession());
}
@Test
public void testMushrooms()
{
ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You get some mushrooms.", "", 0);
woodcuttingPlugin.onChatMessage(chatMessage);
assertNotNull(woodcuttingPlugin.getSession());
}
@Test
public void testBirdsNest()
{
ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0);
when(woodcuttingConfig.showNestNotification()).thenReturn(true);
woodcuttingPlugin.onChatMessage(chatMessage);
verify(notifier).notify("A bird nest has spawned!");
when(woodcuttingConfig.showNestNotification()).thenReturn(false);
woodcuttingPlugin.onChatMessage(chatMessage);
verifyNoMoreInteractions(notifier);
}
@Test
public void testClueNestConfigSameAsSpawn()
{
ChatMessage nestChatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0);
Tile tile = mock(Tile.class);
TileItem beginnerTileItem = mock(TileItem.class);
when(beginnerTileItem.getId()).thenReturn(ItemID.CLUE_NEST_BEGINNER);
ItemSpawned beginnerClueSpawned = new ItemSpawned(tile, beginnerTileItem);
when(woodcuttingConfig.showNestNotification()).thenReturn(true);
when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.BEGINNER);
woodcuttingPlugin.onItemSpawned(beginnerClueSpawned);
woodcuttingPlugin.onChatMessage(nestChatMessage);
woodcuttingPlugin.onGameTick(null);
verify(notifier).notify("A bird nest has spawned!");
}
@Test
public void testClueNestConfigSmallerThanSpawn()
{
ChatMessage nestChatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0);
Tile tile = mock(Tile.class);
TileItem eliteTileItem = mock(TileItem.class);
when(eliteTileItem.getId()).thenReturn(ItemID.CLUE_NEST_ELITE);
ItemSpawned eliteClueSpawned = new ItemSpawned(tile, eliteTileItem);
when(woodcuttingConfig.showNestNotification()).thenReturn(true);
when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.BEGINNER);
woodcuttingPlugin.onItemSpawned(eliteClueSpawned);
woodcuttingPlugin.onChatMessage(nestChatMessage);
woodcuttingPlugin.onGameTick(null);
verify(notifier).notify("A bird nest has spawned!");
}
@Test
public void testClueNestDisabledConfig()
{
ChatMessage nestChatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0);
Tile tile = mock(Tile.class);
TileItem eliteTileItem = mock(TileItem.class);
when(eliteTileItem.getId()).thenReturn(ItemID.CLUE_NEST_ELITE);
ItemSpawned eliteClueSpawned = new ItemSpawned(tile, eliteTileItem);
when(woodcuttingConfig.showNestNotification()).thenReturn(true);
when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.DISABLED);
woodcuttingPlugin.onItemSpawned(eliteClueSpawned);
woodcuttingPlugin.onChatMessage(nestChatMessage);
woodcuttingPlugin.onGameTick(null);
verifyNoInteractions(notifier);
}
@Test
public void testClueNestConfigLargerThanSpawn()
{
ChatMessage nestChatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0);
Tile tile = mock(Tile.class);
TileItem beginnerTileItem = mock(TileItem.class);
when(beginnerTileItem.getId()).thenReturn(ItemID.CLUE_NEST_BEGINNER);
ItemSpawned beginnerClueSpawned = new ItemSpawned(tile, beginnerTileItem);
when(woodcuttingConfig.showNestNotification()).thenReturn(true);
when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.HARD);
woodcuttingPlugin.onItemSpawned(beginnerClueSpawned);
woodcuttingPlugin.onChatMessage(nestChatMessage);
woodcuttingPlugin.onGameTick(null);
verifyNoInteractions(notifier);
}
@Test
public void testClueNestPlayerDrop()
{
ChatMessage nestChatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0);
Tile tile = mock(Tile.class);
TileItem beginnerTileItem = mock(TileItem.class);
when(beginnerTileItem.getId()).thenReturn(ItemID.CLUE_NEST_BEGINNER);
ItemSpawned beginnerClueSpawned = new ItemSpawned(tile, beginnerTileItem);
TileItem nestTileItem = mock(TileItem.class);
when(nestTileItem.getId()).thenReturn(ItemID.BIRD_NEST_22798);
ItemSpawned regularNestSpawned = new ItemSpawned(tile, nestTileItem);
when(woodcuttingConfig.showNestNotification()).thenReturn(true);
// Player drops clue nest
woodcuttingPlugin.onItemSpawned(beginnerClueSpawned);
woodcuttingPlugin.onGameTick(null);
verifyNoInteractions(notifier);
// A regular nest has spawned
woodcuttingPlugin.onItemSpawned(regularNestSpawned);
woodcuttingPlugin.onChatMessage(nestChatMessage);
woodcuttingPlugin.onGameTick(null);
verify(notifier).notify("A bird nest has spawned!");
}
@Test
public void testClueNestOtherItemSpawn()
{
ChatMessage nestChatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0);
Tile tile = mock(Tile.class);
TileItem beginnerTileItem = mock(TileItem.class);
when(beginnerTileItem.getId()).thenReturn(ItemID.CLUE_NEST_BEGINNER);
ItemSpawned beginnerClueSpawned = new ItemSpawned(tile, beginnerTileItem);
TileItem anotherItemTileItem = mock(TileItem.class);
ItemSpawned anotherItemSpawned = new ItemSpawned(tile, anotherItemTileItem);
when(woodcuttingConfig.showNestNotification()).thenReturn(true);
when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.BEGINNER);
woodcuttingPlugin.onItemSpawned(beginnerClueSpawned);
woodcuttingPlugin.onItemSpawned(anotherItemSpawned);
woodcuttingPlugin.onChatMessage(nestChatMessage);
woodcuttingPlugin.onGameTick(null);
verify(notifier).notify("A bird nest has spawned!");
}
}

View File

@@ -33,8 +33,9 @@ import net.runelite.rs.api.RSClient;
@Mixin(RSClient.class)
public abstract class CameraMixin implements RSClient
{
private static final int STANDARD_PITCH_MAX = 383;
private static final int NEW_PITCH_MAX = 512;
public static final int STANDARD_PITCH_MIN = 128;
public static final int STANDARD_PITCH_MAX = 383;
public static final int NEW_PITCH_MAX = 512;
@Shadow("client")
static RSClient client;
@@ -48,6 +49,13 @@ public abstract class CameraMixin implements RSClient
@Inject
static int lastPitchTarget = 128;
@Inject
@Override
public boolean getCameraPitchRelaxerEnabled()
{
return pitchRelaxEnabled;
}
@Inject
public void setCameraPitchRelaxerEnabled(boolean enabled)
{

View File

@@ -27,6 +27,7 @@ package net.runelite.mixins;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.primitives.Doubles;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
@@ -71,6 +72,7 @@ import net.runelite.api.NameableContainer;
import net.runelite.api.Node;
import net.runelite.api.NodeCache;
import net.runelite.api.ObjectComposition;
import net.runelite.api.Perspective;
import static net.runelite.api.Perspective.LOCAL_TILE_SIZE;
import net.runelite.api.Player;
import net.runelite.api.Point;
@@ -134,9 +136,13 @@ import net.runelite.api.widgets.WidgetConfig;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.api.widgets.WidgetItem;
import net.runelite.api.widgets.WidgetType;
import static net.runelite.mixins.CameraMixin.NEW_PITCH_MAX;
import static net.runelite.mixins.CameraMixin.STANDARD_PITCH_MAX;
import static net.runelite.mixins.CameraMixin.STANDARD_PITCH_MIN;
import net.runelite.rs.api.RSAbstractArchive;
import net.runelite.rs.api.RSArchive;
import net.runelite.rs.api.RSChatChannel;
import net.runelite.rs.api.RSClanChannel;
import net.runelite.rs.api.RSClient;
import net.runelite.rs.api.RSEnumComposition;
import net.runelite.rs.api.RSFriendSystem;
@@ -261,6 +267,21 @@ public abstract class RSClientMixin implements RSClient
@Inject
private Integer comparingAppearance = 0;
@Inject
private static boolean hdMinimapEnabled;
@Inject
public static boolean unlockedFps;
@Inject
public static double tmpCamAngleY;
@Inject
public static double tmpCamAngleX;
@Inject
public long lastNanoTime;
@Inject
private List<String> outdatedScripts = new ArrayList<>();
@@ -331,9 +352,6 @@ public abstract class RSClientMixin implements RSClient
{
}
@Inject
private static boolean hdMinimapEnabled;
@Inject
@Override
public String getBuildID()
@@ -949,6 +967,25 @@ public abstract class RSClientMixin implements RSClient
return (RSItemContainer) itemContainers.get(inventory.getId());
}
@Inject
@Override
public RSItemContainer getItemContainer(int id)
{
RSNodeHashTable itemContainers = getItemContainers();
for (Object itemContainer : itemContainers)
{
RSItemContainer container = ((RSItemContainer) itemContainer);
if (((RSItemContainer) itemContainer).getId() == id)
{
return container;
}
}
return null;
}
@Inject
@Override
public boolean isFriended(String name, boolean mustBeLoggedIn)
@@ -1560,7 +1597,8 @@ public abstract class RSClientMixin implements RSClient
@MethodHook("draw")
public void draw(boolean var1)
{
callbacks.clientMainLoop();
callbacks.frame();
updateCamera();
}
@MethodHook("drawInterface")
@@ -2319,24 +2357,23 @@ public abstract class RSClientMixin implements RSClient
@FieldHook("guestClanChannel")
public static void onGuestClanChannelChanged(int idx)
{
client.getCallbacks().post(new ClanChannelChanged(client.getGuestClanChannel(), true));
client.getCallbacks().post(new ClanChannelChanged(client.getGuestClanChannel(), -1, true));
}
@Inject
@FieldHook("currentClanChannels")
public static void onCurrentClanChannelsChanged(int idx)
{
if (idx == -1)
RSClanChannel[] clanChannels = client.getCurrentClanChannels();
if (idx >= 0 && idx < clanChannels.length)
{
// don't fire on array field itself being set
return;
RSClanChannel clanChannel = clanChannels[idx];
client.getCallbacks().post(new ClanChannelChanged(clanChannel, idx, false));
}
client.getCallbacks().post(new ClanChannelChanged(client.getClanChannel(), false));
}
@Inject
public static RSArchive[] archives = new RSArchive[21];
@@ -2387,5 +2424,86 @@ public abstract class RSClientMixin implements RSClient
{
return client.getSequenceDefinition(id);
}
@Inject
@Override
public boolean isUnlockedFps()
{
return unlockedFps;
}
@Inject
public void setUnlockedFps(boolean unlocked)
{
unlockedFps = unlocked;
if (unlocked)
{
posToCameraAngle(client.getMapAngle(), client.getCameraPitch());
}
}
@Inject
public void updateCamera()
{
if (unlockedFps)
{
long nanoTime = System.nanoTime();
long diff = nanoTime - this.lastNanoTime;
this.lastNanoTime = nanoTime;
if (this.getGameState() == GameState.LOGGED_IN)
{
this.interpolateCamera(diff);
}
}
}
@Inject
public void interpolateCamera(long var1)
{
double angleDX = diffToDangle(client.getCamAngleDY(), var1);
double angleDY = diffToDangle(client.getCamAngleDX(), var1);
tmpCamAngleY += angleDX / 2;
tmpCamAngleX += angleDY / 2;
tmpCamAngleX = Doubles.constrainToRange(tmpCamAngleX, Perspective.UNIT * STANDARD_PITCH_MIN, client.getCameraPitchRelaxerEnabled() ? Perspective.UNIT * NEW_PITCH_MAX : Perspective.UNIT * STANDARD_PITCH_MAX);
int yaw = toCameraPos(tmpCamAngleY);
int pitch = toCameraPos(tmpCamAngleX);
client.setCameraYawTarget(yaw);
client.setCameraPitchTarget(pitch);
}
@Inject
public static double diffToDangle(int var0, long var1)
{
double var2 = var0 * Perspective.UNIT;
double var3 = (double) var1 / 2.0E7D;
return var2 * var3;
}
@Inject
@Override
public void posToCameraAngle(int var0, int var1)
{
tmpCamAngleY = var0 * Perspective.UNIT;
tmpCamAngleX = var1 * Perspective.UNIT;
}
@Inject
public static int toCameraPos(double var0)
{
return (int) (var0 / Perspective.UNIT) & 2047;
}
@Inject
@MethodHook("doCycle")
protected final void doCycle()
{
client.getCallbacks().tick();
}
}

View File

@@ -44,6 +44,13 @@ public abstract class RSItemContainerMixin implements RSItemContainer
@Shadow("changedItemContainers")
private static int[] changedItemContainers;
@Inject
@Override
public int getId()
{
return (int) this.getHash();
}
@Inject
@Override
public Item[] getItems()
@@ -131,7 +138,7 @@ public abstract class RSItemContainerMixin implements RSItemContainer
int containerId = changedItemContainers[changedId];
RSNodeHashTable itemContainers = client.getItemContainers();
RSItemContainer changedContainer = (RSItemContainer) itemContainers.get(containerId);
RSItemContainer changedContainerInvOther = (RSItemContainer) itemContainers.get(containerId | 0x8000);
@@ -140,7 +147,7 @@ public abstract class RSItemContainerMixin implements RSItemContainer
ItemContainerChanged event = new ItemContainerChanged(containerId, changedContainer);
client.getCallbacks().postDeferred(event);
}
if (changedContainerInvOther != null)
{
ItemContainerChanged event = new ItemContainerChanged(containerId | 0x8000, changedContainerInvOther);

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.mixins;
import net.runelite.api.mixins.Copy;
import net.runelite.api.mixins.Mixin;
import net.runelite.api.mixins.Replace;
import net.runelite.api.mixins.Shadow;
import net.runelite.rs.api.RSClient;
import net.runelite.rs.api.RSNanoClock;
@Mixin(RSNanoClock.class)
public abstract class RSNanoClockMixin implements RSNanoClock
{
@Shadow("client")
private static RSClient client;
@Copy("wait")
@Replace("wait")
public int copy$wait(int cycleDurationMillis, int var2)
{
if (!client.isUnlockedFps())
{
return copy$wait(cycleDurationMillis, var2);
}
else
{
long nanoTime = System.nanoTime();
if (nanoTime < getLastTimeNano())
{
setLastTimeNano(nanoTime);
return 1;
}
else
{
long cycleDuration = (long) cycleDurationMillis * 1000000L;
long diff = nanoTime - getLastTimeNano();
int cycles = (int) (diff / cycleDuration);
setLastTimeNano(getLastTimeNano() + (long) cycles * cycleDuration);
if (cycles > 10)
{
cycles = 10;
}
return cycles;
}
}
}
}

View File

@@ -42,24 +42,9 @@ public abstract class RuneLiteObjectMixin implements RSRuneLiteObject
@Shadow("client")
private static RSClient client;
@Inject
private static boolean loop;
@Inject
private static RSModel model;
@Inject
RuneLiteObjectMixin()
{
setFinished(true);
}
@Inject
public void setModel(Model var1)
{
model = (RSModel) var1;
}
@Inject
public void setLocation(LocalPoint localPoint, int plane)
{
@@ -69,6 +54,14 @@ public abstract class RuneLiteObjectMixin implements RSRuneLiteObject
setHeight(Perspective.getTileHeight(client, localPoint, plane));
}
@Inject
public void setAnimation(Sequence var1)
{
setFrame(0);
setFrameCycle(0);
setSequenceDefinition((RSSequenceDefinition) var1);
}
@Inject
public void advanceRL(int var1)
{
@@ -90,7 +83,7 @@ public abstract class RuneLiteObjectMixin implements RSRuneLiteObject
}
}
if (loop && finished())
if (isLooping() && finished())
{
setFinished(false);
setFrame(0);
@@ -100,44 +93,9 @@ public abstract class RuneLiteObjectMixin implements RSRuneLiteObject
}
@Inject
public boolean isActive()
public void setModel(Model var1)
{
return !finished();
}
@Inject
public void setActive(boolean active)
{
if (finished() == active)
{
setFinished(!active);
if (active)
{
setFrame(0);
setFrameCycle(0);
client.getGraphicsObjectDeque().addFirst(this);
}
else
{
unlink();
}
}
}
@Inject
public void setShouldLoop(boolean var1)
{
loop = var1;
}
@Inject
public void setAnimation(Sequence var1)
{
setFrame(0);
setFrameCycle(0);
setSequenceDefinition((RSSequenceDefinition) var1);
model = (RSModel) var1;
}
@Inject

View File

@@ -27,7 +27,6 @@ package net.runelite.mixins;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static net.runelite.api.Opcodes.*;
import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.events.ScriptPostFired;
import net.runelite.api.events.ScriptPreFired;
@@ -37,6 +36,10 @@ import net.runelite.api.mixins.Mixin;
import net.runelite.api.mixins.Replace;
import net.runelite.api.mixins.Shadow;
import net.runelite.api.widgets.JavaScriptCallback;
import static net.runelite.cache.script.Opcodes.CAM_FORCEANGLE;
import static net.runelite.cache.script.Opcodes.INVOKE;
import static net.runelite.cache.script.Opcodes.RETURN;
import static net.runelite.cache.script.RuneLiteOpcodes.RUNELITE_EXECUTE;
import net.runelite.rs.api.RSClient;
import net.runelite.rs.api.RSScript;
import net.runelite.rs.api.RSScriptEvent;
@@ -132,6 +135,17 @@ public abstract class ScriptVMMixin implements RSClient
return false;
case RETURN:
client.getCallbacks().post(new ScriptPostFired((int) currentScript.getHash()));
return false;
case CAM_FORCEANGLE:
int[] intStack = client.getIntStack();
int intStackSize = client.getIntStackSize();
int var4 = intStack[intStackSize - 1];
int var3 = intStack[intStackSize - 2];
if (!client.isCameraLocked())
{
client.posToCameraAngle(var4, var3);
}
return false;
}
return false;

View File

@@ -33,6 +33,7 @@ import java.io.IOException;
import net.runelite.cache.IndexType;
import net.runelite.cache.definitions.ScriptDefinition;
import net.runelite.cache.definitions.savers.ScriptSaver;
import net.runelite.cache.script.RuneLiteInstructions;
import net.runelite.cache.script.assembler.Assembler;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;

View File

@@ -1467,4 +1467,13 @@ public interface RSClient extends RSGameEngine, Client
@Import("ModelData_get")
RSModelData getModelData(RSAbstractArchive var0, int var1, int var2);
@Import("isCameraLocked")
boolean isCameraLocked();
boolean getCameraPitchRelaxerEnabled();
boolean isUnlockedFps();
void posToCameraAngle(int var0, int var1);
}

View File

@@ -1,7 +1,6 @@
package net.runelite.rs.api;
import net.runelite.api.ItemComposition;
import net.runelite.api.IterableHashTable;
import net.runelite.mapping.Import;
public interface RSItemComposition extends ItemComposition
@@ -89,9 +88,6 @@ public interface RSItemComposition extends ItemComposition
@Import("params")
RSIterableNodeHashTable getParams();
@Import("params")
void setParams(IterableHashTable params);
@Import("params")
void setParams(RSIterableNodeHashTable params);
}

View File

@@ -1,6 +1,5 @@
package net.runelite.rs.api;
import net.runelite.api.IterableHashTable;
import net.runelite.api.NPCComposition;
import net.runelite.mapping.Import;
@@ -64,9 +63,6 @@ public interface RSNPCComposition extends NPCComposition
@Import("params")
RSIterableNodeHashTable getParams();
@Import("params")
void setParams(IterableHashTable params);
@Import("params")
void setParams(RSIterableNodeHashTable params);
}

View File

@@ -1,3 +1,12 @@
package net.runelite.rs.api;
public interface RSNanoClock {}
import net.runelite.mapping.Import;
public interface RSNanoClock
{
@Import("lastTimeNano")
long getLastTimeNano();
@Import("lastTimeNano")
void setLastTimeNano(long lastNanoTime);
}

View File

@@ -1,6 +1,5 @@
package net.runelite.rs.api;
import net.runelite.api.IterableHashTable;
import net.runelite.api.ObjectComposition;
import net.runelite.mapping.Import;
@@ -40,9 +39,6 @@ public interface RSObjectComposition extends ObjectComposition
@Import("params")
RSIterableNodeHashTable getParams();
@Import("params")
void setParams(IterableHashTable params);
@Import("params")
void setParams(RSIterableNodeHashTable params);

View File

@@ -0,0 +1,4 @@
package net.runelite.rs.api;
public interface RSRuneLiteIterableHashTable
{}

View File

@@ -4,6 +4,8 @@ import net.runelite.api.RuneLiteObject;
public interface RSRuneLiteObject extends RuneLiteObject, RSGraphicsObject
{
boolean isLooping();
void advanceRL(int var1);
RSModel getModelRl();

View File

@@ -1,6 +1,5 @@
package net.runelite.rs.api;
import net.runelite.api.IterableHashTable;
import net.runelite.api.StructComposition;
import net.runelite.mapping.Import;
@@ -12,9 +11,6 @@ public interface RSStructComposition extends StructComposition
@Import("params")
RSIterableNodeHashTable getParams();
@Import("params")
void setParams(IterableHashTable params);
@Import("params")
void setParams(RSIterableNodeHashTable params);
}

View File

@@ -0,0 +1,70 @@
import java.util.Iterator;
import java.util.NoSuchElementException;
public class RuneLiteIterableHashTable implements Iterator
{
public Node node;
public final NodeHashTable nodeHashTable;
public int it;
public RuneLiteIterableHashTable(NodeHashTable nodeHashTable)
{
this.nodeHashTable = nodeHashTable;
}
@Override
public boolean hasNext()
{
if (this.it > 0 && this.nodeHashTable.buckets[this.it - 1] != this.node)
{
return true;
}
else
{
for (int i = this.it; i < this.nodeHashTable.size; ++i)
{
Node bucket = this.nodeHashTable.buckets[i];
Node previous = bucket.previous;
if (bucket != previous)
{
return true;
}
}
return false;
}
}
@Override
public Node next()
{
if (this.it > 0 && this.nodeHashTable.buckets[this.it - 1] != this.node)
{
Node node = this.node;
this.node = node.previous;
return node;
}
else
{
Node node;
Node previous;
do
{
if (this.it >= this.nodeHashTable.size)
{
throw new NoSuchElementException();
}
node = this.nodeHashTable.buckets[this.it++];
previous = node.previous;
} while (node == previous);
this.node = previous.previous;
return previous;
}
}
}

View File

@@ -7,6 +7,10 @@ public class RuneLiteObject extends GraphicsObject
super.isFinished = true;
}
public boolean isLooping() {
return loop;
}
public boolean isActive() {
return !super.isFinished;
}
@@ -35,33 +39,6 @@ public class RuneLiteObject extends GraphicsObject
this.loop = var1;
}
@Override
public void advance(int var1)
{
if (super.sequenceDefinition != null) {
super.advance(var1);
if (this.loop && super.isFinished) {
super.isFinished = false;
super.frame = 0;
super.frameCycle = 0;
}
}
}
@Override
public Model getModel()
{
if (super.sequenceDefinition != null)
{
return super.sequenceDefinition.transformSpotAnimationModel(this.model, super.frame);
}
else
{
return this.model.toSharedSequenceModel(true);
}
}
public void setModel(Model var1) {
this.model = var1;
}