Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
Buy OSRS Gold

Sell OSRS Gold
Encoded

Task Framework Tutorial

Recommended Posts

Task Framework

A set is a collection that contains no duplicate elements.
The task framework is a sorted set of tasks where each task performs a specific action.
Tasks are sorted in descending order by priority.

Each task has a validate and execute method. The validate method returns a boolean that decides whether or not the task should execute. The execute method should be called when the validate method returns true.

Priority determines which task will be returned by the getValidTask method if two or more tasks' validate methods are to return true.
If you find yourself having two tasks' validate methods returning true with the same priority level, add additional priority levels or modify the tasks' validate methods.

TaskSet is the aggregate of all the tasks. Tasks are sorted upon being added to the set. The getValidTask method returns the highest priority task with a validate method that returns true or null if no task is valid.

Don't forgot to change the packaging if you are copy/pasting the following code.

Task.java
package scripts.api.script.frameworks.task;

public interface Task {

    Priority priority();

    boolean validate();

    void execute();

}
Priority.java
package scripts.api.script.frameworks.task;

public enum Priority {

    HIGH,
    MEDIUM,
    LOW,
    NONE

}
TaskSet.java
package scripts.api.script.frameworks.task;

import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;

public class TaskSet extends TreeSet<Task> {

    public TaskSet() {
        super(Comparator.comparing(Task::priority).thenComparing(Comparator.comparing(task -> task.getClass().getName())));
    }

    public TaskSet(Task... tasks) {
        super(Comparator.comparing(Task::priority).thenComparing(Comparator.comparing(task -> task.getClass().getName())));
        addAll(tasks);
    }

    public TaskSet(Comparator<? super Task> comparator) {
        this();
    }

    public TaskSet(Collection<? extends Task> c) {
        this(c.toArray(new Task[c.size()]));
    }

    public TaskSet(SortedSet<Task> s) {
        this(s.toArray(new Task[s.size()]));
    }

    public boolean addAll(Task... tasks) {
        return super.addAll(Arrays.asList(tasks));
    }

    public Task getValidTask() {
        for (Task task : this) {
            if (task.validate()) {
                return task;
            }
        }
        return null;
    }

}
Example Task
public class ExampleTask implements Task {

    @Override
    public String toString() {
        return "Example Task";
    }

    @Override
    public Priority priority() {
        return Priority.LOW;
    }

    @Override
    public boolean validate() {
        return false;
    }

    @Override
    public void execute() {

    }
    
}
Example Script run Method
@Override
public void run() {
    TaskSet tasks = new TaskSet(new ExampleTask());
    while (isRunning) {
        Task task = tasks.getValidTask();
        if (task != null) {
            status = task.toString();
            task.execute();
        }
    }
}
Edited by Encoded
  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

@Encoded So can you tell me how much stuff you should include in each task? Because it seems counter-intuitive to do a new task for every small action if your script is big.

So for example if I want my script to wc and bank before doing another task that could be a quest or other skill. 

I can include my whole wcing/banking code in a single task?

Or should I spread my wcing into various tasks. (one for walking, one for cutting, one for banking)

 

Share this post


Link to post
Share on other sites
5 hours ago, Enano25 said:

@Encoded So can you tell me how much stuff you should include in each task? Because it seems counter-intuitive to do a new task for every small action if your script is big.

So for example if I want my script to wc and bank before doing another task that could be a quest or other skill. 

I can include my whole wcing/banking code in a single task?

Or should I spread my wcing into various tasks. (one for walking, one for cutting, one for banking)

 

I would separate walking, banking, and wcing into 3 tasks. They each need to be done when different conditions are met, and should be assigned different priorities based on the conditions met.

 

Share this post


Link to post
Share on other sites
8 hours ago, Azuz53487 said:

Wasn't this posted somewhere? Can't remember where I saw it.

I had it in the snippets section, but I rewrote it and moved it to tutorials since that's where the other frameworks are posted.

 

7 hours ago, Enano25 said:

@Encoded So can you tell me how much stuff you should include in each task? Because it seems counter-intuitive to do a new task for every small action if your script is big.

So for example if I want my script to wc and bank before doing another task that could be a quest or other skill. 

I can include my whole wcing/banking code in a single task?

Or should I spread my wcing into various tasks. (one for walking, one for cutting, one for banking)

You can break it down as much as you want. I've even made scripts where a Task will have its own TaskSet within it.
If you want to chain TaskSets together, I suggest adding a stopping condition and then creating a linked list of TaskSets so that you can get the next TaskSet once the current's stopping condition is met. That's what I currently do, but this post is about the basics so I didn't include that.

Edited by Encoded
  • Like 1

Share this post


Link to post
Share on other sites
3 hours ago, Encoded said:

I had it in the snippets section, but I rewrote it and moved it to tutorials since that's where the other frameworks are posted.

 

You can break it down as much as you want. I've even made scripts where a Task will have its own TaskSet within it.
If you want to chain TaskSets together, I suggest adding a stopping condition and then creating a linked list of TaskSets so that you can get the next TaskSet once the current's stopping condition is met. That's what I currently do, but this post is about the basics so I didn't include that.

Aha, I see! Great! :)

Share this post


Link to post
Share on other sites

Lets say I have a Mining task...should it be endless mining... or a task set of MineOre tasks. And what if i want to keep a record of total Ore Mined... How would you recommend keeping track of that value outside of the shortlived task class, without creating a major spaghetti mess of code..

With this system I currently have..

Mining/tasks/MineOre.java

Mining/MiningScript.java

 

Unsure how to convey success/fail information from MineOre's execution to mining script

It's really late maybe my mind is just a bit slow

Edit: ok think I understand now, fail shouldn't matter the task is about completing a task in programming sense, not the same as game sense..

Did it mine ore succesfully? No... But was the mine ore TASK completed? Yeah I guess.. makes me wonder whether it should be named AttemptMineOre would probably make more sense

 

Also static counter in another class will solve keeping track of missed ores, will give it a go tomorrow 

Edited by pudsta19

Share this post


Link to post
Share on other sites

Great tutorial @Encoded

Have you experimented with threading in this framework?

Interrupting the current the task for example based on a certain priority of a task that is being validated in another thread?

EG Person appears near you in wilderness triggers task that stops the current task.

Edited by Zapcrack
  • Like 1

Share this post


Link to post
Share on other sites
On 10/28/2019 at 4:21 PM, Zapcrack said:

Great tutorial @Encoded

Have you experimented with threading in this framework?

Interrupting the current the task for example based on a certain priority of a task that is being validated in another thread?

EG Person appears near you in wilderness triggers task that stops the current task.

Instead of dealing unnecessarily with messy threading logic, you could have a service that houses the logic for determining this state you speak of.

IE. determining if there is player next to you. You could then place a dependency on this service for all tasks that are interested in this specific state.

Basically I shouldn't be mining in the wilderness if there is a pker next to me. The mining node has a dependency on the service to determine if there is a pker next to you.

Based on the node framework design this conditional logic should be called from within your mining validator implementation.

Further down stream, while mining the rock in your dynamic sleeping/sleeping method while mining, this state should be checked, and if found the task should return/terminate.

The main loop should then pick up your node that corresponds with what actions you would want to take in this scenario. IE. teleport, run, etc.

 

Also as far as the priority system goes, I typically see this type of structure being used improperly in place of proper conditional/state logic.

Instead of clearly defining the state/validator for the node, people start using a priority system as an override.

Where it really breaks down is when you have a bunch of nodes in a "high" priority, but you want one in an even higher priority, so you add "very high" etc.

to the priority list/enum. 

Debugging this type of thing could become difficult as the states are unclear mixing in the element of priority on top of poor state logic.

Warfront1

  • Like 1

Share this post


Link to post
Share on other sites
On 10/28/2019 at 9:21 PM, Zapcrack said:

Great tutorial @Encoded

Have you experimented with threading in this framework?

Interrupting the current the task for example based on a certain priority of a task that is being validated in another thread?

EG Person appears near you in wilderness triggers task that stops the current task.

You could store your tasks, or actions how i like to call them, in a LIFO structure, like a stack. 
 

public class Task {

    private Stack<InteractableAction> actionStack = new Stack<>();
    private Goal goal;

    public Task(InteractableAction baseAction, Goal goal){
        actionStack.push(baseAction);
        this.goal = goal;
    }

    public boolean completed(){
        return goal.completed();
    }

    public void execute(){
        InteractableAction currentAction = actionStack.peek();
        if(currentAction == null)
            return;
        if(!currentAction.completed()){
            InteractableAction response = (InteractableAction) currentAction.execute();
            if(response != null){
                actionStack.push(response);
            }
        } else {
            actionStack.pop();
        }
    }

    public void end(){
        actionStack = null;
    }

}
public abstract class InteractableAction {

    /*
        return a new action if a new action is required, (e.g walking)
        return null if no new action is required.
     */
    public abstract Action execute();

    /*
        return true if the action has been completed,
        return false otherwise.
     */
    public abstract boolean completed();

}



In this case your base action would be a mining action which is used to mine your rocks, the mining action returns a bankaction when your inventory is full, the bank action returns a walking action when its completed, etc.

in your mining action, you could add a player check, which if true returns a fleeing action to the action stack in the task. this fleeing action has to be completed before any other action can get executed

Share this post


Link to post
Share on other sites
3 hours ago, gef30 said:

You could store your tasks, or actions how i like to call them, in a LIFO structure, like a stack. 
 

public class Task {

    private Stack<InteractableAction> actionStack = new Stack<>();
    private Goal goal;

    public Task(InteractableAction baseAction, Goal goal){
        actionStack.push(baseAction);
        this.goal = goal;
    }

    public boolean completed(){
        return goal.completed();
    }

    public void execute(){
        InteractableAction currentAction = actionStack.peek();
        if(currentAction == null)
            return;
        if(!currentAction.completed()){
            InteractableAction response = (InteractableAction) currentAction.execute();
            if(response != null){
                actionStack.push(response);
            }
        } else {
            actionStack.pop();
        }
    }

    public void end(){
        actionStack = null;
    }

}
public abstract class InteractableAction {

    /*
        return a new action if a new action is required, (e.g walking)
        return null if no new action is required.
     */
    public abstract Action execute();

    /*
        return true if the action has been completed,
        return false otherwise.
     */
    public abstract boolean completed();

}



In this case your base action would be a mining action which is used to mine your rocks, the mining action returns a bankaction when your inventory is full, the bank action returns a walking action when its completed, etc.

in your mining action, you could add a player check, which if true returns a fleeing action to the action stack in the task. this fleeing action has to be completed before any other action can get executed

This looks look a tree framework to me, i have been trying to experiment with the Task framework. I might give this a go after i get a working script running with the Task Framework.

 

11 hours ago, warfront1 said:

Instead of dealing unnecessarily with messy threading logic, you could have a service that houses the logic for determining this state you speak of.

IE. determining if there is player next to you. You could then place a dependency on this service for all tasks that are interested in this specific state.

Basically I shouldn't be mining in the wilderness if there is a pker next to me. The mining node has a dependency on the service to determine if there is a pker next to you.

Based on the node framework design this conditional logic should be called from within your mining validator implementation.

Further down stream, while mining the rock in your dynamic sleeping/sleeping method while mining, this state should be checked, and if found the task should return/terminate.

The main loop should then pick up your node that corresponds with what actions you would want to take in this scenario. IE. teleport, run, etc.

 

Also as far as the priority system goes, I typically see this type of structure being used improperly in place of proper conditional/state logic.

Instead of clearly defining the state/validator for the node, people start using a priority system as an override.

Where it really breaks down is when you have a bunch of nodes in a "high" priority, but you want one in an even higher priority, so you add "very high" etc.

to the priority list/enum. 

Debugging this type of thing could become difficult as the states are unclear mixing in the element of priority on top of poor state logic.

Warfront1

Yeah this seems like a cleaner approach compared to my threading idea, will try this out for my script. Thanks!

Share this post


Link to post
Share on other sites
19 hours ago, Zapcrack said:

This looks look a tree framework to me, i have been trying to experiment with the Task framework. I might give this a go after i get a working script running with the Task Framework.

Its not a tree framework, tho it does not use priority. By using a LIFO structure like a stack, you can push a new task/action to your stack at any given time, which has to be executed first. Since Stack.peek returns the newest entry to the stack.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Our picks

    • Hi everyone,

      I'd like to thank everyone for their patience in this transition period. Since last week, we've worked out the remaining bugs with this integration.

      Some users have still been having issues with connecting their forums account to their Auth0 account. To resolve this, we've imported all forums accounts into Auth0.

      Unfortunately, the accounts which were imported today were using an unsupported password hashing algorithm. Hence, random passwords were set during the import.

      What does this mean for me?

      If you've previously linked your forums account to your Auth0 account, you don't have to do anything. Nothing changes for you.


      If you haven't logged in via our new login yet,

      Try logging in with your forums email address and the last password you used


      If you are unable to login, please use the "Forgot password" tool on the login page:
      Follow the instructions to reset your password
       
      • 1 reply
    • Hello everyone,

      Last week we tried to roll out Auth0 Login, but we lost that battle. Now it's time to win the war!

      Important changes

      When logging into the client, you'll now have to enter your Auth0 account credentials instead of your forums credentials

      Note: 2FA is still handled through your forums account (for the time being)



      Changes for existing users

      You'll have to link your Auth0 account to your forums account here: https://tribot.org/forums/settings/login/?service=11


      Auth0 accounts have been created for most existing users. Please use your forums email address and password to login.



      Important notes

      Make sure to verify your email address upon creating a new Auth0 account


      When we mention your Auth0 account, we mean your account used for auth.tribot.org as displayed below
      • 71 replies
    • To better support the upcoming changes (TRiBot X, new repository), we're switching our login handler to Auth0. Instead of logging in with the standard form, you'll now be required to login through our Auth0 application.

      All existing accounts which have been used within approximately the past year have been imported into Auth0 using the same email and password combination which has been stored on the forums.

      What does this mean for users?

      Your account credentials are now even more securely stored


      You'll be able to login via Facebook, Google, and others in the future


      Is there anything users have to do differently now?

      Existing users: You'll have to login with the standard login, open your Account Settings, then link your Auth0 account


      New users: You'll be redirected to our Auth0 app (auth.tribot.org) where you'll be able to create an account


      Why was this change made?

      The new apps we are creating (such as the new repository) aren't able to use the forums to handle user logins


      To centralize all user accounts in one area


      To ensure that the client login doesn't go down when the forums are having problems


      To speed up our development


      Other considerations

      There's no documentation or official support for using Invision Community combined with Auth0, so there are still a few kinks we're working out


      We're in the works of creating an account management panel specifically for Auth0 accounts (ETA August)


      It's not possible to change email addresses for the time being (this will be resolved this August)


      Changing passwords is a weird process for the time being. To change your password, you'll have to use the "Don't remember your password" tool on the Auth0 login page
      • 11 replies
    • Over the past month, we've been working hard on TRiBot's new repository - a much needed update. This change has been deemed necessary for TRiBot X, and will allow us to really speed up development of all aspects of TRiBot.

      Today we are going to share what we've been working on!


      Now you must be wondering what kind of features the new repository will have.... well, you'll have to be patient for a little while longer. We're still figuring out various technical aspects so we can't provide answers to all possible questions. We're also focusing on development rather than writing about it so that everyone can get access to our latest developments at lightning speed. I will however answer a few users' questions.

      We're planning on a release of this early to mid August, giving users some goodies before TRiBot X's release.

      Thank you all for being patient. I hope everyone is excited as much as I am!
      • 17 replies
    • Over the past few months, I’ve been working diligently on a new project - TRiBot X. Everything has been written from the ground up, with all of the best practices of software engineering. Every aspect of TRiBot has been re-imagined to support three main goals: flexibility, useability, and reliability.
      • 50 replies
  • Recently Browsing   1 member

×
×
  • Create New...