Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.util.Optional;
import java.util.concurrent.*;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;

@Singleton
@Slf4j
Expand Down Expand Up @@ -122,6 +123,40 @@ public void invoke(BooleanSupplier r)
invokeLater(r);
}

public <T> T invoke(Supplier<T> supplier) {
if (client.isClientThread()) {
return supplier.get();
}

CompletableFuture<T> f = new CompletableFuture<>();
invoke(() -> {
try {
f.complete(supplier.get());
} catch (Throwable t) {
f.completeExceptionally(t);
}
});

try {
return f.get(10, TimeUnit.SECONDS); // configurable timeout
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted waiting for client thread", ie);
} catch (TimeoutException te) {
throw new RuntimeException("Timed out waiting for client thread", te);
} catch (ExecutionException ee) {
// unwrap original cause
Throwable cause = ee.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(cause);
}
}

/**
* Will run r on the game thread after this method returns
* If r returns false, r will be ran again, at a later point
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,25 @@ public class ConfigProfile
@Getter
private final long id;
@Getter
@Setter(AccessLevel.PACKAGE)
private String name;
@Setter
public String name;
@Getter
@Setter(AccessLevel.PACKAGE)
private String password;
@Setter
public String password;
@Getter
@Setter(AccessLevel.PACKAGE)
private String bankPin;
@Setter
public String bankPin;
@Getter
@Setter
private long memberExpireDaysTimeStemp;
@Getter
@Setter
private long memberExpireDays;
@Getter
@Setter(AccessLevel.PACKAGE)
private boolean isMember;
@Setter
public boolean isMember;
@Getter
@Setter(AccessLevel.PACKAGE)
@Setter
private String discordWebhookUrl;
@Getter
@Setter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package net.runelite.client.plugins.microbot.example;

import lombok.extern.slf4j.Slf4j;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;

import javax.inject.Inject;
import java.awt.*;

@PluginDescriptor(
name = PluginDescriptor.Default + "Example Plugin",
description = "Performance test for GameObject composition retrieval",
tags = {"performance", "microbot", "test", "gameobject"},
enabledByDefault = false
)
@Slf4j
public class ExamplePlugin extends Plugin {
@Inject
ExampleScript exampleScript;


@Override
protected void startUp() throws AWTException {

exampleScript.run();
}



protected void shutDown() {
exampleScript.shutdown();
}

// on settings change
@Subscribe
public void onConfigChanged(final ConfigChanged event) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package net.runelite.client.plugins.microbot.example;

import lombok.extern.slf4j.Slf4j;
import net.runelite.client.plugins.microbot.Microbot;
import net.runelite.client.plugins.microbot.Script;
import net.runelite.client.plugins.microbot.util.tileobject.Rs2TileObjectApi;
import net.runelite.client.plugins.microbot.util.tileobject.Rs2TileObjectModel;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

/**
* Performance test script for measuring GameObject composition retrieval speed.
*
* This script runs every 5 seconds and performs the following:
* - Gets all GameObjects in the scene
* - Retrieves the ObjectComposition for each GameObject
* - Measures and logs the total time taken
* - Reports average time per object
*
* Useful for performance profiling and optimization testing.
*/
@Slf4j
public class ExampleScript extends Script {

/**
* Main entry point for the performance test script.
*/
public boolean run() {
mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> {
try {
if (!Microbot.isLoggedIn()) return;

// Performance test: Loop over game objects and get compositions
long startTime = System.currentTimeMillis();
AtomicLong endTime = new AtomicLong();

var tileObjects = Microbot.getClientThread().invoke(() -> {
// List<Rs2TileObjectModel> _tileObjects = Rs2TileObjectApi.getObjectsStream().filter(x -> x.getName() != null && !x.getName().isEmpty() && x.getName() != "null").collect(Collectors.toList());
Rs2TileObjectModel test = Rs2TileObjectApi.getNearest(tile -> tile.getName() != null && tile.getName().toLowerCase().contains("tree"));
endTime.set(System.currentTimeMillis());
System.out.println("Retrieved " + test.getName() + " game objects in " + (endTime.get() - startTime) + " ms");

/*for (Rs2TileObjectModel rs2TileObjectModel: _tileObjects) {
var name = rs2TileObjectModel.getName(); // Access name to simulate some processing
System.out.println("Object Name: " + name);
}
*/
return Rs2TileObjectApi.getObjectsStream().collect(Collectors.toList());
});


int compositionCount = 0;

/*for (Rs2TileObjectModel tileObject : tileObjects) {
var name = tileObject.getName(); // Access name to simulate some processing
if (name != null) {
compositionCount++;
System.out.println("composition " + compositionCount + ": " + name);
}
}*/

endTime.set(System.currentTimeMillis());
long durationMs = (endTime.get() - startTime);

/*
log.info("Performance Test Results:");
log.info(" Total GameObjects: {}", tileObjects.size());
log.info(" Compositions retrieved: {}", compositionCount);
log.info(" Time taken: {} ms", durationMs);
log.info(" Average time per object: {} μs",
tileObjects.size() > 0 ? (endTime.get() - startTime) / 1000 / tileObjects.size() : 0);
*/

} catch (Exception ex) {
log.error("Error in performance test loop", ex);
}
}, 0, 5000, TimeUnit.MILLISECONDS);

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1902,7 +1902,9 @@ public static ObjectComposition getObjectComposition(int id) {
ObjectComposition objectComposition = Microbot.getClientThread().runOnClientThreadOptional(() -> Microbot.getClient().getObjectDefinition(id))
.orElse(null);
if (objectComposition == null) return null;
return objectComposition.getImpostorIds() == null ? objectComposition : objectComposition.getImpostor();
return objectComposition.getImpostorIds() == null ?
objectComposition :
Microbot.getClientThread().runOnClientThreadOptional((objectComposition::getImpostor)).orElse(null);
}

public static boolean canWalkTo(TileObject tileObject, int distance) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,7 @@ public static boolean login() {
log.warn("Cannot login - client is not initialised");
return false;
}
int currentWorld = client.getWorld();
int targetWorld = currentWorld > 300 ? currentWorld : getRandomWorld(getActiveProfile().isMember());
int targetWorld = getRandomWorld(getActiveProfile().isMember());
return login(getActiveProfile().getName(), getActiveProfile().getPassword(), targetWorld);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package net.runelite.client.plugins.microbot.util.tileobject;

import net.runelite.api.GameObject;
import net.runelite.api.Player;
import net.runelite.api.Tile;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.plugins.microbot.Microbot;
import net.runelite.client.plugins.microbot.util.gameobject.Rs2GameObject;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

/**
* API for interacting with tile objects in the game world.
*/
public class Rs2TileObjectApi {

private static int lastUpdateObjects = 0;
private static List<Rs2TileObjectModel> tileObjects = new ArrayList<>();

/**
* Get all tile objects in the current scene
* @return Stream of Rs2TileObjectModel
*/
public static Stream<Rs2TileObjectModel> getObjectsStream() {

if (lastUpdateObjects >= Microbot.getClient().getTickCount()) {
return tileObjects.stream();
}

Player player = Microbot.getClient().getLocalPlayer();
if (player == null) return Stream.empty();

List<Rs2TileObjectModel> result = new ArrayList<>();

var tileValues = Microbot.getClient().getTopLevelWorldView().getScene().getTiles()[Microbot.getClient().getTopLevelWorldView().getPlane()];

for (Tile[] tileValue : tileValues) {
for (Tile tile : tileValue) {
if (tile == null) continue;

if (tile.getGameObjects() != null) {
for (GameObject gameObject : tile.getGameObjects()) {
if (gameObject == null) continue;
if (gameObject.getSceneMinLocation().equals(tile.getSceneLocation())) {
result.add(new Rs2TileObjectModel(gameObject));
}
}
}
if (tile.getGroundObject() != null) {
result.add(new Rs2TileObjectModel(tile.getGroundObject()));
}
if (tile.getWallObject() != null) {
result.add(new Rs2TileObjectModel(tile.getWallObject()));
}
if (tile.getDecorativeObject() != null) {
result.add(new Rs2TileObjectModel(tile.getDecorativeObject()));
}
}
}

tileObjects = result;
lastUpdateObjects = Microbot.getClient().getTickCount();
return result.stream();
}

/**
* Returns the nearest tile object matching the supplied predicate, or null if none match.
* Distance is based on straight-line world tile distance from the local player.
*
* @return nearest matching Rs2TileObjectModel or null
*/
public static Rs2TileObjectModel getNearest() {
return getNearest(null);
}

/**
* Returns the nearest tile object matching the supplied predicate, or null if none match.
* Distance is based on straight-line world tile distance from the local player.
*
* @param filter predicate to test objects
* @return nearest matching Rs2TileObjectModel or null
*/
public static Rs2TileObjectModel getNearest(Predicate<Rs2TileObjectModel> filter) {
Player player = Microbot.getClient().getLocalPlayer();
if (player == null) return null;

WorldPoint playerLoc = player.getWorldLocation();

Stream<Rs2TileObjectModel> stream = getObjectsStream();
if (filter != null) {
stream = stream.filter(filter);
}

return stream
.min(Comparator.comparingInt(o -> o.getWorldLocation().distanceTo(playerLoc)))
.orElse(null);
}
}
Loading