Jump to content

Hello, feedback? [Developer]


dubai

Recommended Posts

Hi! I'm looking to get some feedback on this script I made today. I'm also wondering if there is still money to be made from a developers point of view for osbot scripts... as it seems the market has almost everything covered already so it would be a game of undercutting other developers with similar scripts. 

 

Just looking to earn some extra money really as I complete my Computer Science degree. I'd also likely to note that I consider myself a python developer; I have completed a Java bootcamp and got a certificate for that but I'm still not 100% comfortable with java and only have minimal experience (apart from building bots/private servers for runescape when I was a teenager)

 

Here's a copy of my "Deforest Trees" script:

Spoiler
import org.osbot.rs07.api.model.RS2Object;
import org.osbot.rs07.api.ui.Skill;
import org.osbot.rs07.script.Script;
import org.osbot.rs07.script.ScriptManifest;
import org.osbot.rs07.api.map.Area;
import java.awt.*;
import java.util.List;
import java.util.Random;

@ScriptManifest(name = "Deforest Trees", author = "Dubai", version = 1, info = "", logo = "")
public class script_one extends Script {

    private final Area treeArea = new Area(2945, 3398, 2957, 3419);
    private final Area bankArea = new Area(2944, 3368, 2946, 3373);
    private final Random random = new Random();
    private int antiBanCounter = 0;
    private long startTime;
    private int logsChopped = 0; // Counter for logs chopped
    private int previousLogCount = 0; // Variable to keep track of the previous inventory count
    private int initialLevel; // Variable to store the initial level

    @Override
    public void onStart() {
        log("Script started.");
        startTime = System.currentTimeMillis();
        previousLogCount = (int) getInventory().getAmount("Logs"); // Initialize the previous log count
        initialLevel = getSkills().getStatic(Skill.WOODCUTTING); // Store the initial level
    }

    @Override
    public void onExit() {
        log("Script ended.");
    }

    @Override
    public int onLoop() {
        int currentLogCount = (int) getInventory().getAmount("Logs"); // Get the current log count
        if (currentLogCount > previousLogCount) {
            logsChopped += (currentLogCount - previousLogCount); // Increment the counter based on the difference
            previousLogCount = currentLogCount; // Update the previous log count
        }

        if (inventory.isFull()) {
            log("Inventory is full. Walking to the bank...");
            if (!bankArea.contains(myPlayer())) {
                log("Player is not in the bank area. Walking to the bank...");
                getWalking().webWalk(bankArea);
            } else {
                log("Player is in the bank area.");
                if (!getBank().isOpen()) {
                    log("Bank is not open. Attempting to open a random bank booth...");
                    try {
                        openRandomBankBooth();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    log("Bank is open. Depositing items...");
                    if (getBank().depositAllExcept("Axe")) {
                        previousLogCount = 0; // Reset the previous log count after banking
                        try {
                            sleep(random(200, 300));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        getBank().close();
                        log("Deposited logs. Walking back to the tree area...");
                        getWalking().webWalk(treeArea);
                    } else {
                        log("Failed to deposit items. Retrying...");
                    }
                }
            }
        } else {
            log("Inventory is not full. Walking to the tree area...");
            if (!treeArea.contains(myPlayer())) {
                log("Player is not in the tree area. Walking to the tree area...");
                getWalking().webWalk(treeArea);
                addRandomDelay(); // Add random delay
            } else {
                log("Player is in the tree area.");
                RS2Object tree = objects.closest(t -> t != null && (t.getName().equals("Tree") || t.getName().equals("Evergreen")) && treeArea.contains(t));
                if (tree != null && tree.isVisible() && !myPlayer().isAnimating()) {
                    log("Chopping tree...");
                    tree.interact("Chop down");
                    addRandomDelay();
                    performAntiBanActions();
                } else {
                    log("No tree found or player is busy.");
                }
            }
        }

        // Check if the bank is open and handle accordingly
        if (getBank().isOpen()) {
            log("Bank is open. Depositing items...");
            if (getBank().depositAllExcept("Axe")) {
                previousLogCount = 0; // Reset the previous log count after banking
                try {
                    sleep(random(200, 300));
                } catch (InterruptedException e) {
                    e.printStackTrace(); // Handle the exception as needed
                }
                getBank().close();
                log("Deposited logs. Walking back to the tree area...");
                getWalking().webWalk(treeArea);
            } else {
                log("Failed to deposit items. Closing bank and retrying...");
                getBank().close();
            }
        }

        return 5000; // The amount of time in milliseconds before the loop starts over (5 seconds)
    }

    private void openRandomBankBooth() throws InterruptedException {
        List<RS2Object> bankBooths = getObjects().filter(obj -> obj != null && obj.getName().equals("Bank booth"));
        if (!bankBooths.isEmpty()) {
            RS2Object randomBankBooth = bankBooths.get(random.nextInt(bankBooths.size()));
            randomBankBooth.interact("Bank");
            sleep(random(200, 600)); // Wait for the bank to open
        } else {
            log("No bank booths found.");
        }
    }

    private void performAntiBanActions() {
        antiBanCounter++;
        if (antiBanCounter % 2 == 0) { // Perform anti-ban random mouse movement every 2nd time
            log("Performing anti-ban actions...");
            int x = random.nextInt(500);
            int y = random.nextInt(500);
            getMouse().move(x, y);
            try {
                sleep(random(200, 500));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void addRandomDelay() { //Random delay for antiban purposes
        try {
            sleep(random(1000, 15000)); // ADJUST ANTI BAN TIMER HERE
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onPaint(Graphics2D g) {
        long runTime = System.currentTimeMillis() - startTime;
        int currentLevel = getSkills().getStatic(Skill.WOODCUTTING); // Get the current level
        int levelsGained = currentLevel - initialLevel; // Calculate the levels gained

        g.setColor(Color.WHITE);
        g.drawString("Runtime: " + formatTime(runTime), 10, 30);
        g.drawString("Logs Chopped: " + logsChopped, 10, 70); // Display the logs chopped counter
        g.drawString("Levels Gained: " + levelsGained, 10, 50); // Display the levels gained
    }

    private String formatTime(long time) {
        long seconds = (time / 1000) % 60;
        long minutes = (time / (1000 * 60)) % 60;
        long hours = (time / (1000 * 60 * 60)) % 24;
        return String.format("%02d:%02d:%02d", hours, minutes, seconds);
    }
}

Start near Falador west bank and it will walk to trees, chop a full load of trees and then bank, repeat.

I've added some nice and simple yet highly effective antiban including:

-Random mouse movement,

-Random delay between chopping trees

-Picks a random bank booth each time it banks 

 

The script also has some checks to detect if the bot gets stuck and I've got over an hour's runtime so far without it crashing and has a nice and simple paint featuring runtime, logs chopped and levels gained.

Some ways I've considered monetizing my coding skills via osbot would be either, selling scripts, goldfarming using my own scripts or selling accounts that I've created using my own scripts (trade restriction lifted accounts) .

 

Any feedback at all appreciated!

Edited by dubai
added content
Link to comment
Share on other sites

Basic, but looks like it is functional

Consider using conditional sleeps (wait until bank is open for example) rather than a set delay time

Also consider using states to handle actions, because the if-else can become extremely long and confusing for a more complex script

Quick search on google for states, kind of gives you an idea.

 

The antiban is unnecessary IMO, don't think mouse movement is really monitored, but mouseout on breaks sometimes might be good

Better antiban you could add would be to:

1) get the location of the tree object you are cutting

2) check objects at that location to see if it is cut

3) return from break early sometimes when it is cut (e.g. another player chops it)

This is also a better way to keep track of them I think

Edited by Fanny
Link to comment
Share on other sites

8 hours ago, Fanny said:

Basic, but looks like it is functional

Consider using conditional sleeps (wait until bank is open for example) rather than a set delay time

Also consider using states to handle actions, because the if-else can become extremely long and confusing for a more complex script

Quick search on google for states, kind of gives you an idea.

Awesome reply thanks heaps!

Like I mentioned, definitely prefer to code in python and I'm struggling to keep these java classes tidy. One major difference between the 2 languages I've noticed is the ability to keep python much cleaner from the get go. I'll do some research on states and I'll add some conditional sleeps instead of delays. 

 

I did run the script overnight for 11 hours and it didn't break. 

Also in regards to mouse movement. I was unaware that times have changed that much as when I was botting some 10+ years ago, random mouse movements were the first line of defence for antiban in our bots.

 

Apart from all that, do you still think there's money to be made as a developer in the osbot scene? 

Link to comment
Share on other sites

Sadly can't answer that - not got any public scripts, just make them for personal use

Java is definitely a lot harder to write, not a big fan to be honest, but getting the hang of it

 

Few things I found helpful:

I use a class called V for my variables, I find that makes the main script more manageable, can just call V. for those, means you don't have to clog the main script up

public final class V {
    public static final int[] EXPERIENCES= {
            0, 0, 83, 174, 276, 388, 512, 650, 801, 969, 1154, 1358, 1584, 1833, 2107, 2411, 2746, 3115, 3523, 3973,
            4470, 5018, 5624, 6291, 7028, 7842, 8740, 9730, 10824, 12031, 13363, 14833, 16456, 18247, 20224, 22406,
            24815, 27473, 30408, 33648, 37224, 41171, 45529, 50339, 55649, 61512, 67983, 75127, 83014, 91721, 101333,
            111945, 123660, 136594, 150872, 166636, 184040, 203254, 224466, 247886, 273742, 302288, 333804, 368599,
            407015, 449428, 496254, 547953, 605032, 668051, 737627, 814445, 899257, 992895, 1096278, 1210421, 1336443,
            1475581, 1629200, 1798808, 1986068, 2192818, 2421087, 2673114, 2951373, 3258594, 3597792, 3972294, 4385776,
            4842295, 5346332, 5902831, 6517253, 7195629, 7944614, 8771558, 9684577, 10692629, 11805606, 13034431
    };

    public static final Area areaFishing = new Area(3107, 3436, 3110, 3430);
etc...

 

I wrote a custom sleep function that handles conditionally sleeping by loop checking on a delay, but it has added functionality like mouse-out chance and stuff, so that is handy to just call Sleep.sleep(maxTime, ()->boolean);

If you need to check animations, might be good to make a utility class for that, which starts a new thread that checks when you are animating repeatedly and updates the last animated time, that way you can check a custom function like hasAnimated(withinLastSeconds), rather than directly checking isAnimating() - probably a bit memory intensive, but since there is often a delay between animations it avoids that (think smelting)

using a StateEngine is a bit of a cumbersome way of writing stuff, but it is tidier than lots of if-else

 

It was all the static vs. non-static that confused me initially

 

Hope I've been some help anyway! Still learning myself

Link to comment
Share on other sites

12 hours ago, Fanny said:

Sadly can't answer that - not got any public scripts, just make them for personal use

Java is definitely a lot harder to write, not a big fan to be honest, but getting the hang of it

 

Few things I found helpful:

I use a class called V for my variables, I find that makes the main script more manageable, can just call V. for those, means you don't have to clog the main script up

public final class V {
    public static final int[] EXPERIENCES= {
            0, 0, 83, 174, 276, 388, 512, 650, 801, 969, 1154, 1358, 1584, 1833, 2107, 2411, 2746, 3115, 3523, 3973,
            4470, 5018, 5624, 6291, 7028, 7842, 8740, 9730, 10824, 12031, 13363, 14833, 16456, 18247, 20224, 22406,
            24815, 27473, 30408, 33648, 37224, 41171, 45529, 50339, 55649, 61512, 67983, 75127, 83014, 91721, 101333,
            111945, 123660, 136594, 150872, 166636, 184040, 203254, 224466, 247886, 273742, 302288, 333804, 368599,
            407015, 449428, 496254, 547953, 605032, 668051, 737627, 814445, 899257, 992895, 1096278, 1210421, 1336443,
            1475581, 1629200, 1798808, 1986068, 2192818, 2421087, 2673114, 2951373, 3258594, 3597792, 3972294, 4385776,
            4842295, 5346332, 5902831, 6517253, 7195629, 7944614, 8771558, 9684577, 10692629, 11805606, 13034431
    };

    public static final Area areaFishing = new Area(3107, 3436, 3110, 3430);
etc...

 

I wrote a custom sleep function that handles conditionally sleeping by loop checking on a delay, but it has added functionality like mouse-out chance and stuff, so that is handy to just call Sleep.sleep(maxTime, ()->boolean);

If you need to check animations, might be good to make a utility class for that, which starts a new thread that checks when you are animating repeatedly and updates the last animated time, that way you can check a custom function like hasAnimated(withinLastSeconds), rather than directly checking isAnimating() - probably a bit memory intensive, but since there is often a delay between animations it avoids that (think smelting)

using a StateEngine is a bit of a cumbersome way of writing stuff, but it is tidier than lots of if-else

 

It was all the static vs. non-static that confused me initially

 

Hope I've been some help anyway! Still learning myself

Ton of good info there, thanks for the response.

I'll definitely be stealing your v class idea, what an awesome way to keep things clean and I'm kind of anoyed that I didnt think of this lol.

Just wondering with multithreading here; I've been avoiding that for scalability reasons as wont this slow the overall performance of the script down? if it's constantly doing something in the background, wouldn't multithreading be an issue when you're scaling to say 100 bots? 

Link to comment
Share on other sites

7 hours ago, dubai said:

Just wondering with multithreading here; I've been avoiding that for scalability reasons as wont this slow the overall performance of the script down? if it's constantly doing something in the background, wouldn't multithreading be an issue when you're scaling to say 100 bots?

It would slow it down for sure, if you are using only one or two it would be fine, but if you had 100 bots it would likely have a significant impact

The alternative way to do it (hardware limited) would be to ensure that the script ran every cycle (no sleeps) and built a custom sleep looping method into the main script with a SLEEP state in the stateengine, this way the primary onloop function could do things like animation checks first, but this would also mean building the script in such a way that it never processes logic sequentially, always 1 action per loop... (which is ideal as it is "reactive" then to any weird circumstance, but not always practical)... Or you could just do a special check for animation that involves 2 separate checks, half of the expected animation time apart

You'd have to check the impact though, if it only checks once per 500ms and that is all the thread does, it might not have much impact even at 100 bots - it depends how efficiently the CPU handles it I guess

Link to comment
Share on other sites

Posted (edited)
On 8/9/2024 at 6:47 PM, Fanny said:

It would slow it down for sure, if you are using only one or two it would be fine, but if you had 100 bots it would likely have a significant impact

The alternative way to do it (hardware limited) would be to ensure that the script ran every cycle (no sleeps) and built a custom sleep looping method into the main script with a SLEEP state in the stateengine, this way the primary onloop function could do things like animation checks first, but this would also mean building the script in such a way that it never processes logic sequentially, always 1 action per loop... (which is ideal as it is "reactive" then to any weird circumstance, but not always practical)... Or you could just do a special check for animation that involves 2 separate checks, half of the expected animation time apart

You'd have to check the impact though, if it only checks once per 500ms and that is all the thread does, it might not have much impact even at 100 bots - it depends how efficiently the CPU handles it I guess

Yeah I've essentially done just that, added a SLEEP state in one of my cases for my new and improved oak banker script. The scalability seems to be unlimited this way. I'm also getting comfortable adding errorhandling and crash detection methods and I'm struggling to make my scripts break (thats a good thing obviously), getting some big runtimes on all the scripts I make. Next plans are probably questing and minigame bots that do need to be in a more 'reactive' state. 

Starting to look into building a GUI for more AIO type bots aswell but I'm just taking a break from osbot scripting to complete a Springboot course so that should improve my comfortability with Java more. My 3rd Java Udemy course and this one is a chunky boy so pretty keen to plow through it. 

Edited by dubai
Link to comment
Share on other sites

GUI is going to scale badly I imagine, would definitely just keep it plaintext where possible

public void onPaint(final Graphics2D g) {
	// calculations here

	g.setColor(Color.WHITE);
	g.drawString(String.format("Script Runtime: %02d:%02d:%02d", hours, minutes, secs), 10, 40);
}

then just use y=60, y=80, etc for each line you want to include

 

I generally just do:

runtime

items made / gathered on one line

xp gained (+level count)

XP/hr

XP till level

 

Easy enough to keep track, and generally enough info for most scripts

 

I think once you've got pretty solid scripts, the next thing to focus on is weird edge cases... Like 1/1000 times the bot will misclick the bank booth and speak to a banker for example, ideally this kind of edge case should be handled - also things like when fishing, there might be a weird angle where the camera rotates at the exact wrong time and clicks on a random event instead of the spot - if there is a dialogue you should handle this immediately to seem more human-like

sometimes it's as simple as just checking for continuable dialogue before a sleep, and if so return 0; to re-run the current script loop immediately, which should attempt to click the fishing spot again for example

Link to comment
Share on other sites

Posted (edited)
13 hours ago, Fanny said:

GUI is going to scale badly I imagine, would definitely just keep it plaintext where possible

Sorry I more or less was refferring to the script config window that allows users to change how the script will run on startup.

But also yeah I did find a few wierd 1/1000 things like starting my script if the players bank was open would break the script so I added checks for that. Converting it all to cases made things look tidy, not trying to re-invent the wheel though just sorta get the feel of osbot api calls. Fun stuff!

Still pretty keen to push the limits though, I'll have more time soon to get back into so will see what I come up with.

Edited by dubai
Link to comment
Share on other sites

3 hours ago, dubai said:

But also yeah I did find a few wierd 1/1000 things like starting my script if the players bank was open would break the script so I added checks for that. Converting it all to cases made things look tidy, not trying to re-invent the wheel though just sorta get the feel of osbot api calls. Fun stuff!

Sounds like you're making some big improvements, nice work!

One other little thing I thought of I should probably mention is:

try to make generic "utils" functions, rather than specific ones because it will save you time later

 

Say you are crafting, you could write a function to use chisel on uncut sapphire (with functionality to click sapphire or chisel first, and randomly select the closest sapphire to the chisel generally)

OR you could make a general script to use X on Y, with a parameter like "CLOSEST_TO_SLOT_1" or "CLOSEST_TO_SLOT_14"

That way, you can use the same function for using longbow(u) on bowstring, with parameter "CLOSEST_TO_SLOT_14" to use a method that (most often but not always) gets the closest item to slot 14 in the inventory, similarly, this could be used for fletching (knife on log) or whatever. Make a good solid function, and you can just reuse it in whatever other scripts you make

Link to comment
Share on other sites

9 hours ago, Fanny said:

try to make generic "utils" functions, rather than specific ones because it will save you time later

Actually thats a really good idea, got me thinking actually what about going one up? Having generalized Classes rather then just functions you can re-use. For examply my "Tree chopper script" could be re used in a more AIO script by group classes together. Then say for whatever reason you need to re use that class its just a matter of extending its use. This sounds really good in my head but probably is very impracticle and may sound stupid I'm not sure...

Link to comment
Share on other sites

On 8/15/2024 at 6:21 PM, Fanny said:

The more groundwork you lay, the faster it will be to produce great scripts using your existing classes

Ok ok ok... I think I get it now. So are some of the more complex scripts we see on the market here, compiled as whole packages rather than just single class files compiled into a jar?

Link to comment
Share on other sites

10 hours ago, dubai said:

So are some of the more complex scripts we see on the market here, compiled as whole packages rather than just single class files compiled into a jar

I don't know about current scripts as there is no way to view the source code, however, I do have a hoard of scripts from rsbot which are class files not compiled into jar for some reason... Looking through these, it would appear that most scripts were a collection of class files rather than jar packages... Not sure if that is still relevant, but might help

Link to comment
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.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...