1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Tutorial The Usage of Delays

Discussion in 'Tutorials & Resources' started by Yasper, Jan 20, 2017.

  1. Yasper

    Yasper Pleased to help

    Joined:
    Jan 17, 2017
    Messages:
    34
    Likes Received:
    31
    I've been hanging around in the slack for a couple of days now and I feel like not enough people know how to properly perform 'sleeps' or delays as they are called in the Execution class.

    What are delays

    I assume most of you know whet delays are but just to make sure; delays are sleeps performed in your bots to ensure there is enough time between consecutive actions and to ensure an action is completed properly. These are the two cases I will describe to demonstrate the usage of delays.

    The Execution class

    The Execution class (see javadocs) contains all the methods we will require to properly set up delays. The methods you should use are (in most cases):
    • delayUntil(Callable<java.lang.Boolean> condition, int minTimeout, int maxTimeout)
    • delayUntil(Callable<java.lang.Boolean> condition, Callable<java.lang.Boolean> reset, int minTimeout, int maxTimeout)
    • delayWhile(Callable<java.lang.Boolean> condition, Callable<java.lang.Boolean> reset, int minTimeout, int maxTimeout)
    • delayWhile(java.util.concurrent.Callable<java.lang.Boolean> condition, int minTimeout, int maxTimeout)
    For the delayWhile methods I must note that I myself think it's usually more appropriate (just convention wise) to use a DeMorgan negated delayUntil call:
    (so (!booleanValue1 && !booleanValue2) would become (booleanValue1 || booleanValue2))
    However there may be cases where delayWhile is more appropriate (eventhough I cannot think of any right now)

    How to use delayUntil

    As said in the previous section, you can in most cases swap out delayWhile's for delayUntil's and therefore I will just explain how delayUntil should be used because it should give a good enough explanation of how delayWhile should be used.

    The first thing I want to note is that I've left out the methods without a timeout and with a static timeout. Why? Because:

    1. No timeout is a terrible idea, if anything goes wrong causing your condition to not return true your bot can't exit the loop it's stuck in which can allow the bot to 'repair' itself. Never delay without timeouts!
    2. A static timeout is not necessarily bad, however since we're writing bots here and we don't want Jagex to discover that there is a static aspect to our bots we should try to minimize these aspects wherever we can; delays are an example.
      The usage of minTimeout and maxTimeout even if they are only 100ms apart can make a huge difference.
    Now on to how to actually use the proper delay methods. Say we want to select a tinderbox in our inventory, and then want to wait until this action has actually happened. Why? Because if we don't the bot will mass spam our tinderbox before it is registered as selected probably causing many problems and looking very in-human-like. How? Here is an example piece of code of how such a method could look like:

    Code (Text):
    1.  
    2. SpriteItemQueryResults result = Inventory.getItems("Tinderbox");
    3. if (!result .isEmpty()) {
    4.     SpriteItem tinderbox = result.first();
    5.     if (tinderbox.click()) {
    6.         Execution.delayUntil(() -> Inventory.getSelectedItem() != null, 800, 1200);
    7.     }
    8. }
    9.  
    Ignore the actual item retrieval but let's focus on the delay method. Now as you can see if you're familiar with Java 8 I am using Lambda expressions so if you're not familiar with those please read up on them real quick and then continue reading here.

    So as you can see (and should probably understand by now) this delayUntil method will wait for an inventory item to be selected, but if the time we are waiting for exceeds the randomly between 800 and 1200 selected timeout it will exit the waiting loop and our bot will try again. If our bot had for example miss clicked on our tinderbox and no item had been selected, the bot would have waited forever if it didn't have a timeout.

    Now another useful thing to note, is that the delay methods will return true if they were exited because the condition was met, and will return false if delay was exited because the timeout was exceeded. This can be quite useful in situations where you need to know if an action was executed correctly. For example in our code we could use this to decide whether or not to click a log next:

    Code (Text):
    1.  
    2. SpriteItemQueryResults result = Inventory.getItems("Tinderbox");
    3. if (!result .isEmpty()) {
    4.     SpriteItem tinderbox = result.first();
    5.     if (tinderbox.click()) {
    6.         if (Execution.delayUntil(() -> Inventory.getSelectedItem() != null, 800, 1200)) {
    7.             //The condition was met
    8.             //Click our log here
    9.         }
    10.     }
    11. }
    12.  
    I'll add a quick explanation of what the extra reset parameter does for, even though it's probably self explanatory. The reset callable will be checked every time the delay condition is checked to see if the timeout needs to be reset, if your reset callable returns true the 'internal run time' of the delay will be set to 0 and if it returns false it will simply not alter anything.

    What does the delayUntil method look like in code?

    If you want to be able to visualize what this method would look like in code, here's a short snippet that will probably be very similar to the actual implementation of Runemate:

    Code (Text):
    1.  
    2. public static boolean delayUntil(Callable<java.lang.Boolean> condition, Callable<Boolean> reset,
    3.                                  int frequency, int minTimeout, int maxTimeout) {
    4.     Random random = new Random();
    5.     int timeout = (int) (minTimeout + random.nextDouble() * maxTimeout);
    6.     try {
    7.         for (int timer = 0; timer < timeout; timer += frequency) {
    8.             //If the condition returns true, make the delay return true
    9.             if (condition.call())
    10.                 return true;
    11.  
    12.             //If the reset callable returns true, reset the timer 'i' to 0;
    13.             if (reset.call())
    14.                 timer = 0;
    15.  
    16.             //Perform the frequency delay
    17.             Execution.delay(frequency);
    18.            
    19.             //Might be some actions here RuneMate wants to ensure that happen
    20.         }
    21.     } catch (Exception e) {
    22.         e.printStackTrace();
    23.     }
    24.     //The condition did not return true therefore the condition must not have s
    25.     return false;
    26. }
    27.  

    Please use delays from now on, and make sure to use them properly!

    Also props to me not using 'script bot'
     
  2. Savior

    Savior Java Warlord

    Joined:
    Nov 17, 2014
    Messages:
    4,906
    Likes Received:
    2,748
    Nice guide especially for beginners
     

Share This Page

Loading...