Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package net.runelite.client.plugins.microbot.moaaudit;

import lombok.extern.slf4j.Slf4j;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.microbot.util.walker.Rs2Walker;

// TEMP debug plugin: on enable, iterates every Map of Alacrity seasonal transport,
// attempts to teleport, and logs actual landing vs expected coord for each. Used to
// catch bad destination coords in seasonal_transports.tsv. Delete when done.
@PluginDescriptor(
name = PluginDescriptor.Default + "MoA Audit",
description = "[TEMP] Record Map of Alacrity teleport landing tiles",
tags = {"temp", "debug", "league", "microbot"},
enabledByDefault = false
)
@Slf4j
public class MoaAuditPlugin extends Plugin {
private Thread worker;

@Override
protected void startUp() {
worker = new Thread(Rs2Walker::runMoaAudit, "moa-audit");
worker.setDaemon(true);
worker.start();
}

@Override
protected void shutDown() {
if (worker != null) worker.interrupt();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ default boolean useMagicMushtrees() {
@ConfigItem(
keyName = "useSeasonalTransports",
name = "Use Seasonal Transports",
description = "Whether to include seasonal transports (e.g. clue compass) in the path.",
description = "Whether to include seasonal League transports (e.g. Map of Alacrity) in the path. League worlds only.",
position = 19,
section = sectionSettings
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public static boolean isTeleport(TransportType transportType) {
case TELEPORTATION_ITEM:
case TELEPORTATION_MINIGAME:
case TELEPORTATION_SPELL:
case SEASONAL_TRANSPORT:
return true;
default:
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,21 +153,45 @@ public List<Node> getNeighbors(Node node, VisitedTiles visited, PathfinderConfig

Set<Transport> transports = config.getTransportsPacked().getOrDefault(node.packedPosition, Collections.emptySet());

int moaSeenHere = 0;
int moaAddedHere = 0;
int moaVisited = 0;
int moaIgnored = 0;

// Transports are pre-filtered by PathfinderConfig.refreshTransports
// Thus any transports in the list are guaranteed to be valid per the user's settings
for (Transport transport : transports) {
boolean isMoa = transport.getType() == TransportType.SEASONAL_TRANSPORT
&& transport.getDisplayInfo() != null
&& transport.getDisplayInfo().toLowerCase().contains("map of alacrity");
if (isMoa) moaSeenHere++;

//START microbot variables
if (visited.get(transport.getDestination())) continue;
if (visited.get(transport.getDestination())) {
if (isMoa) moaVisited++;
continue;
}

if (TransportType.isTeleport(transport.getType())) {
if (config.isIgnoreTeleportAndItems()) continue;
if (config.isIgnoreTeleportAndItems()) {
if (isMoa) moaIgnored++;
continue;
}
neighbors.add(new TransportNode(transport.getDestination(), node, config.getDistanceBeforeUsingTeleport() + transport.getDuration()));
if (isMoa) moaAddedHere++;
} else {
neighbors.add(new TransportNode(transport.getDestination(), node, transport.getDuration()));
}
//END microbot variables
}

if (moaSeenHere > 0) {
log.debug("[MoA] getNeighbors @ ({},{},{}): seen={} added={} visited={} ignored={} (distanceBeforeUsingTeleport={}, cost={})",
x, y, z, moaSeenHere, moaAddedHere, moaVisited, moaIgnored,
config.getDistanceBeforeUsingTeleport(),
config.getDistanceBeforeUsingTeleport() + 4);
}
Comment on lines +188 to +193
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Logged cost diverges from the actual TransportNode cost.

At line 149 the cost added for a MoA teleport neighbor is config.getDistanceBeforeUsingTeleport() + transport.getDuration(), but this debug line logs config.getDistanceBeforeUsingTeleport() + 4 — a hardcoded 4 instead of transport.getDuration(). If any MoA TSV row has a duration ≠ 4, the "cost" field in this log will silently misreport what the pathfinder actually used, which defeats the purpose of the audit log. It's also a single summary value for a loop that may have added multiple MoA transports with different durations.

🔧 Suggested fix: track the real per-neighbor cost during the loop
         int moaSeenHere = 0;
         int moaAddedHere = 0;
         int moaVisited = 0;
         int moaIgnored = 0;
+        int moaLastCost = -1;
@@
             if (TransportType.isTeleport(transport.getType())) {
                 if (config.isIgnoreTeleportAndItems()) {
                     if (isMoa) moaIgnored++;
                     continue;
                 }
-                neighbors.add(new TransportNode(transport.getDestination(), node, config.getDistanceBeforeUsingTeleport() + transport.getDuration()));
-                if (isMoa) moaAddedHere++;
+                int teleCost = config.getDistanceBeforeUsingTeleport() + transport.getDuration();
+                neighbors.add(new TransportNode(transport.getDestination(), node, teleCost));
+                if (isMoa) { moaAddedHere++; moaLastCost = teleCost; }
             } else {
                 neighbors.add(new TransportNode(transport.getDestination(), node, transport.getDuration()));
             }
@@
         if (moaSeenHere > 0) {
-            log.debug("[MoA] getNeighbors @ ({},{},{}): seen={} added={} visited={} ignored={} (distanceBeforeUsingTeleport={}, cost={})",
-                    x, y, z, moaSeenHere, moaAddedHere, moaVisited, moaIgnored,
-                    config.getDistanceBeforeUsingTeleport(),
-                    config.getDistanceBeforeUsingTeleport() + 4);
+            log.debug("[MoA] getNeighbors @ ({},{},{}): seen={} added={} visited={} ignored={} (distanceBeforeUsingTeleport={}, lastCost={})",
+                    x, y, z, moaSeenHere, moaAddedHere, moaVisited, moaIgnored,
+                    config.getDistanceBeforeUsingTeleport(), moaLastCost);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/CollisionMap.java`
around lines 157 - 162, The debug log after processing MoA neighbors misreports
the TransportNode cost by hardcoding "+ 4" instead of using each transport's
actual duration; update getNeighbors so it accumulates or records the real
per-neighbor cost(s) (e.g., compute cost =
config.getDistanceBeforeUsingTeleport() + transport.getDuration() when creating
each TransportNode) and then include those actual costs in the debug message
(replace the hardcoded "+ 4" with the tracked duration values or a summary of
the per-neighbor costs) so moaSeenHere/moaAddedHere/moaVisited/moaIgnored
logging reflects the true TransportNode cost(s).


if (isBlocked(x, y, z)) {
boolean westBlocked = isBlocked(x - 1, y, z);
boolean eastBlocked = isBlocked(x + 1, y, z);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,13 +332,21 @@ private void refreshTransports(WorldPoint target) {
long useTransportTimeNanos = 0;
Map<TransportType, int[]> typeStats = new java.util.EnumMap<>(TransportType.class);

int moaSeen = 0;
int moaKept = 0;

for (Map.Entry<WorldPoint, Set<Transport>> entry : mergedList.entrySet()) {
WorldPoint point = entry.getKey();
Set<Transport> usableTransports = new HashSet<>(entry.getValue().size());
for (Transport transport : entry.getValue()) {
totalTransports++;
updateActionBasedOnQuestState(transport);

boolean isMoa = transport.getType() == TransportType.SEASONAL_TRANSPORT
&& transport.getDisplayInfo() != null
&& transport.getDisplayInfo().toLowerCase().contains("map of alacrity");
if (isMoa) moaSeen++;

long t0 = System.nanoTime();
boolean usable = useTransport(transport);
long elapsed = System.nanoTime() - t0;
Expand All @@ -357,6 +365,7 @@ private void refreshTransports(WorldPoint target) {
} else {
usableTransports.add(transport);
}
if (isMoa) moaKept++;
}

if (point != null && !usableTransports.isEmpty()) {
Expand Down Expand Up @@ -385,6 +394,10 @@ private void refreshTransports(WorldPoint target) {
.limit(5)
.forEach(e -> log.info("[refreshTransports] {} : count={}, usable={}, time={}ms",
e.getKey(), e.getValue()[0], e.getValue()[1], e.getValue()[2] / 1000));

log.debug("[MoA] refreshTransports: seen={} kept={} (useSeasonalTransports={}, VarbitID.LEAGUE_TYPE={})",
moaSeen, moaKept, useSeasonalTransports,
Microbot.getVarbitValue(10032));
}


Expand Down Expand Up @@ -553,9 +566,22 @@ private boolean varplayerChecks(Transport transport) {
}

private boolean useTransport(Transport transport) {
boolean traceMoa = transport.getType() == TransportType.SEASONAL_TRANSPORT
&& transport.getDisplayInfo() != null
&& transport.getDisplayInfo().toLowerCase().contains("map of alacrity");

// Session blacklist: once an MoA destination fails at runtime (locked region or
// unrecognised name), don't let the pathfinder keep routing through it.
if (traceMoa && Rs2Walker.blacklistedMoaDestinations.contains(
WorldPointUtil.packWorldPoint(transport.getDestination()))) {
return false;
}

// Check if the feature flag is disabled
if (!isFeatureEnabled(transport)) {
log.debug("Transport Type {} is disabled by feature flag", transport.getType());
if (traceMoa) log.debug("[MoA] rejected '{}' — feature flag disabled (useSeasonalTransports={})",
transport.getDisplayInfo(), useSeasonalTransports);
return false;
}
// If the transport requires you to be in a members world (used for more granular member requirements)
Expand All @@ -581,6 +607,8 @@ private boolean useTransport(Transport transport) {
// If the transport has varbit requirements & the varbits do not match
if (!varbitChecks(transport)) {
log.debug("Transport ( O: {} D: {} ) requires varbits {}", transport.getOrigin(), transport.getDestination(), transport.getVarbits());
if (traceMoa) log.debug("[MoA] rejected '{}' — varbit check failed (varbits={}, LEAGUE_TYPE={})",
transport.getDisplayInfo(), transport.getVarbits(), Microbot.getVarbitValue(10032));
return false;
}

Expand Down Expand Up @@ -637,6 +665,8 @@ private boolean useTransport(Transport transport) {
boolean hasRequiredItems = hasRequiredItems(transport);
if (!hasRequiredItems) {
log.debug("Transport ( O: {} D: {} ) requires items {}", transport.getOrigin(), transport.getDestination(), transport.getItemIdRequirements().stream().flatMap(Set::stream).collect(Collectors.toSet()));
if (traceMoa) log.debug("[MoA] rejected '{}' — missing required items {}",
transport.getDisplayInfo(), transport.getItemIdRequirements());
}
return hasRequiredItems;
}
Expand Down
Loading
Loading