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

Sell OSRS Gold
Sign in to follow this  
Wastedbro

[Intermediate] [Kotlin] Writing Your First Practical Kotlin Script

Recommended Posts

With the latest few updates, the Tribot client now supports scripts written in the Kotlin Programming Language and the repository supports Kotlin script submissions.

 

Why Kotlin?

Kotlin is a language that originally started development in 2010. Version 1.0 was released in 2016 and just a year later, Google announced first-class Kotlin support for Android development. As of May 2019, it is Google's preferred language for Android development.

Kotlin is a language that runs on the JVM, like Java. It can run virtually anywhere Java can, as it compiles to very similar bytecode. Not only is it similar, but it's fully compatible. Meaning, you can call Java code from Kotlin, and you can call Kotlin code from Java. 

Many people dislike Java. According to the StackOverflow 2019 Developer survey, Java is the 10th most dreaded language, whereas Kotlin is the 4th most loved language. The entire idea behind Kotlin is to provide a language similar to Java, but easier to read, faster to write, more expressive, as well as more feature-packed.

 

Prerequisites

  • Either moderate Java knowledge or basic Kotlin knowledge
  • IntelliJ IDEA
  • A basic understanding of how to create and configure IntelliJ projects
  • A basic understanding of the Tribot API
  • And understanding of some common programming paradigms
    • Variables
    • Methods
    • Classes
    • etc

 

A Tribot Script Example

One of the best ways to learn something is to do it. There are thousands of resources that can teach you Kotlin, Java, IntelliJ, etc all better than I can. Please utilize those for general knowledge. For this guide, I'm going to focus on practical Kotlin within the Tribot environment.

Let's create a Woodcutting script that cuts yews near the Grand Exchange and banks them.

 

1. Setup

Open up an IntelliJ project. I'll name mine "KotlinWcIntroScript". Once created, add the Tribot JAR as a dependency and create a package under "src" called "scripts".

2. The Script Skeleton

Time to create our first Kotlin class. Right click your "scripts" package and select New -> Kotlin File/Class. Let's name it "GrandYewCutter" and make it a class.

image.thumb.png.fcfd88aa5a26dec42537e6a7abb92f1c.png

 

Now that we have our main script class, let's add some skeleton code:

package scripts

import org.tribot.script.Script
import org.tribot.script.ScriptManifest

@ScriptManifest(name = "Grand Yew Cutter", authors = ["Wastedbro"], category = "Woodcutting", description = "Local version")
class GrandYewCutter : Script()
{
    override fun run()
    {
        
    }
}

Not very different than what you'd see in Java. After all, Kotlin is a very Java-like language.

 

2. Defining Some Constants

Every script needs some data that never needs changed during runtime. Things like tiles, areas, IDs, names, etc. Let's define them!

First, create a new Kotlin class. This time, we'll call it "GrandConstants". Instead of a class, though, we'll be adding an object. See, in Kotlin, there are no static variables. Instead, we have Kotlin Object Definitions, which are basically Singleton classes that can only have one instance. Let's show an example:

package scripts

import org.tribot.api2007.types.RSArea
import org.tribot.api2007.types.RSTile

/**
 * This is a singleton object that contains all of the constant data for the script
 * In Kotlin, using "static" directly is discouraged, and can only be done through annotations
 * Kotlin objects are inherently thread-safe
 */
object GrandConstants
{
    // Using "val" means that the member cannot be changed. In Java, we call this "final". In C++, it's "const"
    // Also notice that we don't need to specify the "type" of the variable. This is called type inference
    val treeAreaTile = RSTile(3207, 3502, 0)
      
    // If we wanted to explicitly state the type, we could have done "val treeAreaTile: RSTile = RSTile(3207, 3502, 0)"

    // Notice how we can create objects by just specifying the Type. There is no "new" keyword in Kotlin

    val treeArea = RSArea(
        arrayOf(
            RSTile(3201, 3506, 0),
            RSTile(3201, 3501, 0),
            RSTile(3205, 3501, 0),
            RSTile(3207, 3499, 0),
            RSTile(3207, 3498, 0),
            RSTile(3226, 3498, 0),
            RSTile(3225, 3507, 0)
        )
    )

    // Also, notice that there are no semi-colons. They are optional in Kotlin, and not recommended

    val bankTile = RSTile(3168, 3489, 0)

    // By default, all Kotlin fields are public, meaning that other classes can access them
    val bankArea = RSArea(bankTile, 10)
}

It's also important to note that these are not public fields. They're actually Kotlin Properties, and you can override their getters and setters in the future if you need to. Unlike Java, you never have to write getter and setter methods in Kotlin.

3. Writing the Overall Script Logic

Time for some logic! For this guide, I'm going to use basic if-else statements. There's no reason to get into logic frameworks at this point. So let's see some code!

package scripts

import org.tribot.api.General
import org.tribot.api2007.Banking
import org.tribot.api2007.Inventory
import org.tribot.api2007.Player
import org.tribot.api2007.WebWalking
import org.tribot.script.Script
import org.tribot.script.ScriptManifest

@ScriptManifest(name = "Grand Yew Cutter", authors = ["Wastedbro"], category = "Woodcutting", description = "Local version")
class GrandYewCutter : Script()
{
    // By using "var", we are saying that this variable can be changed
    private var shouldRun = true

    override fun run()
    {
        while (shouldRun)
        {
            if (isInBank())
            {
                if (Inventory.isFull())
                {
                    if (Banking.isBankScreenOpen())
                    {
                        // Do Banking
                    }
                    else
                    {
                        // Open Bank
                    }
                }
                else
                    WebWalking.walkTo(GrandConstants.treeAreaTile)
            }
            else if (isInTreeArea())
            {
                // If the inventory is full, we need to go to the bank
                if (Inventory.isFull())
                    WebWalking.walkTo(GrandConstants.bankTile)
                else
                {
                    // If we're chopping, just sleep
                    if (isChopping())
                        General.sleep(400, 800)
                    else
                    {
                        // Click Tree
                    }
                }
            }
            else
            {
                // Eh, if we don't where we're at, go to the bank
                WebWalking.walkTo(GrandConstants.bankTile)
            }
        }
    }


    // Methods with a single statement can be condensed into 1 line, and don't require a type specifier
    fun isInBank() = GrandConstants.bankArea.contains(Player.getRSPlayer())
    fun isInTreeArea() = GrandConstants.treeArea.contains(Player.getRSPlayer())

    // Since the player will never be doing anything other than walking, chopping, and banking, we can assume that any animation is the chopping animation
    fun isChopping() = Player.getAnimation() != -1
}

 

4. Filling in the Blanks

Now that we have our overall logic defined, let's add the code for object clicking and banking.

package scripts

import org.tribot.api.General
import org.tribot.api.Timing
import org.tribot.api2007.*
import org.tribot.script.Script
import org.tribot.script.ScriptManifest

@ScriptManifest(name = "Grand Yew Cutter", authors = ["Wastedbro"], category = "Woodcutting", description = "Local version")
class GrandYewCutter : Script()
{
    // By using "var", we are saying that this variable can be changed
    private var shouldRun = true

    override fun run()
    {
        while (shouldRun)
        {
            if (isInBank())
            {
                if (Inventory.isFull())
                {
                    if (Banking.isBankScreenOpen())
                    {
                        // Let's just assume the axe is equipped
                        if (Banking.depositAll() > 0)
                        {
                            General.sleep(250, 800) // Delay in between actions
                            if (Banking.close())
                                General.sleep(250, 500)
                        }
                    }
                    else
                        Banking.openBank()
                }
                else
                    WebWalking.walkTo(GrandConstants.treeAreaTile)
            }
            else if (isInTreeArea())
            {
                // If the inventory is full, we need to go to the bank
                if (Inventory.isFull())
                    WebWalking.walkTo(GrandConstants.bankTile)
                else
                {
                    // If we're chopping, just sleep
                    if (isChopping())
                        General.sleep(400, 800)
                    else
                        clickTree()
                }
            }
            else
            {
                // Eh, if we don't where we're at, go to the bank
                WebWalking.walkTo(GrandConstants.bankTile)
            }
        }
    }

    /**
     * A method that clicks a tree and returns a boolean indicating a successful click
     */
    fun clickTree(): Boolean
    {
        // If an object can be null, it's considered a "Nullable Type". Kotlin won't let you use it without nullchecking it
        val treeObject = Objects.findNearest(20, "Yew").firstOrNull()

        // The question mark actually nullchecks the treeObject for us. If the treeObject, is null, the whole statement becomes null without even calling "click"
        // Since the statement can be null, we compare it to "true". If the tree object is null or the "click" returns false, the if statement will not be true
        if (treeObject?.click("Chop") == true)
        {
            // In Kotlin, lambda expressions are just wrapped in curly brackets
            // We are returning the result of this waitCondition. If the condition is met, this will return true
            return Timing.waitCondition({ isChopping() }, 10000)
        }

        return false
    }

    // Methods with a single statement can be condensed into 1 line, and don't require a type specifier
    fun isInBank() = GrandConstants.bankArea.contains(Player.getRSPlayer())
    fun isInTreeArea() = GrandConstants.treeArea.contains(Player.getRSPlayer())

    // Since the player will never be doing anything other than walking, chopping, and banking, we can assume that any animation is the chopping animation
    fun isChopping() = Player.getAnimation() != -1
}

And there we go, our very first Kotlin script!

 

Running the Script

Running a Kotlin script is the same as any other Tribot Java script. Simply hit "build" in IntelliJ. If you have your output pointed to your tribot "bin" folder, then you can fire up a client and run it. If not, you can manually place the compiled files into your tribot bin.

Kotlin compiles to normal class files, so there's nothing special you need to do!

 

To upload Kotlin scripts to the repo, you can do that same thing you do for java. Just treat the .kt files as if they were .java files.

 

PS: Remember that Kotlin and Java can easily call each other. Don't be afraid to use all of the fantastic Java resources even if you only write your own code in Kotlin!

  • Like 2
  • Thanks 1

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.

Sign in to follow this  

  • Our picks

    • 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
      • 43 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
        • Haha
      • 10 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.
        • Like
      • 50 replies
    • Come give us feedback on the next version of TRiBot!
      • 86 replies
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...