diff --git a/bukkit/pom.xml b/bukkit/pom.xml index a89dc6e8..229b0deb 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -82,13 +82,13 @@ io.papermc.paper paper-api - 1.19.3-R0.1-SNAPSHOT + 1.20-R0.1-SNAPSHOT provided org.spigotmc spigot-api - 1.19-R0.1-SNAPSHOT + 1.20-R0.1-SNAPSHOT provided diff --git a/bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/listeners/BungeeEventWrapper.java b/bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/listeners/BungeeEventWrapper.java index 89f744cd..b165ed99 100644 --- a/bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/listeners/BungeeEventWrapper.java +++ b/bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/listeners/BungeeEventWrapper.java @@ -31,6 +31,7 @@ import ch.andre601.advancedserverlist.bungeecord.objects.BungeePlayerImpl; import ch.andre601.advancedserverlist.api.events.GenericServerListEvent; import ch.andre601.advancedserverlist.bungeecord.objects.BungeeProxyImpl; +import ch.andre601.advancedserverlist.core.events.PingEventHandler; import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore; import ch.andre601.advancedserverlist.core.interfaces.events.GenericEventWrapper; import ch.andre601.advancedserverlist.core.objects.CachedPlayer; @@ -42,6 +43,7 @@ import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.ProxyPingEvent; import java.awt.image.BufferedImage; @@ -153,7 +155,25 @@ public String getPlayerIP(){ @Override public String parsePAPIPlaceholders(String text, BungeePlayerImpl player){ - return text; + if(plugin.getProxy().getPluginManager().getPlugin("PAPIProxyBridge") == null) + return text; + + if(!PingEventHandler.getPAPIUtil().isCompatible()) + return text; + + String server = PingEventHandler.getPAPIUtil().getServer(); + if(server == null || server.isEmpty()) + return text; + + ServerInfo serverInfo = plugin.getProxy().getServerInfo(server); + if(serverInfo == null || serverInfo.getPlayers().isEmpty()) + return text; + + ProxiedPlayer carrier = PingEventHandler.getPAPIUtil().getPlayer(serverInfo.getPlayers()); + if(carrier == null) + return text; + + return PingEventHandler.getPAPIUtil().parse(text, carrier.getUniqueId(), player.getUUID()); } @Override diff --git a/bungeecord/src/main/resources/bungee.yml b/bungeecord/src/main/resources/bungee.yml index 0a508306..92c1e71e 100644 --- a/bungeecord/src/main/resources/bungee.yml +++ b/bungeecord/src/main/resources/bungee.yml @@ -3,4 +3,7 @@ author: 'Andre_601' version: '${plugin.version}' description: '${plugin.description}' +softDepends: + - PAPIProxyBridge + main: 'ch.andre601.advancedserverlist.bungeecord.BungeeCordCore' \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml index bc74a9ad..b835632c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -48,6 +48,10 @@ flexver-repo https://repo.sleeping.town/ + + papiproxybridge-repo + https://repo.william278.net/releases/ + @@ -105,6 +109,12 @@ 32.1.1-jre provided + + net.william278 + papiproxybridge + 1.3 + provided + diff --git a/core/src/main/java/ch/andre601/advancedserverlist/core/check/UpdateCheckThread.java b/core/src/main/java/ch/andre601/advancedserverlist/core/check/UpdateCheckThread.java index fd544177..3554d814 100644 --- a/core/src/main/java/ch/andre601/advancedserverlist/core/check/UpdateCheckThread.java +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/check/UpdateCheckThread.java @@ -32,6 +32,8 @@ public class UpdateCheckThread implements ThreadFactory{ @Override public Thread newThread(@NotNull Runnable r){ - return new Thread(r, "AdvancedServerList Update-Thread"); + Thread t = new Thread(r, "AdvancedServerList-UpdateThread"); + t.setDaemon(true); + return t; } } diff --git a/core/src/main/java/ch/andre601/advancedserverlist/core/check/UpdateChecker.java b/core/src/main/java/ch/andre601/advancedserverlist/core/check/UpdateChecker.java index 63df496b..ce38219c 100644 --- a/core/src/main/java/ch/andre601/advancedserverlist/core/check/UpdateChecker.java +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/check/UpdateChecker.java @@ -52,7 +52,7 @@ public class UpdateChecker{ private final String url = "https://api.modrinth.com/v2/project/advancedserverlist/version?loaders=[\"%s\"]"; private final OkHttpClient client = new OkHttpClient(); - private final AdvancedServerList core; + private final AdvancedServerList core; private final PluginLogger logger; private final String loader; @@ -65,7 +65,7 @@ public class UpdateChecker{ private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new UpdateCheckThread()); - public UpdateChecker(AdvancedServerList core){ + public UpdateChecker(AdvancedServerList core){ this.core = core; this.logger = core.getPlugin().getPluginLogger(); this.loader = core.getPlugin().getLoader(); diff --git a/core/src/main/java/ch/andre601/advancedserverlist/core/commands/CommandHandler.java b/core/src/main/java/ch/andre601/advancedserverlist/core/commands/CommandHandler.java index 3d671413..240e87a8 100644 --- a/core/src/main/java/ch/andre601/advancedserverlist/core/commands/CommandHandler.java +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/commands/CommandHandler.java @@ -36,7 +36,7 @@ public class CommandHandler{ private final List subCommands = new ArrayList<>(); - public CommandHandler(AdvancedServerList core){ + public CommandHandler(AdvancedServerList core){ subCommands.add(new Help()); subCommands.add(new Reload(core)); subCommands.add(new ClearCache(core)); @@ -86,9 +86,9 @@ public void handle(CmdSender sender){ private static class Reload extends PluginCommand{ - private final AdvancedServerList core; + private final AdvancedServerList core; - public Reload(AdvancedServerList core){ + public Reload(AdvancedServerList core){ super("reload"); this.core = core; @@ -122,9 +122,9 @@ public void handle(CmdSender sender){ private static class ClearCache extends PluginCommand{ - private final AdvancedServerList core; + private final AdvancedServerList core; - public ClearCache(AdvancedServerList core){ + public ClearCache(AdvancedServerList core){ super("clearCache"); this.core = core; diff --git a/core/src/main/java/ch/andre601/advancedserverlist/core/events/PingEventHandler.java b/core/src/main/java/ch/andre601/advancedserverlist/core/events/PingEventHandler.java index f3aa1f07..5fb6a215 100644 --- a/core/src/main/java/ch/andre601/advancedserverlist/core/events/PingEventHandler.java +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/events/PingEventHandler.java @@ -31,7 +31,7 @@ import ch.andre601.advancedserverlist.api.profiles.ProfileEntry; import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore; import ch.andre601.advancedserverlist.core.interfaces.events.GenericEventWrapper; -import ch.andre601.advancedserverlist.core.objects.GenericServerImpl; +import ch.andre601.advancedserverlist.core.papi.PAPIUtil; import ch.andre601.advancedserverlist.core.parsing.ComponentParser; import ch.andre601.advancedserverlist.core.profiles.ServerListProfile; import ch.andre601.advancedserverlist.core.profiles.profile.ProfileManager; @@ -39,6 +39,8 @@ public class PingEventHandler{ + private static PAPIUtil papiUtil = null; + public static void handleEvent(GenericEventWrapper event){ if(event.isInvalidProtocol()) return; @@ -124,4 +126,11 @@ public static void handleEvent(GenericEventWrapper< event.updateEvent(); } + + public static PAPIUtil getPAPIUtil(){ + if(papiUtil != null) + return papiUtil; + + return (papiUtil = new PAPIUtil()); + } } diff --git a/core/src/main/java/ch/andre601/advancedserverlist/core/papi/PAPICache.java b/core/src/main/java/ch/andre601/advancedserverlist/core/papi/PAPICache.java new file mode 100644 index 00000000..f35974ac --- /dev/null +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/papi/PAPICache.java @@ -0,0 +1,49 @@ +/* + * MIT License + * + * Copyright (c) 2022-2023 Andre_601 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package ch.andre601.advancedserverlist.core.papi; + +import java.util.function.Supplier; + +public class PAPICache{ + + private CacheValue cache = null; + + public PAPICache(){} + + public String get(Supplier supplier){ + if(cache == null || cache.isExpired()) + cache = new CacheValue(supplier.get(), System.currentTimeMillis()); + + return cache.server(); + } + + private record CacheValue(String server, long timestamp){ + // Consider the cache expired if it is older than 5 seconds. + public boolean isExpired(){ + return (timestamp < 0L) || System.currentTimeMillis() - timestamp >= 5000L; + } + } +} diff --git a/core/src/main/java/ch/andre601/advancedserverlist/core/papi/PAPIUtil.java b/core/src/main/java/ch/andre601/advancedserverlist/core/papi/PAPIUtil.java new file mode 100644 index 00000000..820760bf --- /dev/null +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/papi/PAPIUtil.java @@ -0,0 +1,87 @@ +/* + * MIT License + * + * Copyright (c) 2022-2023 Andre_601 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package ch.andre601.advancedserverlist.core.papi; + +import net.william278.papiproxybridge.api.PlaceholderAPI; + +import java.util.*; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +public class PAPIUtil{ + + private final PlaceholderAPI papi; + + private final PAPICache cache = new PAPICache(); + + public PAPIUtil(){ + this.papi = PlaceholderAPI.createInstance(); + } + + // Make sure the version of PAPIProxyBridge we get has the required methods we need... + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public boolean isCompatible(){ + try{ + papi.getClass().getMethod("findServers"); + return true; + }catch(NoSuchMethodException ex){ + return false; + } + } + + public String getServer(){ + return cache.get(() -> { + List servers; + try{ + servers = papi.findServers().getNow(Collections.emptyList()); + }catch(CancellationException | CompletionException ex){ + return null; + } + + if(servers == null || servers.isEmpty()) + return null; + + return servers.get(0); + }); + } + + public

P getPlayer(Collection

players){ + if(players.isEmpty()) + return null; + + return List.copyOf(players).get(0); + } + + public String parse(String text, UUID carrier, UUID player){ + try{ + CompletableFuture future = papi.formatPlaceholders(text, carrier, player); + return future.getNow(text); + }catch(IllegalArgumentException | CancellationException | CompletionException ex){ + return text; + } + } +} diff --git a/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/FaviconHandler.java b/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/FaviconHandler.java index d078f6a2..6d8ac421 100644 --- a/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/FaviconHandler.java +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/FaviconHandler.java @@ -39,32 +39,27 @@ import java.net.URL; import java.net.URLConnection; import java.util.Locale; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.function.Function; public class FaviconHandler{ - private final Cache favicons = CacheBuilder.newBuilder() + private final Cache> favicons = CacheBuilder.newBuilder() .expireAfterWrite(5, TimeUnit.MINUTES) .build(); - private final AdvancedServerList core; + private final AdvancedServerList core; + private final ThreadPoolExecutor faviconThreadPool; - public FaviconHandler(AdvancedServerList core){ + public FaviconHandler(AdvancedServerList core){ this.core = core; + this.faviconThreadPool = createFaviconThreadPool(); } public F getFavicon(String input, Function function){ try{ - return favicons.get(input, () -> { - BufferedImage image = resolveImage(core, input); - if(image == null) - return null; - - return function.apply(image); - }); - }catch(ExecutionException ignored){ + return favicons.get(input, () -> convert(input, function)).getNow(null); + }catch(ExecutionException e){ return null; } } @@ -73,7 +68,17 @@ public void clearCache(){ favicons.invalidateAll(); } - private BufferedImage resolveImage(AdvancedServerList core, String input){ + private CompletableFuture convert(String input, Function function){ + return CompletableFuture.supplyAsync(() -> { + BufferedImage img = resolveImage(core, input); + if(img == null) + return null; + + return function.apply(img); + }, faviconThreadPool); + } + + private BufferedImage resolveImage(AdvancedServerList core, String input){ InputStream stream; if(input.toLowerCase(Locale.ROOT).startsWith("https://")){ @@ -126,7 +131,7 @@ private BufferedImage resolveImage(AdvancedServerList core, String input){ } } - private InputStream getFromUrl(AdvancedServerList core, String url){ + private InputStream getFromUrl(AdvancedServerList core, String url){ try{ URL faviconUrl = new URL(url); URLConnection connection = faviconUrl.openConnection(); @@ -140,4 +145,12 @@ private InputStream getFromUrl(AdvancedServerList core, String url){ return null; } } + + private ThreadPoolExecutor createFaviconThreadPool(){ + return new ThreadPoolExecutor(3, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024), r -> { + Thread t = new Thread(r, "AdvancedServerList-FaviconThread"); + t.setDaemon(true); + return t; + }); + } } diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index d25aaca4..066a01e5 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -37,4 +37,4 @@ checkUpdates: true # This is used internally to determine if the config needs to be migrated. # Changing or even removing this option could result in your config being broken. # -config-version: 2 +config-version: 2 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 56f0d3ad..44dfc007 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ UTF-8 - 3.2.1 + 3.3.0-b1 Create multiple Server lists based on conditions. v3.0.0 diff --git a/velocity/src/main/java/ch/andre601/advancedserverlist/velocity/listeners/VelocityEventWrapper.java b/velocity/src/main/java/ch/andre601/advancedserverlist/velocity/listeners/VelocityEventWrapper.java index 557e438f..feed67f0 100644 --- a/velocity/src/main/java/ch/andre601/advancedserverlist/velocity/listeners/VelocityEventWrapper.java +++ b/velocity/src/main/java/ch/andre601/advancedserverlist/velocity/listeners/VelocityEventWrapper.java @@ -28,6 +28,7 @@ import ch.andre601.advancedserverlist.api.events.GenericServerListEvent; import ch.andre601.advancedserverlist.api.objects.GenericServer; import ch.andre601.advancedserverlist.api.profiles.ProfileEntry; +import ch.andre601.advancedserverlist.core.events.PingEventHandler; import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore; import ch.andre601.advancedserverlist.core.interfaces.events.GenericEventWrapper; import ch.andre601.advancedserverlist.core.objects.CachedPlayer; @@ -37,6 +38,7 @@ import ch.andre601.advancedserverlist.velocity.objects.VelocityPlayerImpl; import ch.andre601.advancedserverlist.velocity.objects.VelocityProxyImpl; import com.velocitypowered.api.event.proxy.ProxyPingEvent; +import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.api.util.Favicon; @@ -152,7 +154,25 @@ public String getPlayerIP(){ @Override public String parsePAPIPlaceholders(String text, VelocityPlayerImpl player){ - return text; + if(!plugin.getProxy().getPluginManager().isLoaded("papiproxybridge")) + return text; + + if(!PingEventHandler.getPAPIUtil().isCompatible()) + return text; + + String server = PingEventHandler.getPAPIUtil().getServer(); + if(server == null || server.isEmpty()) + return text; + + RegisteredServer registeredServer = plugin.getProxy().getServer(server).orElse(null); + if(registeredServer == null || registeredServer.getPlayersConnected().isEmpty()) + return text; + + Player carrier = PingEventHandler.getPAPIUtil().getPlayer(registeredServer.getPlayersConnected()); + if(carrier == null) + return text; + + return PingEventHandler.getPAPIUtil().parse(text, carrier.getUniqueId(), player.getUUID()); } @Override diff --git a/velocity/src/main/resources/velocity-plugin.json b/velocity/src/main/resources/velocity-plugin.json index 6357098f..6fe86cef 100644 --- a/velocity/src/main/resources/velocity-plugin.json +++ b/velocity/src/main/resources/velocity-plugin.json @@ -6,5 +6,11 @@ "authors": [ "Andre_601" ], + "dependencies": [ + { + "id": "papiproxybridge", + "optional": true + } + ], "main": "ch.andre601.advancedserverlist.velocity.VelocityCore" } \ No newline at end of file