Merge pull request #318 from runelite-extended/normalworldhopper
Restore Worldhopper
This commit is contained in:
@@ -1,126 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.Keybind;
|
||||
|
||||
@ConfigGroup(WorldHopperConfig.GROUP)
|
||||
public interface WorldHopperConfig extends Config
|
||||
{
|
||||
String GROUP = "worldhopper";
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "previousKey",
|
||||
name = "Quick-hop previous",
|
||||
description = "When you press this key you'll hop to the previous world",
|
||||
position = 0
|
||||
)
|
||||
default Keybind previousKey()
|
||||
{
|
||||
return new Keybind(KeyEvent.VK_LEFT, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK);
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "nextKey",
|
||||
name = "Quick-hop next",
|
||||
description = "When you press this key you'll hop to the next world",
|
||||
position = 1
|
||||
)
|
||||
default Keybind nextKey()
|
||||
{
|
||||
return new Keybind(KeyEvent.VK_RIGHT, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK);
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "quickhopOutOfDanger",
|
||||
name = "Quick-hop out of dangerous worlds",
|
||||
description = "Don't hop to a PVP/high risk world when quick-hopping",
|
||||
position = 2
|
||||
)
|
||||
default boolean quickhopOutOfDanger()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showSidebar",
|
||||
name = "Show world hopper sidebar",
|
||||
description = "Show sidebar containing all worlds that mimics in-game interface",
|
||||
position = 3
|
||||
)
|
||||
default boolean showSidebar()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "ping",
|
||||
name = "Show world ping",
|
||||
description = "Shows ping to each game world",
|
||||
position = 4
|
||||
)
|
||||
default boolean ping()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showMessage",
|
||||
name = "Show world hop message in chat",
|
||||
description = "Shows what world is being hopped to in the chat",
|
||||
position = 5
|
||||
)
|
||||
default boolean showWorldHopMessage()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "subscriptionFilter",
|
||||
name = "Show subscription types",
|
||||
description = "Only show free worlds, member worlds, or both types of worlds in sidebar",
|
||||
position = 6
|
||||
)
|
||||
default SubscriptionFilterMode subscriptionFilter()
|
||||
{
|
||||
return SubscriptionFilterMode.BOTH;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showHistory",
|
||||
name = "Show history tab",
|
||||
description = "Shows the history tab",
|
||||
position = 7
|
||||
)
|
||||
default boolean showHistory()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.Keybind;
|
||||
|
||||
@ConfigGroup(WorldHopperConfig.GROUP)
|
||||
public interface WorldHopperConfig extends Config
|
||||
{
|
||||
String GROUP = "worldhopper";
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "previousKey",
|
||||
name = "Quick-hop previous",
|
||||
description = "When you press this key you'll hop to the previous world",
|
||||
position = 0
|
||||
)
|
||||
default Keybind previousKey()
|
||||
{
|
||||
return new Keybind(KeyEvent.VK_LEFT, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK);
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "nextKey",
|
||||
name = "Quick-hop next",
|
||||
description = "When you press this key you'll hop to the next world",
|
||||
position = 1
|
||||
)
|
||||
default Keybind nextKey()
|
||||
{
|
||||
return new Keybind(KeyEvent.VK_RIGHT, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK);
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "quickhopOutOfDanger",
|
||||
name = "Quick-hop out of dangerous worlds",
|
||||
description = "Don't hop to a PVP/high risk world when quick-hopping",
|
||||
position = 2
|
||||
)
|
||||
default boolean quickhopOutOfDanger()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showSidebar",
|
||||
name = "Show world hopper sidebar",
|
||||
description = "Show sidebar containing all worlds that mimics in-game interface",
|
||||
position = 3
|
||||
)
|
||||
default boolean showSidebar()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "ping",
|
||||
name = "Show world ping",
|
||||
description = "Shows ping to each game world",
|
||||
position = 4
|
||||
)
|
||||
default boolean ping()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showMessage",
|
||||
name = "Show world hop message in chat",
|
||||
description = "Shows what world is being hopped to in the chat",
|
||||
position = 5
|
||||
)
|
||||
default boolean showWorldHopMessage()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "subscriptionFilter",
|
||||
name = "Show subscription types",
|
||||
description = "Only show free worlds, member worlds, or both types of worlds in sidebar",
|
||||
position = 6
|
||||
)
|
||||
default SubscriptionFilterMode subscriptionFilter()
|
||||
{
|
||||
return SubscriptionFilterMode.BOTH;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,343 +1,342 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Psikoi <https://github.com/Psikoi>
|
||||
* 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;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
import net.runelite.http.api.worlds.World;
|
||||
import net.runelite.http.api.worlds.WorldType;
|
||||
|
||||
class WorldTableRow extends JPanel
|
||||
{
|
||||
private static final ImageIcon FLAG_AUS;
|
||||
private static final ImageIcon FLAG_UK;
|
||||
private static final ImageIcon FLAG_US;
|
||||
private static final ImageIcon FLAG_GER;
|
||||
|
||||
private static final int WORLD_COLUMN_WIDTH = 60;
|
||||
private static final int PLAYERS_COLUMN_WIDTH = 40;
|
||||
private static final int PING_COLUMN_WIDTH = 35;
|
||||
|
||||
private static final Color CURRENT_WORLD = new Color(66, 227, 17);
|
||||
private static final Color UNAVAILABLE_WORLD = Color.GRAY.darker().darker();
|
||||
private static final Color DANGEROUS_WORLD = new Color(251, 62, 62);
|
||||
private static final Color TOURNAMENT_WORLD = new Color(79, 145, 255);
|
||||
private static final Color MEMBERS_WORLD = new Color(210, 193, 53);
|
||||
private static final Color FREE_WORLD = new Color(200, 200, 200);
|
||||
|
||||
static
|
||||
{
|
||||
FLAG_AUS = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_aus.png"));
|
||||
FLAG_UK = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_uk.png"));
|
||||
FLAG_US = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_us.png"));
|
||||
FLAG_GER = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_ger.png"));
|
||||
}
|
||||
|
||||
private final JMenuItem favoriteMenuOption = new JMenuItem();
|
||||
|
||||
private JLabel worldField;
|
||||
private JLabel playerCountField;
|
||||
private JLabel activityField;
|
||||
private JLabel pingField;
|
||||
private BiConsumer<World, Boolean> onFavorite;
|
||||
|
||||
@Getter
|
||||
private final World world;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private int updatedPlayerCount;
|
||||
|
||||
private int ping;
|
||||
|
||||
private Color lastBackground;
|
||||
private boolean current;
|
||||
|
||||
WorldTableRow(World world, boolean current, boolean favorite, Consumer<World> onSelect, BiConsumer<World, Boolean> onFavorite)
|
||||
{
|
||||
this.current = current;
|
||||
this.world = world;
|
||||
this.onFavorite = onFavorite;
|
||||
this.updatedPlayerCount = world.getPlayers();
|
||||
this.
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new EmptyBorder(2, 0, 2, 0));
|
||||
|
||||
addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent mouseEvent)
|
||||
{
|
||||
if (mouseEvent.getClickCount() == 2)
|
||||
{
|
||||
if (onSelect != null)
|
||||
{
|
||||
onSelect.accept(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent)
|
||||
{
|
||||
if (mouseEvent.getClickCount() == 2)
|
||||
{
|
||||
setBackground(getBackground().brighter());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent mouseEvent)
|
||||
{
|
||||
if (mouseEvent.getClickCount() == 2)
|
||||
{
|
||||
setBackground(getBackground().darker());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent mouseEvent)
|
||||
{
|
||||
WorldTableRow.this.lastBackground = getBackground();
|
||||
setBackground(getBackground().brighter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent mouseEvent)
|
||||
{
|
||||
setBackground(lastBackground);
|
||||
}
|
||||
});
|
||||
|
||||
setFavoriteMenu(favorite);
|
||||
|
||||
final JPopupMenu popupMenu = new JPopupMenu();
|
||||
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
popupMenu.add(favoriteMenuOption);
|
||||
|
||||
setComponentPopupMenu(popupMenu);
|
||||
|
||||
JPanel leftSide = new JPanel(new BorderLayout());
|
||||
JPanel rightSide = new JPanel(new BorderLayout());
|
||||
leftSide.setOpaque(false);
|
||||
rightSide.setOpaque(false);
|
||||
|
||||
JPanel worldField = buildWorldField();
|
||||
worldField.setPreferredSize(new Dimension(WORLD_COLUMN_WIDTH, 0));
|
||||
worldField.setOpaque(false);
|
||||
|
||||
JPanel pingField = buildPingField();
|
||||
pingField.setPreferredSize(new Dimension(PING_COLUMN_WIDTH, 0));
|
||||
pingField.setOpaque(false);
|
||||
|
||||
JPanel playersField = buildPlayersField();
|
||||
playersField.setPreferredSize(new Dimension(PLAYERS_COLUMN_WIDTH, 0));
|
||||
playersField.setOpaque(false);
|
||||
|
||||
JPanel activityField = buildActivityField();
|
||||
activityField.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
activityField.setOpaque(false);
|
||||
|
||||
recolour(current);
|
||||
|
||||
leftSide.add(worldField, BorderLayout.WEST);
|
||||
leftSide.add(playersField, BorderLayout.CENTER);
|
||||
rightSide.add(activityField, BorderLayout.CENTER);
|
||||
rightSide.add(pingField, BorderLayout.EAST);
|
||||
|
||||
add(leftSide, BorderLayout.WEST);
|
||||
add(rightSide, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
void setFavoriteMenu(boolean favorite)
|
||||
{
|
||||
String favoriteAction = favorite ?
|
||||
"Remove " + world.getId() + " from favorites" :
|
||||
"Add " + world.getId() + " to favorites";
|
||||
|
||||
favoriteMenuOption.setText(favoriteAction);
|
||||
|
||||
for (ActionListener listener : favoriteMenuOption.getActionListeners())
|
||||
{
|
||||
favoriteMenuOption.removeActionListener(listener);
|
||||
}
|
||||
|
||||
favoriteMenuOption.addActionListener(e ->
|
||||
{
|
||||
onFavorite.accept(world, !favorite);
|
||||
});
|
||||
}
|
||||
|
||||
void updatePlayerCount(int playerCount)
|
||||
{
|
||||
this.updatedPlayerCount = playerCount;
|
||||
playerCountField.setText(String.valueOf(playerCount));
|
||||
}
|
||||
|
||||
void setPing(int ping)
|
||||
{
|
||||
this.ping = ping;
|
||||
pingField.setText(ping <= 0 ? "-" : Integer.toString(ping));
|
||||
}
|
||||
|
||||
void hidePing()
|
||||
{
|
||||
pingField.setText("-");
|
||||
}
|
||||
|
||||
void showPing()
|
||||
{
|
||||
setPing(ping); // to update pingField
|
||||
}
|
||||
|
||||
int getPing()
|
||||
{
|
||||
return ping;
|
||||
}
|
||||
|
||||
public void recolour(boolean current)
|
||||
{
|
||||
playerCountField.setForeground(current ? CURRENT_WORLD : Color.WHITE);
|
||||
pingField.setForeground(current ? CURRENT_WORLD : Color.WHITE);
|
||||
|
||||
if (current)
|
||||
{
|
||||
activityField.setForeground(CURRENT_WORLD);
|
||||
worldField.setForeground(CURRENT_WORLD);
|
||||
return;
|
||||
}
|
||||
else if (world.getTypes().contains(WorldType.PVP)
|
||||
|| world.getTypes().contains(WorldType.HIGH_RISK)
|
||||
|| world.getTypes().contains(WorldType.DEADMAN)
|
||||
|| world.getTypes().contains(WorldType.SEASONAL_DEADMAN))
|
||||
{
|
||||
activityField.setForeground(DANGEROUS_WORLD);
|
||||
}
|
||||
else if (world.getTypes().contains(WorldType.TOURNAMENT))
|
||||
{
|
||||
activityField.setForeground(TOURNAMENT_WORLD);
|
||||
}
|
||||
else
|
||||
{
|
||||
activityField.setForeground(Color.WHITE);
|
||||
}
|
||||
|
||||
worldField.setForeground(world.getTypes().contains(WorldType.MEMBERS) ? MEMBERS_WORLD : FREE_WORLD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the players list field (containing the amount of players logged in that world).
|
||||
*/
|
||||
private JPanel buildPlayersField()
|
||||
{
|
||||
JPanel column = new JPanel(new BorderLayout());
|
||||
column.setBorder(new EmptyBorder(0, 5, 0, 5));
|
||||
|
||||
playerCountField = new JLabel(world.getPlayers() + "");
|
||||
playerCountField.setFont(FontManager.getRunescapeSmallFont());
|
||||
|
||||
column.add(playerCountField, BorderLayout.WEST);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
private JPanel buildPingField()
|
||||
{
|
||||
JPanel column = new JPanel(new BorderLayout());
|
||||
column.setBorder(new EmptyBorder(0, 5, 0, 5));
|
||||
|
||||
pingField = new JLabel("-");
|
||||
pingField.setFont(FontManager.getRunescapeSmallFont());
|
||||
|
||||
column.add(pingField, BorderLayout.EAST);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the activity list field (containing that world's activity/theme).
|
||||
*/
|
||||
private JPanel buildActivityField()
|
||||
{
|
||||
JPanel column = new JPanel(new BorderLayout());
|
||||
column.setBorder(new EmptyBorder(0, 5, 0, 5));
|
||||
|
||||
activityField = new JLabel(world.getActivity());
|
||||
activityField.setFont(FontManager.getRunescapeSmallFont());
|
||||
|
||||
column.add(activityField, BorderLayout.WEST);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the world list field (containing the country's flag and the world index).
|
||||
*/
|
||||
private JPanel buildWorldField()
|
||||
{
|
||||
JPanel column = new JPanel(new BorderLayout(7, 0));
|
||||
column.setBorder(new EmptyBorder(0, 5, 0, 5));
|
||||
|
||||
worldField = new JLabel(world.getId() + "");
|
||||
|
||||
JLabel flag = new JLabel(getFlag(world.getLocation()));
|
||||
|
||||
column.add(flag, BorderLayout.WEST);
|
||||
column.add(worldField, BorderLayout.CENTER);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
private ImageIcon getFlag(int locationId)
|
||||
{
|
||||
switch (locationId)
|
||||
{
|
||||
case 0:
|
||||
return FLAG_US;
|
||||
case 1:
|
||||
return FLAG_UK;
|
||||
case 3:
|
||||
return FLAG_AUS;
|
||||
default:
|
||||
return FLAG_GER;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2018, Psikoi <https://github.com/Psikoi>
|
||||
* 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;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
import net.runelite.http.api.worlds.World;
|
||||
import net.runelite.http.api.worlds.WorldType;
|
||||
|
||||
class WorldTableRow extends JPanel
|
||||
{
|
||||
private static final ImageIcon FLAG_AUS;
|
||||
private static final ImageIcon FLAG_UK;
|
||||
private static final ImageIcon FLAG_US;
|
||||
private static final ImageIcon FLAG_GER;
|
||||
|
||||
private static final int WORLD_COLUMN_WIDTH = 60;
|
||||
private static final int PLAYERS_COLUMN_WIDTH = 40;
|
||||
private static final int PING_COLUMN_WIDTH = 35;
|
||||
|
||||
private static final Color CURRENT_WORLD = new Color(66, 227, 17);
|
||||
private static final Color UNAVAILABLE_WORLD = Color.GRAY.darker().darker();
|
||||
private static final Color DANGEROUS_WORLD = new Color(251, 62, 62);
|
||||
private static final Color TOURNAMENT_WORLD = new Color(79, 145, 255);
|
||||
private static final Color MEMBERS_WORLD = new Color(210, 193, 53);
|
||||
private static final Color FREE_WORLD = new Color(200, 200, 200);
|
||||
|
||||
static
|
||||
{
|
||||
FLAG_AUS = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_aus.png"));
|
||||
FLAG_UK = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_uk.png"));
|
||||
FLAG_US = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_us.png"));
|
||||
FLAG_GER = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_ger.png"));
|
||||
}
|
||||
|
||||
private final JMenuItem favoriteMenuOption = new JMenuItem();
|
||||
|
||||
private JLabel worldField;
|
||||
private JLabel playerCountField;
|
||||
private JLabel activityField;
|
||||
private JLabel pingField;
|
||||
private BiConsumer<World, Boolean> onFavorite;
|
||||
|
||||
@Getter
|
||||
private final World world;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private int updatedPlayerCount;
|
||||
|
||||
private int ping;
|
||||
|
||||
private Color lastBackground;
|
||||
private boolean current;
|
||||
|
||||
WorldTableRow(World world, boolean current, boolean favorite, Consumer<World> onSelect, BiConsumer<World, Boolean> onFavorite)
|
||||
{
|
||||
this.current = current;
|
||||
this.world = world;
|
||||
this.onFavorite = onFavorite;
|
||||
this.updatedPlayerCount = world.getPlayers();
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new EmptyBorder(2, 0, 2, 0));
|
||||
|
||||
addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent mouseEvent)
|
||||
{
|
||||
if (mouseEvent.getClickCount() == 2)
|
||||
{
|
||||
if (onSelect != null)
|
||||
{
|
||||
onSelect.accept(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent)
|
||||
{
|
||||
if (mouseEvent.getClickCount() == 2)
|
||||
{
|
||||
setBackground(getBackground().brighter());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent mouseEvent)
|
||||
{
|
||||
if (mouseEvent.getClickCount() == 2)
|
||||
{
|
||||
setBackground(getBackground().darker());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent mouseEvent)
|
||||
{
|
||||
WorldTableRow.this.lastBackground = getBackground();
|
||||
setBackground(getBackground().brighter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent mouseEvent)
|
||||
{
|
||||
setBackground(lastBackground);
|
||||
}
|
||||
});
|
||||
|
||||
setFavoriteMenu(favorite);
|
||||
|
||||
final JPopupMenu popupMenu = new JPopupMenu();
|
||||
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
popupMenu.add(favoriteMenuOption);
|
||||
|
||||
setComponentPopupMenu(popupMenu);
|
||||
|
||||
JPanel leftSide = new JPanel(new BorderLayout());
|
||||
JPanel rightSide = new JPanel(new BorderLayout());
|
||||
leftSide.setOpaque(false);
|
||||
rightSide.setOpaque(false);
|
||||
|
||||
JPanel worldField = buildWorldField();
|
||||
worldField.setPreferredSize(new Dimension(WORLD_COLUMN_WIDTH, 0));
|
||||
worldField.setOpaque(false);
|
||||
|
||||
JPanel pingField = buildPingField();
|
||||
pingField.setPreferredSize(new Dimension(PING_COLUMN_WIDTH, 0));
|
||||
pingField.setOpaque(false);
|
||||
|
||||
JPanel playersField = buildPlayersField();
|
||||
playersField.setPreferredSize(new Dimension(PLAYERS_COLUMN_WIDTH, 0));
|
||||
playersField.setOpaque(false);
|
||||
|
||||
JPanel activityField = buildActivityField();
|
||||
activityField.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
activityField.setOpaque(false);
|
||||
|
||||
recolour(current);
|
||||
|
||||
leftSide.add(worldField, BorderLayout.WEST);
|
||||
leftSide.add(playersField, BorderLayout.CENTER);
|
||||
rightSide.add(activityField, BorderLayout.CENTER);
|
||||
rightSide.add(pingField, BorderLayout.EAST);
|
||||
|
||||
add(leftSide, BorderLayout.WEST);
|
||||
add(rightSide, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
void setFavoriteMenu(boolean favorite)
|
||||
{
|
||||
String favoriteAction = favorite ?
|
||||
"Remove " + world.getId() + " from favorites" :
|
||||
"Add " + world.getId() + " to favorites";
|
||||
|
||||
favoriteMenuOption.setText(favoriteAction);
|
||||
|
||||
for (ActionListener listener : favoriteMenuOption.getActionListeners())
|
||||
{
|
||||
favoriteMenuOption.removeActionListener(listener);
|
||||
}
|
||||
|
||||
favoriteMenuOption.addActionListener(e ->
|
||||
{
|
||||
onFavorite.accept(world, !favorite);
|
||||
});
|
||||
}
|
||||
|
||||
void updatePlayerCount(int playerCount)
|
||||
{
|
||||
this.updatedPlayerCount = playerCount;
|
||||
playerCountField.setText(String.valueOf(playerCount));
|
||||
}
|
||||
|
||||
void setPing(int ping)
|
||||
{
|
||||
this.ping = ping;
|
||||
pingField.setText(ping <= 0 ? "-" : Integer.toString(ping));
|
||||
}
|
||||
|
||||
void hidePing()
|
||||
{
|
||||
pingField.setText("-");
|
||||
}
|
||||
|
||||
void showPing()
|
||||
{
|
||||
setPing(ping); // to update pingField
|
||||
}
|
||||
|
||||
int getPing()
|
||||
{
|
||||
return ping;
|
||||
}
|
||||
|
||||
public void recolour(boolean current)
|
||||
{
|
||||
playerCountField.setForeground(current ? CURRENT_WORLD : Color.WHITE);
|
||||
pingField.setForeground(current ? CURRENT_WORLD : Color.WHITE);
|
||||
|
||||
if (current)
|
||||
{
|
||||
activityField.setForeground(CURRENT_WORLD);
|
||||
worldField.setForeground(CURRENT_WORLD);
|
||||
return;
|
||||
}
|
||||
else if (world.getTypes().contains(WorldType.PVP)
|
||||
|| world.getTypes().contains(WorldType.HIGH_RISK)
|
||||
|| world.getTypes().contains(WorldType.DEADMAN)
|
||||
|| world.getTypes().contains(WorldType.SEASONAL_DEADMAN))
|
||||
{
|
||||
activityField.setForeground(DANGEROUS_WORLD);
|
||||
}
|
||||
else if (world.getTypes().contains(WorldType.TOURNAMENT))
|
||||
{
|
||||
activityField.setForeground(TOURNAMENT_WORLD);
|
||||
}
|
||||
else
|
||||
{
|
||||
activityField.setForeground(Color.WHITE);
|
||||
}
|
||||
|
||||
worldField.setForeground(world.getTypes().contains(WorldType.MEMBERS) ? MEMBERS_WORLD : FREE_WORLD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the players list field (containing the amount of players logged in that world).
|
||||
*/
|
||||
private JPanel buildPlayersField()
|
||||
{
|
||||
JPanel column = new JPanel(new BorderLayout());
|
||||
column.setBorder(new EmptyBorder(0, 5, 0, 5));
|
||||
|
||||
playerCountField = new JLabel(world.getPlayers() + "");
|
||||
playerCountField.setFont(FontManager.getRunescapeSmallFont());
|
||||
|
||||
column.add(playerCountField, BorderLayout.WEST);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
private JPanel buildPingField()
|
||||
{
|
||||
JPanel column = new JPanel(new BorderLayout());
|
||||
column.setBorder(new EmptyBorder(0, 5, 0, 5));
|
||||
|
||||
pingField = new JLabel("-");
|
||||
pingField.setFont(FontManager.getRunescapeSmallFont());
|
||||
|
||||
column.add(pingField, BorderLayout.EAST);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the activity list field (containing that world's activity/theme).
|
||||
*/
|
||||
private JPanel buildActivityField()
|
||||
{
|
||||
JPanel column = new JPanel(new BorderLayout());
|
||||
column.setBorder(new EmptyBorder(0, 5, 0, 5));
|
||||
|
||||
activityField = new JLabel(world.getActivity());
|
||||
activityField.setFont(FontManager.getRunescapeSmallFont());
|
||||
|
||||
column.add(activityField, BorderLayout.WEST);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the world list field (containing the country's flag and the world index).
|
||||
*/
|
||||
private JPanel buildWorldField()
|
||||
{
|
||||
JPanel column = new JPanel(new BorderLayout(7, 0));
|
||||
column.setBorder(new EmptyBorder(0, 5, 0, 5));
|
||||
|
||||
worldField = new JLabel(world.getId() + "");
|
||||
|
||||
JLabel flag = new JLabel(getFlag(world.getLocation()));
|
||||
|
||||
column.add(flag, BorderLayout.WEST);
|
||||
column.add(worldField, BorderLayout.CENTER);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
private ImageIcon getFlag(int locationId)
|
||||
{
|
||||
switch (locationId)
|
||||
{
|
||||
case 0:
|
||||
return FLAG_US;
|
||||
case 1:
|
||||
return FLAG_UK;
|
||||
case 3:
|
||||
return FLAG_AUS;
|
||||
default:
|
||||
return FLAG_GER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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