Results 1 to 9 of 9

Thread: [Easy] Implementing timeout failsafes

  1. #1
    Join Date
    Nov 2011
    Posts
    1,532
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default [Easy] Implementing timeout failsafes

    In this tutorial I'll go through some simple techniques you can use to set up timeout failsafes, which you will definitely need for your scripts to deal with expected and unexpected failures correctly.

    What is a timeout failsafe?

    It's my term anyway, but the idea is universal. When you do something, chances are you don't know whether you have succeeded or not. But one thing you know for sure is you have have tried it for X amount of time and have yet to succeed, it's very safe to assume that something is wrong.

    Timeouts are used everywhere in computers. From web pages to server interactions, your favourite PC/console multiplayer games, etc. So why don't we use this idea well in scripting as well?

    I'll show a number of ways to set up timeout failsafes.

    The looping method

    The looping method involves trapping you what want to try inside a loop. The loop is set to expire after time T or a number of trials N. If an attempt is successful however, you will break out from the loop and continue your next step in execution.

    The looping method can be done with all loops that can be constructed in pascal. That said, repeat... until, for... to... do, while...do loops, they all work. I will show some examples.

    To wait for the bankscreen to appear, that's what I did in my first script.
    Simba Code:
    MarkTime(t);
      repeat
        wait(200+random(20));
      until (BankScreen or (TimeFromMark(t) > 10000));
      if (TimeFromMark(t) > 10000) then Exit;
      ;//more code here
    That above code says, if the bankscreen is found within 10 seconds, break out from the loop and proceed to next step (deposit, withdraw). If we break out from the loop but we passed the 10secs timeout, something must be wrong. Exit the function and return false

    Actually it's as easy as that. I generally like the use of repeat... until because it is equivalent to a do-while loop, which means you at least attempt whatever you need to check at least once. but while...do and for...to...do works exactly the same, just different coding style.

    To illustrate, let's make up something to see it in action

    Simba Code:
    for x:= 0 to 9 do
      begin
        wait(100);
        if BankScreen then break;
      end;
      if BankScreen then
        ;//do banking
    This has a similar effect by waiting 100ms before asking whether we're in the bank screen for 10 times. If we are already in bankscreen, it will break and it will execute the banking stuff. If after 10 times we still cannot find the bankscreen, we'll have essentially failed and it will not bank the stuff.

    The WaitFunc and WaitFuncEx method

    For people who doesn't enjoy many levels of nesting with repeat...until blocks, another good option is to use the SRL include WaitFunc and WaitFuncEx functions. These functions can be used to implement timeout failsafes as well. Good thing about it is that they look really neat to me, so it's really worth trying.

    Simba Code:
    function WaitFunc(Func: Function: Boolean; WaitPerLoop, MaxTime: Integer): Boolean;

    {Waits for function Func to be true. WaitPerLoop is how often you
    want to call "Func" function.
    Example: "WaitFunc(@BankScreen, 10 + Random(15), 750);" will check if BankScreen
    is open every 10-25th millisecond, for a maximum of 750 milliseconds.
    Notice the '@'. }

    Basically WaitFunc can use any function that takes no arguments and returns a boolean and wait until timeout or the function returns true. A great example of using this would be to check whether the Bankscreen is opened after we interact with the banker.

    Simba Code:
    function DoBanking: boolean;
    begin
      Result:=False;
      if not WaitFunc(@BankScreen, 100, 3000) then Exit;
      DepositAll;
      Result:=True;
    end;

    Basically it's exactly the same as the loop method but you can get a similar effect in one line. Neat isn't it?

    Note with WaitFunc (or WaitFuncEx) you can only implement a timeout block on the true condition. If you need to implement a timeout on the false condition, you need a wrapper. Let's say we're silly enough to do the exact opposite of the above. We would then have to do:

    Simba Code:
    function NotBankScreen: boolean;
    begin
      Result:= not BankScreen;
    end;

    function SillyBanking: boolean;
    begin
      Result:=False;
      if not WaitFunc(@NotBankScreen, 100, 3000) then Exit;
      DepositAll;
      Result:=True;
    end;

    Notice how we have created a function that wraps the result we want to a true condition, and call it in our SillyBanking function.

    Now we move onto the beastly WaitFuncEx function. SRL says:

    Simba Code:
    function WaitFuncEx(Func: string; var Args: TVariantArray; WaitPerLoop, MaxWait: integer): boolean;

    {Calls Func with arguments Args every WaitPerLoop milliseconds for a max of
    MaxWait milliseconds. Func can return any basic types (boolean, string,
    integer). Boolean: Returns it. String: Returns true if string equals 'true'.
    Integer: Returns true unless it e
    .. note::

      by Dgby714 }

    WaitFuncEx is more complicated, but gives even more flexibility by allowing us to specify an array of arguments to pass to the function that we are going to wait for. It accepts the function name as string, an array containing the arguments to which we want to call, and the usual timing arguments.

    Actually WaitFuncEx is quite flexible that it can accept functions returning non-boolean values. It's a nice touch, but I don't use that flexibility at all. I just keep it for checking boolean returns. For timeout failsafes, boolean return values are usually all you need.

    Onto actual example. To use this function we will have to declare a TVariantArray and set the arguments in the TVariantArray before calling WaitFuncEx. Let's take a look at an example:

    Simba Code:
    function InGrandExchange: boolean;
    var trash: integer;
        v: TVariantArray;
    begin
      v:=[trash, trash, 'Grand', 'UpCharsEx', MSX1, MSY1, MSX2, MSY2];
      Result:= WaitFuncEx('FindText', v, 200, 3000);
    end;

    The above shows a function that tells whether we're in Grand Exchange screen with timeout implemented. Silly but fairly solid.

    Note if you need to call waitFuncEx multiple times, you'll have to set the contents of the TVariantArray everytime before you call waitFuncEx.

    An example below:
    Simba Code:
    function BogusCode: boolean;
    var trash: integer;
        v: TVariantArray;
    begin
      v:=[1, 2, 3, 4];
      if WaitFuncEx('whateverfunction', v, 200, 3000) then
      begin
        v:=['a', 'b', 'c', 'd'];
        if WaitFuncEx('anotherfunction', v, 200, 3000) then
          ;//proceed
      end;
    end;

    So that's about it! You should now know how to do timeout failsafes in various manners. Pick whatever you're comfortable with. Feedback is much welcomed.
    Last edited by Er1k; 02-07-2012 at 02:21 AM.

  2. #2
    Join Date
    Dec 2011
    Location
    Nj
    Posts
    2,341
    Mentioned
    1 Post(s)
    Quoted
    18 Post(s)

    Default

    First! ^Reserved

    I like your tutorial, will be very useful to the newbies!

    For the basics of the basics of pascal, try my TuT. ||Photoshop Editing ||MapleResourceDung Script || Book a flight! BuySellTrip

  3. #3
    Join Date
    Dec 2011
    Location
    East Coast, USA
    Posts
    4,231
    Mentioned
    112 Post(s)
    Quoted
    1869 Post(s)

    Default

    Second! Thanks, will use this!
    GitLab projects | Simba 1.4 | Find me on IRC or Discord | ScapeRune scripts | Come play bot ScapeRune!

    <BenLand100> we're just in the transitional phase where society reclassifies guns as Badâ„¢ before everyone gets laser pistols

  4. #4
    Join Date
    Jan 2012
    Posts
    12
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Thanks for the useful tutorial but I don't understand the use of just exiting that function as a failsafe because it will just loop back round and your script will never be terminated as far as I can tell.

  5. #5
    Join Date
    Feb 2012
    Location
    SRL Jail
    Posts
    1,319
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    When i try the marktime (t) one I get an unknown identifier error and marktime (t) is highlighted... any pointers?

  6. #6
    Join Date
    Jan 2010
    Posts
    1,414
    Mentioned
    0 Post(s)
    Quoted
    1 Post(s)

    Default

    That means you haven't made T an integer. So make T an integer and then try it again.

    Quote Originally Posted by JOEbot View Post
    When i try the marktime (t) one I get an unknown identifier error and marktime (t) is highlighted... any pointers?
    Simba Code:
    (* Main *)

    repeat
      WriteLn('I am an idiot!');
    until(False);

  7. #7
    Join Date
    Apr 2008
    Location
    Marquette, MI
    Posts
    15,252
    Mentioned
    138 Post(s)
    Quoted
    680 Post(s)

    Default

    Very nice! I used timeout failsafes in almost all the new randoms solvers. They're also used EVERYWHERE in MSI. If you wanted, you could add this method to the looping. You don't have to use markTime and timeFromMark as they're SRL functions (not everyone has SRL included).
    Simba Code:
    t := (getSystemTime + 10000);
      while (getSystemTime < t) do
      begin
        if (condition) then
          break;

        wait(50);
      end;

      repeat
        if (condition) then
          break;
      until(getSystemTime > t);
    Just a couple more options.

  8. #8
    Join Date
    May 2008
    Location
    the world 0_o
    Posts
    150
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Will be adding this to list of tuts! Great stuff!

  9. #9
    Join Date
    Feb 2012
    Location
    SRL Jail
    Posts
    1,319
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Quote Originally Posted by RISK View Post
    That means you haven't made T an integer. So make T an integer and then try it again.
    Thanks, since reading the tutorial, Coh3n helped me with that.

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
  •