1. Define your custom method provider, this extends OSBot's MethodProvider class, and then adds on / overrides functionality. In this example, I am overriding getInventory() with my own class which adds a "use" function. The CustomMethodProvider class defined below also supplies a custom "execute" function, which runs an Executable (a different class I have defined later)
class CustomMethodProvider extends MethodProvider {
private ExtendedInventory extendedInventory;
private boolean hasContext;
public void init(final Bot bot) {
super.exchangeContext(bot);
this.extendedInventory = new ExtendedInventory();
extendedInventory.exchangeContext(bot);
hasContext = true;
}
public boolean hasContext() {
return hasContext;
}
// Deprecated as exchangeContext(Bot bot, CustomMethodProvider methodProvider) should be used instead.
@Deprecated
public MethodProvider exchangeContext(final Bot bot) { return super.exchangeContext(bot); }
public CustomMethodProvider exchangeContext(final Bot bot, final CustomMethodProvider methodProvider) {
this.extendedInventory = methodProvider.extendedInventory;
super.exchangeContext(bot);
hasContext = true;
return this;
}
@Override
public ExtendedInventory getInventory() {
return extendedInventory;
}
/**
* Helper function which exchanges context with an Executable
* (if not already exchanged), and then calls Executable::run
* @param executable The Executable to execute
* @throws InterruptedException
*/
public void execute(final Executable executable) throws InterruptedException {
if (!executable.hasContext()) {
executable.exchangeContext(getBot(), this);
}
executable.run();
}
}
Here is my "ExtendedInventory" class:
class ExtendedInventory extends Inventory {
public boolean isUsing(final String itemName) {
return itemName.equals(getSelectedItemName());
}
public boolean use(final String itemName) {
if (isUsing(itemName)) {
return true;
}
if (getInventory().interact("Use", itemName)) {
Sleep.sleepUntil(() -> itemName.equals(getSelectedItemName()), 1000);
return true;
}
return false;
}
}
2. Define the Executable class, this is what we will use for other classes in our script that have a common "onStart" / "onLoop" / "onEnd" pattern:
abstract class Executable extends CustomMethodProvider {
public void onStart() throws InterruptedException {}
public abstract void run() throws InterruptedException;
public void onEnd() throws InterruptedException {}
}
Note how this class extends our CustomMethodProvider, which means anything we define in the CustomMethodProvider class will be available to subclasses of "Executable"
3. In the main script class, set everything up (create an instance of CustomMethodProvider, and exchange context)
@ScriptManifest(author = "Explv", name = "Example", info="", logo = "", version = 0.1)
public class Example extends Script {
private final CustomMethodProvider customMethodProvider = new CustomMethodProvider();
@Override
public void onStart() {
customMethodProvider.init(getBot());
}
@Override
public int onLoop() throws InterruptedException {
return 0;
}
}
4. Finally, whenever you want some other class with an onStart / onLoop / onEnd, just extend the Executable class, and then run it using "execute". Here is a very contrived example:
class ExampleExecutable extends Executable {
private static final String TINDERBOX = "Tinderbox";
private final Executable someOtherExecutable = new SomeOtherExecutable();
@Override
public void run() throws InterruptedException {
if (!getInventory().isUsing(TINDERBOX)) {
getInventory().use(TINDERBOX);
} else {
execute(someOtherExecutable);
}
}
}
class SomeOtherExecutable extends Executable {
@Override
public void run() throws InterruptedException {
getWalking().webWalk(new Area(1, 2, 3, 4));
}
}
@ScriptManifest(author = "Explv", name = "Example", info="", logo = "", version = 0.1)
public class Example extends Script {
private final CustomMethodProvider customMethodProvider = new CustomMethodProvider();
private final Executable exampleExecutable = new ExampleExecutable();
@Override
public void onStart() {
customMethodProvider.init(getBot());
}
@Override
public int onLoop() throws InterruptedException {
customMethodProvider.execute(exampleExecutable);
return 600;
}
}
5. If you want an "Executable" that blocks until completion, then you can either use OSBot's Event class, or define your own "BlockingExecutable" which utlises OSBot's Event, for example:
public abstract class BlockingExecutable extends Executable {
private boolean finished;
private ExecutionFailedException executionFailedException;
@Override
public final void run() throws InterruptedException {
finished = false;
executionFailedException = null;
onStart();
execute(new Event() {
@Override
public int execute() throws InterruptedException {
if (finished) {
setFinished();
} else {
try {
blockingRun();
} catch (ExecutionFailedException executionFailedException) {
BlockingExecutable.this.executionFailedException = executionFailedException;
setFailed();
}
}
return 0;
}
});
onEnd();
if (executionFailedException != null) {
throw executionFailedException;
}
}
protected abstract void blockingRun() throws InterruptedException;
protected void setFinished() {
finished = true;
}
}
public class ExecutionFailedException extends RuntimeException {
public ExecutionFailedException(String message) {
super(message);
}
}