From 2d02d48d3cf9392bb139293c2bb0476ecbdef67b Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Thu, 18 Aug 2022 16:05:00 +0200 Subject: [PATCH 1/7] Add Favicon support for Server list profiles. --- .../bungeecord/events/PingEvent.java | 28 +++- .../core/AdvancedServerList.java | 3 + .../core/profiles/ServerListProfile.java | 6 + .../core/profiles/favicon/FaviconHandler.java | 120 ++++++++++++++++++ core/src/main/resources/profiles/default.yml | 9 ++ .../paper/events/PingEvent.java | 19 ++- pom.xml | 2 +- .../spigot/events/PingEvent.java | 18 +++ spigot/src/main/resources/plugin.yml | 10 +- .../velocity/events/PingEvent.java | 22 +++- 10 files changed, 221 insertions(+), 16 deletions(-) create mode 100644 core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/FaviconHandler.java diff --git a/bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/events/PingEvent.java b/bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/events/PingEvent.java index 0d4c57fd..0595efd4 100644 --- a/bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/events/PingEvent.java +++ b/bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/events/PingEvent.java @@ -31,9 +31,11 @@ import ch.andre601.advancedserverlist.core.parsing.ComponentParser; import ch.andre601.advancedserverlist.core.profiles.ProfileManager; import ch.andre601.advancedserverlist.core.profiles.ServerListProfile; +import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.PlayerPlaceholders; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.ServerPlaceholders; import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; +import net.md_5.bungee.api.Favicon; import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.event.ProxyPingEvent; @@ -41,6 +43,7 @@ import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventPriority; +import java.awt.image.BufferedImage; import java.net.InetSocketAddress; public class PingEvent implements Listener{ @@ -59,15 +62,15 @@ public void onProxyPing(ProxyPingEvent event){ if(protocol == null) return; - InetSocketAddress address = (InetSocketAddress)event.getConnection().getSocketAddress(); + String playerName = plugin.getCore().getPlayerHandler().getPlayerByIp( + ((InetSocketAddress)event.getConnection().getSocketAddress()).getHostString() + ); InetSocketAddress host = event.getConnection().getVirtualHost(); int online = ping.getPlayers().getOnline(); int max = ping.getPlayers().getMax(); - PlayerPlaceholders playerPlaceholders = new PlayerPlaceholders( - new BungeePlayer(plugin.getCore().getPlayerHandler().getPlayerByIp(address.getHostString()), protocol.getProtocol()) - ); + PlayerPlaceholders playerPlaceholders = new PlayerPlaceholders(new BungeePlayer(playerName, protocol.getProtocol())); ServerPlaceholders serverPlaceholders = new ServerPlaceholders(online, max, host == null ? null : host.getHostString()); ServerListProfile profile = ProfileManager.get(plugin.getCore()) @@ -128,7 +131,22 @@ public void onProxyPing(ProxyPingEvent event){ ping.getPlayers().setSample(playerInfos); } - ping.setFavicon(ping.getFaviconObject()); + if(!profile.getFavicon().isEmpty()){ + BufferedImage img = new FaviconHandler(plugin.getCore(), profile.getFavicon(), playerName).getAsBufferedImage(); + if(img == null){ + plugin.getPluginLogger().warn("Could not obtain valid Favicon to use."); + ping.setFavicon(ping.getFaviconObject()); + }else{ + try{ + Favicon favicon = Favicon.create(img); + ping.setFavicon(favicon); + }catch(Exception ex){ + plugin.getPluginLogger().warn("Unable to override Favicon. Reason: %s", ex.getMessage()); + ping.setFavicon(ping.getFaviconObject()); + } + } + } + ping.setVersion(protocol); event.setResponse(ping); diff --git a/core/src/main/java/ch/andre601/advancedserverlist/core/AdvancedServerList.java b/core/src/main/java/ch/andre601/advancedserverlist/core/AdvancedServerList.java index 4cfb762a..315b6eb7 100644 --- a/core/src/main/java/ch/andre601/advancedserverlist/core/AdvancedServerList.java +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/AdvancedServerList.java @@ -129,6 +129,9 @@ private void load(){ return; } + if(!getPath().resolve("favicons").toFile().exists() && getPath().resolve("favicons").toFile().mkdirs()) + getPluginLogger().info("Successfully created favicons folder."); + getPluginLogger().info("Loading Commands..."); plugin.loadCommands(); getPluginLogger().info("Commands loaded!"); diff --git a/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/ServerListProfile.java b/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/ServerListProfile.java index cc811173..f0615821 100644 --- a/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/ServerListProfile.java +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/ServerListProfile.java @@ -43,6 +43,7 @@ public class ServerListProfile{ private final List motd; private final List players; private final String playerCount; + private final String favicon; private final boolean hidePlayers; private final int xMore; @@ -53,6 +54,7 @@ public ServerListProfile(ConfigurationNode node, PluginLogger logger){ this.motd = getList(node, "motd", true); this.players = getList(node, "players", false); this.playerCount = node.node("playerCount").getString(""); + this.favicon = node.node("favicon").getString(""); this.hidePlayers = node.node("hidePlayers").getBoolean(); this.xMore = node.node("xMore").getInt(-1); } @@ -73,6 +75,10 @@ public String getPlayerCount(){ return playerCount; } + public String getFavicon(){ + return favicon; + } + public boolean shouldHidePlayers(){ return hidePlayers; } 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 new file mode 100644 index 00000000..480829f0 --- /dev/null +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/FaviconHandler.java @@ -0,0 +1,120 @@ +/* + * MIT License + * + * Copyright (c) 2022 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.profiles.favicon; + +import ch.andre601.advancedserverlist.core.AdvancedServerList; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.URL; +import java.net.URLConnection; +import java.util.Locale; + +public class FaviconHandler{ + + private final BufferedImage image; + + public FaviconHandler(AdvancedServerList core, String input, String playerName){ + this.image = resolve(core, input, playerName); + } + + public byte[] getAsByteArray(){ + if(image == null) + return null; + + try{ + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.setUseCache(false); + ImageIO.write(image, "PNG", baos); + + return baos.toByteArray(); + }catch(IOException ex){ + return null; + } + } + + public BufferedImage getAsBufferedImage(){ + return image; + } + + private BufferedImage resolve(AdvancedServerList core, String input, String playerName){ + InputStream stream; + + if(input.toLowerCase(Locale.ROOT).startsWith("https://")){ + stream = getFromUrl(core, input); + }else + if(input.equalsIgnoreCase("${player name}")){ + stream = getFromUrl(core, "https://mc-heads.net/avatar/" + playerName + "/64"); + }else{ + File folder = core.getPath().resolve("favicons").toFile(); + if(!folder.exists()) + return null; + + File file = new File(folder, input); + + try{ + stream = new FileInputStream(file); + }catch(FileNotFoundException ex){ + return null; + } + } + + if(stream == null) + return null; + + try{ + BufferedImage original = ImageIO.read(stream); + if(original == null) + return null; + + BufferedImage favicon = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB); + Graphics2D img = favicon.createGraphics(); + + // We use this to resize the image to 64x64 pixels. + img.drawImage(original, 0, 0, 64, 64, null); + img.dispose(); + + return favicon; + }catch(IOException ignored){ + return null; + } + } + + private InputStream getFromUrl(AdvancedServerList core, String url){ + try{ + URL avatarUrl = new URL(url); + URLConnection connection = avatarUrl.openConnection(); + connection.setRequestProperty("User-Agent", "AdvancedServerList/" + core.getVersion()); + connection.connect(); + + return connection.getInputStream(); + }catch(IOException ex){ + return null; + } + } +} diff --git a/core/src/main/resources/profiles/default.yml b/core/src/main/resources/profiles/default.yml index 8fda5df6..2a202db3 100644 --- a/core/src/main/resources/profiles/default.yml +++ b/core/src/main/resources/profiles/default.yml @@ -59,6 +59,15 @@ players: # playerCount: 'Text' +# +# Allows you to override the default Favicon used by the Server/Proxy. +# You can provide three specific inputs: +# - A valid URL pointing to a +# +# +# +favicon: '' + # # When set to 0 or higher will alter the max player count to the online count + xMore. # Example: With 20 online players would 'xMore: 1' show 20/21 and 'xMore: 0' show 20/20 diff --git a/paper/src/main/java/ch/andre601/advancedserverlist/paper/events/PingEvent.java b/paper/src/main/java/ch/andre601/advancedserverlist/paper/events/PingEvent.java index 11bc82f0..72978cb7 100644 --- a/paper/src/main/java/ch/andre601/advancedserverlist/paper/events/PingEvent.java +++ b/paper/src/main/java/ch/andre601/advancedserverlist/paper/events/PingEvent.java @@ -28,6 +28,7 @@ import ch.andre601.advancedserverlist.core.parsing.ComponentParser; import ch.andre601.advancedserverlist.core.profiles.ProfileManager; import ch.andre601.advancedserverlist.core.profiles.ServerListProfile; +import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.Placeholders; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.PlayerPlaceholders; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.ServerPlaceholders; @@ -40,7 +41,9 @@ import org.bukkit.OfflinePlayer; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.util.CachedServerIcon; +import java.awt.image.BufferedImage; import java.net.InetSocketAddress; import java.util.*; @@ -119,7 +122,21 @@ public void onServerPing(PaperServerListPingEvent event){ event.getPlayerSample().addAll(getPlayers(profile.getPlayers(), playerPlaceholders, serverPlaceholders, player.getPlayer())); } - event.setServerIcon(event.getServerIcon()); + if(!profile.getFavicon().isEmpty()){ + BufferedImage img = new FaviconHandler(plugin.getCore(), profile.getFavicon(), player.getName()).getAsBufferedImage(); + if(img == null){ + plugin.getPluginLogger().warn("Could not obtain valid Favicon to use."); + event.setServerIcon(event.getServerIcon()); + }else{ + try{ + CachedServerIcon favicon = Bukkit.loadServerIcon(img); + event.setServerIcon(favicon); + }catch(Exception ex){ + plugin.getPluginLogger().warn("Unable to override Favicon. Reason: %s", ex.getMessage()); + event.setServerIcon(event.getServerIcon()); + } + } + } } private List getPlayers(List lines, Placeholders playerPlaceholders, Placeholders serverPlaceholders, OfflinePlayer player){ diff --git a/pom.xml b/pom.xml index 2f22bc1a..0e661a36 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ UTF-8 - 1.4.0 + 1.5.0 Create multiple Server lists based on conditions. 16 diff --git a/spigot/src/main/java/ch/andre601/advancedserverlist/spigot/events/PingEvent.java b/spigot/src/main/java/ch/andre601/advancedserverlist/spigot/events/PingEvent.java index 7c04801a..bbdc9aab 100644 --- a/spigot/src/main/java/ch/andre601/advancedserverlist/spigot/events/PingEvent.java +++ b/spigot/src/main/java/ch/andre601/advancedserverlist/spigot/events/PingEvent.java @@ -28,6 +28,7 @@ import ch.andre601.advancedserverlist.core.parsing.ComponentParser; import ch.andre601.advancedserverlist.core.profiles.ProfileManager; import ch.andre601.advancedserverlist.core.profiles.ServerListProfile; +import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.PlayerPlaceholders; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.ServerPlaceholders; import ch.andre601.advancedserverlist.spigot.SpigotCore; @@ -46,6 +47,7 @@ import org.bukkit.OfflinePlayer; import org.bukkit.event.Listener; +import java.io.InputStream; import java.net.InetSocketAddress; import java.util.*; @@ -156,6 +158,22 @@ public void onPacketSending(PacketEvent event){ .toString() )); } + + if(!profile.getFavicon().isEmpty()){ + byte[] bytes = new FaviconHandler(spigotPlugin.getCore(), profile.getFavicon(), player.getName()).getAsByteArray(); + if(bytes == null){ + spigotPlugin.getPluginLogger().warn("Could not obtain valid Favicon to use."); + ping.setFavicon(ping.getFavicon()); + }else{ + try{ + WrappedServerPing.CompressedImage favicon = WrappedServerPing.CompressedImage.fromPng(bytes); + ping.setFavicon(favicon); + }catch(Exception ex){ + spigotPlugin.getPluginLogger().warn("Unable to override Favicon. Reason: %s", ex.getMessage()); + ping.setFavicon(ping.getFavicon()); + } + } + } } }); } diff --git a/spigot/src/main/resources/plugin.yml b/spigot/src/main/resources/plugin.yml index 1e4a1abe..52472691 100644 --- a/spigot/src/main/resources/plugin.yml +++ b/spigot/src/main/resources/plugin.yml @@ -8,11 +8,11 @@ main: 'ch.andre601.advancedserverlist.spigot.SpigotCore' api-version: 1.19 libraries: - - 'org.spongepowered:configurate-yaml:${plugin.depend.configurate}' - - 'net.kyori:adventure-api:${plugin.depend.kyori-adventure}' - - 'net.kyori:adventure-text-minimessage:${plugin.depend.kyori-minimessage}' - - 'net.kyori:adventure-text-serializer-legacy:${plugin.depend.kyori-legacy}' - - 'net.kyori:adventure-platform-bukkit:${plugin.depend.kyori-platform-bukkit}' + - 'org.spongepowered:configurate-yaml:4.1.2' + - 'net.kyori:adventure-api:4.11.0' + - 'net.kyori:adventure-text-minimessage:4.11.0' + - 'net.kyori:adventure-text-serializer-legacy:4.11.0' + - 'net.kyori:adventure-platform-bukkit:4.1.2' softdepend: - 'ProtocolLib' diff --git a/velocity/src/main/java/ch/andre601/advancedserverlist/velocity/events/PingEvent.java b/velocity/src/main/java/ch/andre601/advancedserverlist/velocity/events/PingEvent.java index dd29385e..c39f6e65 100644 --- a/velocity/src/main/java/ch/andre601/advancedserverlist/velocity/events/PingEvent.java +++ b/velocity/src/main/java/ch/andre601/advancedserverlist/velocity/events/PingEvent.java @@ -29,6 +29,7 @@ import ch.andre601.advancedserverlist.core.parsing.ComponentParser; import ch.andre601.advancedserverlist.core.profiles.ProfileManager; import ch.andre601.advancedserverlist.core.profiles.ServerListProfile; +import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.PlayerPlaceholders; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.ServerPlaceholders; import ch.andre601.advancedserverlist.velocity.VelocityCore; @@ -37,7 +38,9 @@ import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyPingEvent; import com.velocitypowered.api.proxy.server.ServerPing; +import com.velocitypowered.api.util.Favicon; +import java.awt.image.BufferedImage; import java.net.InetSocketAddress; import java.util.List; @@ -58,15 +61,13 @@ public void onProxyPing(ProxyPingEvent event){ return; ServerPing.Builder builder = ping.asBuilder(); - InetSocketAddress address = event.getConnection().getRemoteAddress(); + String playerName = plugin.getCore().getPlayerHandler().getPlayerByIp(event.getConnection().getRemoteAddress().getHostString()); InetSocketAddress host = event.getConnection().getVirtualHost().orElse(null); int online = builder.getOnlinePlayers(); int max = builder.getMaximumPlayers(); - PlayerPlaceholders playerPlaceholders = new PlayerPlaceholders( - new VelocityPlayer(plugin.getCore().getPlayerHandler().getPlayerByIp(address.getHostString()), protocol.getProtocol()) - ); + PlayerPlaceholders playerPlaceholders = new PlayerPlaceholders(new VelocityPlayer(playerName, protocol.getProtocol())); ServerPlaceholders serverPlaceholders = new ServerPlaceholders(online, max, host == null ? null : host.getHostString()); ServerListProfile profile = ProfileManager.get(plugin.getCore()) @@ -123,6 +124,19 @@ public void onProxyPing(ProxyPingEvent event){ builder.clearSamplePlayers().samplePlayers(playerSamples); } + if(!profile.getFavicon().isEmpty()){ + BufferedImage img = new FaviconHandler(plugin.getCore(), profile.getFavicon(), playerName).getAsBufferedImage(); + if(img == null){ + plugin.getPluginLogger().warn("Could not obtain valid Favicon to use."); + }else{ + try{ + Favicon favicon = Favicon.create(img); + builder.favicon(favicon); + }catch(Exception ex){ + plugin.getPluginLogger().warn("Unable to override Favicon. Reason: %s", ex.getMessage());} + } + } + event.setPing(builder.build()); } } From bd95be080e5e915e2107190f6c32e7a7b9224281 Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Thu, 18 Aug 2022 16:11:04 +0200 Subject: [PATCH 2/7] Add missing profile comments --- core/src/main/resources/profiles/default.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/resources/profiles/default.yml b/core/src/main/resources/profiles/default.yml index 2a202db3..fe2134c5 100644 --- a/core/src/main/resources/profiles/default.yml +++ b/core/src/main/resources/profiles/default.yml @@ -62,9 +62,11 @@ playerCount: 'Text' # # Allows you to override the default Favicon used by the Server/Proxy. # You can provide three specific inputs: -# - A valid URL pointing to a -# +# - A valid URL pointing to a PNG file. +# - ${player name} for a per-player avatar being displayed (Uses mc-heads.net) +# - Image file name (i.e. example.png) for a file in the "favicons" folder. # +# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#favicon # favicon: '' From 563362504f9d95929a35bfad732d344784f9da9c Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Sat, 20 Aug 2022 23:43:55 +0200 Subject: [PATCH 3/7] Use some kind of caching... I guess? --- .../bungeecord/events/PingEvent.java | 5 +- core/pom.xml | 5 + .../core/AdvancedServerList.java | 4 +- .../core/profiles/favicon/FaviconHandler.java | 95 ++---------- .../core/profiles/favicon/GenericFavicon.java | 136 ++++++++++++++++++ .../paper/events/PingEvent.java | 5 +- .../spigot/events/PingEvent.java | 6 +- .../velocity/events/PingEvent.java | 5 +- 8 files changed, 171 insertions(+), 90 deletions(-) create mode 100644 core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/GenericFavicon.java diff --git a/bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/events/PingEvent.java b/bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/events/PingEvent.java index 0595efd4..079baea0 100644 --- a/bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/events/PingEvent.java +++ b/bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/events/PingEvent.java @@ -32,6 +32,7 @@ import ch.andre601.advancedserverlist.core.profiles.ProfileManager; import ch.andre601.advancedserverlist.core.profiles.ServerListProfile; import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler; +import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.PlayerPlaceholders; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.ServerPlaceholders; import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; @@ -132,7 +133,9 @@ public void onProxyPing(ProxyPingEvent event){ } if(!profile.getFavicon().isEmpty()){ - BufferedImage img = new FaviconHandler(plugin.getCore(), profile.getFavicon(), playerName).getAsBufferedImage(); + String favName = StringReplacer.replace(profile.getFavicon(), playerPlaceholders.getReplacements()); + + BufferedImage img = new FaviconHandler(plugin.getCore(), favName).get().getAsBufferedImage(); if(img == null){ plugin.getPluginLogger().warn("Could not obtain valid Favicon to use."); ping.setFavicon(ping.getFaviconObject()); diff --git a/core/pom.xml b/core/pom.xml index 6dfcbae1..358e0518 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -64,6 +64,11 @@ adventure-text-serializer-legacy 4.11.0 + + com.github.ben-manes.caffeine + caffeine + 1.3.3 + diff --git a/core/src/main/java/ch/andre601/advancedserverlist/core/AdvancedServerList.java b/core/src/main/java/ch/andre601/advancedserverlist/core/AdvancedServerList.java index 315b6eb7..2aff82d8 100644 --- a/core/src/main/java/ch/andre601/advancedserverlist/core/AdvancedServerList.java +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/AdvancedServerList.java @@ -29,9 +29,7 @@ import ch.andre601.advancedserverlist.core.file.FileHandler; import ch.andre601.advancedserverlist.core.interfaces.PluginCore; import ch.andre601.advancedserverlist.core.interfaces.PluginLogger; -import ch.andre601.advancedserverlist.core.profiles.players.GenericPlayer; import ch.andre601.advancedserverlist.core.profiles.players.PlayerHandler; -import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.PlayerPlaceholders; import java.io.IOException; import java.io.InputStream; @@ -113,7 +111,7 @@ private void load(){ getPluginLogger().info("Starting AdvancedServerList v%s...", version); - getPluginLogger().info("Proxy: " + plugin.getPlatformName() + " " + plugin.getPlatformVersion()); + getPluginLogger().info("Platform: " + plugin.getPlatformName() + " " + plugin.getPlatformVersion()); if(getFileHandler().loadConfig()){ getPluginLogger().info("Successfully loaded config.yml!"); 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 480829f0..e46f3141 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 @@ -26,95 +26,26 @@ package ch.andre601.advancedserverlist.core.profiles.favicon; import ch.andre601.advancedserverlist.core.AdvancedServerList; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; -import javax.imageio.ImageIO; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.*; -import java.net.URL; -import java.net.URLConnection; -import java.util.Locale; +import java.util.concurrent.TimeUnit; public class FaviconHandler{ - private final BufferedImage image; + private final Cache favicons = Caffeine.newBuilder() + .expireAfterWrite(5, TimeUnit.MINUTES) + .build(); - public FaviconHandler(AdvancedServerList core, String input, String playerName){ - this.image = resolve(core, input, playerName); - } - - public byte[] getAsByteArray(){ - if(image == null) - return null; - - try{ - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.setUseCache(false); - ImageIO.write(image, "PNG", baos); - - return baos.toByteArray(); - }catch(IOException ex){ - return null; - } - } - - public BufferedImage getAsBufferedImage(){ - return image; - } - - private BufferedImage resolve(AdvancedServerList core, String input, String playerName){ - InputStream stream; - - if(input.toLowerCase(Locale.ROOT).startsWith("https://")){ - stream = getFromUrl(core, input); - }else - if(input.equalsIgnoreCase("${player name}")){ - stream = getFromUrl(core, "https://mc-heads.net/avatar/" + playerName + "/64"); - }else{ - File folder = core.getPath().resolve("favicons").toFile(); - if(!folder.exists()) - return null; - - File file = new File(folder, input); + private final AdvancedServerList core; + private final String input; - try{ - stream = new FileInputStream(file); - }catch(FileNotFoundException ex){ - return null; - } - } - - if(stream == null) - return null; - - try{ - BufferedImage original = ImageIO.read(stream); - if(original == null) - return null; - - BufferedImage favicon = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB); - Graphics2D img = favicon.createGraphics(); - - // We use this to resize the image to 64x64 pixels. - img.drawImage(original, 0, 0, 64, 64, null); - img.dispose(); - - return favicon; - }catch(IOException ignored){ - return null; - } + public FaviconHandler(AdvancedServerList core, String input){ + this.core = core; + this.input = input; } - private InputStream getFromUrl(AdvancedServerList core, String url){ - try{ - URL avatarUrl = new URL(url); - URLConnection connection = avatarUrl.openConnection(); - connection.setRequestProperty("User-Agent", "AdvancedServerList/" + core.getVersion()); - connection.connect(); - - return connection.getInputStream(); - }catch(IOException ex){ - return null; - } + public GenericFavicon get(){ + return favicons.get(input, k -> new GenericFavicon(core, input)); } } diff --git a/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/GenericFavicon.java b/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/GenericFavicon.java new file mode 100644 index 00000000..ce786995 --- /dev/null +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/GenericFavicon.java @@ -0,0 +1,136 @@ +/* + * MIT License + * + * Copyright (c) 2022 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.profiles.favicon; + +import ch.andre601.advancedserverlist.core.AdvancedServerList; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.URL; +import java.net.URLConnection; +import java.util.Locale; + +public class GenericFavicon{ + + private final AdvancedServerList core; + private final String input; + private final BufferedImage image; + + public GenericFavicon(AdvancedServerList core, String input){ + this.core = core; + this.input = input; + this.image = resolve(); + } + + public byte[] getAsByteArray(){ + if(image == null){ + this.core.getPluginLogger().warn("Cannot convert Favicon BufferedImage to Byte array. BufferedImage was null."); + return null; + } + + try{ + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.setUseCache(false); + ImageIO.write(image, "PNG", baos); + + return baos.toByteArray(); + }catch(IOException ex){ + this.core.getPluginLogger().warn("Cannot convert Favicon BufferedImage to Byte array. %s", ex.getMessage()); + return null; + } + } + + public BufferedImage getAsBufferedImage(){ + return image; + } + + private BufferedImage resolve(){ + InputStream stream; + + if(input.toLowerCase(Locale.ROOT).startsWith("https://")){ + stream = getFromUrl(input); + }else + if(input.toLowerCase(Locale.ROOT).endsWith(".png")){ + File folder = core.getPath().resolve("favicons").toFile(); + if(!folder.exists()){ + this.core.getPluginLogger().warn("Cannot get Favicon for profile. Favicons folder is not present."); + return null; + } + + File file = new File(folder, input); + + try{ + stream = new FileInputStream(file); + }catch(FileNotFoundException ex){ + this.core.getPluginLogger().warn("Unable to create Favicon data. %s", ex.getMessage()); + return null; + } + }else{ + stream = getFromUrl("https://mc-heads.net/avatar/" + input + "/64"); + } + + if(stream == null){ + this.core.getPluginLogger().warn("Unable to get Data for Favicon. InputStream was null."); + return null; + } + + try{ + BufferedImage original = ImageIO.read(stream); + if(original == null) + return null; + + BufferedImage favicon = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB); + Graphics2D img = favicon.createGraphics(); + + // We use this to resize the image to 64x64 pixels. + img.drawImage(original, 0, 0, 64, 64, null); + img.dispose(); + + return favicon; + }catch(IOException ex){ + this.core.getPluginLogger().warn("Cannot resize favicon into 64x64. %s", ex.getMessage()); + return null; + } + } + + private InputStream getFromUrl(String url){ + try{ + URL avatarUrl = new URL(url); + URLConnection connection = avatarUrl.openConnection(); + connection.setRequestProperty("User-Agent", "AdvancedServerList/" + core.getVersion()); + connection.connect(); + + return connection.getInputStream(); + }catch(IOException ex){ + this.core.getPluginLogger().warn("Unable to connect to URL %s! %s", url, ex.getMessage()); + return null; + } + } + + +} diff --git a/paper/src/main/java/ch/andre601/advancedserverlist/paper/events/PingEvent.java b/paper/src/main/java/ch/andre601/advancedserverlist/paper/events/PingEvent.java index 72978cb7..ecd34e3a 100644 --- a/paper/src/main/java/ch/andre601/advancedserverlist/paper/events/PingEvent.java +++ b/paper/src/main/java/ch/andre601/advancedserverlist/paper/events/PingEvent.java @@ -29,6 +29,7 @@ import ch.andre601.advancedserverlist.core.profiles.ProfileManager; import ch.andre601.advancedserverlist.core.profiles.ServerListProfile; import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler; +import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.Placeholders; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.PlayerPlaceholders; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.ServerPlaceholders; @@ -123,7 +124,9 @@ public void onServerPing(PaperServerListPingEvent event){ } if(!profile.getFavicon().isEmpty()){ - BufferedImage img = new FaviconHandler(plugin.getCore(), profile.getFavicon(), player.getName()).getAsBufferedImage(); + String favName = StringReplacer.replace(profile.getFavicon(), playerPlaceholders.getReplacements()); + + BufferedImage img = new FaviconHandler(plugin.getCore(), favName).get().getAsBufferedImage(); if(img == null){ plugin.getPluginLogger().warn("Could not obtain valid Favicon to use."); event.setServerIcon(event.getServerIcon()); diff --git a/spigot/src/main/java/ch/andre601/advancedserverlist/spigot/events/PingEvent.java b/spigot/src/main/java/ch/andre601/advancedserverlist/spigot/events/PingEvent.java index bbdc9aab..a6e26757 100644 --- a/spigot/src/main/java/ch/andre601/advancedserverlist/spigot/events/PingEvent.java +++ b/spigot/src/main/java/ch/andre601/advancedserverlist/spigot/events/PingEvent.java @@ -29,6 +29,7 @@ import ch.andre601.advancedserverlist.core.profiles.ProfileManager; import ch.andre601.advancedserverlist.core.profiles.ServerListProfile; import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler; +import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.PlayerPlaceholders; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.ServerPlaceholders; import ch.andre601.advancedserverlist.spigot.SpigotCore; @@ -47,7 +48,6 @@ import org.bukkit.OfflinePlayer; import org.bukkit.event.Listener; -import java.io.InputStream; import java.net.InetSocketAddress; import java.util.*; @@ -160,7 +160,9 @@ public void onPacketSending(PacketEvent event){ } if(!profile.getFavicon().isEmpty()){ - byte[] bytes = new FaviconHandler(spigotPlugin.getCore(), profile.getFavicon(), player.getName()).getAsByteArray(); + String favName = StringReplacer.replace(profile.getFavicon(), playerPlaceholders.getReplacements()); + + byte[] bytes = new FaviconHandler(spigotPlugin.getCore(), favName).get().getAsByteArray(); if(bytes == null){ spigotPlugin.getPluginLogger().warn("Could not obtain valid Favicon to use."); ping.setFavicon(ping.getFavicon()); diff --git a/velocity/src/main/java/ch/andre601/advancedserverlist/velocity/events/PingEvent.java b/velocity/src/main/java/ch/andre601/advancedserverlist/velocity/events/PingEvent.java index c39f6e65..e038f2ca 100644 --- a/velocity/src/main/java/ch/andre601/advancedserverlist/velocity/events/PingEvent.java +++ b/velocity/src/main/java/ch/andre601/advancedserverlist/velocity/events/PingEvent.java @@ -30,6 +30,7 @@ import ch.andre601.advancedserverlist.core.profiles.ProfileManager; import ch.andre601.advancedserverlist.core.profiles.ServerListProfile; import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler; +import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.PlayerPlaceholders; import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.ServerPlaceholders; import ch.andre601.advancedserverlist.velocity.VelocityCore; @@ -125,7 +126,9 @@ public void onProxyPing(ProxyPingEvent event){ } if(!profile.getFavicon().isEmpty()){ - BufferedImage img = new FaviconHandler(plugin.getCore(), profile.getFavicon(), playerName).getAsBufferedImage(); + String favName = StringReplacer.replace(profile.getFavicon(), playerPlaceholders.getReplacements()); + + BufferedImage img = new FaviconHandler(plugin.getCore(), favName).get().getAsBufferedImage(); if(img == null){ plugin.getPluginLogger().warn("Could not obtain valid Favicon to use."); }else{ From dd0bc8a43a733ba797820e325ed27d1c8316dfd0 Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Sat, 20 Aug 2022 23:53:32 +0200 Subject: [PATCH 4/7] Cache the bytes array --- .../core/profiles/favicon/GenericFavicon.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/GenericFavicon.java b/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/GenericFavicon.java index ce786995..bdb86327 100644 --- a/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/GenericFavicon.java +++ b/core/src/main/java/ch/andre601/advancedserverlist/core/profiles/favicon/GenericFavicon.java @@ -41,6 +41,8 @@ public class GenericFavicon{ private final String input; private final BufferedImage image; + private byte[] bytes = null; + public GenericFavicon(AdvancedServerList core, String input){ this.core = core; this.input = input; @@ -48,6 +50,9 @@ public GenericFavicon(AdvancedServerList core, String input){ } public byte[] getAsByteArray(){ + if(bytes != null && bytes.length > 0) + return bytes; + if(image == null){ this.core.getPluginLogger().warn("Cannot convert Favicon BufferedImage to Byte array. BufferedImage was null."); return null; @@ -58,7 +63,7 @@ public byte[] getAsByteArray(){ ImageIO.setUseCache(false); ImageIO.write(image, "PNG", baos); - return baos.toByteArray(); + return (bytes = baos.toByteArray()); }catch(IOException ex){ this.core.getPluginLogger().warn("Cannot convert Favicon BufferedImage to Byte array. %s", ex.getMessage()); return null; From a5f6e689f047e29f78bfaed4dcb4cdafe691616c Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Sun, 21 Aug 2022 01:31:20 +0200 Subject: [PATCH 5/7] Shade in Caffeine --- core/pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/pom.xml b/core/pom.xml index 358e0518..3e553dfa 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -68,6 +68,7 @@ com.github.ben-manes.caffeine caffeine 1.3.3 + compile @@ -91,6 +92,12 @@ 3.3.0 true + + + com.github.benmanes + ch.andre601.advancedserverlist.core.depends.caffeine + + *:* From b88ee87c96f4f98b96ed76e60420717475f31ead Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Sun, 21 Aug 2022 23:28:11 +0200 Subject: [PATCH 6/7] Add notes to favicon setting --- core/src/main/resources/profiles/default.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/resources/profiles/default.yml b/core/src/main/resources/profiles/default.yml index fe2134c5..ce174ece 100644 --- a/core/src/main/resources/profiles/default.yml +++ b/core/src/main/resources/profiles/default.yml @@ -66,6 +66,11 @@ playerCount: 'Text' # - ${player name} for a per-player avatar being displayed (Uses mc-heads.net) # - Image file name (i.e. example.png) for a file in the "favicons" folder. # +# NOTE: +# - The favicon will be cached for 5 minutes to avoid spam and rate limits. +# - BungeeCord/Waterfall may report issues that the plugin takes a long time to process. +# This is something I cannot really get improved without help. +# # Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#favicon # favicon: '' From 50f878e39c12ced04147fda53edc9e14debe27cc Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Sun, 21 Aug 2022 23:30:59 +0200 Subject: [PATCH 7/7] Add assets --- .github/assets/logo.png | Bin 0 -> 12732 bytes .github/assets/version_1.5.0.jpg | Bin 0 -> 70434 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .github/assets/logo.png create mode 100644 .github/assets/version_1.5.0.jpg diff --git a/.github/assets/logo.png b/.github/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bc30311779307975c10973cb41d2b6fb63155d43 GIT binary patch literal 12732 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