dubai Posted August 7 Share Posted August 7 (edited) 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 August 7 by dubai added content Quote Link to comment Share on other sites More sharing options...
Fanny Posted August 7 Share Posted August 7 (edited) 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 August 7 by Fanny Quote Link to comment Share on other sites More sharing options...
dubai Posted August 7 Author Share Posted August 7 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? Quote Link to comment Share on other sites More sharing options...
Fanny Posted August 8 Share Posted August 8 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 Quote Link to comment Share on other sites More sharing options...
dubai Posted August 9 Author Share Posted August 9 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? Quote Link to comment Share on other sites More sharing options...
Fanny Posted August 9 Share Posted August 9 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 Quote Link to comment Share on other sites More sharing options...
Buttons Posted August 9 Share Posted August 9 theres always money to be made making scripts my guy 1 Quote Link to comment Share on other sites More sharing options...
dubai Posted August 13 Author Share Posted August 13 (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 August 13 by dubai Quote Link to comment Share on other sites More sharing options...
Fanny Posted August 13 Share Posted August 13 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 Quote Link to comment Share on other sites More sharing options...
dubai Posted August 14 Author Share Posted August 14 (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 August 14 by dubai Quote Link to comment Share on other sites More sharing options...
Fanny Posted August 14 Share Posted August 14 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 Quote Link to comment Share on other sites More sharing options...
dubai Posted August 14 Author Share Posted August 14 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... Quote Link to comment Share on other sites More sharing options...
Fanny Posted August 15 Share Posted August 15 8 hours ago, dubai said: Having generalized Classes The more groundwork you lay, the faster it will be to produce great scripts using your existing classes 1 Quote Link to comment Share on other sites More sharing options...
dubai Posted August 17 Author Share Posted August 17 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? Quote Link to comment Share on other sites More sharing options...
Fanny Posted August 17 Share Posted August 17 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 Quote Link to comment Share on other sites More sharing options...