Jump to content
Frequently Asked Questions
  • Are you not able to open the client? Try following our getting started guide
  • Still not working? Try downloading and running JarFix
  • Help! My bot doesn't do anything! Enable fresh start in client settings and restart the client
  • How to purchase with PayPal/OSRS/Crypto gold? You can purchase vouchers from other users
  • A Simplier ParentNode Framework


    Mad

    Recommended Posts

    Sometimes script writers are developing large scripts and would like an easier way to organize their scripts into deeper categories, than just regular tasks. Well this is possible with a ParentNode framework.

     

    This tutorial is mostly conceptual, it does show the code, but it isn't written in Java. You're welcome to copy it, but I wrote this to show conceptually what it should look like, and you can easily replicate in Java.

     

    Assuming you understand the basic Node framework, lets get started:

     

     

     

    Task Interface

     

    The first thing you'll need to make is an interface to hold all the children, and a condition on when to validate.

    You can name this anything you want, but I chose to name it Task.

     

     

     

     

    package org.maddev.util.task
    
    interface Task {
    
        val validate : Boolean;
        val childen : Set<TaskChild>; //Use a TreeSet if you need priority.
    
    }
    
     

    This interface contains a Set full of TaskChild objects, which we will define later on.

    Since Sets cannot contain duplicates, this makes it the proper data structure to use.

     

    TaskChild Interface

     

    We've written our Task interface, now we need to write an interface for the actual children.

    This interface needs to hold two methods, a validate and an execute.

    You can also add status if you want, or anything you desire.

     

    interface TaskChild {
    
        val validate : Boolean;
        fun execute() : Boolean;
        val status : String;
    
    }
    
     

    As you can see, we have three separate methods, a validate, execute, and a status.

    My execute returns a Boolean just because I prefer it like that, but you can return anything you want.

     

    Adding Child Tasks

     

    We now need to add the child tasks to our parent task class.

     

    I went ahead and created a ParentTask class called Test

     

    class Test : Task {
    
        override val validate: Boolean
            get() = true;
    
        override val childen: Set<TaskChild>
            get() = setOf(TestChild())
    
    }
    
    Inside of it, we have our validate, and then our Set of TaskChildren, which contains the one we are about to create.

     

    So, lets create a class called TestChild and implement TaskChild

     

    class TestChild : MadFighter(), TaskChild {
    
        override val status: String
            get() = "Testing."
    
        override fun execute(): Boolean {
            return true;
        }
    
        override val validate: Boolean
            get() = true;
    
    }
    
    Easy enough, we have our status, execute, and our validate.

     

    TaskWrapper

     

    Now we need to write a wrapper to get us the best child node to execute.

     

    First thing we need to do is define a Set of "Task" objects (The parent tasks), that we will store.

     

    private var tasks = setOf<Task>();
    
     

    Then we will need to pass our tasks in the constructor of our TaskWrapper and add them to the set.

    I'm using a vararg in the constructor to pass the tasks.

     

    class TaskWrapper(vararg tasks : Task) {
    
     

    Now in the constructor/initializer:

     

    init {
            this.tasks = tasks.toHashSet();
    }
    
    
     

    GetValidParent

     

    We will now need a method to get the first Task object (The parent tasks) that is validated.

    If you're using Java 7, you may have to use a for loop. If you're using Java 8, you can use a stream,

    which is very similar to this syntax.

     

    fun getValidParent() : Task? {
            return tasks.filter { x -> x.validate; }.firstOrNull();
    }
    
     

    Pretty easy to see whats happening here, we are looping through the set we created earlier and checking for the first one that is validated.

    Also we are returning null in-case one doesn't validate, so we don't get any errors.

     

    GetValidChild

     

    Perfect, we have a method that returns the best parent, now all we have to do is iterate through the children of the parent and find the best one.

     

    fun getValidChild() : TaskChild? {
            return getValidParent()!!.childen.filter { x -> x.validate }.firstOrNull();
     }
    
     

    Cool, now we have the best child, all to do is execute it.

     

    fun execute() : Unit {
            getValidChild()!!.execute();
    }
    
     

     

     

    Full TaskWrapper Class

     

    class TaskWrapper(vararg tasks : Task) {
    
        private var tasks = setOf<Task>();
    
        init {
            this.tasks = tasks.toHashSet();
        }
    
        fun getValidParent() : Task? {
            return tasks.filter { x -> x.validate; }.firstOrNull();
        }
    
        fun getValidChild() : TaskChild? {
            return getValidParent()!!.childen.filter { x -> x.validate }.firstOrNull();
        }
    
        fun execute() : Unit {
            getValidChild()!!.execute();
        }
    
    }
    
     

    Now all you need to do is call the TaskWrapper class in your onStart and add the Task objects (The task parents), then run the execute method in your loop. Super simple.

     

    open class MadFighter : AbstractScript() {
    
        val tasks = TaskWrapper(Test());
    
        override fun onLoop(): Int {
            tasks.execute();
            return Calculations.random(455,622);
        }
    
    }
    
    Thanks for reading, hope this helps :)
    Link to comment
    Share on other sites

    It would probably be best to give a Java implementation, as Kotlin has a lot of new syntax others may not be aware of.

    Also, a neat feature in Kotlin is the elimination of null references (thank god). Although none of the new systems really apply here, you still don't need to be using null. Here are a few pointers:

    Parent nodes seem to be a necessity. If there is no parent nodes, there is no script. Adding parent nodes is the responsibility of the scripter - if they don't add a node, an error should occur. This means instead of returning null, simply call first() and let the NoSuchElementException inform the user:

    fun getValidParent() : Task {
        return tasks.filter { it.validate }.first()
    }

    Task no longer has to be nullable, which means you can remove the double bang when calling members from it:

    fun getValidChild() : TaskChild {
        return getValidParent().childen.filter { it.validate }.first()
    }

    Null could be eliminated from this for the same reason: if no child nodes are specified for a parent, that's the scripter's fault, so let the NoSuchElement fall through. Now you can remove the double bang from that call:

    fun execute() {
        getValidChild().execute()
    }

    As you can tell, semicolons are considered "noise" and are not needed. Functions are type Unit by default.

    And look at that, no nulls :)

    Link to comment
    Share on other sites

    Its good for programmers to be able to take psuedo-code and turn it into a project in their chosen language, and that is what I provided, unless they are using kotlin. Its extremely easy to use Java to recreate this with just a basic for loop.

    Link to comment
    Share on other sites

    Its good for programmers to be able to take psuedo-code and turn it into a project in their chosen language, and that is what I provided, unless they are using kotlin. Its extremely easy to use Java to recreate this with just a basic for loop.

    That's true, but this isn't really pseudo-code. Those familiar with Ruby have a totally different view on the double bang, and may interpret it differently than how it's used here.

     

    It's just a suggestion. I actually find it pretty neat that you're giving scripters a taste of Kotlin - it's an amazing language. I just feel a lot of scripters may not make use of this strictly because they are not familiar with the syntax, and would rather not be bothered learning a new language. Ik, you could say "then they don't deserve this system", but really no one here should be forced to learn new syntax that they may never apply anywhere

    Link to comment
    Share on other sites

    Archived

    This topic is now archived and is closed to further replies.

    ×
    ×
    • Create New...

    Important Information

    We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.