mirror of
https://github.com/filoghost/HolographicDisplays.git
synced 2024-12-19 07:17:37 +01:00
Refactor BungeeCord-related code
This commit is contained in:
parent
9a56a032de
commit
57fd78eac5
@ -30,7 +30,6 @@ import me.filoghost.holographicdisplays.object.internal.InternalHologram;
|
|||||||
import me.filoghost.holographicdisplays.object.internal.InternalHologramManager;
|
import me.filoghost.holographicdisplays.object.internal.InternalHologramManager;
|
||||||
import me.filoghost.holographicdisplays.placeholder.AnimationsRegistry;
|
import me.filoghost.holographicdisplays.placeholder.AnimationsRegistry;
|
||||||
import me.filoghost.holographicdisplays.placeholder.PlaceholdersManager;
|
import me.filoghost.holographicdisplays.placeholder.PlaceholdersManager;
|
||||||
import me.filoghost.holographicdisplays.task.BungeeCleanupTask;
|
|
||||||
import me.filoghost.holographicdisplays.task.WorldPlayerCounterTask;
|
import me.filoghost.holographicdisplays.task.WorldPlayerCounterTask;
|
||||||
import me.filoghost.holographicdisplays.util.NMSVersion;
|
import me.filoghost.holographicdisplays.util.NMSVersion;
|
||||||
import org.bstats.bukkit.MetricsLite;
|
import org.bstats.bukkit.MetricsLite;
|
||||||
@ -38,6 +37,7 @@ import org.bukkit.Bukkit;
|
|||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacketSettings {
|
public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacketSettings {
|
||||||
|
|
||||||
@ -112,7 +112,6 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
|
|||||||
|
|
||||||
// Start repeating tasks.
|
// Start repeating tasks.
|
||||||
placeholderManager.startRefreshTask(this);
|
placeholderManager.startRefreshTask(this);
|
||||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new BungeeCleanupTask(bungeeServerTracker), 5 * 60 * 20, 5 * 60 * 20);
|
|
||||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new WorldPlayerCounterTask(), 0L, 3 * 20);
|
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new WorldPlayerCounterTask(), 0L, 3 * 20);
|
||||||
|
|
||||||
HologramCommandManager commandManager = new HologramCommandManager(configManager, internalHologramManager, nmsManager);
|
HologramCommandManager commandManager = new HologramCommandManager(configManager, internalHologramManager, nmsManager);
|
||||||
@ -142,8 +141,7 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
|
|||||||
public void load(boolean deferHologramsCreation, ErrorCollector errorCollector) {
|
public void load(boolean deferHologramsCreation, ErrorCollector errorCollector) {
|
||||||
placeholderManager.untrackAll();
|
placeholderManager.untrackAll();
|
||||||
internalHologramManager.clearAll();
|
internalHologramManager.clearAll();
|
||||||
bungeeServerTracker.resetTrackedServers();
|
|
||||||
|
|
||||||
configManager.reloadCustomPlaceholders(errorCollector);
|
configManager.reloadCustomPlaceholders(errorCollector);
|
||||||
configManager.reloadMainConfig(errorCollector);
|
configManager.reloadMainConfig(errorCollector);
|
||||||
HologramDatabase hologramDatabase = configManager.loadHologramDatabase(errorCollector);
|
HologramDatabase hologramDatabase = configManager.loadHologramDatabase(errorCollector);
|
||||||
@ -153,7 +151,7 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
|
|||||||
errorCollector.add(e, "failed to load animation files");
|
errorCollector.add(e, "failed to load animation files");
|
||||||
}
|
}
|
||||||
|
|
||||||
bungeeServerTracker.restartTask(Configuration.bungeeRefreshSeconds);
|
bungeeServerTracker.restart(Configuration.bungeeRefreshSeconds, TimeUnit.SECONDS);
|
||||||
|
|
||||||
if (deferHologramsCreation) {
|
if (deferHologramsCreation) {
|
||||||
// For the initial load: holograms are loaded later, when the worlds are ready
|
// For the initial load: holograms are loaded later, when the worlds are ready
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) filoghost and contributors
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
package me.filoghost.holographicdisplays.bridge.bungeecord;
|
|
||||||
|
|
||||||
import me.filoghost.fcommons.logging.Log;
|
|
||||||
import me.filoghost.holographicdisplays.HolographicDisplays;
|
|
||||||
import me.filoghost.holographicdisplays.disk.Configuration;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
public class BungeeChannel implements PluginMessageListener {
|
|
||||||
|
|
||||||
private static final String BUNGEECORD_CHANNEL = "BungeeCord";
|
|
||||||
private static final String REDISBUNGEE_CHANNEL = "legacy:redisbungee";
|
|
||||||
|
|
||||||
private final BungeeServerTracker bungeeServerTracker;
|
|
||||||
|
|
||||||
public BungeeChannel(BungeeServerTracker bungeeServerTracker) {
|
|
||||||
this.bungeeServerTracker = bungeeServerTracker;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void register(Plugin plugin) {
|
|
||||||
Bukkit.getMessenger().registerOutgoingPluginChannel(plugin, BUNGEECORD_CHANNEL);
|
|
||||||
Bukkit.getMessenger().registerIncomingPluginChannel(plugin, BUNGEECORD_CHANNEL, this);
|
|
||||||
|
|
||||||
Bukkit.getMessenger().registerOutgoingPluginChannel(plugin, REDISBUNGEE_CHANNEL);
|
|
||||||
Bukkit.getMessenger().registerIncomingPluginChannel(plugin, REDISBUNGEE_CHANNEL, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getTargetChannel() {
|
|
||||||
if (Configuration.useRedisBungee) {
|
|
||||||
return REDISBUNGEE_CHANNEL;
|
|
||||||
} else {
|
|
||||||
return BUNGEECORD_CHANNEL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPluginMessageReceived(String channel, Player player, byte[] message) {
|
|
||||||
if (channel.equals(getTargetChannel())) {
|
|
||||||
DataInputStream in = new DataInputStream(new ByteArrayInputStream(message));
|
|
||||||
|
|
||||||
try {
|
|
||||||
String subChannel = in.readUTF();
|
|
||||||
|
|
||||||
if (subChannel.equals("PlayerCount")) {
|
|
||||||
String server = in.readUTF();
|
|
||||||
|
|
||||||
if (in.available() > 0) {
|
|
||||||
int online = in.readInt();
|
|
||||||
|
|
||||||
BungeeServerInfo serverInfo = bungeeServerTracker.getOrCreateServerInfo(server);
|
|
||||||
serverInfo.setOnlinePlayers(online);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (EOFException e) {
|
|
||||||
// Do nothing.
|
|
||||||
} catch (IOException e) {
|
|
||||||
// This should never happen.
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void askPlayerCount(String server) {
|
|
||||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
|
||||||
DataOutputStream out = new DataOutputStream(b);
|
|
||||||
|
|
||||||
try {
|
|
||||||
out.writeUTF("PlayerCount");
|
|
||||||
out.writeUTF(server);
|
|
||||||
} catch (IOException e) {
|
|
||||||
// It should not happen.
|
|
||||||
Log.warning("I/O Exception while asking for player count on server '" + server + "'.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// OR, if you don't need to send it to a specific player
|
|
||||||
Collection<? extends Player> players = Bukkit.getOnlinePlayers();
|
|
||||||
if (players.size() > 0) {
|
|
||||||
players.iterator().next().sendPluginMessage(HolographicDisplays.getInstance(), getTargetChannel(), b.toByteArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) filoghost and contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
package me.filoghost.holographicdisplays.bridge.bungeecord;
|
||||||
|
|
||||||
|
import me.filoghost.fcommons.logging.Log;
|
||||||
|
import me.filoghost.holographicdisplays.disk.Configuration;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class BungeeMessenger implements PluginMessageListener {
|
||||||
|
|
||||||
|
private static final String BUNGEECORD_CHANNEL = "BungeeCord";
|
||||||
|
private static final String REDISBUNGEE_CHANNEL = "legacy:redisbungee";
|
||||||
|
|
||||||
|
private final Plugin plugin;
|
||||||
|
private final PlayerCountCallback playerCountCallback;
|
||||||
|
|
||||||
|
private BungeeMessenger(Plugin plugin, PlayerCountCallback playerCountCallback) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.playerCountCallback = playerCountCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BungeeMessenger registerNew(Plugin plugin, PlayerCountCallback playerCountCallback) {
|
||||||
|
BungeeMessenger bungeeMessenger = new BungeeMessenger(plugin, playerCountCallback);
|
||||||
|
|
||||||
|
Bukkit.getMessenger().registerOutgoingPluginChannel(plugin, BUNGEECORD_CHANNEL);
|
||||||
|
Bukkit.getMessenger().registerIncomingPluginChannel(plugin, BUNGEECORD_CHANNEL, bungeeMessenger);
|
||||||
|
|
||||||
|
Bukkit.getMessenger().registerOutgoingPluginChannel(plugin, REDISBUNGEE_CHANNEL);
|
||||||
|
Bukkit.getMessenger().registerIncomingPluginChannel(plugin, REDISBUNGEE_CHANNEL, bungeeMessenger);
|
||||||
|
|
||||||
|
return bungeeMessenger;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPluginMessageReceived(String channel, Player player, byte[] message) {
|
||||||
|
if (Configuration.pingerEnabled || !channel.equals(getTargetChannel())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataInputStream in = new DataInputStream(new ByteArrayInputStream(message));
|
||||||
|
|
||||||
|
try {
|
||||||
|
String subChannel = in.readUTF();
|
||||||
|
if (!subChannel.equals("PlayerCount")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String server = in.readUTF();
|
||||||
|
int online = in.readInt();
|
||||||
|
playerCountCallback.onReceive(server, online);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.warning("Error while decoding player count from BungeeCord.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPlayerCountRequest(String server) {
|
||||||
|
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream out = new DataOutputStream(byteOut);
|
||||||
|
|
||||||
|
try {
|
||||||
|
out.writeUTF("PlayerCount");
|
||||||
|
out.writeUTF(server);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.warning("Error while encoding player count message for server \"" + server + "\".", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the message through a random player (BungeeCord will not forward it to them)
|
||||||
|
Collection<? extends Player> players = Bukkit.getOnlinePlayers();
|
||||||
|
if (players.size() > 0) {
|
||||||
|
Player player = players.iterator().next();
|
||||||
|
player.sendPluginMessage(plugin, getTargetChannel(), byteOut.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTargetChannel() {
|
||||||
|
if (Configuration.useRedisBungee) {
|
||||||
|
return REDISBUNGEE_CHANNEL;
|
||||||
|
} else {
|
||||||
|
return BUNGEECORD_CHANNEL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface PlayerCountCallback {
|
||||||
|
|
||||||
|
void onReceive(String serverName, int playerCount);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) filoghost and contributors
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
package me.filoghost.holographicdisplays.bridge.bungeecord;
|
|
||||||
|
|
||||||
import me.filoghost.holographicdisplays.disk.Configuration;
|
|
||||||
|
|
||||||
public class BungeeServerInfo {
|
|
||||||
|
|
||||||
private volatile boolean isOnline;
|
|
||||||
private volatile int onlinePlayers;
|
|
||||||
private volatile int maxPlayers;
|
|
||||||
|
|
||||||
// The two lines of a motd
|
|
||||||
private volatile String motd1; // Should never be null
|
|
||||||
private volatile String motd2; // Should never be null
|
|
||||||
|
|
||||||
private volatile long lastRequest;
|
|
||||||
|
|
||||||
protected BungeeServerInfo() {
|
|
||||||
isOnline = false;
|
|
||||||
this.motd1 = "";
|
|
||||||
this.motd2 = "";
|
|
||||||
updateLastRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isOnline() {
|
|
||||||
return isOnline;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnline(boolean isOnline) {
|
|
||||||
this.isOnline = isOnline;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOnlinePlayers() {
|
|
||||||
return onlinePlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnlinePlayers(int onlinePlayers) {
|
|
||||||
this.onlinePlayers = onlinePlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxPlayers() {
|
|
||||||
return maxPlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMaxPlayers(int maxPlayers) {
|
|
||||||
this.maxPlayers = maxPlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMotd1() {
|
|
||||||
return motd1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMotd2() {
|
|
||||||
return motd2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMotd(String motd) {
|
|
||||||
if (motd == null) {
|
|
||||||
this.motd1 = "";
|
|
||||||
this.motd2 = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int separatorIndex = motd.indexOf("\n");
|
|
||||||
if (separatorIndex >= 0) {
|
|
||||||
String line1 = motd.substring(0, separatorIndex);
|
|
||||||
String line2 = motd.substring(separatorIndex + 1);
|
|
||||||
this.motd1 = Configuration.pingerTrimMotd ? line1.trim() : line1;
|
|
||||||
this.motd2 = Configuration.pingerTrimMotd ? line2.trim() : line2;
|
|
||||||
} else {
|
|
||||||
this.motd1 = Configuration.pingerTrimMotd ? motd.trim() : motd;
|
|
||||||
this.motd2 = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLastRequest() {
|
|
||||||
return lastRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateLastRequest() {
|
|
||||||
this.lastRequest = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -7,8 +7,9 @@ package me.filoghost.holographicdisplays.bridge.bungeecord;
|
|||||||
|
|
||||||
import me.filoghost.fcommons.logging.Log;
|
import me.filoghost.fcommons.logging.Log;
|
||||||
import me.filoghost.holographicdisplays.HolographicDisplays;
|
import me.filoghost.holographicdisplays.HolographicDisplays;
|
||||||
import me.filoghost.holographicdisplays.bridge.bungeecord.serverpinger.PingResponse;
|
import me.filoghost.holographicdisplays.bridge.bungeecord.pinger.PingResponse;
|
||||||
import me.filoghost.holographicdisplays.bridge.bungeecord.serverpinger.ServerPinger;
|
import me.filoghost.holographicdisplays.bridge.bungeecord.pinger.ServerPinger;
|
||||||
|
import me.filoghost.holographicdisplays.core.DebugLogger;
|
||||||
import me.filoghost.holographicdisplays.disk.Configuration;
|
import me.filoghost.holographicdisplays.disk.Configuration;
|
||||||
import me.filoghost.holographicdisplays.disk.ServerAddress;
|
import me.filoghost.holographicdisplays.disk.ServerAddress;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@ -17,184 +18,122 @@ import org.bukkit.plugin.Plugin;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class BungeeServerTracker {
|
public class BungeeServerTracker {
|
||||||
|
|
||||||
private static final String PINGER_NOT_ENABLED_ERROR = "[Please enable pinger]";
|
private static final long UNTRACK_AFTER_TIME_WITHOUT_REQUESTS = TimeUnit.MINUTES.toMillis(10);
|
||||||
|
|
||||||
private final BungeeChannel bungeeChannel;
|
private final ConcurrentMap<String, TrackedServer> trackedServers;
|
||||||
private final Map<String, BungeeServerInfo> trackedServers;
|
private final BungeeMessenger bungeeMessenger;
|
||||||
|
|
||||||
private int taskID = -1;
|
private int taskID = -1;
|
||||||
|
|
||||||
public BungeeServerTracker(Plugin plugin) {
|
public BungeeServerTracker(Plugin plugin) {
|
||||||
bungeeChannel = new BungeeChannel(this);
|
|
||||||
bungeeChannel.register(plugin);
|
|
||||||
trackedServers = new ConcurrentHashMap<>();
|
trackedServers = new ConcurrentHashMap<>();
|
||||||
|
bungeeMessenger = BungeeMessenger.registerNew(plugin, this::updateServerInfoFromBungee);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetTrackedServers() {
|
public void restart(int updateInterval, TimeUnit timeUnit) {
|
||||||
trackedServers.clear();
|
trackedServers.clear();
|
||||||
}
|
|
||||||
|
|
||||||
public void track(String server) {
|
|
||||||
if (!trackedServers.containsKey(server)) {
|
|
||||||
BungeeServerInfo info = new BungeeServerInfo();
|
|
||||||
info.setMotd(Configuration.pingerOfflineMotd);
|
|
||||||
trackedServers.put(server, info);
|
|
||||||
|
|
||||||
if (!Configuration.pingerEnabled) {
|
|
||||||
bungeeChannel.askPlayerCount(server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected BungeeServerInfo getOrCreateServerInfo(String server) {
|
|
||||||
BungeeServerInfo info = trackedServers.get(server);
|
|
||||||
if (info == null) {
|
|
||||||
info = new BungeeServerInfo();
|
|
||||||
info.setMotd(Configuration.pingerOfflineMotd);
|
|
||||||
trackedServers.put(server, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPlayersOnline(String server) {
|
|
||||||
BungeeServerInfo info = trackedServers.get(server);
|
|
||||||
if (info != null) {
|
|
||||||
info.updateLastRequest();
|
|
||||||
return info.getOnlinePlayers();
|
|
||||||
} else {
|
|
||||||
// It was not tracked, add it.
|
|
||||||
track(server);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMaxPlayers(String server) {
|
|
||||||
if (!Configuration.pingerEnabled) {
|
|
||||||
return PINGER_NOT_ENABLED_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
BungeeServerInfo info = trackedServers.get(server);
|
|
||||||
if (info != null) {
|
|
||||||
info.updateLastRequest();
|
|
||||||
return String.valueOf(info.getMaxPlayers());
|
|
||||||
} else {
|
|
||||||
// It was not tracked, add it.
|
|
||||||
track(server);
|
|
||||||
return "0";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMotd1(String server) {
|
|
||||||
if (!Configuration.pingerEnabled) {
|
|
||||||
return PINGER_NOT_ENABLED_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
BungeeServerInfo info = trackedServers.get(server);
|
|
||||||
if (info != null) {
|
|
||||||
info.updateLastRequest();
|
|
||||||
return info.getMotd1();
|
|
||||||
} else {
|
|
||||||
// It was not tracked, add it.
|
|
||||||
track(server);
|
|
||||||
return Configuration.pingerOfflineMotd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMotd2(String server) {
|
|
||||||
if (!Configuration.pingerEnabled) {
|
|
||||||
return PINGER_NOT_ENABLED_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
BungeeServerInfo info = trackedServers.get(server);
|
|
||||||
if (info != null) {
|
|
||||||
info.updateLastRequest();
|
|
||||||
return info.getMotd2();
|
|
||||||
} else {
|
|
||||||
// It was not tracked, add it.
|
|
||||||
track(server);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOnlineStatus(String server) {
|
|
||||||
if (!Configuration.pingerEnabled) {
|
|
||||||
return PINGER_NOT_ENABLED_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
BungeeServerInfo info = trackedServers.get(server);
|
|
||||||
if (info != null) {
|
|
||||||
info.updateLastRequest();
|
|
||||||
return info.isOnline() ? Configuration.pingerStatusOnline : Configuration.pingerStatusOffline;
|
|
||||||
} else {
|
|
||||||
// It was not tracked, add it.
|
|
||||||
track(server);
|
|
||||||
return Configuration.pingerStatusOffline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, BungeeServerInfo> getTrackedServers() {
|
|
||||||
return trackedServers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restartTask(int refreshSeconds) {
|
|
||||||
if (taskID != -1) {
|
if (taskID != -1) {
|
||||||
Bukkit.getScheduler().cancelTask(taskID);
|
Bukkit.getScheduler().cancelTask(taskID);
|
||||||
}
|
}
|
||||||
|
|
||||||
taskID = Bukkit.getScheduler().scheduleSyncRepeatingTask(HolographicDisplays.getInstance(), () -> {
|
|
||||||
if (Configuration.pingerEnabled) {
|
|
||||||
runAsyncPinger();
|
|
||||||
} else {
|
|
||||||
for (String server : trackedServers.keySet()) {
|
|
||||||
bungeeChannel.askPlayerCount(server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}, 1, refreshSeconds * 20L);
|
taskID = Bukkit.getScheduler().scheduleSyncRepeatingTask(HolographicDisplays.getInstance(),
|
||||||
|
this::runPeriodicUpdateTask, 1, timeUnit.toSeconds(updateInterval) * 20L);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runAsyncPinger() {
|
public ServerInfo getCurrentServerInfo(String serverName) {
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(HolographicDisplays.getInstance(), () -> {
|
// If it wasn't already tracked, send an update request instantly
|
||||||
for (ServerAddress serverAddress : Configuration.pingerServers) {
|
if (!Configuration.pingerEnabled && !trackedServers.containsKey(serverName)) {
|
||||||
BungeeServerInfo serverInfo = getOrCreateServerInfo(serverAddress.getName());
|
bungeeMessenger.sendPlayerCountRequest(serverName);
|
||||||
boolean displayOffline = false;
|
}
|
||||||
|
|
||||||
try {
|
TrackedServer trackedServer = trackedServers.computeIfAbsent(serverName, TrackedServer::new);
|
||||||
PingResponse data = ServerPinger.fetchData(serverAddress, Configuration.pingerTimeout);
|
trackedServer.updateLastRequest();
|
||||||
|
return trackedServer.serverInfo;
|
||||||
if (data.isOnline()) {
|
}
|
||||||
serverInfo.setOnline(true);
|
|
||||||
serverInfo.setOnlinePlayers(data.getOnlinePlayers());
|
private void runPeriodicUpdateTask() {
|
||||||
serverInfo.setMaxPlayers(data.getMaxPlayers());
|
removeUnusedServers();
|
||||||
serverInfo.setMotd(data.getMotd());
|
|
||||||
} else {
|
if (Configuration.pingerEnabled) {
|
||||||
displayOffline = true;
|
Bukkit.getScheduler().runTaskAsynchronously(HolographicDisplays.getInstance(), () -> {
|
||||||
}
|
for (TrackedServer trackedServer : trackedServers.values()) {
|
||||||
} catch (SocketTimeoutException e) {
|
updateServerInfoWithPinger(trackedServer);
|
||||||
// Common error, avoid logging
|
|
||||||
displayOffline = true;
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
Log.warning("Couldn't fetch data from " + serverAddress + ": unknown host address.");
|
|
||||||
displayOffline = true;
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.warning("Couldn't fetch data from " + serverAddress + ".", e);
|
|
||||||
displayOffline = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (displayOffline) {
|
|
||||||
serverInfo.setOnline(false);
|
|
||||||
serverInfo.setOnlinePlayers(0);
|
|
||||||
serverInfo.setMaxPlayers(0);
|
|
||||||
serverInfo.setMotd(Configuration.pingerOfflineMotd);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
for (String serverName : trackedServers.keySet()) {
|
||||||
|
bungeeMessenger.sendPlayerCountRequest(serverName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateServerInfoWithPinger(TrackedServer trackedServer) {
|
||||||
|
ServerAddress serverAddress = Configuration.pingerServerAddresses.get(trackedServer.serverName);
|
||||||
|
|
||||||
|
if (serverAddress != null) {
|
||||||
|
trackedServer.serverInfo = pingServer(serverAddress);
|
||||||
|
} else {
|
||||||
|
trackedServer.serverInfo = ServerInfo.offline("[Unknown server: " + trackedServer.serverName + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateServerInfoFromBungee(String serverName, int onlinePlayers) {
|
||||||
|
TrackedServer trackedServer = trackedServers.get(serverName);
|
||||||
|
if (trackedServer != null) {
|
||||||
|
trackedServer.serverInfo = ServerInfo.online(onlinePlayers, 0, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServerInfo pingServer(ServerAddress serverAddress) {
|
||||||
|
try {
|
||||||
|
PingResponse data = ServerPinger.fetchData(serverAddress, Configuration.pingerTimeout);
|
||||||
|
return ServerInfo.online(data.getOnlinePlayers(), data.getMaxPlayers(), data.getMotd());
|
||||||
|
} catch (SocketTimeoutException e) {
|
||||||
|
// Common error, do not log
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
Log.warning("Couldn't fetch data from " + serverAddress + ": unknown host address.");
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.warning("Couldn't fetch data from " + serverAddress + ".", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerInfo.offline(Configuration.pingerOfflineMotd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeUnusedServers() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
|
||||||
|
trackedServers.values().removeIf(trackedServer -> {
|
||||||
|
if (now - trackedServer.lastRequest > UNTRACK_AFTER_TIME_WITHOUT_REQUESTS) {
|
||||||
|
DebugLogger.info("Untracked unused server \"" + trackedServer.serverName + "\".");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class TrackedServer {
|
||||||
|
|
||||||
|
private final String serverName;
|
||||||
|
private volatile ServerInfo serverInfo;
|
||||||
|
private volatile long lastRequest;
|
||||||
|
|
||||||
|
private TrackedServer(String serverName) {
|
||||||
|
this.serverName = serverName;
|
||||||
|
this.serverInfo = ServerInfo.offline(Configuration.pingerOfflineMotd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLastRequest() {
|
||||||
|
this.lastRequest = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) filoghost and contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
package me.filoghost.holographicdisplays.bridge.bungeecord;
|
||||||
|
|
||||||
|
import me.filoghost.fcommons.Strings;
|
||||||
|
import me.filoghost.holographicdisplays.disk.Configuration;
|
||||||
|
|
||||||
|
public class ServerInfo {
|
||||||
|
|
||||||
|
private final boolean online;
|
||||||
|
private final int onlinePlayers;
|
||||||
|
private final int maxPlayers;
|
||||||
|
private final String motdLine1, motdLine2;
|
||||||
|
|
||||||
|
public static ServerInfo online(int onlinePlayers, int maxPlayers, String motd) {
|
||||||
|
return new ServerInfo(true, onlinePlayers, maxPlayers, motd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServerInfo offline(String motd) {
|
||||||
|
return new ServerInfo(false, 0, 0, motd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServerInfo(boolean online, int onlinePlayers, int maxPlayers, String motd) {
|
||||||
|
this.online = online;
|
||||||
|
this.onlinePlayers = onlinePlayers;
|
||||||
|
this.maxPlayers = maxPlayers;
|
||||||
|
|
||||||
|
if (Strings.isEmpty(motd)) {
|
||||||
|
motdLine1 = "";
|
||||||
|
motdLine2 = "";
|
||||||
|
} else if (motd.contains("\n")) {
|
||||||
|
String[] lines = Strings.split(motd, "\n", 2);
|
||||||
|
if (Configuration.pingerTrimMotd) {
|
||||||
|
lines = Strings.trim(lines);
|
||||||
|
}
|
||||||
|
motdLine1 = lines[0];
|
||||||
|
motdLine2 = lines.length > 1 ? lines[1] : "";
|
||||||
|
} else {
|
||||||
|
if (Configuration.pingerTrimMotd) {
|
||||||
|
motd = motd.trim();
|
||||||
|
}
|
||||||
|
motdLine1 = motd;
|
||||||
|
motdLine2 = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOnline() {
|
||||||
|
return online;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnlinePlayers() {
|
||||||
|
return onlinePlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxPlayers() {
|
||||||
|
return maxPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMotdLine1() {
|
||||||
|
return motdLine1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMotdLine2() {
|
||||||
|
return motdLine2;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) filoghost and contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
package me.filoghost.holographicdisplays.bridge.bungeecord.pinger;
|
||||||
|
|
||||||
|
import me.filoghost.holographicdisplays.core.DebugLogger;
|
||||||
|
import me.filoghost.holographicdisplays.disk.ServerAddress;
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
import org.json.simple.JSONValue;
|
||||||
|
|
||||||
|
public class PingResponse {
|
||||||
|
|
||||||
|
private final String motd;
|
||||||
|
private final int onlinePlayers;
|
||||||
|
private final int maxPlayers;
|
||||||
|
|
||||||
|
protected static PingResponse fromJson(String jsonString, ServerAddress address) {
|
||||||
|
if (jsonString == null || jsonString.isEmpty()) {
|
||||||
|
logInvalidResponse(jsonString, address);
|
||||||
|
return errorResponse("Invalid ping response (null or empty)");
|
||||||
|
}
|
||||||
|
|
||||||
|
Object jsonObject = JSONValue.parse(jsonString);
|
||||||
|
|
||||||
|
if (!(jsonObject instanceof JSONObject)) {
|
||||||
|
logInvalidResponse(jsonString, address);
|
||||||
|
return errorResponse("Invalid ping response (wrong format)");
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject json = (JSONObject) jsonObject;
|
||||||
|
|
||||||
|
Object descriptionObject = json.get("description");
|
||||||
|
|
||||||
|
String motd;
|
||||||
|
int onlinePlayers = 0;
|
||||||
|
int maxPlayers = 0;
|
||||||
|
|
||||||
|
if (descriptionObject == null) {
|
||||||
|
logInvalidResponse(jsonString, address);
|
||||||
|
return errorResponse("Invalid ping response (description not found)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptionObject instanceof JSONObject) {
|
||||||
|
Object text = ((JSONObject) descriptionObject).get("text");
|
||||||
|
if (text == null) {
|
||||||
|
logInvalidResponse(jsonString, address);
|
||||||
|
return errorResponse("Invalid ping response (text not found)");
|
||||||
|
}
|
||||||
|
motd = text.toString();
|
||||||
|
} else {
|
||||||
|
motd = descriptionObject.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object playersObject = json.get("players");
|
||||||
|
|
||||||
|
if (playersObject instanceof JSONObject) {
|
||||||
|
JSONObject playersJson = (JSONObject) playersObject;
|
||||||
|
|
||||||
|
Object onlineObject = playersJson.get("online");
|
||||||
|
if (onlineObject instanceof Number) {
|
||||||
|
onlinePlayers = ((Number) onlineObject).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object maxObject = playersJson.get("max");
|
||||||
|
if (maxObject instanceof Number) {
|
||||||
|
maxPlayers = ((Number) maxObject).intValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PingResponse(motd, onlinePlayers, maxPlayers);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logInvalidResponse(String responseJsonString, ServerAddress address) {
|
||||||
|
DebugLogger.warning("Received invalid JSON response from IP \"" + address + "\": " + responseJsonString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PingResponse errorResponse(String error) {
|
||||||
|
return new PingResponse(error, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PingResponse(String motd, int onlinePlayers, int maxPlayers) {
|
||||||
|
this.motd = motd;
|
||||||
|
this.onlinePlayers = onlinePlayers;
|
||||||
|
this.maxPlayers = maxPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMotd() {
|
||||||
|
return motd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnlinePlayers() {
|
||||||
|
return onlinePlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxPlayers() {
|
||||||
|
return maxPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) filoghost and contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
package me.filoghost.holographicdisplays.bridge.bungeecord.pinger;
|
||||||
|
|
||||||
|
import me.filoghost.holographicdisplays.disk.ServerAddress;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class ServerPinger {
|
||||||
|
|
||||||
|
public static PingResponse fetchData(final ServerAddress serverAddress, int timeout) throws IOException {
|
||||||
|
try (Socket socket = openSocket(serverAddress)) {
|
||||||
|
socket.setSoTimeout(timeout);
|
||||||
|
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
|
||||||
|
DataInputStream in = new DataInputStream(socket.getInputStream());
|
||||||
|
|
||||||
|
// Handshake packet
|
||||||
|
ByteArrayOutputStream handshakeBytes = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream handshakeOut = new DataOutputStream(handshakeBytes);
|
||||||
|
handshakeOut.writeByte(0x00); // Packet ID
|
||||||
|
writeVarInt(handshakeOut, 4); // Protocol version
|
||||||
|
writeString(handshakeOut, serverAddress.getAddress());
|
||||||
|
handshakeOut.writeShort(serverAddress.getPort());
|
||||||
|
writeVarInt(handshakeOut, 1); // Next state: status request
|
||||||
|
writeByteArray(out, handshakeBytes.toByteArray());
|
||||||
|
|
||||||
|
// Status request packet
|
||||||
|
writeByteArray(out, new byte[]{ 0x00 }); // Packet ID
|
||||||
|
|
||||||
|
// Response packet
|
||||||
|
readVarInt(in); // Packet size
|
||||||
|
readVarInt(in); // Packet ID
|
||||||
|
String responseJson = readString(in);
|
||||||
|
|
||||||
|
return PingResponse.fromJson(responseJson, serverAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Socket openSocket(ServerAddress serverAddress) throws IOException {
|
||||||
|
return new Socket(serverAddress.getAddress(), serverAddress.getPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readString(DataInputStream in) throws IOException {
|
||||||
|
byte[] bytes = readByteArray(in);
|
||||||
|
return new String(bytes, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeString(DataOutputStream out, String s) throws IOException {
|
||||||
|
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
|
||||||
|
writeByteArray(out, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] readByteArray(DataInputStream in) throws IOException {
|
||||||
|
int length = readVarInt(in);
|
||||||
|
byte[] bytes = new byte[length];
|
||||||
|
in.readFully(bytes);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeByteArray(DataOutputStream out, byte[] bytes) throws IOException {
|
||||||
|
writeVarInt(out, bytes.length);
|
||||||
|
out.write(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readVarInt(DataInputStream in) throws IOException {
|
||||||
|
int i = 0;
|
||||||
|
int j = 0;
|
||||||
|
while (true) {
|
||||||
|
int k = in.readByte();
|
||||||
|
i |= (k & 0x7F) << j++ * 7;
|
||||||
|
if (j > 5) {
|
||||||
|
throw new RuntimeException("VarInt too big");
|
||||||
|
}
|
||||||
|
if ((k & 0x80) != 0x80) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeVarInt(DataOutputStream out, int paramInt) throws IOException {
|
||||||
|
while ((paramInt & 0xFFFFFF80) != 0x0) {
|
||||||
|
out.writeByte((paramInt & 0x7F) | 0x80);
|
||||||
|
paramInt >>>= 7;
|
||||||
|
}
|
||||||
|
out.writeByte(paramInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) filoghost and contributors
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
package me.filoghost.holographicdisplays.bridge.bungeecord.serverpinger;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
class PacketUtils {
|
|
||||||
|
|
||||||
public static void writeString(final DataOutputStream out, final String s, final Charset charset) throws IOException {
|
|
||||||
if (charset == StandardCharsets.UTF_8) {
|
|
||||||
writeVarInt(out, s.length());
|
|
||||||
} else {
|
|
||||||
out.writeShort(s.length());
|
|
||||||
}
|
|
||||||
out.write(s.getBytes(charset));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int readVarInt(final DataInputStream in) throws IOException {
|
|
||||||
int i = 0;
|
|
||||||
int j = 0;
|
|
||||||
while (true) {
|
|
||||||
final int k = in.readByte();
|
|
||||||
i |= (k & 0x7F) << j++ * 7;
|
|
||||||
if (j > 5) {
|
|
||||||
throw new RuntimeException("VarInt too big");
|
|
||||||
}
|
|
||||||
if ((k & 0x80) != 0x80) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeVarInt(final DataOutputStream out, int paramInt) throws IOException {
|
|
||||||
while ((paramInt & 0xFFFFFF80) != 0x0) {
|
|
||||||
out.write((paramInt & 0x7F) | 0x80);
|
|
||||||
paramInt >>>= 7;
|
|
||||||
}
|
|
||||||
out.write(paramInt);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void closeQuietly(Closeable closeable) {
|
|
||||||
try {
|
|
||||||
if (closeable != null) {
|
|
||||||
closeable.close();
|
|
||||||
}
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) filoghost and contributors
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
package me.filoghost.holographicdisplays.bridge.bungeecord.serverpinger;
|
|
||||||
|
|
||||||
import me.filoghost.holographicdisplays.disk.ServerAddress;
|
|
||||||
import me.filoghost.holographicdisplays.core.DebugLogger;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
import org.json.simple.JSONValue;
|
|
||||||
|
|
||||||
public class PingResponse
|
|
||||||
{
|
|
||||||
private boolean isOnline;
|
|
||||||
private String motd;
|
|
||||||
private int onlinePlayers;
|
|
||||||
private int maxPlayers;
|
|
||||||
|
|
||||||
public PingResponse(String jsonString, ServerAddress address) {
|
|
||||||
if (jsonString == null || jsonString.isEmpty()) {
|
|
||||||
motd = "Invalid ping response";
|
|
||||||
DebugLogger.warning("Received empty Json response from IP \"" + address.toString() + "\".");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object jsonObject = JSONValue.parse(jsonString);
|
|
||||||
|
|
||||||
if (!(jsonObject instanceof JSONObject)) {
|
|
||||||
motd = "Invalid ping response";
|
|
||||||
DebugLogger.warning("Received invalid Json response from IP \"" + address.toString() + "\": " + jsonString);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject json = (JSONObject) jsonObject;
|
|
||||||
isOnline = true;
|
|
||||||
|
|
||||||
Object descriptionObject = json.get("description");
|
|
||||||
|
|
||||||
if (descriptionObject != null) {
|
|
||||||
if (descriptionObject instanceof JSONObject) {
|
|
||||||
Object text = ((JSONObject) descriptionObject).get("text");
|
|
||||||
if (text != null) {
|
|
||||||
motd = text.toString();
|
|
||||||
} else {
|
|
||||||
motd = "Invalid ping response (text not found)";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
motd = descriptionObject.toString();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
motd = "Invalid ping response (description not found)";
|
|
||||||
DebugLogger.warning("Received invalid Json response from IP \"" + address.toString() + "\": " + jsonString);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object playersObject = json.get("players");
|
|
||||||
|
|
||||||
if (playersObject instanceof JSONObject) {
|
|
||||||
JSONObject playersJson = (JSONObject) playersObject;
|
|
||||||
|
|
||||||
Object onlineObject = playersJson.get("online");
|
|
||||||
if (onlineObject instanceof Number) {
|
|
||||||
onlinePlayers = ((Number) onlineObject).intValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
Object maxObject = playersJson.get("max");
|
|
||||||
if (maxObject instanceof Number) {
|
|
||||||
maxPlayers = ((Number) maxObject).intValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isOnline() {
|
|
||||||
return isOnline;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMotd() {
|
|
||||||
return motd;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOnlinePlayers() {
|
|
||||||
return onlinePlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxPlayers() {
|
|
||||||
return maxPlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) filoghost and contributors
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
package me.filoghost.holographicdisplays.bridge.bungeecord.serverpinger;
|
|
||||||
|
|
||||||
import me.filoghost.holographicdisplays.disk.ServerAddress;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
public class ServerPinger {
|
|
||||||
|
|
||||||
public static PingResponse fetchData(final ServerAddress serverAddress, int timeout) throws IOException {
|
|
||||||
Socket socket = null;
|
|
||||||
DataOutputStream dataOut = null;
|
|
||||||
DataInputStream dataIn = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
socket = new Socket(serverAddress.getAddress(), serverAddress.getPort());
|
|
||||||
socket.setSoTimeout(timeout);
|
|
||||||
dataOut = new DataOutputStream(socket.getOutputStream());
|
|
||||||
dataIn = new DataInputStream(socket.getInputStream());
|
|
||||||
final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
|
||||||
final DataOutputStream handshake = new DataOutputStream(byteOut);
|
|
||||||
handshake.write(0);
|
|
||||||
PacketUtils.writeVarInt(handshake, 4);
|
|
||||||
PacketUtils.writeString(handshake, serverAddress.getAddress(), StandardCharsets.UTF_8);
|
|
||||||
handshake.writeShort(serverAddress.getPort());
|
|
||||||
PacketUtils.writeVarInt(handshake, 1);
|
|
||||||
byte[] bytes = byteOut.toByteArray();
|
|
||||||
PacketUtils.writeVarInt(dataOut, bytes.length);
|
|
||||||
dataOut.write(bytes);
|
|
||||||
bytes = new byte[] { 0 };
|
|
||||||
PacketUtils.writeVarInt(dataOut, bytes.length);
|
|
||||||
dataOut.write(bytes);
|
|
||||||
PacketUtils.readVarInt(dataIn);
|
|
||||||
PacketUtils.readVarInt(dataIn);
|
|
||||||
final byte[] responseData = new byte[PacketUtils.readVarInt(dataIn)];
|
|
||||||
dataIn.readFully(responseData);
|
|
||||||
final String jsonString = new String(responseData, StandardCharsets.UTF_8);
|
|
||||||
return new PingResponse(jsonString, serverAddress);
|
|
||||||
} finally {
|
|
||||||
PacketUtils.closeQuietly(dataOut);
|
|
||||||
PacketUtils.closeQuietly(dataIn);
|
|
||||||
PacketUtils.closeQuietly(socket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -19,8 +19,8 @@ import org.bukkit.ChatColor;
|
|||||||
import java.time.DateTimeException;
|
import java.time.DateTimeException;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.Map;
|
||||||
|
|
||||||
public class Configuration {
|
public class Configuration {
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ public class Configuration {
|
|||||||
public static String pingerStatusOnline;
|
public static String pingerStatusOnline;
|
||||||
public static String pingerStatusOffline;
|
public static String pingerStatusOffline;
|
||||||
public static boolean pingerTrimMotd;
|
public static boolean pingerTrimMotd;
|
||||||
public static List<ServerAddress> pingerServers;
|
public static Map<String, ServerAddress> pingerServerAddresses;
|
||||||
|
|
||||||
public static void load(MainConfigModel config, ErrorCollector errorCollector) {
|
public static void load(MainConfigModel config, ErrorCollector errorCollector) {
|
||||||
spaceBetweenLines = config.spaceBetweenLines;
|
spaceBetweenLines = config.spaceBetweenLines;
|
||||||
@ -61,13 +61,13 @@ public class Configuration {
|
|||||||
pingerStatusOnline = StringConverter.toReadableFormat(config.pingerStatusOnline);
|
pingerStatusOnline = StringConverter.toReadableFormat(config.pingerStatusOnline);
|
||||||
pingerStatusOffline = StringConverter.toReadableFormat(config.pingerStatusOffline);
|
pingerStatusOffline = StringConverter.toReadableFormat(config.pingerStatusOffline);
|
||||||
pingerTrimMotd = config.pingerTrimMotd;
|
pingerTrimMotd = config.pingerTrimMotd;
|
||||||
|
|
||||||
pingerServers = new ArrayList<>();
|
pingerServerAddresses = new HashMap<>();
|
||||||
if (pingerEnabled) {
|
if (pingerEnabled) {
|
||||||
for (String singleServer : config.pingerServers) {
|
for (String singleServer : config.pingerServers) {
|
||||||
ServerAddress serverAddress = parseServerAddress(singleServer, errorCollector);
|
ServerAddress serverAddress = parseServerAddress(singleServer, errorCollector);
|
||||||
if (serverAddress != null) {
|
if (serverAddress != null) {
|
||||||
pingerServers.add(serverAddress);
|
pingerServerAddresses.put(serverAddress.getName(), serverAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,15 @@
|
|||||||
*/
|
*/
|
||||||
package me.filoghost.holographicdisplays.placeholder;
|
package me.filoghost.holographicdisplays.placeholder;
|
||||||
|
|
||||||
|
import me.filoghost.fcommons.Strings;
|
||||||
import me.filoghost.fcommons.logging.Log;
|
import me.filoghost.fcommons.logging.Log;
|
||||||
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer;
|
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer;
|
||||||
|
import me.filoghost.holographicdisplays.bridge.bungeecord.ServerInfo;
|
||||||
import me.filoghost.holographicdisplays.bridge.bungeecord.BungeeServerTracker;
|
import me.filoghost.holographicdisplays.bridge.bungeecord.BungeeServerTracker;
|
||||||
import me.filoghost.holographicdisplays.core.Utils;
|
import me.filoghost.holographicdisplays.core.Utils;
|
||||||
import me.filoghost.holographicdisplays.core.hologram.StandardTextLine;
|
import me.filoghost.holographicdisplays.core.hologram.StandardTextLine;
|
||||||
import me.filoghost.holographicdisplays.core.nms.entity.NMSArmorStand;
|
import me.filoghost.holographicdisplays.core.nms.entity.NMSArmorStand;
|
||||||
|
import me.filoghost.holographicdisplays.disk.Configuration;
|
||||||
import me.filoghost.holographicdisplays.task.WorldPlayerCounterTask;
|
import me.filoghost.holographicdisplays.task.WorldPlayerCounterTask;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
@ -25,6 +28,8 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class PlaceholdersManager {
|
public class PlaceholdersManager {
|
||||||
|
|
||||||
|
private static final String PINGER_NOT_ENABLED_ERROR = "[Please enable pinger]";
|
||||||
|
|
||||||
private static final Pattern BUNGEE_ONLINE_PATTERN = makePlaceholderWithArgsPattern("online");
|
private static final Pattern BUNGEE_ONLINE_PATTERN = makePlaceholderWithArgsPattern("online");
|
||||||
private static final Pattern BUNGEE_MAX_PATTERN = makePlaceholderWithArgsPattern("max_players");
|
private static final Pattern BUNGEE_MAX_PATTERN = makePlaceholderWithArgsPattern("max_players");
|
||||||
@ -170,28 +175,22 @@ public class PlaceholdersManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final String serverName = extractArgumentFromPlaceholder(matcher);
|
final String serverName = extractArgumentFromPlaceholder(matcher);
|
||||||
bungeeServerTracker.track(serverName); // Track this server.
|
|
||||||
|
|
||||||
if (serverName.contains(",")) {
|
if (serverName.contains(",")) {
|
||||||
String[] split = serverName.split(",");
|
String[] serversToTrack = Strings.splitAndTrim(serverName, ",");
|
||||||
for (int i = 0; i < split.length; i++) {
|
|
||||||
split[i] = split[i].trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
final String[] serversToTrack = split;
|
|
||||||
|
|
||||||
// Add it to tracked servers.
|
// Add it to tracked servers.
|
||||||
bungeeReplacers.put(matcher.group(), () -> {
|
bungeeReplacers.put(matcher.group(), () -> {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (String serverToTrack : serversToTrack) {
|
for (String serverToTrack : serversToTrack) {
|
||||||
count += bungeeServerTracker.getPlayersOnline(serverToTrack);
|
count += bungeeServerTracker.getCurrentServerInfo(serverToTrack).getOnlinePlayers();
|
||||||
}
|
}
|
||||||
return String.valueOf(count);
|
return String.valueOf(count);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Normal, single tracked server.
|
// Normal, single tracked server.
|
||||||
bungeeReplacers.put(matcher.group(), () -> {
|
bungeeReplacers.put(matcher.group(), () -> {
|
||||||
return String.valueOf(bungeeServerTracker.getPlayersOnline(serverName));
|
return String.valueOf(bungeeServerTracker.getCurrentServerInfo(serverName).getOnlinePlayers());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,11 +203,14 @@ public class PlaceholdersManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final String serverName = extractArgumentFromPlaceholder(matcher);
|
final String serverName = extractArgumentFromPlaceholder(matcher);
|
||||||
bungeeServerTracker.track(serverName); // Track this server.
|
|
||||||
|
|
||||||
// Add it to tracked servers.
|
// Add it to tracked servers.
|
||||||
bungeeReplacers.put(matcher.group(), () -> {
|
bungeeReplacers.put(matcher.group(), () -> {
|
||||||
return bungeeServerTracker.getMaxPlayers(serverName);
|
if (!Configuration.pingerEnabled) {
|
||||||
|
return PINGER_NOT_ENABLED_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.valueOf(bungeeServerTracker.getCurrentServerInfo(serverName).getMaxPlayers());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,11 +222,14 @@ public class PlaceholdersManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final String serverName = extractArgumentFromPlaceholder(matcher);
|
final String serverName = extractArgumentFromPlaceholder(matcher);
|
||||||
bungeeServerTracker.track(serverName); // Track this server.
|
|
||||||
|
|
||||||
// Add it to tracked servers.
|
// Add it to tracked servers.
|
||||||
bungeeReplacers.put(matcher.group(), () -> {
|
bungeeReplacers.put(matcher.group(), () -> {
|
||||||
return bungeeServerTracker.getMotd1(serverName);
|
if (!Configuration.pingerEnabled) {
|
||||||
|
return PINGER_NOT_ENABLED_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bungeeServerTracker.getCurrentServerInfo(serverName).getMotdLine1();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,11 +241,14 @@ public class PlaceholdersManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final String serverName = extractArgumentFromPlaceholder(matcher);
|
final String serverName = extractArgumentFromPlaceholder(matcher);
|
||||||
bungeeServerTracker.track(serverName); // Track this server.
|
|
||||||
|
|
||||||
// Add it to tracked servers.
|
// Add it to tracked servers.
|
||||||
bungeeReplacers.put(matcher.group(), () -> {
|
bungeeReplacers.put(matcher.group(), () -> {
|
||||||
return bungeeServerTracker.getMotd2(serverName);
|
if (!Configuration.pingerEnabled) {
|
||||||
|
return PINGER_NOT_ENABLED_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bungeeServerTracker.getCurrentServerInfo(serverName).getMotdLine2();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,11 +260,19 @@ public class PlaceholdersManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final String serverName = extractArgumentFromPlaceholder(matcher);
|
final String serverName = extractArgumentFromPlaceholder(matcher);
|
||||||
bungeeServerTracker.track(serverName); // Track this server.
|
|
||||||
|
|
||||||
// Add it to tracked servers.
|
// Add it to tracked servers.
|
||||||
bungeeReplacers.put(matcher.group(), () -> {
|
bungeeReplacers.put(matcher.group(), () -> {
|
||||||
return bungeeServerTracker.getOnlineStatus(serverName);
|
if (!Configuration.pingerEnabled) {
|
||||||
|
return PINGER_NOT_ENABLED_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerInfo serverInfo = bungeeServerTracker.getCurrentServerInfo(serverName);
|
||||||
|
if (serverInfo.isOnline()) {
|
||||||
|
return Configuration.pingerStatusOnline;
|
||||||
|
} else {
|
||||||
|
return Configuration.pingerStatusOffline;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) filoghost and contributors
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
package me.filoghost.holographicdisplays.task;
|
|
||||||
|
|
||||||
import me.filoghost.holographicdisplays.bridge.bungeecord.BungeeServerInfo;
|
|
||||||
import me.filoghost.holographicdisplays.bridge.bungeecord.BungeeServerTracker;
|
|
||||||
import me.filoghost.holographicdisplays.core.DebugLogger;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A task to remove unused server data in the server tracker.
|
|
||||||
*/
|
|
||||||
public class BungeeCleanupTask implements Runnable {
|
|
||||||
|
|
||||||
private static final long MAX_INACTIVITY = TimeUnit.MINUTES.toMillis(10);
|
|
||||||
|
|
||||||
private final BungeeServerTracker bungeeServerTracker;
|
|
||||||
|
|
||||||
public BungeeCleanupTask(BungeeServerTracker bungeeServerTracker) {
|
|
||||||
this.bungeeServerTracker = bungeeServerTracker;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
Iterator<Entry<String, BungeeServerInfo>> iter = bungeeServerTracker.getTrackedServers().entrySet().iterator();
|
|
||||||
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
Entry<String, BungeeServerInfo> next = iter.next();
|
|
||||||
long lastRequest = next.getValue().getLastRequest();
|
|
||||||
|
|
||||||
if (lastRequest != 0 && now - lastRequest > MAX_INACTIVITY) {
|
|
||||||
// Don't track that server anymore.
|
|
||||||
iter.remove();
|
|
||||||
DebugLogger.info("Removed bungee server \"" + next.getKey() + "\" from tracking due to inactivity.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user