Default Worldhopper

This commit is contained in:
James Munson
2019-05-20 03:03:19 -07:00
parent f4ae6fcc5e
commit dcb0626ed1
7 changed files with 1060 additions and 1161 deletions

View File

@@ -112,15 +112,4 @@ public interface WorldHopperConfig extends Config
{ {
return SubscriptionFilterMode.BOTH; return SubscriptionFilterMode.BOTH;
} }
@ConfigItem(
keyName = "showHistory",
name = "Show history tab",
description = "Shows the history tab",
position = 7
)
default boolean showHistory()
{
return true;
}
} }

View File

@@ -75,13 +75,13 @@ import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.input.KeyManager; import net.runelite.client.input.KeyManager;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.worldhopper.ping.Ping;
import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.ExecutorServiceExceptionLogger; import net.runelite.client.util.ExecutorServiceExceptionLogger;
import net.runelite.client.util.HotkeyListener; import net.runelite.client.util.HotkeyListener;
import net.runelite.client.util.Text; import net.runelite.client.util.Text;
import net.runelite.client.util.WorldUtil; import net.runelite.client.util.WorldUtil;
import net.runelite.client.util.ping.Ping;
import net.runelite.http.api.worlds.World; import net.runelite.http.api.worlds.World;
import net.runelite.http.api.worlds.WorldClient; import net.runelite.http.api.worlds.WorldClient;
import net.runelite.http.api.worlds.WorldResult; import net.runelite.http.api.worlds.WorldResult;
@@ -258,49 +258,10 @@ public class WorldHopperPlugin extends Plugin
panel.setFilterMode(config.subscriptionFilter()); panel.setFilterMode(config.subscriptionFilter());
updateList(); updateList();
break; break;
case "showHistory":
panel.updateLayout();
break;
} }
} }
} }
boolean showHistory()
{
return config.showHistory();
}
Map<String, String> getHistory()
{
Map<String, String> history = configManager.getConfiguration(WorldHopperConfig.GROUP, "history", Map.class);
if (history == null)
{
history = new HashMap<String, String>();
}
return history;
}
void clearHistory()
{
Map<String, String> history = getHistory();
history.clear();
configManager.setConfiguration(WorldHopperConfig.GROUP, "history", history);
}
void addToHistory()
{
addToHistory(client.getWorld());
}
void addToHistory(int world)
{
long unixTime = System.currentTimeMillis() / 1000L;
Map<String, String> history = getHistory();
history.put(String.valueOf(world), String.valueOf(unixTime));
configManager.setConfiguration(WorldHopperConfig.GROUP, "history", history);
}
private void setFavoriteConfig(int world) private void setFavoriteConfig(int world)
{ {
configManager.setConfiguration(WorldHopperConfig.GROUP, "favorite_" + world, true); configManager.setConfiguration(WorldHopperConfig.GROUP, "favorite_" + world, true);
@@ -457,12 +418,6 @@ public class WorldHopperPlugin extends Plugin
lastWorld = newWorld; lastWorld = newWorld;
} }
} }
if (gameStateChanged.getGameState() == GameState.LOGGED_IN)
{
addToHistory(client.getWorld());
panel.updateList();
}
} }
@Subscribe @Subscribe
@@ -699,8 +654,6 @@ public class WorldHopperPlugin extends Plugin
quickHopTargetWorld = rsWorld; quickHopTargetWorld = rsWorld;
displaySwitcherAttempts = 0; displaySwitcherAttempts = 0;
addToHistory(worldId);
} }
@Subscribe @Subscribe

View File

@@ -26,26 +26,15 @@ package net.runelite.client.plugins.worldhopper;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout; import java.awt.GridLayout;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -64,10 +53,7 @@ class WorldSwitcherPanel extends PluginPanel
private static final int PLAYERS_COLUMN_WIDTH = 40; private static final int PLAYERS_COLUMN_WIDTH = 40;
private static final int PING_COLUMN_WIDTH = 47; private static final int PING_COLUMN_WIDTH = 47;
private final JPanel headerContainer;
private final JPanel headerHistContainer;
private final JPanel listContainer = new JPanel(); private final JPanel listContainer = new JPanel();
private final JPanel histContainer = new JPanel();
private WorldTableHeader worldHeader; private WorldTableHeader worldHeader;
private WorldTableHeader playersHeader; private WorldTableHeader playersHeader;
@@ -78,7 +64,6 @@ class WorldSwitcherPanel extends PluginPanel
private boolean ascendingOrder = true; private boolean ascendingOrder = true;
private ArrayList<WorldTableRow> rows = new ArrayList<>(); private ArrayList<WorldTableRow> rows = new ArrayList<>();
private ArrayList<WorldTableRow> histRows = new ArrayList<>();
private WorldHopperPlugin plugin; private WorldHopperPlugin plugin;
@Setter(AccessLevel.PACKAGE) @Setter(AccessLevel.PACKAGE)
private SubscriptionFilterMode filterMode; private SubscriptionFilterMode filterMode;
@@ -90,35 +75,12 @@ class WorldSwitcherPanel extends PluginPanel
setBorder(null); setBorder(null);
setLayout(new DynamicGridLayout(0, 1)); setLayout(new DynamicGridLayout(0, 1));
headerContainer = buildHeader(); JPanel headerContainer = buildHeader();
headerHistContainer = buildHistoryHeader();
listContainer.setLayout(new GridLayout(0, 1)); listContainer.setLayout(new GridLayout(0, 1));
histContainer.setLayout(new GridLayout(0, 1));
updateLayout(); add(headerContainer);
} add(listContainer);
void updateLayout()
{
if (this.getComponentCount() > 0)
{
for (Component c : this.getComponents())
{
this.remove(c);
}
}
if (plugin.showHistory())
{
Component tabs = createTabs();
add(tabs);
}
else
{
add(headerContainer);
add(listContainer);
}
} }
void switchCurrentHighlight(int newWorld, int lastWorld) void switchCurrentHighlight(int newWorld, int lastWorld)
@@ -134,18 +96,6 @@ class WorldSwitcherPanel extends PluginPanel
row.recolour(false); row.recolour(false);
} }
} }
for (WorldTableRow row : histRows)
{
if (row.getWorld().getId() == newWorld)
{
row.recolour(true);
}
else if (row.getWorld().getId() == lastWorld)
{
row.recolour(false);
}
}
} }
void updateListData(Map<Integer, Integer> worldData) void updateListData(Map<Integer, Integer> worldData)
@@ -160,16 +110,6 @@ class WorldSwitcherPanel extends PluginPanel
} }
} }
for (WorldTableRow worldTableRow : histRows)
{
World world = worldTableRow.getWorld();
Integer playerCount = worldData.get(world.getId());
if (playerCount != null)
{
worldTableRow.updatePlayerCount(playerCount);
}
}
// If the list is being ordered by player count, then it has to be re-painted // If the list is being ordered by player count, then it has to be re-painted
// to properly display the new data // to properly display the new data
if (orderIndex == WorldOrder.PLAYERS) if (orderIndex == WorldOrder.PLAYERS)
@@ -194,21 +134,6 @@ class WorldSwitcherPanel extends PluginPanel
break; break;
} }
} }
for (WorldTableRow worldTableRow : histRows)
{
if (worldTableRow.getWorld().getId() == world)
{
worldTableRow.setPing(ping);
// If the panel is sorted by ping, re-sort it
if (orderIndex == WorldOrder.PING)
{
updateList();
}
break;
}
}
} }
void hidePing() void hidePing()
@@ -217,11 +142,6 @@ class WorldSwitcherPanel extends PluginPanel
{ {
worldTableRow.hidePing(); worldTableRow.hidePing();
} }
for (WorldTableRow worldTableRow : histRows)
{
worldTableRow.hidePing();
}
} }
void showPing() void showPing()
@@ -230,11 +150,6 @@ class WorldSwitcherPanel extends PluginPanel
{ {
worldTableRow.showPing(); worldTableRow.showPing();
} }
for (WorldTableRow worldTableRow : histRows)
{
worldTableRow.showPing();
}
} }
void updateList() void updateList()
@@ -270,127 +185,16 @@ class WorldSwitcherPanel extends PluginPanel
}); });
listContainer.removeAll(); listContainer.removeAll();
histContainer.removeAll();
Map<String, String> history = plugin.getHistory();
Map<String, String> matchedHist = new HashMap<>();
for (int i = 0; i < rows.size(); i++) for (int i = 0; i < rows.size(); i++)
{ {
WorldTableRow row = rows.get(i); WorldTableRow row = rows.get(i);
row.setBackground(i % 2 == 0 ? ODD_ROW : ColorScheme.DARK_GRAY_COLOR); row.setBackground(i % 2 == 0 ? ODD_ROW : ColorScheme.DARK_GRAY_COLOR);
listContainer.add(row); listContainer.add(row);
String worldNum = String.valueOf(row.getWorld().getId());
if (history.containsKey(worldNum))
{
// Add toa list that we can sort later
matchedHist.put(worldNum, history.get(worldNum));
}
}
// Sort by ascending
matchedHist = matchedHist.entrySet().stream()
.sorted(Map.Entry.<String, String>comparingByValue().reversed())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(e1, e2) -> e1, LinkedHashMap::new));
// Add matched rows to history list
Iterator it = matchedHist.entrySet().iterator();
int histRowCount = 0;
while (it.hasNext())
{
Map.Entry pair = (Map.Entry) it.next();
for (WorldTableRow r : rows)
{
WorldTableRow histRow = r;
histRow.setBackground(histRowCount % 2 == 0 ? ODD_ROW : ColorScheme.DARK_GRAY_COLOR);
if (String.valueOf(r.getWorld().getId()).equals(pair.getKey()))
{
histContainer.add(r);
histRowCount++;
break;
}
}
it.remove();
} }
listContainer.revalidate(); listContainer.revalidate();
listContainer.repaint(); listContainer.repaint();
histContainer.revalidate();
histContainer.repaint();
}
Component createTabs()
{
// Constraints for GB Layout
GridBagConstraints listConst = new GridBagConstraints();
listConst.gridx = 0;
listConst.gridy = 1;
listConst.fill = GridBagConstraints.HORIZONTAL;
GridBagConstraints headConst = new GridBagConstraints();
headConst.gridx = 0;
headConst.gridy = 0;
headConst.fill = GridBagConstraints.HORIZONTAL;
GridBagConstraints resetConst = new GridBagConstraints();
resetConst.gridx = 0;
resetConst.gridy = 2;
resetConst.fill = GridBagConstraints.HORIZONTAL;
// Border so that the scrollbar doesn't go over ping
Border paddingBorder = BorderFactory.createEmptyBorder(0, 0, 0, 5);
// Clear history button
JButton resetBtn = new JButton("Clear History");
resetBtn.addActionListener(e ->
{
plugin.clearHistory();
plugin.addToHistory();
updateList();
});
// World Selector page
JPanel worldPanel = new JPanel();
worldPanel.setBorder(paddingBorder);
worldPanel.setLayout(new GridBagLayout());
worldPanel.add(headerContainer, headConst);
worldPanel.add(listContainer, listConst);
// History page
JPanel histPanel = new JPanel();
histPanel.setBorder(paddingBorder);
histPanel.setLayout(new GridBagLayout());
histPanel.add(headerHistContainer, headConst);
histPanel.add(histContainer, listConst);
histPanel.add(resetBtn, resetConst);
JTabbedPane worldTabs = new JTabbedPane();
worldTabs.setName("tabs");
worldTabs.addTab("Worlds", worldPanel);
worldTabs.addTab("History", histPanel);
// This is a fix for preventing stretching of WorldTableRows
worldTabs.addChangeListener(e ->
{
switch (worldTabs.getSelectedIndex())
{
case 0:
histPanel.remove(histContainer);
if (worldPanel.getComponentCount() < 2)
{
worldPanel.add(listContainer, listConst);
}
break;
case 1:
worldPanel.remove(listContainer);
if (histPanel.getComponentCount() < 3)
{
histPanel.add(histContainer, listConst);
}
break;
}
});
return worldTabs;
} }
void updateFavoriteMenu(int world, boolean favorite) void updateFavoriteMenu(int world, boolean favorite)
@@ -402,14 +206,6 @@ class WorldSwitcherPanel extends PluginPanel
row.setFavoriteMenu(favorite); row.setFavoriteMenu(favorite);
} }
} }
for (WorldTableRow row : histRows)
{
if (row.getWorld().getId() == world)
{
row.setFavoriteMenu(favorite);
}
}
} }
void resetAllFavoriteMenus() void resetAllFavoriteMenus()
@@ -419,21 +215,10 @@ class WorldSwitcherPanel extends PluginPanel
row.setFavoriteMenu(false); row.setFavoriteMenu(false);
} }
for (WorldTableRow row : histRows)
{
row.setFavoriteMenu(false);
}
} }
void populate(List<World> worlds) void populate(List<World> worlds)
{ {
Map<Integer, Integer> pingHistory = new HashMap<>();
for (WorldTableRow row : rows)
{
pingHistory.put(row.getWorld().getId(), row.getPing());
}
rows.clear(); rows.clear();
for (int i = 0; i < worlds.size(); i++) for (int i = 0; i < worlds.size(); i++)
@@ -456,8 +241,7 @@ class WorldSwitcherPanel extends PluginPanel
break; break;
} }
Integer ping = pingHistory.getOrDefault(world.getId(), 0); rows.add(buildRow(world, i % 2 == 0, world.getId() == plugin.getCurrentWorld() && plugin.getLastWorld() != 0, plugin.isFavorite(world)));
rows.add(buildRow(world, i % 2 == 0, world.getId() == plugin.getCurrentWorld() && plugin.getLastWorld() != 0, plugin.isFavorite(world), ping));
} }
updateList(); updateList();
@@ -493,35 +277,6 @@ class WorldSwitcherPanel extends PluginPanel
/** /**
* Builds the entire table header. * Builds the entire table header.
*/ */
private JPanel buildHistoryHeader()
{
JPanel header = new JPanel(new BorderLayout());
JPanel leftSide = new JPanel(new BorderLayout());
JPanel rightSide = new JPanel(new BorderLayout());
WorldTableHeader pingHeader = new WorldTableHeader("Ping", false, ascendingOrder, plugin::refresh);
pingHeader.setPreferredSize(new Dimension(PING_COLUMN_WIDTH, 0));
WorldTableHeader worldHeader = new WorldTableHeader("World", false, ascendingOrder, plugin::refresh);
worldHeader.setPreferredSize(new Dimension(WORLD_COLUMN_WIDTH, 0));
WorldTableHeader playersHeader = new WorldTableHeader("#", false, ascendingOrder, plugin::refresh);
playersHeader.setPreferredSize(new Dimension(PLAYERS_COLUMN_WIDTH, 0));
WorldTableHeader activityHeader = new WorldTableHeader("Activity", false, ascendingOrder, plugin::refresh);
leftSide.add(worldHeader, BorderLayout.WEST);
leftSide.add(playersHeader, BorderLayout.CENTER);
rightSide.add(activityHeader, BorderLayout.CENTER);
rightSide.add(pingHeader, BorderLayout.EAST);
header.add(leftSide, BorderLayout.WEST);
header.add(rightSide, BorderLayout.CENTER);
return header;
}
private JPanel buildHeader() private JPanel buildHeader()
{ {
JPanel header = new JPanel(new BorderLayout()); JPanel header = new JPanel(new BorderLayout());
@@ -606,7 +361,7 @@ class WorldSwitcherPanel extends PluginPanel
/** /**
* Builds a table row, that displays the world's information. * Builds a table row, that displays the world's information.
*/ */
private WorldTableRow buildRow(World world, boolean stripe, boolean current, boolean favorite, Integer ping) private WorldTableRow buildRow(World world, boolean stripe, boolean current, boolean favorite)
{ {
WorldTableRow row = new WorldTableRow(world, current, favorite, WorldTableRow row = new WorldTableRow(world, current, favorite,
world1 -> world1 ->

View File

@@ -96,9 +96,8 @@ class WorldTableRow extends JPanel
this.world = world; this.world = world;
this.onFavorite = onFavorite; this.onFavorite = onFavorite;
this.updatedPlayerCount = world.getPlayers(); this.updatedPlayerCount = world.getPlayers();
this.
setLayout(new BorderLayout()); setLayout(new BorderLayout());
setBorder(new EmptyBorder(2, 0, 2, 0)); setBorder(new EmptyBorder(2, 0, 2, 0));
addMouseListener(new MouseAdapter() addMouseListener(new MouseAdapter()

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.client.plugins.worldhopper.ping;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
interface IPHlpAPI extends Library
{
IPHlpAPI INSTANCE = Native.loadLibrary("IPHlpAPI", IPHlpAPI.class);
Pointer IcmpCreateFile();
boolean IcmpCloseHandle(Pointer handle);
int IcmpSendEcho(Pointer IcmpHandle, int DestinationAddress, Pointer RequestData, short RequestSize, Pointer RequestOptions, IcmpEchoReply ReplyBuffer, int ReplySize, int Timeout);
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.client.plugins.worldhopper.ping;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinDef;
import java.util.Arrays;
import java.util.List;
public class IcmpEchoReply extends Structure
{
private static final int IP_OPTION_INFO_SIZE = 1 + 1 + 1 + 1 + (Pointer.SIZE == 8 ? 12 : 4); // on 64bit vms add 4 byte padding
public static final int SIZE = 4 + 4 + 4 + 2 + 2 + Pointer.SIZE + IP_OPTION_INFO_SIZE;
public WinDef.ULONG address;
public WinDef.ULONG status;
public WinDef.ULONG roundTripTime;
public WinDef.USHORT dataSize;
public WinDef.USHORT reserved;
public WinDef.PVOID data;
public WinDef.UCHAR ttl;
public WinDef.UCHAR tos;
public WinDef.UCHAR flags;
public WinDef.UCHAR optionsSize;
public WinDef.PVOID optionsData;
IcmpEchoReply(Pointer p)
{
super(p);
}
@Override
protected List<String> getFieldOrder()
{
return Arrays.asList("address", "status", "roundTripTime", "dataSize", "reserved", "data", "ttl", "tos", "flags", "optionsSize", "optionsData");
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.client.plugins.worldhopper.ping;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.util.OSType;
import net.runelite.http.api.worlds.World;
@Slf4j
public class Ping
{
private static final String RUNELITE_PING = "RuneLitePing";
private static final int TIMEOUT = 2000;
private static final int PORT = 43594;
public static int ping(World world)
{
try
{
switch (OSType.getOSType())
{
case Windows:
return windowsPing(world);
default:
return tcpPing(world);
}
}
catch (IOException ex)
{
log.warn("error pinging", ex);
return -1;
}
}
private static int windowsPing(World world) throws UnknownHostException
{
IPHlpAPI ipHlpAPI = IPHlpAPI.INSTANCE;
Pointer ptr = ipHlpAPI.IcmpCreateFile();
InetAddress inetAddress = InetAddress.getByName(world.getAddress());
byte[] address = inetAddress.getAddress();
String dataStr = RUNELITE_PING;
int dataLength = dataStr.length() + 1;
Pointer data = new Memory(dataLength);
data.setString(0L, dataStr);
IcmpEchoReply icmpEchoReply = new IcmpEchoReply(new Memory(IcmpEchoReply.SIZE + dataLength));
assert icmpEchoReply.size() == IcmpEchoReply.SIZE;
int packed = (address[0] & 0xff) | ((address[1] & 0xff) << 8) | ((address[2] & 0xff) << 16) | ((address[3] & 0xff) << 24);
int ret = ipHlpAPI.IcmpSendEcho(ptr, packed, data, (short) (dataLength), Pointer.NULL, icmpEchoReply, IcmpEchoReply.SIZE + dataLength, TIMEOUT);
if (ret != 1)
{
ipHlpAPI.IcmpCloseHandle(ptr);
return -1;
}
int rtt = Math.toIntExact(icmpEchoReply.roundTripTime.longValue());
ipHlpAPI.IcmpCloseHandle(ptr);
return rtt;
}
private static int tcpPing(World world) throws IOException
{
try (Socket socket = new Socket())
{
socket.setSoTimeout(TIMEOUT);
InetAddress inetAddress = InetAddress.getByName(world.getAddress());
long start = System.nanoTime();
socket.connect(new InetSocketAddress(inetAddress, PORT));
long end = System.nanoTime();
return (int) ((end - start) / 1000000L);
}
}
}