Default Worldhopper
This commit is contained in:
@@ -112,15 +112,4 @@ public interface WorldHopperConfig extends Config
|
||||
{
|
||||
return SubscriptionFilterMode.BOTH;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showHistory",
|
||||
name = "Show history tab",
|
||||
description = "Shows the history tab",
|
||||
position = 7
|
||||
)
|
||||
default boolean showHistory()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,13 +75,13 @@ import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.input.KeyManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
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.NavigationButton;
|
||||
import net.runelite.client.util.ExecutorServiceExceptionLogger;
|
||||
import net.runelite.client.util.HotkeyListener;
|
||||
import net.runelite.client.util.Text;
|
||||
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.WorldClient;
|
||||
import net.runelite.http.api.worlds.WorldResult;
|
||||
@@ -258,49 +258,10 @@ public class WorldHopperPlugin extends Plugin
|
||||
panel.setFilterMode(config.subscriptionFilter());
|
||||
updateList();
|
||||
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)
|
||||
{
|
||||
configManager.setConfiguration(WorldHopperConfig.GROUP, "favorite_" + world, true);
|
||||
@@ -457,12 +418,6 @@ public class WorldHopperPlugin extends Plugin
|
||||
lastWorld = newWorld;
|
||||
}
|
||||
}
|
||||
|
||||
if (gameStateChanged.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
addToHistory(client.getWorld());
|
||||
panel.updateList();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@@ -699,8 +654,6 @@ public class WorldHopperPlugin extends Plugin
|
||||
|
||||
quickHopTargetWorld = rsWorld;
|
||||
displaySwitcherAttempts = 0;
|
||||
|
||||
addToHistory(worldId);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
|
||||
@@ -26,26 +26,15 @@ package net.runelite.client.plugins.worldhopper;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.Border;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Setter;
|
||||
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 PING_COLUMN_WIDTH = 47;
|
||||
|
||||
private final JPanel headerContainer;
|
||||
private final JPanel headerHistContainer;
|
||||
private final JPanel listContainer = new JPanel();
|
||||
private final JPanel histContainer = new JPanel();
|
||||
|
||||
private WorldTableHeader worldHeader;
|
||||
private WorldTableHeader playersHeader;
|
||||
@@ -78,7 +64,6 @@ class WorldSwitcherPanel extends PluginPanel
|
||||
private boolean ascendingOrder = true;
|
||||
|
||||
private ArrayList<WorldTableRow> rows = new ArrayList<>();
|
||||
private ArrayList<WorldTableRow> histRows = new ArrayList<>();
|
||||
private WorldHopperPlugin plugin;
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private SubscriptionFilterMode filterMode;
|
||||
@@ -90,35 +75,12 @@ class WorldSwitcherPanel extends PluginPanel
|
||||
setBorder(null);
|
||||
setLayout(new DynamicGridLayout(0, 1));
|
||||
|
||||
headerContainer = buildHeader();
|
||||
headerHistContainer = buildHistoryHeader();
|
||||
JPanel headerContainer = buildHeader();
|
||||
|
||||
listContainer.setLayout(new GridLayout(0, 1));
|
||||
histContainer.setLayout(new GridLayout(0, 1));
|
||||
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
add(headerContainer);
|
||||
add(listContainer);
|
||||
}
|
||||
|
||||
void switchCurrentHighlight(int newWorld, int lastWorld)
|
||||
@@ -134,18 +96,6 @@ class WorldSwitcherPanel extends PluginPanel
|
||||
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)
|
||||
@@ -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
|
||||
// to properly display the new data
|
||||
if (orderIndex == WorldOrder.PLAYERS)
|
||||
@@ -194,21 +134,6 @@ class WorldSwitcherPanel extends PluginPanel
|
||||
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()
|
||||
@@ -217,11 +142,6 @@ class WorldSwitcherPanel extends PluginPanel
|
||||
{
|
||||
worldTableRow.hidePing();
|
||||
}
|
||||
|
||||
for (WorldTableRow worldTableRow : histRows)
|
||||
{
|
||||
worldTableRow.hidePing();
|
||||
}
|
||||
}
|
||||
|
||||
void showPing()
|
||||
@@ -230,11 +150,6 @@ class WorldSwitcherPanel extends PluginPanel
|
||||
{
|
||||
worldTableRow.showPing();
|
||||
}
|
||||
|
||||
for (WorldTableRow worldTableRow : histRows)
|
||||
{
|
||||
worldTableRow.showPing();
|
||||
}
|
||||
}
|
||||
|
||||
void updateList()
|
||||
@@ -270,127 +185,16 @@ class WorldSwitcherPanel extends PluginPanel
|
||||
});
|
||||
|
||||
listContainer.removeAll();
|
||||
histContainer.removeAll();
|
||||
|
||||
Map<String, String> history = plugin.getHistory();
|
||||
Map<String, String> matchedHist = new HashMap<>();
|
||||
for (int i = 0; i < rows.size(); i++)
|
||||
{
|
||||
WorldTableRow row = rows.get(i);
|
||||
row.setBackground(i % 2 == 0 ? ODD_ROW : ColorScheme.DARK_GRAY_COLOR);
|
||||
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.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)
|
||||
@@ -402,14 +206,6 @@ class WorldSwitcherPanel extends PluginPanel
|
||||
row.setFavoriteMenu(favorite);
|
||||
}
|
||||
}
|
||||
|
||||
for (WorldTableRow row : histRows)
|
||||
{
|
||||
if (row.getWorld().getId() == world)
|
||||
{
|
||||
row.setFavoriteMenu(favorite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resetAllFavoriteMenus()
|
||||
@@ -419,21 +215,10 @@ class WorldSwitcherPanel extends PluginPanel
|
||||
row.setFavoriteMenu(false);
|
||||
}
|
||||
|
||||
for (WorldTableRow row : histRows)
|
||||
{
|
||||
row.setFavoriteMenu(false);
|
||||
}
|
||||
}
|
||||
|
||||
void populate(List<World> worlds)
|
||||
{
|
||||
Map<Integer, Integer> pingHistory = new HashMap<>();
|
||||
|
||||
for (WorldTableRow row : rows)
|
||||
{
|
||||
pingHistory.put(row.getWorld().getId(), row.getPing());
|
||||
}
|
||||
|
||||
rows.clear();
|
||||
|
||||
for (int i = 0; i < worlds.size(); i++)
|
||||
@@ -456,8 +241,7 @@ class WorldSwitcherPanel extends PluginPanel
|
||||
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), ping));
|
||||
rows.add(buildRow(world, i % 2 == 0, world.getId() == plugin.getCurrentWorld() && plugin.getLastWorld() != 0, plugin.isFavorite(world)));
|
||||
}
|
||||
|
||||
updateList();
|
||||
@@ -493,35 +277,6 @@ class WorldSwitcherPanel extends PluginPanel
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
JPanel header = new JPanel(new BorderLayout());
|
||||
@@ -606,7 +361,7 @@ class WorldSwitcherPanel extends PluginPanel
|
||||
/**
|
||||
* 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,
|
||||
world1 ->
|
||||
|
||||
@@ -96,9 +96,8 @@ class WorldTableRow extends JPanel
|
||||
this.world = world;
|
||||
this.onFavorite = onFavorite;
|
||||
this.updatedPlayerCount = world.getPlayers();
|
||||
this.
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new EmptyBorder(2, 0, 2, 0));
|
||||
|
||||
addMouseListener(new MouseAdapter()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user