Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved data fetching, Folia support; #11

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,147 +1,136 @@
package com.extendedclip.papi.expansion.pinger;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import me.clip.placeholderapi.expansion.Cacheable;
import me.clip.placeholderapi.expansion.Configurable;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.expansion.Taskable;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.*;

@SuppressWarnings("unused")
public class PingerExpansion extends PlaceholderExpansion implements Cacheable, Taskable, Configurable {
private BukkitTask pingTask = null;

private String online = "&aOnline";

private String offline = "&cOffline";

private final Map<String, Pinger> servers = new ConcurrentHashMap<>();

private final Map<String, InetSocketAddress> toPing = new ConcurrentHashMap<>();
@Nullable
private LoadingCache<String, Future<Pinger>> cache;

private int interval = 60;

public Map<String, Object> getDefaults() {
Map<String, Object> defaults = new HashMap<>();
defaults.put("check_interval", Integer.valueOf(30));
defaults.put("check_interval", 30);
defaults.put("online", "&aOnline");
defaults.put("offline", "&cOffline");
return defaults;
}

public void start() {
String on = getString("online", "&aOnline");
this.online = (on != null) ? on : "&aOnline";
String off = getString("offline", "&cOffline");
this.offline = (off != null) ? off : "&cOffline";
this.online = getString("online", this.online);
this.offline = getString("offline", this.offline);

int time = getInt("check_interval", 60);
if (time > 0)
this.interval = time;
this.pingTask = (new BukkitRunnable() {
public void run() {
if (PingerExpansion.this.toPing.isEmpty())
return;
for (Map.Entry<String, InetSocketAddress> address : PingerExpansion.this.toPing.entrySet()) {
PingerExpansion.Pinger r;
try {
r = new PingerExpansion.Pinger(address.getValue().getHostName(), address.getValue().getPort());
if (r.fetchData()) {
PingerExpansion.this.servers.put(address.getKey(), r);
continue;
}
if (PingerExpansion.this.servers.containsKey(address.getKey()))
PingerExpansion.this.servers.remove(address.getKey());
} catch (Exception exception) {
}
}
}
}).runTaskTimerAsynchronously(getPlaceholderAPI(), 20L, 20L * this.interval);
if (time > 0) this.interval = time;

this.cache = CacheBuilder.newBuilder()
.concurrencyLevel(1)
.refreshAfterWrite(this.interval, TimeUnit.SECONDS)
.build(new PingerCacheLoader());
}

public void stop() {
try {
this.pingTask.cancel();
} catch (Exception exception) {
}
this.pingTask = null;
if (this.cache == null) return;
this.cache.asMap().values().forEach(future -> future.cancel(true));
this.cache = null;
}

public void clear() {
this.servers.clear();
this.toPing.clear();
if (this.cache == null) return;
this.cache.cleanUp();
}

public boolean canRegister() {
return true;
}

public String getAuthor() {
public @NotNull String getAuthor() {
return "clip";
}

public String getIdentifier() {
public @NotNull String getIdentifier() {
return "pinger";
}

public String getPlugin() {
return null;
}

public String getVersion() {
return "1.0.1";
public @NotNull String getVersion() {
return "1.0.2";
}

public String onPlaceholderRequest(Player p, String identifier) {
int place = identifier.indexOf("_");
final int place = identifier.indexOf("_");
if (place == -1)
return null;
String type = identifier.substring(0, place);
String address = identifier.substring(place + 1);
Pinger r = null;
for (String a : this.servers.keySet()) {
if (a.equalsIgnoreCase(address)) {
r = this.servers.get(a);
break;

final String type = identifier.substring(0, place).toLowerCase(Locale.ROOT);
final String address = identifier.substring(place + 1);

if (this.cache == null) return null;
try {
final Future<Pinger> future = this.cache.get(address);
final Pinger pinger = future.isDone() ? future.get() : null;

switch (type) {
case "motd":
if (pinger == null) return "";
return pinger.getMotd();

case "count":
case "players":
if (pinger == null) return "0";
return String.valueOf(pinger.getPlayersOnline());

case "max":
case "maxplayers":
if (pinger == null) return "0";
return String.valueOf(pinger.getMaxPlayers());

case "pingversion":
case "pingv":
if (pinger == null) return "-1";
return String.valueOf(pinger.getPingVersion());

case "gameversion":
case "version":
if (pinger == null) return "";
return pinger.getGameVersion();

case "online":
case "isonline":
if (pinger == null) return this.offline;
return this.online;
}
} catch (InterruptedException | ExecutionException ignored) {
}
if (r == null)
if (!this.toPing.containsKey(address)) {
int port = 25565;
String add = address;
if (address.contains(":")) {
add = address.substring(0, address.indexOf(":"));
try {
port = Integer.parseInt(address.substring(address.indexOf(":") + 1));
} catch (Exception exception) {
}
}
this.toPing.put(address, new InetSocketAddress(add, port));
}
if (type.equalsIgnoreCase("motd"))
return (r != null) ? r.getMotd() : "";
if (type.equalsIgnoreCase("count") || type.equalsIgnoreCase("players"))
return (r != null) ? String.valueOf(r.getPlayersOnline()) : "0";
if (type.equalsIgnoreCase("max") || type.equalsIgnoreCase("maxplayers"))
return (r != null) ? String.valueOf(r.getMaxPlayers()) : "0";
if (type.equalsIgnoreCase("pingversion") || type.equalsIgnoreCase("pingv"))
return (r != null) ? String.valueOf(r.getPingVersion()) : "-1";
if (type.equalsIgnoreCase("gameversion") || type.equalsIgnoreCase("version"))
return (r != null && r.getGameVersion() != null) ? r.getGameVersion() : "";
if (type.equalsIgnoreCase("online") || type.equalsIgnoreCase("isonline"))
return (r != null) ? this.online : this.offline;

return null;
}

public final class Pinger {
private static class Pinger {
private String address = "localhost";

private int port = 25565;
Expand Down Expand Up @@ -321,4 +310,28 @@ public boolean fetchData() {
return true;
}
}

private static class PingerCacheLoader extends CacheLoader<String, Future<Pinger>> {
@Override
public Future<Pinger> load(final String key) {
final InetSocketAddress address = this.address(key);
final Pinger pinger = new Pinger(address.getHostName(), address.getPort());

return CompletableFuture.supplyAsync(() -> {
if (!pinger.fetchData()) return null;

return pinger;
});
}

private InetSocketAddress address(final String key) {
final int index = key.indexOf(":");
if (index == -1) return new InetSocketAddress(key, 25565);

final String host = key.substring(0, index);
final int port = Integer.parseInt(key.substring(index + 1));

return new InetSocketAddress(host, port);
}
}
}