-
Notifications
You must be signed in to change notification settings - Fork 141
Adding Private Server Support
OSBC is meant for OSRS-based games. In other words, by default, it is only suitable for games that use the default OSRS interface style (with minor variations). Bots for games that have highly customized interfaces or use the 2005, 2006, or 2010 gameframes will require additional modification to work with OSBC, and it cannot be guaranteed that the utilities will work correctly.
This tutorial uses Zaros RSPS
as an example. Much of this does not actually apply to Zaros RSPS, it's just the first RSPS that came to mind. If you are adding support for a game that is not Zaros, you can replace all instances of Zaros
with the name of your game.
All games should have a base bot class. This class should inherit from Bot
or RuneLiteBot
(if it has RuneLite support).
- Create a new folder in
src/model/
that will contain all bots for the RSPS. The name of the folder should be the name of the RSPS, or an abbreviation of it. - Within this folder, create a new Python file. The name of the file should be the name of the RSPS, or an abbreviation of it, followed by
_bot.py
. For example, for the RSPS Zaros, the file would be namedzaros_bot.py
and look like this:
from abc import ABCMeta
from model.runelite_bot import RuneLiteBot, RuneLiteWindow
class ZarosBot(RuneLiteBot, metaclass=ABCMeta):
win: RuneLiteWindow = None
def __init__(self, bot_title, description) -> None:
super().__init__("Zaros", bot_title, description, RuneLiteWindow("Zaros"))
As you can see, this class inherits from RuneLiteBot
, as well as ABCMeta
. This means that ZarosBot
is an abstract class, and cannot be instantiated. Therefore, when we create a new bot for Zaros, we will need to create a new class that inherits from ZarosBot
.
The notable features of this class are as follow:
- The
super().__init__()
function is called with"Zaros"
as the first argument. This is the name of the RSPS, and will be used to tell OSBC where Zaros bots should exist on the user interface, and what settings file to use when launching RuneLite (since this game is RuneLite-based). - The
super().__init__()
function is also passed an instance ofRuneLiteWindow
with"Zaros"
as the argument. This allows theWindow
utility to locate the Zaros game window while it is running. If your game has a slightly customized interface, you may need to extend theRuneLiteWindow()
class to add additional functionality. We will discuss this in a later section.
Now that we have a base class, we can create a new bot for Zaros. This bot will inherit from ZarosBot
.
- Copy the
src/model/osrs/template.py
file to the folder you created in Part 1. Rename the file to the name of the bot you are creating. For example, if you are creating a bot for Zaros calledZarosWoodcutter
, the file could be namedwoodcutter.py
. Here's what the class might look like:
import time
from model.zaros.zaros_bot import ZarosBot
class ZarosWoodcutter(ZarosBot):
def __init__(self):
bot_title = "Woodcutter"
description = "This bot chops logs in Zaros RSPS."
super().__init__(bot_title=bot_title, description=description)
# Set option variables below (initial value is only used during UI-less testing)
self.running_time = 1
def create_options(self):
self.options_builder.add_slider_option("running_time", "How long to run (minutes)?", 1, 500)
def save_options(self, options: dict):
for option in options:
if option == "running_time":
self.running_time = options[option]
else:
self.log_msg(f"Unknown option: {option}")
print("Developer: ensure that the option keys are correct, and that options are being unpacked correctly.")
self.options_set = False
return
self.log_msg(f"Running time: {self.running_time} minutes.")
self.log_msg("Options set successfully.")
self.options_set = True
def main_loop(self):
# Main loop
start_time = time.time()
end_time = self.running_time * 60
while time.time() - start_time < end_time:
# -- Perform bot actions here --
self.update_progress((time.time() - start_time) / end_time)
self.update_progress(1)
self.log_msg("Finished.")
self.stop()
This bot is practically the same as the original template. The only real difference is that it inherits from the new game baseclass instead.
- Inside your
src/model/zaros
folder, add an__init__.py
file. This file is required to tell OSBC where to find your bot classes in order to add them to the UI dynamically. The contents of this file should be as follows:
from .woodcutter import ZarosWoodcutter
If your game is based on RuneLite, you will need to create a new settings file for it. This file will be used to launch RuneLite with the correct settings for your game. Creating a good base settings file for your RSPS can be challenging. Using Zaros RSPS as the example again, here's the best way to start:
- In
src/runelite_settings/
, duplicate theosrs_settings.properties
file and name itzaros_settings.properties
. - Launch OSBC.
- In the navigation pane, you should be able to select Zaros from the dropdown menu and see the Zaros bot you created in Part 2.
- Go ahead and launch Zaros RSPS using the
Launch Zaros
button in the Home View.- You may need to locate the executable file first.
- Log into the game and take a look at the RuneLite settings it has. Ideally, we'd like it to be as similar to the
osrs_settings.properties
as possible - but due to RSPS's often having mismatched RuneLite versions, some of the settings from this duplicated file may not have applied.- Do your best to modify the plugins that may not have been configured properly.
- When you're satisfied with the settings, log out and close RuneLite. Your settings have been saved to
src/runelite_settings/temp.properties
. - Edit
src/runelite_settings/temp.properties
file, removing any lines that containrsprofile
. These lines may expose your account name, so it is best to remove them. - Delete the
zaros_settings.properties
file you created in Step 1, and renametemp.properties
tozaros_settings.properties
to replace it.
Now, when you launch Zaros RSPS using OSBC, it will use the settings you configured in zaros_settings.properties
.
If your game has a slightly customized interface, you may need to extend the RuneLiteWindow()
class to add additional functionality. This is especially true if your game has a custom title bar, custom minimap frames, or if you want to add additional functionality to the Window
utility.
Before you go ahead and create a new window class, you should first try to use the default RuneLiteWindow
class. If it works, you don't need to create a new window class.
- In your game's baseclass, create an extension of
RuneLiteWindow
:
from abc import ABCMeta
from model.runelite_bot import RuneLiteBot, RuneLiteWindow
class ZarosWindow(RuneLiteWindow):
def __init__(self) -> None:
"""
In this init function, we can set the padding for the window.
E.g., `padding_top` refers to the number of pixels between the top of the Zaros window
to where the game view actually starts.
"""
super().__init__("Zaros", padding_top=26, padding_left=0)
class ZarosBot(RuneLiteBot, metaclass=ABCMeta):
win: RuneLiteWindow = None
def __init__(self, bot_title, description) -> None:
super().__init__("Zaros", bot_title, description, ZarosWindow())
Now, instead of creating ZarosBot
with a RuneLiteWindow("Zaros")
reference, we can just use ZarosWindow()
instead.
Let's pretend that Zaros RSPS has a custom minimap that looks something like this.
OSBC needs to identify the UI on bot startup, and it will fail since it can't find the original OSRS-style minimap. To fix this, we can photoshop the Zaros minimap such that it only contains the frame of the minimap (i.e., the parts of the minimap that never change).
Now, let's override the __locate_minimap()
function of the Window
class. This function is called at bot start to locate the minimap and its inner components. By defining our own function here, we can tell OSBC to look for the new minimap frame instead. A good course of action would be to copy the code from the Window
class, then modify it accordingly.
class ZarosWindow(RuneLiteWindow):
def __init__(self) -> None:
"""
In this init function, we can set the padding for the window.
E.g., `padding_top` refers to the number of pixels between the top of the Zaros window
to where the game view actually starts.
"""
super().__init__("Zaros", padding_top=26, padding_left=0)
def __locate_minimap(self, client_rect: Rectangle) -> bool:
"""
Locates the minimap area on the clent window and all of its internal positions.
Args:
client_rect: The client area to search in.
Returns:
True if successful, False otherwise.
"""
# 'm' refers to minimap area
if m := imsearch.search_img_in_rect("path to our new minimap", client_rect):
self.client_fixed = False
self.compass_orb = Rectangle(...)
self.hp_orb_text = Rectangle(...)
self.minimap = Rectangle(...)
self.prayer_orb = Rectangle(...)
self.prayer_orb_text = Rectangle(...)
self.run_orb = Rectangle(...)
self.run_orb_text = Rectangle(...)
self.spec_orb = Rectangle(...)
self.spec_orb_text = Rectangle(...)
self.total_xp = Rectangle(...)
return True
else:
# locate the fixed version of the minimap...
# omitted for brevity
...
print("Window.__locate_minimap(): Failed to find minimap.")
return False
Now, this function will be used to locate the minimap instead of the default one.
This is a very simplified example of how to define a custom window class.