Results 1 to 12 of 12

Thread: Advanced Lape Reflection Tutorial

  1. #1
    Join Date
    Oct 2006
    Posts
    6,751
    Mentioned
    95 Post(s)
    Quoted
    532 Post(s)

    Default Advanced Lape Reflection Tutorial

    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 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 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.

    java Code:
    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:

    java Code:
    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:

    Simba Code:
    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:
    Simba Code:
    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:

    Simba Code:
    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.

    Simba Code:
    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:
    Simba Code:
    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:



    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:
    Simba Code:
    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:

    Simba Code:
    TReflectWidget.GetWidget(ContainerIndex, ParentIndex);

    Using the picture above as a reference, if we do the following:
    Simba Code:
    var  TestWidget: TReflectWidget;
    begin
      TestWidget.GetWidget(7, 1);
      TestWidget.Free;
    end;

    The Variable "TestWidget" corresponds to:



    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.
    Simba Code:
    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:



    We would access it like so:
    Simba Code:
    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.
    Simba Code:
    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:

    Simba Code:
    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:
    Simba Code:
    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.

    Simba Code:
    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.

    Simba Code:
    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:

    Simba Code:
    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.
    Last edited by Kyle; 01-19-2016 at 12:12 AM.
    “The long-lived and those who will die soonest lose the same thing. The present is all that they can give up, since that is all you have, and what you do not have, you cannot lose.” - Marcus Aurelius

  2. #2
    Join Date
    Jun 2007
    Location
    The land of the long white cloud.
    Posts
    3,700
    Mentioned
    260 Post(s)
    Quoted
    2006 Post(s)

  3. #3
    Join Date
    May 2013
    Posts
    75
    Mentioned
    3 Post(s)
    Quoted
    48 Post(s)

    Default

    Quote Originally Posted by elfyyy View Post
    ...
    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:
    Simba Code:
    {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:
    Simba Code:
    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:
    Simba Code:
    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.

  4. #4
    Join Date
    Oct 2006
    Posts
    6,751
    Mentioned
    95 Post(s)
    Quoted
    532 Post(s)

    Default

    Quote Originally Posted by MariusK View Post
    ...
    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:
    Simba Code:
    Reflect.Smart.GetFieldInt(0, Client.Foo);

    vs

    Simba Code:
    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
    JAVA Code:
    public class foo {
        int bar = 0;
        static int meh = 0;
    }
    main.java
    JAVA Code:
    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'
        }
    }
    Last edited by Kyle; 04-01-2015 at 07:13 PM.
    “The long-lived and those who will die soonest lose the same thing. The present is all that they can give up, since that is all you have, and what you do not have, you cannot lose.” - Marcus Aurelius

  5. #5
    Join Date
    Apr 2013
    Posts
    680
    Mentioned
    13 Post(s)
    Quoted
    341 Post(s)

    Default

    @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.

    Code:
    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?

    <------------------>



  6. #6
    Join Date
    Sep 2014
    Location
    C:\Simba\
    Posts
    553
    Mentioned
    8 Post(s)
    Quoted
    71 Post(s)

    Default

    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

    Simba Code:
    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.
    Last edited by Joopi; 01-06-2016 at 11:36 AM.
    Feel free to ask me any questions, I will do my best to answer them!

    Previously known as YouPee.

  7. #7
    Join Date
    Apr 2013
    Posts
    680
    Mentioned
    13 Post(s)
    Quoted
    341 Post(s)

    Default

    Quote Originally Posted by Joopi View Post
    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

    Simba Code:
    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

    Code:
    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 ^

    <------------------>



  8. #8
    Join Date
    Apr 2013
    Posts
    680
    Mentioned
    13 Post(s)
    Quoted
    341 Post(s)

    Default

    This is where i am up to; i just need to sort out the interact or click part.
    Code:
    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.

    <------------------>



  9. #9
    Join Date
    Mar 2012
    Posts
    106
    Mentioned
    2 Post(s)
    Quoted
    49 Post(s)

    Default

    Quote Originally Posted by AFools View Post
    @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.

    Code:
    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?
    Simba Code:
    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.

  10. #10
    Join Date
    Apr 2013
    Posts
    680
    Mentioned
    13 Post(s)
    Quoted
    341 Post(s)

    Default

    Thank you i will give this a go and report back.

    <------------------>



  11. #11
    Join Date
    Jan 2014
    Posts
    51
    Mentioned
    5 Post(s)
    Quoted
    24 Post(s)

    Default

    Thanks for the tutorial very helpful

  12. #12
    Join Date
    May 2016
    Posts
    5
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    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?

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •