This is aimed mainly at noobs who want to know a simple clean way to introduce multiplayer, and have good script structure.
The purpose of this tutorial is to educate on basic boiler plate code and some hidden gems for multiplayer support with OSR.
You can place your Player Definitions where ever you want, I just prefer mine the way it is shown here. Cheers.
Final Product: Boilerplate code (Good structure)
Simba Code:
program MultiPlayerTut;
{$DEFINE SMART}
{$I SRL/OSR.simba}
{$H-} // H, R, X Compiler Directives - H: http://www.delphibasics.co.uk/RTL.asp?Name=$H
{$R+} // R: http://www.delphibasics.co.uk/RTL.asp?Name=$R
{$X+} // X: http://www.delphibasics.co.uk/RTL.asp?Name=$X
const
RUN_TIME = (8 * ONE_HOUR) + (30 * ONE_MINUTE);
type
TState = (WALKING, BANKING, CUTTING, BURNING, FLETCHING);
type
TBot = record
LastStatsTick: Int64;
State: TState;
Antiban: TAntiban;
end;
var
Bot: TBot;
procedure TBot.DeclarePlayers();
begin
// TPlayer = record
// LoginName: String;
// Password: String;
// BankPin: String;
// isMember: Boolean;
// isActive: Boolean;
// World: Int32; // if not defined, will use random world using the filter
// WorldFilter: TRSWorldFilter;
// Data: Pointer;
Players.Add(['weasel99', '123456', '9000', True, True, 302]);
// OR
with Players.New()^ do
begin
LoginName := 'username';
Password := 'password';
BankPin := 'pin';
isActive := True;
end;
// OR however u wanna do it
end;
function TBot.GetState(): TState;
begin
if 6 > 3 then
Exit(FLETCHING);
// if BrazierIsFull then
// etc.
if 9 > 3 then
Exit(BANKING);
end;
procedure TBot.ProcessWhileWaiting(AntiBan: Boolean = True);
begin
//Self.PrintStats();
//Antiban.DoAntiban
end;
function TBot.SetupAntiban(): Boolean;
var
anti: TAntiban;
begin
Antiban := anti;
srl.StartTime := GetTickCount();
// Skill to keep track of, Log size
Result := Antiban.Init([SKILL_WOODCUTTING], 4);
// Method to use, How Often to run the method, Time Variation decimal to multiply by to give randomness on anti ban
Antiban.AddTask([@Antiban.CheckSkill, ONE_MINUTE * 5, 0.33]);
Antiban.AddTask([@Antiban.CheckStats, ONE_MINUTE * 10]);
// Run every Lasts for? Logout chance Variation (0..1)
Antiban.AddBreak([30,30,0.05,0.25]);
end;
procedure TBot.Init();
begin
//Self.LoadSettings();
srl.Setup([soDebug]);
Self.DeclarePlayers();
Self.SetupAntiban();
// Setup DTMS or RSWalker here before logging into current
Players.LoginCurrent();
end;
procedure TBot.Run();
begin
MainScreen.SetAngle(True);
//Minimap.SetCompassAngle(Random( - 20, 20));
repeat
while Antiban.TimeRunning < (RUN_TIME) do // Bots until players runtime is reached
begin
State := Self.GetState();
Self.ProcessWhileWaiting(False);
case State of
WALKING: ;//Self.ExitWintertodt(); or whatever u wanna call on this state
BANKING: ;
CUTTING: ;
BURNING: ;
FLETCHING: ;
end;
if not srl.IsLoggedIn then
begin
Break; // Break out of current Runtime loop and move on to next player.
end;
end;
until (Players.Next() = Self.SetupAntiban());
end;
procedure TBot.Free();
begin
//RSW.Free();
{$IFDEF DRAW}
//debugBmp.Free();
{$ifdecl TWindowOverlay}
//Overlay.Free();
{$endif}
{$ENDIF}
//FreeDTMs([dtm_bronzeAxe, dtm_ironAxe, dtm_steelAxe, dtm_blackAxe]);
end;
begin
ClearDebug();
Bot.Init();
AddOnTerminate(@Bot.Free);
Bot.Run();
end.
This is a Constant that any user of your script can change to their liking. You could put Multiplayer constants up here but I'm more of a variables type.
Up to you ultimately! RUN_TIME. This refers to how long we want one player to bot for before pulling the plug and rotating to the next player. All time in Simba/Windows API
is based on milliseconds. Which is 1/1000th of a second. So in order to convert that into our time (in minutes/hours). We have to do some multiplication.
Luckily SRL provides us with some handy constants prebaked with support for that. So RUN_TIME is currently set to 8 hours and 30 minutes in milliseconds. Which
is 30600000 milliseconds.
Simba Code:
const
RUN_TIME = (8 * ONE_HOUR) + (30 * ONE_MINUTE);
This is called an Enumeration, this gives names to our tasks we will be performing in our script. This is helpful to keep your eye on the prize.
Detect a certain state that the bot is in. And then execute your reaction. This is much cleaner then just calling if/thens or throwing functions around.
With this we know exactly what the script is doing. We keep track of State in our bot.
Simba Code:
type
TState = (WALKING, BANKING, CUTTING, BURNING, FLETCHING);
This is called a record. Simba and SRL use a lot of these to group variables together for instances of these records. In our case here, we are using the record TBot
to provide us with a variable to keep track of time since stats were printed on the screen LastStatsTick, State of the Bot State: TState, and SRL's Antiban.
This prevents the need of global variables, and even using too many local variables. With this we can create an instance of TBot called Bot,
and whenever we want to access and change things. We can do Bot.State or Bot.Antiban
Simba Code:
type
TBot = record
LastStatsTick: Int64;
State: TState;
Antiban: TAntiban;
end;
This creates an Instance of TBot. A Global Variable we created in our script.
Other Global variables include srl, MainScreen, etc.
This adds players to SRL's global variable Players. That you can then use later in your script. As for why its called TBot.DeclarePlayers, this is the
ownership that we need to embrace. This gives TBot its own procedure that it can give access to its variables. So if you call Self inside that procedure. You get access
to the current TBot variable instance that is running. So we could create as many instances as we want of TBot and TBot would be changing data for each individual instance.
So you do this through Self.State Self.Antiban from within TBot's procedures/functions. Same idea is used with Simba Forms
Simba Code:
procedure TBot.DeclarePlayers();
This is where you check what your bot is doing. Its action/verb with your own finding/checking methods. Then you return that State to your main loop for Reaction.
This is where a majority of your IF Statements will go. Make sure you order your checking methods in the order you wish them to be executed first. Its possible that multiple
conditions could be true. E.g. Inventory full and IsNearTree. So you have to take precedence and decide which is more important to deal with first and return that State.
Exit() This is used to terminate a method and return to where it was called from and continue to execute there. Exit(FLETCHING) This terminates the method AS WELL AS
giving a Result TState of FLETCHING
Simba Code:
function TBot.GetState(): TState;
begin
if 6 > 3 then
Exit(FLETCHING);
// if BrazierIsFull then
// etc.
if 9 > 3 then
Exit(BANKING);
end;
We use the local variable anti to provide us with a way to reset our Antiban on the presence of a new player. We could also
set different Antiban/Breaking constants/variables for each individual player. Or you can choose to use the same Antiban/Break settings for each player.
In the former case you would have to create an array of that data or lots of variables/constants with that data somewhere that you can deal with here.
So the reason we have a Result and we reset our Antiban. Is because we want to call this function in our main loop over and over for each new player.
It just so happens that no matter what Antiban.Init will return false. We will exploit this later on
Simba Code:
function TBot.SetupAntiban(): Boolean;
This is your script loading whatever DTMs, bitmaps, or whatever you need to do before you enter your main loop.
In our case we setup SRL with a basic debugging option. Load our players, Setup antiban and login our first player.
This is similar to perhaps Visual Studio C++,.NET,etc main app or form programming. Where you'll notice
they Initialize the form, then they run it. So e.g. we do similar with Bot.Init and Bot.Run. Program flow.
This makes sure when your script terminates that it frees the data that your DTMs, Walking, etc. are using in memory.
The reason we run this AFTER Bot.Init(); is because after Bot.Init we have loaded all our DTM's, resources.
And thus we need a way to deal with them. ClearDebug just clears Simba's debug box at the bottom of the screen.
@ Symbol gets the memory/address/pointer or whatever you wanna call it. OF Bot.Free method. And passes it to
that AddOnTerminate procedure so that it can execute Bot.Free itself in its own procedure.
Simba Code:
AddOnTerminate(@Bot.Free);
Where it all comes together. The Main Loop. The most advanced part of our boilerplate. MainScreen.SetAngle(True);
This angles your OSRS camera to be at birds eye view above your player. //Minimap.SetCompassAngle(Random( - 20, 20)); This would
randomly turn your camera.
Simba Code:
procedure TBot.Run();
begin
MainScreen.SetAngle(True);
//Minimap.SetCompassAngle(Random( - 20, 20));
repeat
while Antiban.TimeRunning < (RUN_TIME) do // Bots until players runtime is reached
begin
State := Self.GetState();
Self.ProcessWhileWaiting(False);
case State of
WALKING: ;//Self.ExitWintertodt(); or whatever u wanna call on this state
BANKING: ;
CUTTING: ;
BURNING: ;
FLETCHING: ;
end;
if not srl.IsLoggedIn then
begin
Break; // Break out of current Runtime loop and move on to next player.
end;
end;
until (Players.Next() = Self.SetupAntiban());
end;
This is your main loop. Exit; will break out of everything and lead to the end of your main loop. Break;
will stop whatever loop your in and execute the code outside of it. This is useful for nested loops like we have here.
Continue; This goes to the next iteration in our loop. So in the case here. It would call the Until part again.
Players.Next() This returns True if it was successfully able to switch to the next player, log them in, and
set the previous player as inactive. This provides the best way to loop through your players. If it Returns False. Then
there is no more active players. So we can just call Continue; or Break; to get our next player out in front.
Since Self.SetupAntiban will always return False. We can use that advantage to call it on every loop. Which will reset
the anti ban / anti break Self.Antiban variable for our Next player.
When False = False. Players.Next runs out of active players so it returns false. The Loop finnishes and the script is over.
Simba Code:
until (Players.Next() = Self.SetupAntiban());
Antiban.TimeRunning gets the time since SRL Start time which we set previously in
SetupAntiBan() srl.StartTime := GetTickCount();.
So we basically use this as a time check. And what is great is our main loop resets this timer everytime it switches to a new player!
Might have to create a new variable or something if u want total time for a proggy. But this is great. And best of all. Its only used
2 times in the whole SRL include. So we can basically make this variable our b*tch with no issues. This executes the current player
for the constant run time (8 hours 30 minutes) or until something causes the loop to be broken out of. You could change this to take into account break times
but this is fine. For more information check out
https://github.com/SRL/SRL/blob/mast.../antiban.simba
Simba Code:
while Antiban.TimeRunning < (RUN_TIME) do
Process while waiting can be your onscreen GUI, or other MISC stuff you need to do. I grabbed this from
Patriq's Wintertodt.simba script. A lot of this boilerplate comes from his code. So congrats to him.
But a lot of scripts utilize this kind of boiler plate. So be aware it is not Patriq's feat alone. But Credit to where credit is due. To him.
Cheers for his TBot and etc. So we check what state we are in. Then we react. To whatever we think the bot needs to do on that condition.
Simba Code:
State := Self.GetState();
Self.ProcessWhileWaiting(False);
case State of
WALKING: ;//Self.ExitWintertodt(); or whatever u wanna call on this state
BANKING: ;
CUTTING: ;
BURNING: ;
FLETCHING: ;
end;
That's it!... The bulk of your script is still left to complete. If conditions and more to be
thrown into your main loop and supporting functions. Plan diligently with this boilerplate, and you will have no issues.
Remember to use Antiban.DoBreak and Antiban.DoAntiban mixed throughout your scripts to keep up the force.