diff --git a/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java index 69db696418d..94436f9823c 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java @@ -51,6 +51,7 @@ public class NpcDefinition public int crawlRotate180Animation = -1; public int crawlRotateLeftAnimation = -1; public int crawlRotateRightAnimation = -1; + public boolean idleAnimRestart; public short[] recolorToFind; public short[] recolorToReplace; public short[] retextureToFind; @@ -81,4 +82,5 @@ public class NpcDefinition public boolean canHideForOverlap; public int overlapTintHSL = 39188; public boolean unknown1 = false; + public boolean zbuf = true; } diff --git a/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java index 3a93d10baee..d229f09316c 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java @@ -68,6 +68,7 @@ public class ObjectDefinition private boolean obstructsGround = false; private int contouredGround = -1; private int supportsItems = -1; + private int raise; private int[] configChangeDest; private int category; private boolean isRotated = false; diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java index 726148d1385..0c0aa1ed5fc 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java @@ -383,6 +383,10 @@ else if (opcode == 129) { def.unknown1 = true; } + else if (opcode == 130) + { + def.idleAnimRestart = true; + } else if (opcode == 145) { def.canHideForOverlap = true; @@ -391,6 +395,10 @@ else if (opcode == 146) { def.overlapTintHSL = stream.readUnsignedShort(); } + else if (opcode == 147) + { + def.zbuf = false; + } else if (opcode == 249) { length = stream.readUnsignedByte(); diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java index f11aab4ac8b..d2abc4b3f41 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java @@ -396,6 +396,10 @@ else if (opcode == 95) { def.setSoundVisibility(is.readUnsignedByte()); } + else if (opcode == 96) + { + def.setRaise(is.readUnsignedByte()); + } else if (opcode == 249) { int length = is.readUnsignedByte(); diff --git a/gradle.properties b/gradle.properties index 84df6847473..813b1934873 100644 --- a/gradle.properties +++ b/gradle.properties @@ -28,10 +28,10 @@ org.gradle.parallel=true org.gradle.caching=false project.build.group=net.runelite -project.build.version=1.12.14 +project.build.version=1.12.15 glslang.path= -microbot.version=2.1.16 +microbot.version=2.1.17 microbot.commit.sha=nogit microbot.repo.url=http://138.201.81.246:8081/repository/microbot-snapshot/ microbot.repo.username= diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index 99430850dfe..2133c6fd70a 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -144,8 +144,7 @@ public interface Client extends OAuthApi, GameEngine void setGameState(GameState gameState); /** - * Causes the client to shutdown. It is faster than - * {@link java.applet.Applet#stop()} because it doesn't wait for 4000ms. + * Causes the client to shutdown. * This will call {@link System#exit} when it is done */ void stopNow(); diff --git a/runelite-api/src/main/java/net/runelite/api/ClientConfiguration.java b/runelite-api/src/main/java/net/runelite/api/ClientConfiguration.java new file mode 100644 index 00000000000..f7d0eb891e3 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/ClientConfiguration.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api; + +import java.net.URL; + +public interface ClientConfiguration +{ + URL getCodeBase(); + String getParameter(String key); + void onError(String code); +} diff --git a/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java b/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java index 4b80d581f50..15081b5584f 100644 --- a/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java +++ b/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java @@ -55,6 +55,10 @@ public interface DecorativeObject extends TileObject */ int getYOffset(); + int getXOffset2(); + + int getYOffset2(); + /** * A bitfield containing various flags: *
{@code
diff --git a/runelite-api/src/main/java/net/runelite/api/GameEngine.java b/runelite-api/src/main/java/net/runelite/api/GameEngine.java
index 1de44385eaa..c37befbdfb7 100644
--- a/runelite-api/src/main/java/net/runelite/api/GameEngine.java
+++ b/runelite-api/src/main/java/net/runelite/api/GameEngine.java
@@ -31,6 +31,9 @@
*/
public interface GameEngine
{
+ void setConfiguration(ClientConfiguration configuration);
+ void initialize();
+
/**
* Gets the canvas that contains everything.
*
diff --git a/runelite-api/src/main/java/net/runelite/api/Perspective.java b/runelite-api/src/main/java/net/runelite/api/Perspective.java
index c8477c051b6..ca5d282e49b 100644
--- a/runelite-api/src/main/java/net/runelite/api/Perspective.java
+++ b/runelite-api/src/main/java/net/runelite/api/Perspective.java
@@ -132,8 +132,8 @@ public static Point localToCanvas(@Nonnull Client client, @Nonnull LocalPoint po
}
LocalPoint entityLocation = we.getLocalLocation();
- int height = getTileHeight(we.getWorldView(), point.getX(), point.getY(), plane); // height in wv
- height += getTileHeight(wv, entityLocation.getX(), entityLocation.getY(), wv.getPlane()); // height of we
+ int height = we.getWorldView().getTileHeight(point.getX(), point.getY(), plane); // height in wv
+ height += wv.getTileHeight(entityLocation.getX(), entityLocation.getY(), wv.getPlane()); // height of we
height -= heightOffset;
WorldView subWv = we.getWorldView();
@@ -671,30 +671,6 @@ public static int getFootprintTileHeight(@Nonnull Client client, @Nonnull LocalP
return h;
}
- /**
- * Get the height of a location, in local coordinates. Interpolates the height from the adjacent tiles.
- * Does not account for bridges.
- * @return
- */
- private static int getTileHeight(@Nonnull WorldView wv, int localX, int localY, int plane)
- {
- int offset = wv.isTopLevel() ? ESCENE_OFFSET : 0;
- int sceneX = (localX >> LOCAL_COORD_BITS) + offset;
- int sceneY = (localY >> LOCAL_COORD_BITS) + offset;
- if (sceneX >= 0 && sceneY >= 0 && sceneX < wv.getSizeX() + offset && sceneY < wv.getSizeY() + offset)
- {
- int[][][] tileHeights = wv.getScene().getTileHeights();
-
- int x = localX & (LOCAL_TILE_SIZE - 1);
- int y = localY & (LOCAL_TILE_SIZE - 1);
- int var8 = x * tileHeights[plane][sceneX + 1][sceneY] + (LOCAL_TILE_SIZE - x) * tileHeights[plane][sceneX][sceneY] >> LOCAL_COORD_BITS;
- int var9 = tileHeights[plane][sceneX][sceneY + 1] * (LOCAL_TILE_SIZE - x) + x * tileHeights[plane][sceneX + 1][sceneY + 1] >> LOCAL_COORD_BITS;
- return (LOCAL_TILE_SIZE - y) * var8 + y * var9 >> LOCAL_COORD_BITS;
- }
-
- return 0;
- }
-
/**
* Calculates a tile polygon from offset worldToScreen() points.
*
@@ -761,10 +737,11 @@ public static Polygon getCanvasTileAreaPoly(
}
int offset = wv.isTopLevel() ? ESCENE_OFFSET : 0;
+ int escene = offset << 1;
final int msx = localLocation.getSceneX() + offset;
final int msy = localLocation.getSceneY() + offset;
- if (msx < 0 || msy < 0 || msx >= wv.getSizeX() + offset || msy >= wv.getSizeY() + offset)
+ if (msx < 0 || msy < 0 || msx >= wv.getSizeX() + escene || msy >= wv.getSizeY() + escene)
{
// out of scene
return null;
@@ -778,10 +755,10 @@ public static Polygon getCanvasTileAreaPoly(
var scene = wv.getScene();
final byte[][][] tileSettings = scene.getExtendedTileSettings();
- int tilePlane = level;
+ int mapLevel = level;
if (level < Constants.MAX_Z - 1 && (tileSettings[1][msx][msy] & TILE_FLAG_BRIDGE) == TILE_FLAG_BRIDGE)
{
- tilePlane = level + 1;
+ mapLevel = level + 1;
}
final int swX = localLocation.getX() - (sizeX * LOCAL_TILE_SIZE / 2);
@@ -796,10 +773,10 @@ public static Polygon getCanvasTileAreaPoly(
final int nwX = neX;
final int nwY = swY;
- final int swHeight = getTileHeight(wv, swX, swY, tilePlane) - heightOffset;
- final int nwHeight = getTileHeight(wv, nwX, nwY, tilePlane) - heightOffset;
- final int neHeight = getTileHeight(wv, neX, neY, tilePlane) - heightOffset;
- final int seHeight = getTileHeight(wv, seX, seY, tilePlane) - heightOffset;
+ final int swHeight = wv.getTileHeight(swX, swY, mapLevel) - heightOffset;
+ final int nwHeight = wv.getTileHeight(nwX, nwY, mapLevel) - heightOffset;
+ final int neHeight = wv.getTileHeight(neX, neY, mapLevel) - heightOffset;
+ final int seHeight = wv.getTileHeight(seX, seY, mapLevel) - heightOffset;
Point p1 = localToCanvas(client, wv.getId(), swX, swY, swHeight);
Point p2 = localToCanvas(client, wv.getId(), nwX, nwY, nwHeight);
diff --git a/runelite-api/src/main/java/net/runelite/api/Renderable.java b/runelite-api/src/main/java/net/runelite/api/Renderable.java
index b47d52fcd28..b3162b3e91b 100644
--- a/runelite-api/src/main/java/net/runelite/api/Renderable.java
+++ b/runelite-api/src/main/java/net/runelite/api/Renderable.java
@@ -24,6 +24,8 @@
*/
package net.runelite.api;
+import org.intellij.lang.annotations.MagicConstant;
+
/**
* Represents an object that can be rendered.
*/
@@ -42,4 +44,12 @@ public interface Renderable extends Node
void setModelHeight(int modelHeight);
int getAnimationHeightOffset();
+
+ @MagicConstant(intValues = {RENDERMODE_DEFAULT, RENDERMODE_SORTED, RENDERMODE_SORTED_NO_DEPTH, RENDERMODE_UNSORTED})
+ int getRenderMode();
+
+ int RENDERMODE_DEFAULT = 0;
+ int RENDERMODE_SORTED = 1;
+ int RENDERMODE_SORTED_NO_DEPTH = 2;
+ int RENDERMODE_UNSORTED = 3;
}
diff --git a/runelite-api/src/main/java/net/runelite/api/WorldView.java b/runelite-api/src/main/java/net/runelite/api/WorldView.java
index 01bf39cb8fe..34c1636d9f7 100644
--- a/runelite-api/src/main/java/net/runelite/api/WorldView.java
+++ b/runelite-api/src/main/java/net/runelite/api/WorldView.java
@@ -264,4 +264,10 @@ Projectile createProjectile(int id, int plane, int startX, int startY, int start
*/
@MagicConstant(intValues = {Constants.CLICK_ACTION_NONE, Constants.CLICK_ACTION_WALK, Constants.CLICK_ACTION_SET_HEADING})
int getYellowClickAction();
+
+ /**
+ * Gets the tile height at the given coordinates, interpolating the height from adjacent tiles.
+ * @return
+ */
+ int getTileHeight(int x, int y, int maplevel);
}
diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java
index 188281d9eca..83fee7358ad 100644
--- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java
+++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java
@@ -32,11 +32,49 @@
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
-import joptsimple.*;
+import java.io.File;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import javax.management.ObjectName;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import javax.swing.SwingUtilities;
+import joptsimple.ArgumentAcceptingOptionSpec;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+import joptsimple.ValueConversionException;
+import joptsimple.ValueConverter;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
-import net.runelite.api.Constants;
import net.runelite.client.account.SessionManager;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.discord.DiscordService;
@@ -152,7 +190,6 @@ public class RuneLite
private Gson gson;
@Inject
- @Nullable
private Client client;
@Inject
@@ -435,15 +472,10 @@ public void start() throws Exception
// Start the applet
copyJagexCache();
- // Client size must be set prior to init
- var applet = (Applet) client;
- applet.setSize(Constants.GAME_FIXED_SIZE);
-
System.setProperty("jagex.disableBouncyCastle", "true");
System.setProperty("jagex.userhome", RUNELITE_DIR.getAbsolutePath());
- applet.init();
- applet.start();
+ client.initialize();
SplashScreen.stage(.57, null, "Loading configuration");
diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java
index a120f34b255..27cbaec3d84 100644
--- a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java
+++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java
@@ -32,7 +32,6 @@
import com.google.inject.binder.ConstantBindingBuilder;
import com.google.inject.name.Names;
import com.google.inject.util.Providers;
-import java.applet.Applet;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
@@ -152,13 +151,6 @@ else if (entry.getValue() instanceof Boolean)
requestStaticInjection(Microbot.class);
}
- @Provides
- @Singleton
- Applet provideApplet(Client client)
- {
- return (Applet) client;
- }
-
@Provides
@Singleton
Client provideClient()
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabInterface.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabInterface.java
index 0cc7546ff96..b88520f1770 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabInterface.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabInterface.java
@@ -611,7 +611,7 @@ private void opTagTab(ScriptEvent event)
{
if (client.getVarbitValue(VarbitID.BANK_CURRENTTAB) == PotionStorage.BANKTAB_POTIONSTORE)
{
- // Opening a tag tab with the potion store open would leave the store open in the bankground,
+ // Opening a tag tab with the potion store open would leave the store open in the background,
// making deposits not work. Force close the potion store.
log.debug("Closing potion store");
client.menuAction(-1, InterfaceID.Bankmain.POTIONSTORE_BUTTON, MenuAction.CC_OP, 1, -1, "Potion store", "");
@@ -873,7 +873,9 @@ public void onMenuOptionClicked(MenuOptionClicked event)
}
}
- if (event.getMenuOption().startsWith("View tab") || event.getMenuOption().equals("View all items") || event.getMenuOption().equals("Potion store"))
+ MenuEntry menuEntry = event.getMenuEntry();
+ if (event.getMenuOption().startsWith("View tab") || event.getMenuOption().equals("View all items")
+ || (menuEntry.getType() == MenuAction.CC_OP && menuEntry.getParam1() == InterfaceID.Bankmain.POTIONSTORE_BUTTON))
{
closeTag(false);
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
index 4744cb2f184..e1eb875364a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
@@ -28,7 +28,21 @@
import com.google.common.primitives.Ints;
import com.google.inject.Provides;
import lombok.extern.slf4j.Slf4j;
-import net.runelite.api.*;
+import net.runelite.api.BufferProvider;
+import net.runelite.api.Client;
+import net.runelite.api.Constants;
+import net.runelite.api.FloatProjection;
+import net.runelite.api.GameObject;
+import net.runelite.api.GameState;
+import net.runelite.api.Model;
+import net.runelite.api.Perspective;
+import net.runelite.api.Projection;
+import net.runelite.api.Renderable;
+import net.runelite.api.Scene;
+import net.runelite.api.TextureProvider;
+import net.runelite.api.TileObject;
+import net.runelite.api.WorldEntity;
+import net.runelite.api.WorldView;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.PostClientTick;
import net.runelite.api.hooks.DrawCallbacks;
@@ -1214,12 +1228,13 @@ public void drawTemp(Projection worldProjection, Scene scene, GameObject gameObj
Renderable renderable = gameObject.getRenderable();
int size = m.getFaceCount() * 3 * VAO.VERT_SIZE;
- if (renderable instanceof Player || m.getFaceTransparencies() != null)
+ int renderMode = renderable.getRenderMode();
+ if (renderMode == Renderable.RENDERMODE_SORTED_NO_DEPTH || m.getFaceTransparencies() != null)
{
// opaque player faces have their own vao and are drawn in a separate pass from normal opaque faces
// because they are not depth tested. transparent player faces don't need their own vao because normal
// transparent faces are already not depth tested
- VAO o = renderable instanceof Player ? vaoPO.get(size) : vaoO.get(size);
+ VAO o = renderMode == Renderable.RENDERMODE_SORTED_NO_DEPTH ? vaoPO.get(size) : vaoO.get(size);
VAO a = vaoA.get(size);
int start = a.vbo.vb.position();
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java
index 65c90c91eda..1989414d130 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java
@@ -322,7 +322,7 @@ private int uploadZoneTile(Scene scene, Zone zone, Tile t, GpuIntBuffer vertexBu
uploadZoneRenderable(renderable, zone, 0, decorativeObject.getX() + decorativeObject.getXOffset(), decorativeObject.getZ(), decorativeObject.getY() + decorativeObject.getYOffset(), -1, -1, -1, -1, decorativeObject.getId(), vertexBuffer, ab);
Renderable renderable2 = decorativeObject.getRenderable2();
- uploadZoneRenderable(renderable2, zone, 0, decorativeObject.getX(), decorativeObject.getZ(), decorativeObject.getY(), -1, -1, -1, -1, decorativeObject.getId(), vertexBuffer, ab);
+ uploadZoneRenderable(renderable2, zone, 0, decorativeObject.getX() + decorativeObject.getXOffset2(), decorativeObject.getZ(), decorativeObject.getY() + decorativeObject.getYOffset2(), -1, -1, -1, -1, decorativeObject.getId(), vertexBuffer, ab);
}
GroundObject groundObject = t.getGroundObject();
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.java
index b3e77b14b48..5bc9bed8e19 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.java
@@ -313,7 +313,6 @@ static class AlphaModel
// only set for static geometry as they require sorting
int radius;
int[] packedFaces;
- byte[] renderPriorities;
static final int SKIP = 1; // temporary model is in a closer zone
static final int TEMP = 2; // temporary model added to a closer zone
@@ -422,7 +421,6 @@ void addAlphaModel(int vao, Model model, int startpos, int endpos, int x, int y,
assert radius >= 0;
- m.renderPriorities = model.getFaceRenderPriorities();
m.radius = 2 + (int) Math.sqrt(radius);
assert packedFaces.length > 0;
@@ -462,7 +460,6 @@ void removeTemp()
{
alphaModels.remove(i);
m.packedFaces = null;
- m.renderPriorities = null;
modelCache.add(m);
}
m.flags &= ~AlphaModel.SKIP;
@@ -643,54 +640,16 @@ void renderAlpha(int zx, int zz, int cyaw, int cpitch, int minLevel, int current
flush();
}
- byte[] faceRenderPriorities = m.renderPriorities;
final int start = m.startpos / (VERT_SIZE >> 2); // ints to verts
- if (faceRenderPriorities == null)
+ for (int i = diameter - 1; i >= 0; --i)
{
- for (int i = diameter - 1; i >= 0; --i)
+ for (char face = zsortHead[i]; face != (char) -1; face = zsortNext[face])
{
- for (char face = zsortHead[i]; face != (char) -1; face = zsortNext[face])
- {
- int faceIdx = face * 3;
- faceIdx += start;
- alphaElements.put(faceIdx++);
- alphaElements.put(faceIdx++);
- alphaElements.put(faceIdx++);
- }
- }
- }
- else
- {
- // Vanilla uses priority draw order for alpha faces and not depth draw order
- // And since we don't have the full model here, only the alpha faces, we can't compute the
- // 10/11 insertion points either. Just ignore those since I think they are mostly for players,
- // which are rendered differently anyway.
- Arrays.fill(numOfPriority, 0);
-
- for (int i = diameter - 1; i >= 0; --i)
- {
- for (char face = zsortHead[i]; face != (char) -1; face = zsortNext[face])
- {
- final byte pri = faceRenderPriorities[face];
- final int distIdx = numOfPriority[pri]++;
-
- orderedFaces[pri][distIdx] = face;
- }
- }
-
- for (int pri = 0; pri < 12; ++pri)
- {
- final int priNum = numOfPriority[pri];
- final int[] priFaces = orderedFaces[pri];
-
- for (int faceIdx = 0; faceIdx < priNum; ++faceIdx)
- {
- final int face = priFaces[faceIdx];
- int idx = face * 3 + start;
- alphaElements.put(idx++);
- alphaElements.put(idx++);
- alphaElements.put(idx++);
- }
+ int faceIdx = face * 3;
+ faceIdx += start;
+ alphaElements.put(faceIdx++);
+ alphaElements.put(faceIdx++);
+ alphaElements.put(faceIdx++);
}
}
}
@@ -806,7 +765,6 @@ void multizoneLocs(Scene scene, int zx, int zz, int cx, int cz, Zone[][] zones)
m2.zofz = (byte) (closestZoneZ - zz);
m2.packedFaces = m.packedFaces;
- m2.renderPriorities = m.renderPriorities;
m2.radius = m.radius;
m2.flags = AlphaModel.TEMP;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java
index fe8708a9782..9acd04a10f4 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java
@@ -280,7 +280,7 @@ private void init()
// Regular overload (NMZ)
add(combo(SUPER_ATTACK_POT, SUPER_STRENGTH_POT, SUPER_DEFENCE_POT, superRangingPot, superMagicPot, heal(HITPOINTS, -50)), ItemID.NZONE1DOSEOVERLOADPOTION, ItemID.NZONE2DOSEOVERLOADPOTION, ItemID.NZONE3DOSEOVERLOADPOTION, ItemID.NZONE4DOSEOVERLOADPOTION);
// Blighted overload (DMM)
- add(combo(boost(ATTACK, perc(.15, 8)), boost(STRENGTH, perc(.15, 8)), new BoostedStatBoost(DEFENCE, false, perc(.1, -1)), boost(RANGED, perc(.1, 7)), boost(MAGIC, perc(.1, 1)), heal(HITPOINTS, -25)), ItemID.DEADMAN1DOSEOVERLOAD, ItemID.DEADMAN2DOSEOVERLOAD, ItemID.DEADMAN3DOSEOVERLOAD, ItemID.DEADMAN4DOSEOVERLOAD);
+ add(combo(boost(ATTACK, perc(.15, 8)), boost(STRENGTH, perc(.15, 8)), new BoostedStatBoost(DEFENCE, false, perc(.1, -1)), boost(RANGED, perc(.1, 7)), boost(MAGIC, perc(.1, 1)), heal(HITPOINTS, -10)), ItemID.DEADMAN1DOSEOVERLOAD, ItemID.DEADMAN2DOSEOVERLOAD, ItemID.DEADMAN3DOSEOVERLOAD, ItemID.DEADMAN4DOSEOVERLOAD);
// Bandages (Castle Wars)
add(new CastleWarsBandage(), ItemID.CASTLEWARS_BANDAGES);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java
index 603d5fb3044..bd0120d8d88 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java
@@ -1604,7 +1604,7 @@ else if (parent != null && menuEntry.getOption().hashCode() == wornItemSwapConfi
{
final int componentId = w.getId(); // on dynamic components, this is the parent layer id
final int itemId = w.getIndex() == -1 ? -1 : ItemVariationMapping.map(w.getItemId());
- final Integer op = getUiSwapConfig(shiftModifier(), componentId, itemId);
+ final Integer op = getMigratedUiSwapConfig(shiftModifier(), componentId, itemId);
if (op != null && op == menuEntry.getIdentifier())
{
swap(menu, menuEntries, index, menuEntries.length - 1);
@@ -1985,6 +1985,30 @@ private int defaultOp(ItemComposition itemComposition, boolean shift)
return -1; // use
}
+ private Integer getMigratedUiSwapConfig(boolean shift, int componentId, int itemId)
+ {
+ Integer swap = getUiSwapConfig(shift, componentId, itemId);
+ if (componentId == InterfaceID.Bankmain.ITEMS)
+ {
+ // remap 12.13 -> 12.12 for 1/28/2026 game update
+ if (swap == null)
+ {
+ swap = getUiSwapConfig(shift, InterfaceID.Bankmain.SCROLLBAR, itemId);
+ if (swap != null)
+ {
+ unsetUiSwapConfig(shift, InterfaceID.Bankmain.SCROLLBAR, itemId);
+ setUiSwapConfig(shift, InterfaceID.Bankmain.ITEMS, itemId, swap);
+ log.debug("Migrated swap {} for {} from scrollbar to items", swap, itemId);
+ }
+ }
+ else
+ {
+ unsetUiSwapConfig(shift, InterfaceID.Bankmain.SCROLLBAR, itemId);
+ }
+ }
+ return swap;
+ }
+
private Integer getUiSwapConfig(boolean shift, int componentId, int itemId)
{
String config = configManager.getConfiguration(MenuEntrySwapperConfig.GROUP,
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/api/actor/Rs2ActorModel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/api/actor/Rs2ActorModel.java
index 8a04bbabc1f..1767e21ee5c 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/api/actor/Rs2ActorModel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/api/actor/Rs2ActorModel.java
@@ -402,6 +402,11 @@ public int getAnimationHeightOffset()
return actor.getAnimationHeightOffset();
}
+ @Override
+ public int getRenderMode() {
+ return actor.getRenderMode();
+ }
+
@Override
public Model getModel()
{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/api/tileitem/models/Rs2TileItemModel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/api/tileitem/models/Rs2TileItemModel.java
index c691c6a222e..d04f1735697 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/api/tileitem/models/Rs2TileItemModel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/api/tileitem/models/Rs2TileItemModel.java
@@ -77,6 +77,11 @@ public int getAnimationHeightOffset() {
return tileItem.getAnimationHeightOffset();
}
+ @Override
+ public int getRenderMode() {
+ return tileItem.getRenderMode();
+ }
+
@Override
public Node getNext() {
return tileItem.getNext();
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/MagicMushtree.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/MagicMushtree.java
new file mode 100644
index 00000000000..12d5eaa87dd
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/MagicMushtree.java
@@ -0,0 +1,99 @@
+package net.runelite.client.plugins.microbot.shortestpath;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import net.runelite.api.TileObject;
+import net.runelite.api.coords.WorldPoint;
+import net.runelite.client.plugins.microbot.util.player.Rs2Player;
+import net.runelite.client.plugins.microbot.util.widget.Rs2Widget;
+
+import static net.runelite.client.plugins.microbot.util.Global.sleepUntil;
+
+/**
+ * Handles the Magic Mushtree (Mycelium Transportation System) on Fossil Island.
+ * The mushtree network connects four locations:
+ * - House on the Hill
+ * - Verdant Valley
+ * - Sticky Swamp
+ * - Mushroom Meadow
+ */
+@Getter
+@RequiredArgsConstructor
+public enum MagicMushtree {
+ HOUSE_ON_THE_HILL("House on the Hill", new WorldPoint(3764, 3879, 1)),
+ VERDANT_VALLEY("Verdant Valley", new WorldPoint(3760, 3758, 0)),
+ STICKY_SWAMP("Sticky Swamp", new WorldPoint(3676, 3755, 0)),
+ MUSHROOM_MEADOW("Mushroom Meadow", new WorldPoint(3676, 3871, 0));
+
+ private final String destinationName;
+ private final WorldPoint destination;
+
+ // Object IDs for the Magic Mushtrees
+ public static final int MUSHTREE_HOUSE_ON_HILL = 30920;
+ public static final int MUSHTREE_OTHER = 30924;
+
+ private static final int OFFSET = 10;
+
+ /**
+ * Checks if the given object ID is a Magic Mushtree.
+ */
+ public static boolean isMagicMushtree(int objectId) {
+ return objectId == MUSHTREE_HOUSE_ON_HILL || objectId == MUSHTREE_OTHER;
+ }
+
+ /**
+ * Checks if the given TileObject is a Magic Mushtree.
+ */
+ public static boolean isMagicMushtree(TileObject tileObject) {
+ return tileObject != null && isMagicMushtree(tileObject.getId());
+ }
+
+ /**
+ * Handles the Magic Mushtree transport after the initial "Use" interaction.
+ * Waits for the menu to appear, then clicks the appropriate destination.
+ *
+ * @param transport The transport containing the destination
+ * @return true if the transport was handled successfully
+ */
+ public static boolean handleTransport(Transport transport) {
+ WorldPoint dest = transport.getDestination();
+ MagicMushtree destination = getByDestination(dest);
+
+ if (destination == null) {
+ return false;
+ }
+
+ // Wait for the mushtree menu widget to appear
+ if (!sleepUntil(() -> Rs2Widget.hasWidget("Mycelium"), 5000)) {
+ return false;
+ }
+
+ // Click the destination option
+ if (!Rs2Widget.clickWidget(destination.getDestinationName())) {
+ return false;
+ }
+
+ // Wait until we arrive at destination
+ sleepUntil(() -> Rs2Player.getWorldLocation().distanceTo(dest) < OFFSET, 10000);
+ return true;
+ }
+
+ /**
+ * Gets the MagicMushtree enum by destination WorldPoint.
+ */
+ public static MagicMushtree getByDestination(WorldPoint destination) {
+ if (destination == null) return null;
+
+ for (MagicMushtree mushtree : values()) {
+ WorldPoint dest = mushtree.getDestination();
+ if (dest.equals(destination)) {
+ return mushtree;
+ }
+ // Also match by X and Y only (ignore plane differences in destination matching)
+ if (dest.getX() == destination.getX() && dest.getY() == destination.getY()) {
+ return mushtree;
+ }
+ }
+ return null;
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/ActorModel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/ActorModel.java
index 7f538df1147..0e46d2fec71 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/ActorModel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/ActorModel.java
@@ -397,6 +397,11 @@ public int getAnimationHeightOffset()
return actor.getAnimationHeightOffset();
}
+ @Override
+ public int getRenderMode() {
+ return actor.getRenderMode();
+ }
+
@Override
public Model getModel()
{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/enums/BankLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/enums/BankLocation.java
index f76956c04a8..af2d3fb40cd 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/enums/BankLocation.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/enums/BankLocation.java
@@ -11,6 +11,7 @@
import net.runelite.api.gameval.VarbitID;
import net.runelite.client.plugins.microbot.Microbot;
import net.runelite.client.plugins.microbot.util.equipment.Rs2Equipment;
+import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory;
import net.runelite.client.plugins.microbot.util.player.Rs2Player;
@Getter
@@ -38,6 +39,9 @@ public enum BankLocation {
CRAFTING_GUILD(new WorldPoint(2936, 3281, 0), true),
DARKFROST(new WorldPoint(1527, 3292, 0), true),
DARKMEYER(new WorldPoint(3604, 3366, 0), true),
+ DEEPFIN_POINT(new WorldPoint(1935, 2755,0), true),
+ DEEPFIN_MINE_MID(new WorldPoint(2011, 9186,0), true),
+ DEEPFIN_MINE_EAST(new WorldPoint(2094, 9197,0), true),
DORGESH_KAAN_BANK(new WorldPoint(2702, 5350, 0), true),
DRAYNOR_VILLAGE(new WorldPoint(3093, 3245, 0), false),
DUEL_ARENA(new WorldPoint(3381, 3268, 0), true),
@@ -98,7 +102,8 @@ public enum BankLocation {
SHILO_VILLAGE(new WorldPoint(2852, 2954, 0), true),
SOPHANEM(new WorldPoint(2799, 5169, 0), true),
SULPHUR_MINE(new WorldPoint(1453, 3858, 0), true),
- TAL_TEKLAN(new WorldPoint(1243, 3121, 0), true),
+ SUNBLEAK_ISLAND(new WorldPoint(2195, 2314, 0), true),
+ TAL_TEKLAN(new WorldPoint(1243, 3121, 0), true),
TREE_GNOME_STRONGHOLD_NIEVE(new WorldPoint(2445, 3424, 1), true),
TZHAAR(new WorldPoint(2446, 5178, 0), true),
VARLAMORE_EAST(new WorldPoint(1780, 3094, 0), true),
@@ -128,7 +133,9 @@ public boolean hasRequirements() {
boolean isWearingCraftingGuild = (Rs2Equipment.isWearing("brown apron") || Rs2Equipment.isWearing("golden apron")) ||
(Rs2Equipment.isWearing("max cape") || Rs2Equipment.isWearing("max hood")) ||
(Rs2Equipment.isWearing("crafting cape") || Rs2Equipment.isWearing("crafting hood"));
- return isWearingCraftingGuild && (hasMaxedCrafting || hasFaladorHardDiary);
+ // Also check if crafting cape is in inventory (can equip it to teleport and enter)
+ boolean hasCraftingCapeInInventory = Rs2Inventory.contains("crafting cape") || Rs2Inventory.contains("crafting cape(t)");
+ return (isWearingCraftingGuild || hasCraftingCapeInInventory) && (hasMaxedCrafting || hasFaladorHardDiary);
case LUMBRIDGE_BASEMENT:
return Rs2Player.getQuestState(Quest.RECIPE_FOR_DISASTER__ANOTHER_COOKS_QUEST) == QuestState.FINISHED;
case COOKS_GUILD:
@@ -259,6 +266,18 @@ public boolean hasRequirements() {
case PRIFDDINAS_SOUTH:
// Requires Song of the elves to be completed
return Rs2Player.getQuestState(Quest.SONG_OF_THE_ELVES) == QuestState.FINISHED;
+ case SUNBLEAK_ISLAND:
+ // Requires sailing level 72
+ return Rs2Player.getSkillRequirement(Skill.SAILING, 72, false);
+ case DEEPFIN_POINT:
+ // Requires sailing level 67
+ return Rs2Player.getSkillRequirement(Skill.SAILING, 67, false);
+ case DEEPFIN_MINE_MID:
+ // Requires sailing level 67 + Bank to be made
+ return Rs2Player.getSkillRequirement(Skill.SAILING, 67, false);
+ case DEEPFIN_MINE_EAST:
+ // Requires sailing level 67 + Bank to be made
+ return Rs2Player.getSkillRequirement(Skill.SAILING, 67, false);
default:
return true;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/poh/data/MountedDigsite.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/poh/data/MountedDigsite.java
index 1ece55792b4..7e63bc90f40 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/poh/data/MountedDigsite.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/poh/data/MountedDigsite.java
@@ -35,7 +35,7 @@ public boolean execute() {
return false;
}
if (pendant.getId() == objectId) {
- //The correct id for the object means it has the right left click option, so we can just use that.
+ // The correct id for the object means it has the right left click option, so we can just use that.
return Rs2GameObject.interact(pendant, destinationName);
}
Widget widget = getWidget();
@@ -49,6 +49,10 @@ public boolean execute() {
return Rs2Widget.clickWidget(destinationName);
}
+ private static Widget getWidget() {
+ return Rs2Widget.getWidget(InterfaceID.MENU, 3);
+ }
+
public static final Integer[] IDS = {ObjectID.POH_AMULET_DIGSITE, ObjectID.POH_AMULET_DIG_LITHKREN, ObjectID.POH_AMULET_DIG_FOSSIL, ObjectID.POH_AMULET_DIG_DIGSITE};
public static DecorativeObject getObject() {
@@ -63,10 +67,6 @@ public static boolean isMountedDigsite(DecorativeObject go) {
return false;
}
- private static Widget getWidget() {
- return Rs2Widget.getWidget(InterfaceID.MENU, 3);
- }
-
@Override
public String displayInfo() {
return "MountedDigsite -> " + destinationName;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java
index 7a0b6944c48..5e97769a763 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java
@@ -1605,9 +1605,8 @@ private static boolean handleTransports(List path, int indexOfStartP
}
if (object != null) {
- System.out.println("Object Type: " + Rs2GameObject.getObjectType(object));
-
- if (!(object instanceof GroundObject)) {
+ // Skip reachability check for GroundObjects and Magic Mushtrees
+ if (!(object instanceof GroundObject) && !MagicMushtree.isMagicMushtree(transport.getObjectId())) {
if (!Rs2Tile.isTileReachable(transport.getOrigin())) {
break;
}
@@ -1811,6 +1810,11 @@ private static boolean handleObjectExceptions(Transport transport, TileObject ti
sleepUntil(() -> Rs2Player.getWorldLocation().distanceTo2D(transport.getDestination()) < OFFSET, 10000);
return true;
}
+
+ // Handle Magic Mushtree (Fossil Island Mycelium Transportation System)
+ if (MagicMushtree.isMagicMushtree(tileObject)) {
+ return MagicMushtree.handleTransport(transport);
+ }
return false;
}
@@ -1925,6 +1929,18 @@ private static boolean handleInventoryTeleports(Transport transport, int itemId)
if (itemAction.equalsIgnoreCase("open") && itemId == ItemID.BOOKOFSCROLLS_CHARGED) {
return handleMasterScrollBook(destination);
+ } else if (isDialogueBasedTeleportItem(transport.getDisplayInfo())) {
+ // Multi-destination teleport items: wait for destination selection dialogue
+ Rs2Dialogue.sleepUntilSelectAnOption();
+ Rs2Dialogue.clickOption(destination);
+ log.info("Traveling to {} - ({})", transport.getDisplayInfo(), transport.getDestination());
+ return true;
+ } else if (transport.getDisplayInfo().toLowerCase().contains("burning amulet")) {
+ // Burning amulet in inventory: confirm wilderness teleport
+ Rs2Dialogue.sleepUntilInDialogue();
+ Rs2Dialogue.clickOption("Okay, teleport to level");
+ log.info("Traveling to {} - ({})", transport.getDisplayInfo(), transport.getDestination());
+ return true;
} else if (wildernessTransport) {
Rs2Dialogue.sleepUntilInDialogue();
return Rs2Dialogue.clickOption("Yes", "Okay");
@@ -1960,6 +1976,28 @@ private static boolean handleWearableTeleports(Transport transport, int itemId)
return false;
}
+ /**
+ * Checks if the teleport item requires dialogue-based destination selection.
+ * These are items that, when rubbed/activated, show a dialogue menu to choose destination.
+ *
+ * @param displayInfo the displayInfo from the transport
+ * @return true if the item requires dialogue handling
+ */
+ private static boolean isDialogueBasedTeleportItem(String displayInfo) {
+ if (displayInfo == null) return false;
+ String lowerDisplayInfo = displayInfo.toLowerCase();
+ return lowerDisplayInfo.contains("slayer ring")
+ || lowerDisplayInfo.contains("games necklace")
+ || lowerDisplayInfo.contains("skills necklace")
+ || lowerDisplayInfo.contains("ring of dueling")
+ || lowerDisplayInfo.contains("ring of wealth")
+ || lowerDisplayInfo.contains("amulet of glory")
+ || lowerDisplayInfo.contains("combat bracelet")
+ || lowerDisplayInfo.contains("digsite pendant")
+ || lowerDisplayInfo.contains("necklace of passage")
+ || lowerDisplayInfo.contains("giantsoul amulet");
+ }
+
/**
* Checks if the player's current location is within the specified area defined by the given world points.
*
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java
index b41c8c893e2..dcf08f32660 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java
@@ -26,6 +26,7 @@
import com.google.inject.Provides;
import java.awt.Color;
+import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import javax.inject.Inject;
import net.runelite.api.Client;
@@ -43,6 +44,7 @@
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
+import net.runelite.client.task.Schedule;
@PluginDescriptor(
name = "Minimap",
@@ -68,6 +70,9 @@ public class MinimapPlugin extends Plugin
@Inject
private ClientThread clientThread;
+ @Inject
+ private ConfigManager configManager;
+
private SpritePixels[] originalDotSprites;
@Provides
@@ -83,6 +88,11 @@ protected void startUp()
storeOriginalDots();
replaceMapDots();
client.setMinimapZoom(config.zoom());
+ Double zoomLevel = configManager.getConfiguration(MinimapConfig.GROUP, "zoomLevel", double.class);
+ if (zoomLevel != null && zoomLevel > 0d)
+ {
+ client.setMinimapZoom(zoomLevel);
+ }
}
@Override
@@ -131,6 +141,13 @@ else if (event.getKey().equals("zoom"))
replaceMapDots();
}
+ @Schedule(period = 11, unit = ChronoUnit.SECONDS, asynchronous = true)
+ public void saveZoom()
+ {
+ double zoom = client.getMinimapZoom();
+ configManager.setConfiguration(MinimapConfig.GROUP, "zoomLevel", zoom);
+ }
+
@Subscribe
public void onScriptPostFired(ScriptPostFired scriptPostFired)
{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java
index a63ec469d3a..7805e0cc9be 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java
@@ -83,6 +83,7 @@
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.worldhopper.ping.Ping;
import net.runelite.client.plugins.worldhopper.ping.RetransmitCalculator;
+import net.runelite.client.plugins.worldhopper.ping.TCP_INFO_v0;
import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.overlay.OverlayManager;
@@ -449,13 +450,17 @@ else if (BEFORE_OPTIONS.contains(option))
@Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged)
{
- // If the player has disabled the side bar plugin panel, do not update the UI
- if (config.showSidebar() && gameStateChanged.getGameState() == GameState.LOGGED_IN)
+ if (gameStateChanged.getGameState() == GameState.LOGGED_IN)
{
if (lastWorld != client.getWorld())
{
int newWorld = client.getWorld();
- panel.switchCurrentHighlight(newWorld, lastWorld);
+ // If the player has disabled the side bar plugin panel, do not update the UI
+ if (config.showSidebar())
+ {
+ panel.switchCurrentHighlight(newWorld, lastWorld);
+ }
+ currentPing = -1;
lastWorld = newWorld;
}
}
@@ -831,7 +836,7 @@ private void pingInitialWorlds()
for (World world : worldResult.getWorlds())
{
- int ping = ping(world);
+ int ping = ping(world, false);
SwingUtilities.invokeLater(() -> panel.updatePing(world.getId(), ping));
}
@@ -872,7 +877,7 @@ private void pingNextWorld()
return;
}
- int ping = ping(world);
+ int ping = ping(world, false);
log.trace("Ping for world {} is: {}", world.getId(), ping);
if (panel.isActive())
@@ -900,8 +905,26 @@ private void pingCurrentWorld()
return;
}
- int ping = ping(currentWorld);
- log.trace("Ping for current world is: {}", currentPing);
+ int ping = ping(currentWorld, true);
+ log.trace("Ping for current world is: {}", ping);
+
+ FileDescriptor fd = client.getSocketFD();
+ int rtt = -1;
+ if (fd != null)
+ {
+ TCP_INFO_v0 tcpInfo = Ping.getTcpInfo(fd);
+ if (tcpInfo != null)
+ {
+ rtt = (int) (tcpInfo.RttUs.longValue() / 1000L);
+ retransmitCalculator.record(tcpInfo);
+ }
+ }
+
+ if (ping < 0)
+ {
+ ping = rtt; // use rtt for ping if icmp is blocked
+ storedPings.put(currentWorld.getId(), rtt);
+ }
if (ping < 0)
{
@@ -914,12 +937,6 @@ private void pingCurrentWorld()
{
SwingUtilities.invokeLater(() -> panel.updatePing(currentWorld.getId(), currentPing));
}
-
- FileDescriptor fd = client.getSocketFD();
- if (fd != null)
- {
- retransmitCalculator.record(fd);
- }
}
Integer getStoredPing(World world)
@@ -932,9 +949,9 @@ Integer getStoredPing(World world)
return storedPings.get(world.getId());
}
- private int ping(World world)
+ private int ping(World world, boolean isCurrentWorld)
{
- int ping = Ping.ping(world);
+ int ping = Ping.ping(world, !isCurrentWorld);
storedPings.put(world.getId(), ping);
return ping;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/Ping.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/Ping.java
index cd542567d72..8dccc36488e 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/Ping.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/Ping.java
@@ -29,7 +29,11 @@
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
+import com.sun.jna.platform.win32.WinNT;
+import com.sun.jna.ptr.IntByReference;
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -49,7 +53,13 @@ public class Ping
private static short seq;
+ @Deprecated
public static int ping(World world)
+ {
+ return ping(world, false);
+ }
+
+ public static int ping(World world, boolean useTcpPing)
{
InetAddress inetAddress;
try
@@ -73,20 +83,29 @@ public static int ping(World world)
switch (OSType.getOSType())
{
case Windows:
- return windowsPing(inetAddress);
+ int p = windowsPing(inetAddress);
+ if (p == -1 && useTcpPing)
+ {
+ p = tcpPing(inetAddress);
+ }
+ return p;
case MacOS:
case Linux:
try
{
return icmpPing(inetAddress, OSType.getOSType() == OSType.MacOS);
}
- catch (Exception ex)
+ catch (IOException ex)
{
log.debug("error during icmp ping", ex);
- return tcpPing(inetAddress);
}
+ // FALLTHROUGH
default:
- return tcpPing(inetAddress);
+ if (useTcpPing)
+ {
+ return tcpPing(inetAddress);
+ }
+ return -1;
}
}
catch (IOException ex)
@@ -269,4 +288,58 @@ private static int tcpPing(InetAddress inetAddress) throws IOException
return (int) ((end - start) / 1000000L);
}
}
+
+ public static TCP_INFO_v0 getTcpInfo(FileDescriptor fd)
+ {
+ if (OSType.getOSType() != OSType.Windows)
+ {
+ return null;
+ }
+
+ int handle;
+ try
+ {
+ Field f = FileDescriptor.class.getDeclaredField("fd");
+ f.setAccessible(true);
+ handle = f.getInt(fd);
+ }
+ catch (NoSuchFieldException | IllegalAccessException ex)
+ {
+ log.debug(null, ex);
+ return null;
+ }
+
+ IntByReference tcpInfoVersion = new IntByReference(0); // Version 0 of TCP_INFO
+ TCP_INFO_v0 info = new TCP_INFO_v0();
+ IntByReference bytesReturned = new IntByReference();
+
+ Ws2_32 winsock = Ws2_32.INSTANCE;
+ int rc;
+ try
+ {
+ rc = winsock.WSAIoctl(
+ new WinNT.HANDLE(Pointer.createConstant(handle)),
+ Ws2_32.SIO_TCP_INFO,
+ tcpInfoVersion.getPointer(), Integer.BYTES,
+ info.getPointer(), info.size(),
+ bytesReturned,
+ Pointer.NULL,
+ Pointer.NULL
+ );
+ }
+ catch (UnsatisfiedLinkError ex)
+ {
+ // probably Windows 7
+ log.debug("WSAIoctl()", ex);
+ return null;
+ }
+ if (rc != 0)
+ {
+ log.debug("WSAIoctl(SIO_TCP_INFO) error"); // WSAGetLastError() seems to always be 0?
+ return null;
+ }
+
+ info.read();
+ return info;
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/RetransmitCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/RetransmitCalculator.java
index 68e29189729..3ced1859c3e 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/RetransmitCalculator.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/RetransmitCalculator.java
@@ -24,14 +24,8 @@
*/
package net.runelite.client.plugins.worldhopper.ping;
-import com.sun.jna.Pointer;
-import com.sun.jna.platform.win32.WinNT;
-import com.sun.jna.ptr.IntByReference;
-import java.io.FileDescriptor;
-import java.lang.reflect.Field;
import java.util.Arrays;
import lombok.extern.slf4j.Slf4j;
-import net.runelite.client.util.OSType;
@Slf4j
public class RetransmitCalculator
@@ -44,58 +38,8 @@ public class RetransmitCalculator
private final long[] bytesRetrans = new long[SAMPLES];
private int loss;
- public void record(FileDescriptor fd)
+ public void record(TCP_INFO_v0 info)
{
- if (OSType.getOSType() != OSType.Windows)
- {
- return;
- }
-
- int handle;
- try
- {
- Field f = FileDescriptor.class.getDeclaredField("fd");
- f.setAccessible(true);
- handle = f.getInt(fd);
- }
- catch (NoSuchFieldException | IllegalAccessException ex)
- {
- log.debug(null, ex);
- return;
- }
-
- IntByReference tcpInfoVersion = new IntByReference(0); // Version 0 of TCP_INFO
- TCP_INFO_v0 info = new TCP_INFO_v0();
- IntByReference bytesReturned = new IntByReference();
-
- Ws2_32 winsock = Ws2_32.INSTANCE;
- int rc;
- try
- {
- rc = winsock.WSAIoctl(
- new WinNT.HANDLE(Pointer.createConstant(handle)),
- Ws2_32.SIO_TCP_INFO,
- tcpInfoVersion.getPointer(), Integer.BYTES,
- info.getPointer(), info.size(),
- bytesReturned,
- Pointer.NULL,
- Pointer.NULL
- );
- }
- catch (UnsatisfiedLinkError ex)
- {
- // probably Windows 7
- log.debug("WSAIoctl()", ex);
- return;
- }
- if (rc != 0)
- {
- log.debug("WSAIoctl(SIO_TCP_INFO) error"); // WSAGetLastError() seems to always be 0?
- return;
- }
-
- info.read();
-
int nextIndex = index++ & (SAMPLES - 1);
long connectionTime = info.ConnectionTimeMs.longValue();
diff --git a/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java b/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java
index e7604b2b252..5bd4e772b5d 100644
--- a/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java
+++ b/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java
@@ -27,7 +27,6 @@
package net.runelite.client.rs;
import com.google.common.base.Strings;
-import java.applet.Applet;
import java.io.IOException;
import java.util.Map;
import java.util.function.Supplier;
@@ -193,7 +192,7 @@ private Client loadClient(RSConfig config) throws ClassNotFoundException, Illega
.loadClass(initialClass);
Client rs = (Client) clientClass.newInstance();
- ((Applet) rs).setStub(new RSAppletStub(config, runtimeConfigLoader));
+ rs.setConfiguration(new RSAppletStub(config, runtimeConfigLoader));
log.info("injected-client {}", rs.getBuildID());
diff --git a/runelite-client/src/main/java/net/runelite/client/rs/RSAppletStub.java b/runelite-client/src/main/java/net/runelite/client/rs/RSAppletStub.java
index 327526ba4fd..54adf8c5818 100644
--- a/runelite-client/src/main/java/net/runelite/client/rs/RSAppletStub.java
+++ b/runelite-client/src/main/java/net/runelite/client/rs/RSAppletStub.java
@@ -25,42 +25,22 @@
*/
package net.runelite.client.rs;
-import java.applet.Applet;
-import java.applet.AppletContext;
-import java.applet.AppletStub;
-import java.applet.AudioClip;
-import java.awt.Image;
-import java.io.IOException;
-import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
-import java.util.Enumeration;
-import java.util.Iterator;
import javax.swing.SwingUtilities;
import lombok.RequiredArgsConstructor;
+import net.runelite.api.ClientConfiguration;
import net.runelite.client.RuntimeConfig;
import net.runelite.client.RuntimeConfigLoader;
import net.runelite.client.ui.FatalErrorDialog;
import net.runelite.client.util.LinkBrowser;
@RequiredArgsConstructor
-class RSAppletStub implements AppletStub
+class RSAppletStub implements ClientConfiguration
{
private final RSConfig config;
private final RuntimeConfigLoader runtimeConfigLoader;
- @Override
- public boolean isActive()
- {
- return true;
- }
-
- @Override
- public URL getDocumentBase()
- {
- return getCodeBase();
- }
-
@Override
public URL getCodeBase()
{
@@ -81,129 +61,58 @@ public String getParameter(String name)
}
@Override
- public AppletContext getAppletContext()
+ public void onError(String code)
{
- return new AppletContext()
+ try
{
- @Override
- public AudioClip getAudioClip(URL url)
- {
- return null;
- }
-
- @Override
- public Image getImage(URL url)
- {
- return null;
- }
-
- @Override
- public Applet getApplet(String name)
- {
- return null;
- }
-
- @Override
- public Enumeration