Merge remote-tracking branch 'runelite/master'

This commit is contained in:
Owain van Brakel
2021-08-15 23:10:04 +02:00
12 changed files with 144 additions and 44 deletions

View File

@@ -45,6 +45,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.AtomicMoveNotSupportedException; import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files; import java.nio.file.Files;
@@ -337,11 +338,14 @@ public class ConfigManager
File tempFile = File.createTempFile("runelite", null, parent); File tempFile = File.createTempFile("runelite", null, parent);
try (FileOutputStream out = new FileOutputStream(tempFile)) try (FileOutputStream out = new FileOutputStream(tempFile);
FileChannel channel = out.getChannel();
OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8))
{ {
out.getChannel().lock(); channel.lock();
properties.store(new OutputStreamWriter(out, StandardCharsets.UTF_8), "RuneLite configuration"); properties.store(writer, "RuneLite configuration");
// FileOutputStream.close() closes the associated channel, which frees the lock channel.force(true);
// FileChannel.close() frees the lock
} }
try try

View File

@@ -95,16 +95,29 @@ public interface NpcIndicatorsConfig extends Config
position = 4, position = 4,
keyName = "npcColor", keyName = "npcColor",
name = "Highlight Color", name = "Highlight Color",
description = "Color of the NPC highlight", description = "Color of the NPC highlight border, menu, and text",
section = renderStyleSection section = renderStyleSection
) )
default Color getHighlightColor() default Color highlightColor()
{ {
return Color.CYAN; return Color.CYAN;
} }
@Alpha
@ConfigItem( @ConfigItem(
position = 5, position = 5,
keyName = "fillColor",
name = "Fill Color",
description = "Color of the NPC highlight fill",
section = renderStyleSection
)
default Color fillColor()
{
return new Color(0, 255, 255, 20);
}
@ConfigItem(
position = 6,
keyName = "borderWidth", keyName = "borderWidth",
name = "Border Width", name = "Border Width",
description = "Width of the highlighted NPC border", description = "Width of the highlighted NPC border",
@@ -116,7 +129,7 @@ public interface NpcIndicatorsConfig extends Config
} }
@ConfigItem( @ConfigItem(
position = 6, position = 7,
keyName = "outlineFeather", keyName = "outlineFeather",
name = "Outline feather", name = "Outline feather",
description = "Specify between 0-4 how much of the model outline should be faded", description = "Specify between 0-4 how much of the model outline should be faded",

View File

@@ -267,7 +267,7 @@ public class NpcIndicatorsPlugin extends Plugin implements NpcIndicatorsService
if (color == null && highlightedNpcs.contains(npc) && config.highlightMenuNames() && (!npc.isDead() || !config.ignoreDeadNpcs())) if (color == null && highlightedNpcs.contains(npc) && config.highlightMenuNames() && (!npc.isDead() || !config.ignoreDeadNpcs()))
{ {
color = config.getHighlightColor(); color = config.highlightColor();
} }
if (color != null) if (color != null)

View File

@@ -57,7 +57,7 @@ public class NpcMinimapOverlay extends Overlay
{ {
for (NPC npc : plugin.getHighlightedNpcs()) for (NPC npc : plugin.getHighlightedNpcs())
{ {
renderNpcOverlay(graphics, npc, Text.removeTags(npc.getName()), config.getHighlightColor()); renderNpcOverlay(graphics, npc, Text.removeTags(npc.getName()), config.highlightColor());
} }
return null; return null;

View File

@@ -49,7 +49,6 @@ import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition; import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayUtil; import net.runelite.client.ui.overlay.OverlayUtil;
import net.runelite.client.ui.overlay.outline.ModelOutlineRenderer; import net.runelite.client.ui.overlay.outline.ModelOutlineRenderer;
import net.runelite.client.util.ColorUtil;
import net.runelite.client.util.Text; import net.runelite.client.util.Text;
public class NpcSceneOverlay extends Overlay public class NpcSceneOverlay extends Overlay
@@ -92,7 +91,7 @@ public class NpcSceneOverlay extends Overlay
for (NPC npc : plugin.getHighlightedNpcs()) for (NPC npc : plugin.getHighlightedNpcs())
{ {
renderNpcOverlay(graphics, npc, config.getHighlightColor()); renderNpcOverlay(graphics, npc);
} }
return null; return null;
@@ -113,18 +112,12 @@ public class NpcSceneOverlay extends Overlay
return; return;
} }
final Color color = config.getHighlightColor();
final LocalPoint centerLp = new LocalPoint( final LocalPoint centerLp = new LocalPoint(
lp.getX() + Perspective.LOCAL_TILE_SIZE * (npc.getNpcSize() - 1) / 2, lp.getX() + Perspective.LOCAL_TILE_SIZE * (npc.getNpcSize() - 1) / 2,
lp.getY() + Perspective.LOCAL_TILE_SIZE * (npc.getNpcSize() - 1) / 2); lp.getY() + Perspective.LOCAL_TILE_SIZE * (npc.getNpcSize() - 1) / 2);
final Polygon poly = Perspective.getCanvasTileAreaPoly(client, centerLp, npc.getNpcSize()); final Polygon poly = Perspective.getCanvasTileAreaPoly(client, centerLp, npc.getNpcSize());
renderPoly(graphics, config.highlightColor(), config.fillColor(), poly);
if (poly != null)
{
OverlayUtil.renderPolygon(graphics, poly, color);
}
final Instant now = Instant.now(); final Instant now = Instant.now();
final double baseTick = ((npc.getDiedOnTick() + npc.getRespawnTime()) - client.getTickCount()) * (Constants.GAME_TICK_LENGTH / 1000.0); final double baseTick = ((npc.getDiedOnTick() + npc.getRespawnTime()) - client.getTickCount()) * (Constants.GAME_TICK_LENGTH / 1000.0);
@@ -148,7 +141,7 @@ public class NpcSceneOverlay extends Overlay
} }
} }
private void renderNpcOverlay(Graphics2D graphics, NPC actor, Color color) private void renderNpcOverlay(Graphics2D graphics, NPC actor)
{ {
NPCComposition npcComposition = actor.getTransformedComposition(); NPCComposition npcComposition = actor.getTransformedComposition();
if (npcComposition == null || !npcComposition.isInteractible() if (npcComposition == null || !npcComposition.isInteractible()
@@ -157,10 +150,13 @@ public class NpcSceneOverlay extends Overlay
return; return;
} }
final Color borderColor = config.highlightColor();
final Color fillColor = config.fillColor();
if (config.highlightHull()) if (config.highlightHull())
{ {
Shape objectClickbox = actor.getConvexHull(); Shape objectClickbox = actor.getConvexHull();
renderPoly(graphics, color, objectClickbox); renderPoly(graphics, borderColor, fillColor, objectClickbox);
} }
if (config.highlightTile()) if (config.highlightTile())
@@ -169,7 +165,7 @@ public class NpcSceneOverlay extends Overlay
LocalPoint lp = actor.getLocalLocation(); LocalPoint lp = actor.getLocalLocation();
Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, lp, size); Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, lp, size);
renderPoly(graphics, color, tilePoly); renderPoly(graphics, borderColor, fillColor, tilePoly);
} }
if (config.highlightSouthWestTile()) if (config.highlightSouthWestTile())
@@ -182,12 +178,12 @@ public class NpcSceneOverlay extends Overlay
Polygon southWestTilePoly = Perspective.getCanvasTilePoly(client, new LocalPoint(x, y)); Polygon southWestTilePoly = Perspective.getCanvasTilePoly(client, new LocalPoint(x, y));
renderPoly(graphics, color, southWestTilePoly); renderPoly(graphics, borderColor, fillColor, southWestTilePoly);
} }
if (config.highlightOutline()) if (config.highlightOutline())
{ {
modelOutlineRenderer.drawOutline(actor, (int)config.borderWidth(), color, config.outlineFeather()); modelOutlineRenderer.drawOutline(actor, (int)config.borderWidth(), borderColor, config.outlineFeather());
} }
if (config.drawNames() && actor.getName() != null) if (config.drawNames() && actor.getName() != null)
@@ -197,19 +193,19 @@ public class NpcSceneOverlay extends Overlay
if (textLocation != null) if (textLocation != null)
{ {
OverlayUtil.renderTextLocation(graphics, textLocation, npcName, color); OverlayUtil.renderTextLocation(graphics, textLocation, npcName, borderColor);
} }
} }
} }
private void renderPoly(Graphics2D graphics, Color color, Shape polygon) private void renderPoly(Graphics2D graphics, Color borderColor, Color fillColor, Shape polygon)
{ {
if (polygon != null) if (polygon != null)
{ {
graphics.setColor(color); graphics.setColor(borderColor);
graphics.setStroke(new BasicStroke((float) config.borderWidth())); graphics.setStroke(new BasicStroke((float) config.borderWidth()));
graphics.draw(polygon); graphics.draw(polygon);
graphics.setColor(ColorUtil.colorWithAlpha(color, 20)); graphics.setColor(fillColor);
graphics.fill(polygon); graphics.fill(polygon);
} }
} }

View File

@@ -58,9 +58,9 @@ import static net.runelite.api.ObjectID.YEW_36683;
enum Tree enum Tree
{ {
REGULAR_TREE(null, TREE, TREE_1277, TREE_1278, TREE_1279, TREE_1280), REGULAR_TREE(null, TREE, TREE_1277, TREE_1278, TREE_1279, TREE_1280),
OAK_TREE(Duration.ofMillis(8500), ObjectID.OAK_TREE, OAK_TREE_4540, OAK_10820), OAK_TREE(Duration.ofMillis(8500), OAK_TREE_4540, OAK_10820),
WILLOW_TREE(Duration.ofMillis(8500), WILLOW, WILLOW_10829, WILLOW_10831, WILLOW_10833), WILLOW_TREE(Duration.ofMillis(8500), WILLOW, WILLOW_10829, WILLOW_10831, WILLOW_10833),
MAPLE_TREE(Duration.ofSeconds(35), ObjectID.MAPLE_TREE, MAPLE_TREE_10832, MAPLE_TREE_36681) MAPLE_TREE(Duration.ofSeconds(35), MAPLE_TREE_10832, MAPLE_TREE_36681)
{ {
@Override @Override
Duration getRespawnTime(int region) Duration getRespawnTime(int region)

View File

@@ -29,6 +29,7 @@ import com.google.common.primitives.Bytes;
import com.sun.jna.Memory; import com.sun.jna.Memory;
import com.sun.jna.Pointer; import com.sun.jna.Pointer;
import java.io.IOException; import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
@@ -43,6 +44,7 @@ public class Ping
private static final byte[] RUNELITE_PING = "RuneLitePing".getBytes(Charsets.UTF_8); private static final byte[] RUNELITE_PING = "RuneLitePing".getBytes(Charsets.UTF_8);
private static final int TIMEOUT = 2000; // ms private static final int TIMEOUT = 2000; // ms
private static final int PORT = 43594; private static final int PORT = 43594;
private static final int MAX_IPV4_HEADER_SIZE = 60;
private static short seq; private static short seq;
@@ -59,19 +61,27 @@ public class Ping
return -1; return -1;
} }
if (!(inetAddress instanceof Inet4Address))
{
log.debug("Only ipv4 ping is supported");
return -1;
}
try try
{ {
switch (OSType.getOSType()) switch (OSType.getOSType())
{ {
case Windows: case Windows:
return windowsPing(inetAddress); return windowsPing(inetAddress);
case MacOS:
case Linux: case Linux:
try try
{ {
return linuxPing(inetAddress); return icmpPing(inetAddress, OSType.getOSType() == OSType.MacOS);
} }
catch (Exception ex) catch (Exception ex)
{ {
log.debug("error during icmp ping", ex);
return tcpPing(inetAddress); return tcpPing(inetAddress);
} }
default: default:
@@ -111,7 +121,7 @@ public class Ping
} }
} }
private static int linuxPing(InetAddress inetAddress) throws IOException private static int icmpPing(InetAddress inetAddress, boolean includeIpHeader) throws IOException
{ {
RLLibC libc = RLLibC.INSTANCE; RLLibC libc = RLLibC.INSTANCE;
byte[] address = inetAddress.getAddress(); byte[] address = inetAddress.getAddress();
@@ -137,12 +147,19 @@ public class Ping
byte[] request = { byte[] request = {
8, // type 8 - ipv4 echo request 8, // type 8 - ipv4 echo request
0, // code 0, // code
0, 0, // checksum (set by kernel) 0, 0, // checksum
0, 0, // id (set by kernel) 0, 0, // id - set by kernel on Linux. MacOS uses pid, however sending 0 appears to work fine.
(byte) (((seqno >> 8) & 0xff)), (byte) (seqno & 0xff) (byte) (((seqno >> 8) & 0xff)), (byte) (seqno & 0xff)
}; };
// append payload // append payload
request = Bytes.concat(request, RUNELITE_PING); request = Bytes.concat(request, RUNELITE_PING);
final short checksum = checksum(request);
// on Linux the kernel automatically sets the checksum and ignores what we set,
// however it is required on MacOS
request[2] = (byte) ((checksum >> 8) & 0xff);
request[3] = (byte) (checksum & 0xff);
// struct sockaddr_in // struct sockaddr_in
byte[] addr = { byte[] addr = {
(byte) libc.AF_INET, 0, // sin_family (byte) libc.AF_INET, 0, // sin_family
@@ -151,21 +168,44 @@ public class Ping
0, 0, 0, 0, 0, 0, 0, 0 // padding 0, 0, 0, 0, 0, 0, 0, 0 // padding
}; };
// response size/buffer
int size = 8 + RUNELITE_PING.length + // struct icmphdr + response
(includeIpHeader ? MAX_IPV4_HEADER_SIZE : 0); // struct ip
Memory response = new Memory(size);
long start = System.nanoTime(); long start = System.nanoTime();
if (libc.sendto(sock, request, request.length, 0, addr, addr.length) != request.length) if (libc.sendto(sock, request, request.length, 0, addr, addr.length) != request.length)
{ {
return -1; return -1;
} }
int size = 8 + RUNELITE_PING.length; // struct icmphdr + response int rlen = libc.recvfrom(sock, response, size, 0, null, null);
Memory response = new Memory(size); long end = System.nanoTime();
if (libc.recvfrom(sock, response, size, 0, null, null) != size) if (rlen <= 0)
{ {
return -1; return -1;
} }
long end = System.nanoTime();
short seq = (short) (((response.getByte(6) & 0xff) << 8) | response.getByte(7) & 0xff); int icmpHeaderOffset = 0;
if (includeIpHeader)
{
int ihl = response.getByte(0) & 0xf;
icmpHeaderOffset = ihl << 2; // to bytes
}
if (icmpHeaderOffset + 7 >= rlen)
{
log.warn("packet too short (received {} bytes but icmp header offset is {})", rlen, icmpHeaderOffset);
return -1;
}
if (response.getByte(icmpHeaderOffset) != 0) // ICMP type - echo reply
{
log.warn("non-echo reply");
return -1;
}
short seq = (short) (((response.getByte(icmpHeaderOffset + 6) & 0xff) << 8) | response.getByte(icmpHeaderOffset + 7) & 0xff);
if (seqno != seq) if (seqno != seq)
{ {
log.warn("sequence number mismatch ({} != {})", seqno, seq); log.warn("sequence number mismatch ({} != {})", seqno, seq);
@@ -180,6 +220,24 @@ public class Ping
} }
} }
// IP checksum
private static short checksum(byte[] data)
{
int a = 0;
for (int i = 0; i < data.length - 1; i += 2)
{
a += ((data[i] & 0xff) << 8) | (data[i + 1] & 0xff);
}
if ((data.length & 1) != 0)
{
a += (data[data.length - 1] & 0xff) << 8;
}
a = (a >> 16 & 0xffff) + (a & 0xffff);
return (short) (~a & 0xffff);
}
private static int tcpPing(InetAddress inetAddress) throws IOException private static int tcpPing(InetAddress inetAddress) throws IOException
{ {
try (Socket socket = new Socket()) try (Socket socket = new Socket())

View File

@@ -27,6 +27,7 @@ package net.runelite.client.plugins.worldhopper.ping;
import com.sun.jna.Native; import com.sun.jna.Native;
import com.sun.jna.Pointer; import com.sun.jna.Pointer;
import com.sun.jna.platform.unix.LibC; import com.sun.jna.platform.unix.LibC;
import net.runelite.client.util.OSType;
interface RLLibC extends LibC interface RLLibC extends LibC
{ {
@@ -34,9 +35,9 @@ interface RLLibC extends LibC
int AF_INET = 2; int AF_INET = 2;
int SOCK_DGRAM = 2; int SOCK_DGRAM = 2;
int SOL_SOCKET = 1; int SOL_SOCKET = OSType.getOSType() == OSType.MacOS ? 0xffff : 1;
int IPPROTO_ICMP = 1; int IPPROTO_ICMP = 1;
int SO_RCVTIMEO = 20; int SO_RCVTIMEO = OSType.getOSType() == OSType.MacOS ? 0x1006 : 20;
int socket(int domain, int type, int protocol); int socket(int domain, int type, int protocol);

View File

@@ -151,6 +151,14 @@
"ry2": 47, "ry2": 47,
"z1": 0, "z1": 0,
"z2": 0 "z2": 0
},
{ // Tatie's house
"rx1": 28,
"ry1": 5,
"rx2": 35,
"ry2": 12,
"z1": 0,
"z2": 0
} }
], ],
"13151":[ // Prif NE "13151":[ // Prif NE
@@ -315,6 +323,14 @@
"ry2": 25, "ry2": 25,
"z1": 0, "z1": 0,
"z2": 0 "z2": 0
},
{ // Crafting shop
"rx1": 21,
"ry1": 60,
"rx2": 23,
"ry2": 60,
"z1": 0,
"z2": 0
} }
], ],
"13150": [ // Prif SE "13150": [ // Prif SE

View File

@@ -719,7 +719,7 @@
"level": 83, "level": 83,
"icon": 21338, "icon": 21338,
"name": "Amethyst Bolt Tips", "name": "Amethyst Bolt Tips",
"xp": 60 "xp": 4
}, },
{ {
"level": 83, "level": 83,
@@ -743,13 +743,13 @@
"level": 85, "level": 85,
"icon": 21350, "icon": 21350,
"name": "Amethyst Arrowtips", "name": "Amethyst Arrowtips",
"xp": 60 "xp": 4
}, },
{ {
"level": 87, "level": 87,
"icon": 21352, "icon": 21352,
"name": "Amethyst Javelin Heads", "name": "Amethyst Javelin Heads",
"xp": 60 "xp": 12
}, },
{ {
"level": 87, "level": 87,
@@ -757,6 +757,12 @@
"name": "Light Orb", "name": "Light Orb",
"xp": 70 "xp": 70
}, },
{
"level": 89,
"icon": 25853,
"name": "Amethyst Dart Tip",
"xp": 7.5
},
{ {
"level": 89, "level": 89,
"icon": 19538, "icon": 19538,

View File

@@ -564,6 +564,12 @@
"name": "Magic Shield", "name": "Magic Shield",
"xp": 183 "xp": 183
}, },
{
"level": 90,
"icon": 25849,
"name": "Amethyst Dart",
"xp": 21
},
{ {
"level": 90, "level": 90,
"icon": 11212, "icon": 11212,

View File

@@ -125,7 +125,7 @@ public class NpcIndicatorsPluginTest
{ {
when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("goblin"); when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("goblin");
when(npcIndicatorsConfig.highlightMenuNames()).thenReturn(true); when(npcIndicatorsConfig.highlightMenuNames()).thenReturn(true);
when(npcIndicatorsConfig.getHighlightColor()).thenReturn(Color.BLUE); when(npcIndicatorsConfig.highlightColor()).thenReturn(Color.BLUE);
npcIndicatorsPlugin.rebuild(); npcIndicatorsPlugin.rebuild();