PDA

View Full Version : Advanced Lape Reflection Tutorial



Kyle
01-25-2015, 04:18 AM
Advanced Lape Reflection Include Tutorial



Now that the Lape-Reflection include is basically finished and I have some extra time, I have decided to write the last part of the two tutorials I had planned on doing for OSR reflection. This will hopefully teach scripters how the include is laid out internally, and how to use it to your advantage to make your own reflection functions specifically for your needs.

This will be written in a way that assumes you already have a decent knowledge of scripting with Lape. I suggest you read my basic scripting tutorial (https://villavu.com/forum/showthread.php?t=111664) if you haven’t already.

Table of Contents

Intro
Words of Caution
Smart functions
THooks
Hook Caching
Widgets
Settings array
Actor record
Item Record
Definitions
Closing




Intro


Since most of the people reading this I assume will not have a lot of java experience or direct knowledge with reflection, I would like to try to clear up a couple things that might help understanding the rest of the tutorial.

In java, there are two 'main' types of fields, static and non static. If the field is static, then outside of the class it was declared in, it can only be accessed with a created instance of that class. Here (http://tutorials.jenkov.com/java/fields.html) is a pretty quick and good explanation about the different types of fields.

Now, all a "Hook" is, is the name of a field in an OSR gamepack. This is an example of a class in Gamepack #71 of the "Item" class, that I shortened, and re-factored it some in order to better understand it.

public final class Item extends Renderable
{
int AMOUNT;
int ID;

protected final g() {
return getItemDefinition(this.ID * -1947959454, (byte)(-37)).k(this.AMOUNT * 19586299, -1951670365);
}
}

So, this method, when called, will invoke the getItemDefinition method, taking the ID and Amount as parameters. If you notice, both of these fields are being multiplied by a integer. This is done by jagex as a obfuscation method, to make it harder to get the fields from the client.

This actual piece of code looks like:


public final class o extends cg
{
int j;
int c;

protected final cl g() {
return x.j(this.c * -1947959454, (byte)(-37)).k(this.j * 19586299, -1951670365);
}
}


So, for this gamepack, the Hook for Item_Amount is 'j' with a multiplier of 19586299. The Hook for Item_ID is 'c' with a multiplier of -1947959454.




Words of Caution


While using reflection with smart, you must always make sure that at all times when you load a pointer into memory, whether it is using a Reflect.Smart.GetObject function or a TReflectWidget.Get function that you free it. Once you start making your own functions, there isn't any memory management done by the include. You can easily introduce memory leaks if you are not careful, which can become a big problem. One common occurrence of leaks is when using a loop. When you load anything into memory, make sure that before every continue/break that the pointer is freed, and again at the end of the function. Another way this can be done is with a try except finally loop.



SMART functions


The complete list of all SMART functions are as follows:

function TReflectionSmart.GetArraySize(
Ref: Integer; Hook: THook; Dimension: Integer): Integer;
begin
Result := SmartGetFieldArraySize(Reflect.Smart.Target, Ref, Hook.Path, Dimension);
end;

function TReflectionSmart.GetFieldObject(Ref: Integer; Hook: THook): Integer;
begin
Result := SmartGetFieldObject(Reflect.Smart.Target, Ref, Hook.Path);
end;

procedure TReflectPointer.GetFieldObject(Ref: Integer; Hook: THook);
begin
Self.Reference := SmartGetFieldObject(Reflect.Smart.Target, Ref, Hook.Path);
end;

procedure TReflectPointer.GetFieldObject(Hook: THook); overload;
begin
Self.GetFieldObject(Self.Reference, Hook);
end;

function TReflectionSmart.GetFieldLongL(Ref: Integer; Hook: THook): Integer;
begin
Result := SmartGetFieldLongL(Reflect.Smart.Target, Ref, Hook.Path);
end;

function TReflectPointer.GetFieldLongL(Hook: THook): Integer;
begin
Result := SmartGetFieldLongL(Reflect.Smart.Target, Self.Reference, Hook.Path);
end;

function TReflectionSmart.GetFieldInt(Ref: Integer; Hook: THook): Integer;
begin
Result := SmartGetFieldInt(Reflect.Smart.Target, Ref, Hook.Path) * Hook.Multi;
end;

function TReflectPointer.GetFieldInt(Hook: THook): Integer;
begin
Result := SmartGetFieldInt(Reflect.Smart.Target, Self.Reference, Hook.Path)
* Hook.Multi;
end;

function TReflectionSmart.GetFieldShort(Ref: Integer; Hook: THook): Integer;
begin
Result := SmartGetFieldShort(Reflect.Smart.Target, Ref, Hook.Path);
end;

function TReflectionSmart.GetFieldFloat(Ref: Integer; Hook: THook): Extended;
begin
Result := SmartGetFieldFloat(Reflect.Smart.Target, Ref, Hook.Path);
end;

function TReflectionSmart.GetFieldDouble(Ref: Integer; Hook: THook): Extended;
begin
Result := SmartGetFieldDouble(Reflect.Smart.Target, Ref, Hook.Path);
end;

function TReflectionSmart.GetFieldBoolean(Ref: Integer; Hook: THook): Boolean;
begin
Result := SmartGetFieldBoolean(Reflect.Smart.Target, Ref, Hook.Path);
end;

function TReflectionSmart.GetFieldByte(Ref: Integer; Hook: THook): Integer;
begin
Result := SmartGetFieldByte(Reflect.Smart.Target, Ref, Hook.Path);
end;

function TReflectionSmart.GetFieldArrayObject(
Ref: Integer; Hook: THook; Index: Integer): Integer;
begin
Result := SmartGetFieldArrayObject(Reflect.Smart.Target, Ref, Hook.Path, Index);
end;

procedure TReflectPointer.GetFieldArrayObject(
Ref: Integer; Hook: THook; Index: Integer);
begin
Self.Reference := SmartGetFieldArrayObject(Reflect.Smart.Target, Ref,
Hook.Path, Index);
end;

function TReflectionSmart.GetFieldArrayInt(
Ref: Integer; Hook: THook; Index: Integer): Integer;
begin
Result := SmartGetFieldArrayInt(Reflect.Smart.Target, Ref, Hook.Path, Index);
end;

function TReflectionSmart.GetFieldArrayFloat(
Ref: Integer; Hook: THook; Index: Integer): Extended;
begin
Result := SmartGetFieldArrayFloat(Reflect.Smart.Target, Ref, Hook.Path, Index);
end;

function TReflectionSmart.GetFieldArrayDouble(
Ref: Integer; Hook: THook; Index: Integer): Extended;
begin
Result := SmartGetFieldArrayDouble(Reflect.Smart.Target, Ref, Hook.Path, Index);
end;

function TReflectionSmart.GetFieldArrayBoolean(
Ref: Integer; Hook: THook; Index: Integer): Boolean;
begin
Result := SmartGetFieldArrayBool(Reflect.Smart.Target, Ref, Hook.Path, Index);
end;

function TReflectionSmart.GetFieldArrayLongH(
Ref: Integer; Hook: THook; Index: Integer): Integer;
begin
Result := SmartGetFieldArrayLongH(Reflect.Smart.Target, Ref, Hook.Path, Index);
end;

function TReflectionSmart.GetFieldArrayLongL(
Ref: Integer; Hook: THook; Index: Integer): Integer;
begin
Result := SmartGetFieldArrayLongL(Reflect.Smart.Target, Ref, Hook.Path, Index);
end;

function TReflectionSmart.GetFieldArrayByte(
Ref: Integer; Hook: THook; Index: Integer): Integer;
begin
Result := SmartGetFieldArrayByte(Reflect.Smart.Target, Ref, Hook.Path, Index);
end;

function TReflectionSmart.GetFieldArrayShort(
Ref: Integer; Hook: THook; Index: Integer): Integer;
begin
Result := SmartGetFieldArrayShort(Reflect.Smart.Target, Ref, Hook.Path, Index);
end;

function TReflectionSmart.GetFieldArrayChar(
Ref: Integer; Hook: THook; Index: Integer): Integer;
begin
Result := SmartGetFieldArrayChar(Reflect.Smart.Target, Ref, Hook.Path, Index);
end;

function TReflectionSmart.GetFieldArray2DObject(
Ref: Integer; Hook: THook; X, Y: Integer): Integer;
begin
Result := SmartGetFieldArray2DObject(Reflect.Smart.Target, Ref, Hook.Path, X, Y);
end;

procedure TReflectPointer.GetFieldArray2DObject(
Ref: Integer; Hook: THook; X, Y: Integer);
begin
Self.Reference := SmartGetFieldArray2DObject(
Reflect.Smart.Target, Ref, Hook.Path, X, Y);
end;

function TReflectionSmart.GetFieldArray2DInt(
Ref: Integer; Hook: THook; X, Y: Integer): Integer;
begin
Result := SmartGetFieldArray2DInt(Reflect.Smart.Target, Ref, Hook.Path, X, Y);
end;

function TReflectionSmart.GetFieldArray2DDouble(
Ref: Integer; Hook: THook; X, Y: Integer): Extended;
begin
Result := SmartGetFieldArray2DDouble(Reflect.Smart.Target, Ref, Hook.Path, X, Y);
end;

function TReflectionSmart.GetFieldArray2DFloat(
Ref: Integer; Hook: THook; X, Y: Integer): Extended;
begin
Result := SmartGetFieldArray2DFloat(Reflect.Smart.Target, Ref, Hook.Path, X, Y);
end;

function TReflectionSmart.GetFieldArray2DBoolean(
Ref: Integer; Hook: THook; X, Y: Integer): Boolean;
begin
Result := SmartGetFieldArray2DBoolean(Reflect.Smart.Target, Ref, Hook.Path, X, Y);
end;

function TReflectionSmart.GetFieldArray2DLongH(
Ref: Integer; Hook: THook; X, Y: Integer): Extended;
begin
Result := SmartGetFieldArray2DLongH(Reflect.Smart.Target, Ref, Hook.Path, X, Y);
end;

function TReflectionSmart.GetFieldArray2DLongL(
Ref: Integer; Hook: THook; X, Y: Integer): Extended;
begin
Result := SmartGetFieldArray2DLongL(Reflect.Smart.Target, Ref, Hook.Path, X, Y);
end;

function TReflectionSmart.GetFieldArray2DByte(
Ref: Integer; Hook: THook; X, Y: Integer): Integer;
begin
Result := SmartGetFieldArray2DByte(Reflect.Smart.Target, Ref, Hook.Path, X, Y);
end;

function TReflectionSmart.GetFieldArray2DChar(
Ref: Integer; Hook: THook; X, Y: Integer): Integer;
begin
Result := SmartGetFieldArray2DChar(Reflect.Smart.Target, Ref, Hook.Path, X, Y);
end;

function TReflectionSmart.GetFieldArray2DShort(
Ref: Integer; Hook: THook; X, Y: Integer): Integer;
begin
Result := SmartGetFieldArray2DShort(Reflect.Smart.Target, Ref, Hook.Path, X, Y);
end;

function TReflectionSmart.GetFieldArray3DObject(
Ref: Integer; Hook: THook; X, Y, Z: Integer): Integer;
begin
Result := SmartGetFieldArray3DObject(
Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);
end;

procedure TReflectPointer.GetFieldArray3DObject(
Ref: Integer; Hook: THook; X, Y, Z: Integer);
begin
Self.Reference := SmartGetFieldArray3DObject(
Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);
end;

function TReflectionSmart.GetFieldArray3DByte(
Ref: Integer; Hook: THook; X, Y, Z: Integer): Integer;
begin
Result := SmartGetFieldArray3DByte(
Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);
end;

function TReflectionSmart.GetFieldArray3DChar(
Ref: Integer; Hook: THook; X, Y, Z: Integer): Integer;
begin
Result := SmartGetFieldArray3DChar(Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);
end;

function TReflectionSmart.GetFieldArray3DShort(
Ref: Integer; Hook: THook; X, Y, Z: Integer): Integer;
begin
Result := SmartGetFieldArray3DShort(
Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);
end;

function TReflectionSmart.GetFieldArray3DInt(
Ref: Integer; Hook: THook; X, Y, Z: Integer): Integer;
begin
Result := SmartGetFieldArray3DInt(
Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);
end;

function TReflectionSmart.GetFieldArray3DFloat(
Ref: Integer; Hook: THook; X, Y, Z: Integer): Extended;
begin
Result := SmartGetFieldArray3DFloat(
Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);
end;

function TReflectionSmart.GetFieldArray3DDouble(
Ref: Integer; Hook: THook; X, Y, Z: Integer): Extended;
begin
Result := SmartGetFieldArray3DDouble(
Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);
end;

function TReflectionSmart.GetFieldArray3DBoolean(
Ref: Integer; Hook: THook; X, Y, Z: Integer): Boolean;
begin
Result := SmartGetFieldArray3DBoolean(
Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);
end;

function TReflectionSmart.GetFieldArray3DLongH(
Ref: Integer; Hook: THook; X, Y, Z: Integer): Extended;
begin
Result := SmartGetFieldArray3DLongH(
Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);
end;

function TReflectionSmart.GetFieldArray3DLongL(
Ref: Integer; Hook: THook; X, Y, Z: Integer): Extended;
begin
Result := SmartGetFieldArray3DLongL(
Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);
end;

procedure TReflectionSmart.FreeObject(Reference: Integer);
begin
if not Reflect.Smart.IsNull(Reference) then
SmartFreeObject(Reflect.Smart.Target, Reference);
end;

procedure TReflectPointer.Free;
begin
if not Reflect.Smart.IsNull(Self.Reference) then
SmartFreeObject(Reflect.Smart.Target, Self.Reference);
end;

function TReflectionSmart.IsPathValid(Ref: Integer; Hook: THook): Boolean;
begin
Result := SmartIsPathValid(Reflect.Smart.Target, Ref, Hook.Path);
end;

function TReflectionSmart.AreEqual(RefOne, RefTwo: Integer): Boolean;
begin
Result := SmartIsEqual(Reflect.Smart.Target, RefOne, RefTwo);
end;

function TReflectionSmart.IsNull(Ref: Integer): Boolean;
begin
Result := SmartIsNull(Reflect.Smart.Target, Ref);
end;

function TReflectionSmart.StringFromString(
JavaString: Integer; Str: String): Integer;
begin
Result := SmartStringFromString(Reflect.Smart.Target, JavaString, Str);
end;

function TReflectionSmart.StringFromBytes(Bytes: Integer; Str: String): Integer;
begin
Result := SmartStringFromBytes(Reflect.Smart.Target, Bytes, Str);
end;

function TReflectionSmart.StringFromChars(Chars: Integer; Str: String): Integer;
begin
Result := SmartStringFromChars(Reflect.Smart.Target, Chars, Str);
end;

function TReflectionSmart.GetFieldString(Ref: Integer; Hook: THook): String;
var
StrInt: Integer;
begin
StrInt := Reflect.Smart.GetFieldObject(Ref, Hook);
SetLength(Result, 512);
SetLength(Result, Reflect.Smart.StringFromString(StrInt, Result));
Result := Replace(Result, 'Â', '', [RfReplaceAll]);
Result := Replace(Result, #160, #32, [RfReplaceAll]);
Reflect.Smart.FreeObject(StrInt);
end;

function TReflectionSmart.GetFieldArrayString(
Ref: Integer; Hook: THook; Index: Integer): String;
var
StrInt: Integer;
begin
StrInt := Reflect.Smart.GetFieldArrayObject(Ref, Hook, Index);
SetLength(Result, 512);
SetLength(Result, Reflect.Smart.StringFromString(StrInt, Result));
Result := Replace(Result, 'Â', '', [RfReplaceAll]);
Result := Replace(Result, #160, #32, [RfReplaceAll]);
Reflect.Smart.FreeObject(StrInt);
end;

procedure _TReflectionSmartFreeAll;
var
I: Integer;
begin
for I := 0 to High(_ReflectionSmartList) do
if _ReflectionSmartList[I] <> nil then
PReflectionSmart(_ReflectionSmartList[I])^.Free;
SetLength(_ReflectionSmartList, 0);
end;


As you can see, all of the GetObject functions are accessable through the type:
type
TReflectPointer = record
Reference: Integer;
end;

While you can still access them through Reflect.Smart.GetObject();, for memory management, it is highly advised to use to former in all of your functions. A quick example of how to use one of the object functions to get our local players current animation is as follows:


function OurAnimation: Integer;
var
LocPlayer: TReflectPointer;
begin
LocPlayer.GetFieldObject(0, Client_LocalPlayer);
Result := Reflect.Smart.GetFieldInt(LocPlayer.Reference, Actor_Animation);
LocPlayer.Free;
end;


The first value in any of the SMART functions is the reference for the field. What this means, is in java, if a field is not static, it can only be accessed through an instance of the method that it is in. So the first part of the function was to load an instance of our local player into memory. Then, using that instance, we can grab any data we want from the instance, in this case, the animation. You may be wondering why the hook is "Actor_Animations" and not "LocalPlayer_Animation", which is a valid observation. I will clear this up in the "Actor" section of the tutorial.

Now, I mentioned that only non-static fields have to have an instance created. This means that any static fields can be accessed directly without creating an instance. So for any THook with the prefix "Client_" you leave the Reference of the Smart function as "0".



THooks


In reflection/Injection, the Hooks are the names of the classes/fields in the gamepack for the current revision. Since we can't invoke methods in smart, the only hooks that are of any use to us are the hooks for fields. If you aren't familiar with java, you can think of a field as a variable that can access and store different types of in-game data. Any field that is of the type integer or long, will almost always have a multiplier that must be multiplied by the field value in order to get the correct value.
It is for this reason that the fields are stored in a type THook.

type THook = record
Path: string;
Multi: Integer;
end;

So each THook holds the path name and the multiplier. Since only Integers and longs can have a multiplier, the multiplier for other types will just stay at 1. This layout of the THooks make it so that whenever an internal smart function is called, it will automatically multiply the field by the multiplier in order to get the correct value. This means you will never have to manually multiply any of the multipliers to get the actual value, it will be done internally for you.




Hook Caching


I was tempted to exclude this part from the tutorial, as it isn't anything that a scripter will use from the include, but for the sake of completeness and fully explaining the include, I will briefly go through it. The name "Hook Caching" isn't an accurate name for what we do in the include, but it’s the name I originally came up with and I never changed it.

For some functions and uses, instead of loading the reference into memory, using the data from it, and then immediately freeing it, we can actually store this reference in an array and access it later, or to free all the items in the array at once. The "HookCache" is a T2DArray that is accessed by using an enum in which to store, retrieve, and free the references when it is needed. The enum is:
type
TCacheKey = (ckNull, ckRegion, ckSceneTile, ckObject, ckNpcNode, ckNpcDef,
ckNpcName, ckNpcOverHead, ckGroundArray, ckGroundList, ckLocalPlayer);

var
HookCache: T2DIntegerArray;

For example, the array HookCache[ckNpcNode] will hold all the references regarding the Npc used throughout the include. This is used in GroundItems and Objects in this same manner. For both of them, we must iterate through the entire array of tiles in order to then iterate the linked list in GroundItems case, or grab the Object for Objects case. The entire loaded region is 104x104 tiles large, which is an enormous amount of tiles to go through! The include handles this as follows: Each time we go to a tile, we load the reference into a slot in the array, then next tile we go up a slot, until the entire plane is iterated. Then, at the end of the function, we free all references at the same time. This made object finding go from ~5 seconds for all tiles to around ~1 second. The Caching for Npc is similar, but there isn't a large increase in performance. LocalPlayer is used in a completely different way, and I will talk about that in the "Actor" section.




Widgets


This is the section that I assume most people will be interested in, and probably the most useful in your scripts. You can make incredibly advanced custom functions using widgets throughout your scripts. Widgets are kind of hard to easily explain, but basically they are just components that are on the Rs screen. You can almost think along the lines of, if it isn't an object, item, player, or npc, and you can do stuff with it/ get data from it, then it is probably a widget. They are mostly all rectangles, excluding sprites, and even then, their bounds are still rectangular.

With the intro out of the way, I think the easiest way to show how widgets are in game, is with the following picture:

http://i.imgur.com/JZyP882.png

The widget array is a 2D array containing widgets, which this include calls "Parent". These Parents may or may not have children. The children are widgets themselves, having all the same features that the parent has. A child can not have a child itself. So, the way this looks is:
Client_GetWidgets[ContainerIndex][Parent].GetChildren[ChildIndex]

Now hopefully that makes sense, but if not I will try to clarify it. The main function that we use, which accesses a single widget in the array is:

TReflectWidget.GetWidget(ContainerIndex, ParentIndex);

Using the picture above as a reference, if we do the following:

var TestWidget: TReflectWidget;
begin
TestWidget.GetWidget(7, 1);
TestWidget.Free;
end;

The Variable "TestWidget" corresponds to:

http://i.imgur.com/6kWbqRY.png

Since that widget does not have any children, that is as far as we can go along the array, there are no more widgets that can be accessed from there. Now that we have the widget, there are many functions that we can use on it. I will only talk about the ones that are useful and not only there in order to help other functions.

TReflectWidget.GetText; //Returns the text of the widget, IF it has text
TReflectWidget.GetItemID;//Returns ItemID, only used in bank
TReflectWidget.GetItemStackSizes;//Returns Item StackSizes, only used in bank
TReflectWidget.IsHidden;//Returns true if the widget is hidden on our screen
TReflectWidget.GetBounds;//Returns a TBox of the widgets bounds
TReflectWidget.Interact;//Will click the widget


For the following widget:

http://i.imgur.com/cGXQzAP.png

We would access it like so:

var TestWidget, TestChild: TReflectWidget;
begin
TestWidget.GetParent(5, 8);
TestChild.GetChild(TestWidget, 2);
TestWidget.Free;
TestChild.Free;
end;


Something I would like to Point out is, since "TestWidget" and "TestChild" are both widgets, you can use the exact same functions above on both of them, even on "TestWidget", even though it has a child.

There are three more functions that can be used with widgets, that are needed when debugging widgets, and also for other various functions.

TReflectWidget.Exists(ContainerIndex, ParentIndex: Integer);//Returns if there is a widget at the indices
TReflectWidget.IsValid(ContainerIndex, ParentIndex: Integer);//Returns if widget exists and is visible at indices
function TReflectWidget.HasChild;//Returns if a widget has a child


Please note that .Exists and .IsValid can only be used for finding a parent, and HasChild can only be used on a parent.

Here is an example of a quick debug that will print off all valid parents and child widgets, with their text, if any:


var
ParentWidget, ChildWidget: TReflectWidget;
I, J, S, ChildWidth: Integer;

begin
Reflect.Setup;
for I := 0 to 1000 do
for J := 0 to 1000 do
begin
if (not ParentWidget.Exists(I, J)) then
Continue;
ParentWidget.GetWidget(I, J);
if (not ParentWidget.HasChild) then
begin
Writeln('Parent => ' + ToStr(I) + ', ' + 'Child => ' + ToStR(J) + ' => ' + 'Text => ' + ParentWidget.GetText);
ParentWidget.Free;
Continue;
end;
ChildWidth := Reflect.Smart.GetArraySize(ParentWidget.Reference, Widget_Children, 0);
for S := 0 to ChildWidth do
begin
ChildWidget.GetChild(ParentWidget, S);
Writeln('Parent => ' + ToStr(I) + ', ' + 'Child => ' + ToStR(J) + ' => ' + ' ChildWidget => ' + ToStr(S) + ' => ' + 'Text => ' + ChildWidget.GetText);
ChildWidget.Free;
end;
ParentWidget.Free;
end;
end.


I will post a second debug script tomorrow which will draw the bounds of all found widgets.





Settings Array


I mentioned the Settings array in the Basic tutorial, and since it is pretty easy to understand, I don't think I will need to go in to much detail on it. Basically, the settings array is a 1 dimensional integer array [I that has a length of 2000. These integer values can correspond to a lot of different data in the game. The individual integers are commonly refereed to as "varps" and the easiest way to describe them is that they hold in game variables, which change depending on their value. We have two function in the include that are used for them:

Reflect.Internals.GetSettingArray;//Returns the entire Setting array, used for debugging
Reflect.Internals.GetSetting(Setting: Integer);//Returns the value of the varp at "Setting"


The easiest way to find out what a varp does for a specific action, is for example for auto retaliate. If you turn it off, print out entire settings array, turn it on, print out the entire array again, and compare the two on DiffChecker, you will see that one value is different. Then you can use: Reflect.Internals(ThatIndex); to determine if autoretailiate is on or not. They will often correspond to Bits, which can be manipulated using bitshifts. I won't go into detail about this, since if you know how bitwise operators work then this will be easy for you. If you want an example, go to Prayer.simba and see how I determine which prayer is on.




Actor Record


The actor record is a very important feature in this include. In the OSR client Hierarchy, Npcs, Players, and our local player all extend from the actor class. What this means is, every field that actor has, Npcs, Players, and LocalPlayer also have. This is why, like I previously mentioned, we can use "Actor_" hooks on Client_LocalPlayer and we are able to get the fields from it. The way that the include handles these is quite efficient.


type
TReflectActor = type(TReflectPointer);

So that means, that TReflectActor has one field, Reference: Integer; This makes the functions used in actor, extremely easy to be used with the other classes that extend from it.


type
TReflectActor = type(TReflectPointer);

function TReflectActor.GetSpokenText: string;
begin
Result := Reflect.Smart.GetFieldString(Self.Reference, Actor_SpokenText);
end;

function TReflectActor.GetTile: TTile;
begin
Result.X := Reflect.Internals.BaseX +
Reflect.Smart.GetFieldInt(Self.Reference, Actor_WorldX) div 128;
Result.Y:= Reflect.Internals.BaseY +
Reflect.Smart.GetFieldInt(Self.Reference, Actor_WorldY) div 128;
end;

function TReflectActor.GetQueue(Index: Integer = 0): TTile;
begin
Result := Point(Reflect.Smart.GetFieldArrayInt(
Self.Reference, Actor_QueueX, Index) + Reflect.Internals.BaseX,
Reflect.Smart.GetFieldArrayInt(Self.Reference, Actor_QueueY, 0) +
Reflect.Internals.BaseY);
end;

function TReflectActor.GetAnimation: Integer;
begin
Result := Reflect.Smart.GetFieldInt(Self.Reference, Actor_Animation);
end;

function TReflectActor.GetHealth: Integer;
begin
Result := Reflect.Smart.GetFieldInt(Self.Reference, Actor_Health);
end;

function TReflectActor.GetMaxHealth: Integer;
begin
Result := Reflect.Smart.GetFieldInt(Self.Reference, Actor_MaxHealth);
end;

function TReflectActor.GetInteractingIndex: Integer;
begin
Result := Reflect.Smart.GetFieldInt(Self.Reference, Actor_InteractingIndex);
end;

function TReflectActor.GetCombatCycle: Integer;
begin
Result := Reflect.Smart.GetFieldInt(Self.Reference, Actor_CombatCycle);
end;

function TReflectActor.IsMoving: Boolean;
begin
Result := (Self.GetQueue.X <> Self.GetTile.X) or
(Self.GetQueue.Y <> Self.GetTile.Y);
end;

function TReflectActor.IsUnderAttack: Boolean;
begin
Result := (Self.GetCombatCycle > Reflect.Internals.GetClientLoopCycle);
end;


As you can see, the functions all use: Self.Reference as their object reference. That means, that no matter if we are writing a function for Npcs or Players or LocalPlayer, all we have to do is set the reference of that Record to the reference of it, and all the actor functions work for it.

For LocalPlayer.simba, what we do is:


type
TReflectLocalPlayer = record(TReflectActor)
Username, Password, Pin: string;
Active: Boolean;
end;

procedure TReflectLocalPlayer.Create;
begin
if HookCache[TCacheKey.ckLocalPlayer][0] = 0 then
Reflect.Mem.GetObject(ckNull, ckLocalPlayer, Client_LocalPlayer, 0, 0)
else
begin
Reflect.Mem.FreeObjects(ckLocalPlayer, False);
Reflect.Mem.GetObject(ckNull, ckLocalPlayer, Client_LocalPlayer, 0, 0)
end;
Self.Reference := HookCache[TCacheKey.ckLocalPlayer][0];
end;

Now, in the .Create procedure, that sets the Reference of our LocalPlayer to our current instance. We store that reference in the hook cache, so it is not needed to create and free it each time that we use a function.
If you notice, there are a lot of functions in LocalPlayer.simba that are not using the actor class, and their reference in the SmartGet functions are 0. This is again because the field is static, and can be accessed without an instance created.




Definitions


In the OSR gamepack, Npcs, Objects, and Items all have a separate class in which is called the "Definition." So NpcDefinition is one for example. This definition class contains many useful fields which are not accessible in the main Class for Npc. Getting the additional fields in NpcDefinition isn't too hard, as you can just load the instance of it and grab the fields like the others. But for objects and Items, it is much more difficult for us. For java bots that can invoke methods, it is fairly easy to do, as you can feed a method within the definition class the ID and get the fields that way.

Since simba can't invoke methods, you must iterate a hashtable in order to get the fields from it. This is definitely do-able, but it isn't all that efficient, and you won't always be able to get the definitions for all ID's, since OSR only keeps a certain amount loaded at a time. The way that this include handles this problem, is by having a .txt list of all definitions for npcs, items, and objects, which is loaded into memory at the start of the script. This is accessed whenever a function regarding them is called, and is free'd upon termination. There is nearly no difference in speed, and every definition is loaded.




Closing


There was a lot covered in this tutorial, and I wrote it rather quickly, so if there are any sections that you need clarification on, please let me know and I will expand on it, or if you see any mistakes or typo's. I will also be editing this and making some parts easier to understand as time goes on.

The Mayor
01-25-2015, 05:08 AM
Another very nice tut Rep +

MariusK
04-01-2015, 06:50 PM
...

Could you give us a bit more detail on how is client THooks different from other ones?
Now I assume that if we look at i.e. Actor hooks and want to add a new one we just:
{Actor = ab}
Actor_Animation: THook = ['be', 2003772957];
Actor_QueueX: THook = ['by', 1];

Actor_NEWRANDOMHOOK: THook = ['xx', 1];

The hook I added would link to ab.xx field, right?

Now, if we look at Client hooks, we see full paths:
Client_LoopCycle: THook = ['client.n', -663489053];
Client_Region: THook = ['ab.dc', 1];
Client_Plane: THook = ['aa.hk', 1605115673];
Why is it so?

Also, how is client.class differences in general, if any, compared to other classes?

Also, for example:
Client_CurrentLevels: THook = ['client.ho', 1];
I think this Hook only works for our player. Now, if I wanted to see all the cooking levels of fellow players around me, should I look for it in client.class or somewhere else? I am a bit confused now, because I found combat levels of ALL players in player.class, so why it is different now?

Thank you.

Kyle
04-01-2015, 07:08 PM
...

Anything that is listed under client, that has class.field is a static field. They don't neccassarily have to live within the client class, which is why not all of the hooks are client.foo. In an object orientatied language such as java, if a field is made static, you don't have to create an instance of the class in order to access it. This is why you can do:

Reflect.Smart.GetFieldInt(0, Client.Foo);


vs


var
Obj: TReflectPointer;
begin
Obj.GetFieldObject(0, Client_LocalPlayer);
Reflect.Smart.GetFieldInt(Obj.Reference, Actor_Animation);
end;


There are many fields, such as "Client_CurrentLevels" that you mentioned, which ONLY work for our local player, and not any other players. The only fields that work for LocalPlayer, Npcs, and Players are fields located in the Actor class. This is because the Npcs, LocalPlayer, and other Players all extend from Actor class(So they inherit the same fields).

Say for example, we have two classes in java, one is called main which contains our main method, and the other is called foo and contains two fields..

foo.java

public class foo {
int bar = 0;
static int meh = 0;
}

main.java

public class main {
public static void main(String[] args) {
System.out.println(foo.meh); //Static, so we can directly access
foo newInstance = new foo(); //Create a instance of 'foo' called 'newInstance'
System.out.println(newInstance.bar); //Can now access 'bar' through 'newInstance'
}
}

AFools
01-06-2016, 11:09 AM
Kyle; Unfortunately we missed another "text" that; as i seem to learn best by comparison and as i can't exactly find anything to compare here.



Parent => 219, Child => 0 => ChildWidget => 0 => Text => Select an Option
Parent => 219, Child => 0 => ChildWidget => 1 => Text => Pay servant 5000 coins
Parent => 219, Child => 0 => ChildWidget => 2 => Text => Don't
Parent => 219, Child => 0 => ChildWidget => 3 => Text => Fire servant


I have debugged the text; but what next?

Joopi
01-06-2016, 11:32 AM
Not sure if this is what you meant but if you want to find a widget then you could i.e. do it like this

Function RBankOpen() : Boolean;
Var
Widget : TReflectWidget;
Begin
If Widget.IsValid(12, 4) Then
Result := True;
Widget.Free;
End;

edit: The 12 represents what the Parent => points at, and the 4 is what the Child => points at (iirc). Not sure when there is a childwidget though.

AFools
01-06-2016, 11:40 AM
Not sure if this is what you meant but if you want to find a widget then you could i.e. do it like this

Function RBankOpen() : Boolean;
Var
Widget : TReflectWidget;
Begin
If Widget.IsValid(12, 4) Then
Result := True;
Widget.Free;
End;

edit: The 12 represents what the Parent => points at, and the 4 is what the Child => points at (iirc). Not sure when there is a childwidget though.

Well i have been playing around with




Const
Widget_LevelUp_Container = 219;
LevelUp_Continue = 1;

function R_DidLevelUp(doClick: boolean = false): boolean;
var W : TReflectWidget;
begin
W.GetWidget(Widget_LevelUp_Container, LevelUp_Continue);
result := W.NotNull;
if result then begin
if doClick then
W.Interact;
W.Free;
end;
end;

Excuse the names; i edited it from ineedbots level up widget. I see he used .interact? obviously your function doesn't click? Thank you for what you have posted; it gives another avenue to compare;

I have gotten the widget (Boolean) working, it now return true or false. Which is great! but the (Reflect.Chat.GetChatIndices) return null for the text

**** edit ^

AFools
01-06-2016, 11:51 AM
This is where i am up to; i just need to sort out the interact or click part.


Function sOption : Boolean;
Var
Widget : TReflectWidget;
Begin
If Widget.IsValid(219, 0) Then
Result := True;
if result then begin
// if doClick then
Widget.Interact;
Widget.Free;
End;
end


It doesn't matter what i do; i cannot seem to get the data i debugged to interact. I can get other widgets to work.

One Kid
01-06-2016, 03:40 PM
Kyle; Unfortunately we missed another "text" that; as i seem to learn best by comparison and as i can't exactly find anything to compare here.



Parent => 219, Child => 0 => ChildWidget => 0 => Text => Select an Option
Parent => 219, Child => 0 => ChildWidget => 1 => Text => Pay servant 5000 coins
Parent => 219, Child => 0 => ChildWidget => 2 => Text => Don't
Parent => 219, Child => 0 => ChildWidget => 3 => Text => Fire servant


I have debugged the text; but what next?


function GetOption(s:String):TBox;
var
ParentWidget, ChildWidget:TReflectWidget;
i:integer;
begin
if not ParentWidget.Exists(219, 0) then
exit;
ParentWidget.GetWidget(219, 0);
for i:= 0 to ParentWidget.GetWidth do
begin
ChildWidget.GetChild(ParentWidget, i);
if Pos(s, ChildWidget.GetText) then
Result := ChildWidget.GetBounds;
else
continue;



end;


end;



I have not tried this, but it should return the coords of the option that you need, in the form of a TBox.

AFools
01-06-2016, 05:52 PM
Thank you i will give this a go and report back.

Mj
01-30-2016, 01:39 AM
Thanks for the tutorial very helpful

dl_bit
05-30-2016, 09:30 PM
This tutorial is awesome, I just started scripting with lape I have experience with Python, C#,Java, Perl. I'm looking for any further documentation related to the Reflection library I can't find much beyond the source code. I'm gonna check and see if I can find any working scripts to see how they're implementing certain things.

I'm also curious about working with widgets, as it is discussed in this thread do I have to run the widget debug script above to get the widget parent/child properties of the widget I wish to use?