Skip to content
This repository has been archived by the owner on Apr 9, 2024. It is now read-only.

Commit

Permalink
Merge pull request #16 from Andre601/feature/improve-fakeplayers-crea…
Browse files Browse the repository at this point in the history
…tion

Improve handling of fake players
Andre601 authored Sep 8, 2022
2 parents 5cb1245 + 4539fe4 commit 99509b5
Showing 19 changed files with 381 additions and 271 deletions.
Original file line number Diff line number Diff line change
@@ -30,17 +30,23 @@
import ch.andre601.advancedserverlist.bungeecord.events.PingEvent;
import ch.andre601.advancedserverlist.bungeecord.logging.BungeeLogger;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginCore;
import ch.andre601.advancedserverlist.core.interfaces.core.ProxyCore;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.Placeholders;
import net.md_5.bungee.api.Favicon;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.plugin.Plugin;
import org.bstats.bungeecord.Metrics;
import org.bstats.charts.SimplePie;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
public class BungeeCordCore extends Plugin implements ProxyCore<Favicon, ServerPing.PlayerInfo>{

private AdvancedServerList core;
private FaviconHandler<Favicon> faviconHandler = null;
@@ -86,7 +92,7 @@ public AdvancedServerList getCore(){
}

@Override
public Path getPath(){
public Path getFolderPath(){
return getDataFolder().toPath();
}

@@ -112,4 +118,20 @@ public String getPlatformName(){
public String getPlatformVersion(){
return getProxy().getVersion();
}

@Override
public List<ServerPing.PlayerInfo> createPlayers(List<String> lines, Placeholders... placeholders){
List<ServerPing.PlayerInfo> players = new ArrayList<>(lines.size());

for(String line : lines){
String parsed = ComponentParser.text(line)
.replacements(placeholders[0])
.replacements(placeholders[1])
.toString();

players.add(new ServerPing.PlayerInfo(parsed, UUID.randomUUID()));
}

return players;
}
}
Original file line number Diff line number Diff line change
@@ -27,7 +27,6 @@

import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
import ch.andre601.advancedserverlist.bungeecord.BungeePlayer;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.ProfileManager;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
@@ -80,8 +79,8 @@ public void onProxyPing(ProxyPingEvent event){
if(profile == null)
return;

if(profile.getXMore() >= 0){
max = online + profile.getXMore();
if(profile.isExtraPlayersEnabled()){
max = online + profile.getExtraPlayers();
ping.getPlayers().setMax(max);
}

@@ -100,15 +99,9 @@ public void onProxyPing(ProxyPingEvent event){

if(profile.shouldHidePlayers()){
ping.setPlayers(null);

ping.setFavicon(ping.getFaviconObject());
ping.setVersion(protocol);

event.setResponse(ping);
return;
}

if(!profile.getPlayerCount().isEmpty()){
if(!profile.getPlayerCount().isEmpty() && !profile.shouldHidePlayers()){
protocol.setName(ComponentParser.text(profile.getPlayerCount())
.replacements(playerPlaceholders)
.replacements(serverPlaceholders)
@@ -117,13 +110,8 @@ public void onProxyPing(ProxyPingEvent event){
protocol.setProtocol(-1);
}

if(!profile.getPlayers().isEmpty()){
String players = ComponentParser.list(profile.getPlayers())
.replacements(playerPlaceholders)
.replacements(serverPlaceholders)
.toString();

ServerPing.PlayerInfo[] playerInfos = AdvancedServerList.getPlayers(ServerPing.PlayerInfo.class, players)
if(!profile.getPlayers().isEmpty() && !profile.shouldHidePlayers()){
ServerPing.PlayerInfo[] playerInfos = plugin.createPlayers(profile.getPlayers(), playerPlaceholders, serverPlaceholders)
.toArray(new ServerPing.PlayerInfo[0]);

if(playerInfos.length > 0)
@@ -137,6 +125,7 @@ public void onProxyPing(ProxyPingEvent event){
try{
return Favicon.create(image);
}catch(Exception ex){
plugin.getPluginLogger().warn("Unable to create Favicon. %s", ex.getMessage());
return null;
}
});
Original file line number Diff line number Diff line change
@@ -27,27 +27,24 @@

import ch.andre601.advancedserverlist.core.commands.CommandHandler;
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.interfaces.core.PluginCore;
import ch.andre601.advancedserverlist.core.profiles.players.PlayerHandler;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Path;
import java.util.*;

public class AdvancedServerList{

private final PluginCore plugin;
private final PluginCore<?> plugin;
private final FileHandler fileHandler;
private final CommandHandler commandHandler;
private final PlayerHandler playerHandler;

private String version;

public AdvancedServerList(PluginCore plugin){
public AdvancedServerList(PluginCore<?> plugin){
this.plugin = plugin;
this.fileHandler = new FileHandler(this);
this.commandHandler = new CommandHandler(this);
@@ -56,41 +53,8 @@ public AdvancedServerList(PluginCore plugin){
load();
}

public static <T> List<T> getPlayers(Class<T> clazz, String text){
try{
String[] lines = text.split("\n");

final List<T> players = new ArrayList<>(lines.length);
final Constructor<T> constructor = clazz.getDeclaredConstructor(String.class, UUID.class);

constructor.setAccessible(true);

for(String line : lines){
players.add(constructor.newInstance(line, UUID.randomUUID()));
}

return players;
}catch(NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex){
return Collections.emptyList();
}
}

public void disable(){
getPluginLogger().info("Saving cache.data file...");
getPlayerHandler().save();
getPluginLogger().info("AdvancedServerList disabled!");
}

public void clearFaviconCache(){
plugin.clearFaviconCache();
}

public PluginLogger getPluginLogger(){
return plugin.getPluginLogger();
}

public Path getPath(){
return plugin.getPath();
public PluginCore<?> getPlugin(){
return plugin;
}

public FileHandler getFileHandler(){
@@ -109,58 +73,69 @@ public String getVersion(){
return version;
}

public void disable(){
getPlugin().getPluginLogger().info("Saving cache.data file...");
getPlayerHandler().save();
getPlugin().getPluginLogger().info("AdvancedServerList disabled!");
}

public void clearFaviconCache(){
plugin.clearFaviconCache();
}

private void load(){
printBanner();
resolveVersion();
getPluginLogger().info("Starting AdvancedServerList v%s...", version);
getPluginLogger().info("Platform: " + plugin.getPlatformName() + " " + plugin.getPlatformVersion());

getPlugin().getPluginLogger().info("Starting AdvancedServerList v%s...", version);

getPlugin().getPluginLogger().info("Platform: " + plugin.getPlatformName() + " " + plugin.getPlatformVersion());

if(getFileHandler().loadConfig()){
getPluginLogger().info("Successfully loaded config.yml!");
getPlugin().getPluginLogger().info("Successfully loaded config.yml!");
}else{
getPluginLogger().warn("Unable to load config.yml! Check previous lines for errors.");
getPlugin().getPluginLogger().warn("Unable to load config.yml! Check previous lines for errors.");
return;
}

if(getFileHandler().loadProfiles()){
getPluginLogger().info("Successfully loaded " + getFileHandler().getProfiles().size() + " profiles!");
getPlugin().getPluginLogger().info("Successfully loaded " + getFileHandler().getProfiles().size() + " profiles!");
}else{
getPluginLogger().warn("Unable to load profiles! Check previous lines for errors.");
getPlugin().getPluginLogger().warn("Unable to load profiles! Check previous lines for errors.");
return;
}

if(!getPath().resolve("favicons").toFile().exists() && getPath().resolve("favicons").toFile().mkdirs())
getPluginLogger().info("Successfully created favicons folder.");

getPluginLogger().info("Loading Commands...");
Path folder = getPlugin().getFolderPath().resolve("favicons");
if(!folder.toFile().exists() && folder.toFile().mkdirs())
getPlugin().getPluginLogger().info("Successfully created favicons folder.");

getPlugin().getPluginLogger().info("Loading Commands...");
plugin.loadCommands();
getPluginLogger().info("Commands loaded!");
getPluginLogger().info("Loading events...");
getPlugin().getPluginLogger().info("Commands loaded!");

getPlugin().getPluginLogger().info("Loading events...");
plugin.loadEvents();
getPluginLogger().info("Events loaded!");
getPluginLogger().info("Loading cache.data...");
getPlugin().getPluginLogger().info("Events loaded!");

getPlugin().getPluginLogger().info("Loading cache.data...");
getPlayerHandler().load();
getPluginLogger().info("Loading bStats metrics. Disable it in the global config under /plugins/bstats/");

getPlugin().getPluginLogger().info("Loading bStats metrics. Disable it in the global config under /plugins/bstats/");
plugin.loadMetrics();
getPluginLogger().info("Metrics loaded!");
getPluginLogger().info("AdvancedServerList is ready!");
getPlugin().getPluginLogger().info("Metrics loaded!");

getPlugin().getPluginLogger().info("AdvancedServerList is ready!");
}

private void printBanner(){
getPluginLogger().info("");
getPluginLogger().info(" _____ _");
getPluginLogger().info(" /\\ / ____| |");
getPluginLogger().info(" / \\ | (___ | |");
getPluginLogger().info(" / /\\ \\ \\___ \\| |");
getPluginLogger().info(" / ____ \\ ____) | |____");
getPluginLogger().info("/_/ \\_\\_____/|______|");
getPluginLogger().info("");
getPlugin().getPluginLogger().info("");
getPlugin().getPluginLogger().info(" _____ _");
getPlugin().getPluginLogger().info(" /\\ / ____| |");
getPlugin().getPluginLogger().info(" / \\ | (___ | |");
getPlugin().getPluginLogger().info(" / /\\ \\ \\___ \\| |");
getPlugin().getPluginLogger().info(" / ____ \\ ____) | |____");
getPlugin().getPluginLogger().info("/_/ \\_\\_____/|______|");
getPlugin().getPluginLogger().info("");
}

private void resolveVersion(){
Original file line number Diff line number Diff line change
@@ -52,12 +52,12 @@ public class FileHandler{

private ConfigurationNode node = null;

public FileHandler(AdvancedServerList plugin){
this.plugin = plugin;
this.logger = plugin.getPluginLogger();
public FileHandler(AdvancedServerList core){
this.plugin = core;
this.logger = core.getPlugin().getPluginLogger();

this.config = plugin.getPath().resolve("config.yml");
this.profilesFolder = plugin.getPath().resolve("profiles");
this.config = core.getPlugin().getFolderPath().resolve("config.yml");
this.profilesFolder = core.getPlugin().getFolderPath().resolve("profiles");
}

public List<ServerListProfile> getProfiles(){
Original file line number Diff line number Diff line change
@@ -23,14 +23,15 @@
*
*/

package ch.andre601.advancedserverlist.core.interfaces;
package ch.andre601.advancedserverlist.core.interfaces.core;

import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;

import java.nio.file.Path;

public interface PluginCore<T>{
public interface PluginCore<F>{

void loadCommands();

@@ -42,11 +43,11 @@ public interface PluginCore<T>{

AdvancedServerList getCore();

Path getPath();
Path getFolderPath();

PluginLogger getPluginLogger();

FaviconHandler<T> getFaviconHandler();
FaviconHandler<F> getFaviconHandler();

String getPlatformName();

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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.interfaces.core;

import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.Placeholders;

import java.util.List;

public interface ProxyCore<F, P> extends PluginCore<F>{
List<P> createPlayers(List<String> lines, Placeholders... placeholders);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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.interfaces.core;

import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.Placeholders;

import java.util.List;

public interface ServerCore<F, P, P2> extends PluginCore<F>{
List<P> createPlayers(List<String> lines, P2 player, Placeholders... placeholders);
}
Original file line number Diff line number Diff line change
@@ -45,18 +45,20 @@ public class ServerListProfile{
private final String playerCount;
private final String favicon;
private final boolean hidePlayers;
private final int xMore;
private final boolean extraPlayersEnabled;
private final int extraPlayers;

public ServerListProfile(ConfigurationNode node, PluginLogger logger){
this.priority = node.node("priority").getInt();
this.expressions = createExpressions(getList(node, "conditions", false), logger);
this.expressions = createExpressions(getList(node, false, "conditions"), logger);

this.motd = getList(node, "motd", true);
this.players = getList(node, "players", false);
this.playerCount = node.node("playerCount").getString("");
this.motd = getList(node, true, "motd");
this.players = getList(node, false, "playerCount", "hover");
this.playerCount = node.node("playerCount", "text").getString("");
this.favicon = node.node("favicon").getString("");
this.hidePlayers = node.node("hidePlayers").getBoolean();
this.xMore = node.node("xMore").getInt(-1);
this.hidePlayers = node.node("playerCount", "hidePlayers").getBoolean();
this.extraPlayersEnabled = node.node("playerCount", "extraPlayers", "enabled").getBoolean();
this.extraPlayers = node.node("playerCount", "extraPlayers", "amount").getInt();
}

public int getPriority(){
@@ -83,8 +85,12 @@ public boolean shouldHidePlayers(){
return hidePlayers;
}

public int getXMore(){
return xMore;
public boolean isExtraPlayersEnabled(){
return extraPlayersEnabled;
}

public int getExtraPlayers(){
return extraPlayers;
}

public boolean evalConditions(Map<String, Object> replacements){
@@ -119,10 +125,10 @@ private List<Expression> createExpressions(List<String> list, PluginLogger logge
return expressions;
}

private List<String> getList(ConfigurationNode node, String key, boolean trim){
private List<String> getList(ConfigurationNode node, boolean trim, Object... path){
List<String> list;
try{
list = node.node(key).getList(String.class);
list = node.node(path).getList(String.class);
}catch(SerializationException ex){
return Collections.emptyList();
}
Original file line number Diff line number Diff line change
@@ -42,9 +42,9 @@
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

public class FaviconHandler<T>{
public class FaviconHandler<F>{

private final Cache<String, T> favicons = Caffeine.newBuilder()
private final Cache<String, F> favicons = Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();

@@ -54,7 +54,7 @@ public FaviconHandler(AdvancedServerList core){
this.core = core;
}

public T getFavicon(String input, Function<BufferedImage, T> function){
public F getFavicon(String input, Function<BufferedImage, F> function){
return favicons.get(input, k -> {
BufferedImage image = resolveImage(core, input);
if(image == null)
@@ -75,9 +75,9 @@ private BufferedImage resolveImage(AdvancedServerList core, String input){
stream = getFromUrl(core, input);
}else
if(input.toLowerCase(Locale.ROOT).endsWith(".png")){
File folder = core.getPath().resolve("favicons").toFile();
File folder = core.getPlugin().getFolderPath().resolve("favicons").toFile();
if(!folder.exists()){
core.getPluginLogger().warn("Cannot get Favicon %s from favicons folder. Folder doesn't exist!", input);
core.getPlugin().getPluginLogger().warn("Cannot get Favicon %s from favicons folder. Folder doesn't exist!", input);
return null;
}

@@ -86,23 +86,23 @@ private BufferedImage resolveImage(AdvancedServerList core, String input){
try{
stream = new FileInputStream(file);
}catch(IOException ex){
core.getPluginLogger().warn("Cannot create Favicon from File %s.", input);
core.getPluginLogger().warn("Cause: %s", ex.getMessage());
core.getPlugin().getPluginLogger().warn("Cannot create Favicon from File %s.", input);
core.getPlugin().getPluginLogger().warn("Cause: %s", ex.getMessage());
return null;
}
}else{
stream = getFromUrl(core, "https://mc-heads.net/avatar/" + input + "/64");
}

if(stream == null){
core.getPluginLogger().warn("Cannot create Favicon. InputStream was null.");
core.getPlugin().getPluginLogger().warn("Cannot create Favicon. InputStream was null.");
return null;
}

try{
BufferedImage original = ImageIO.read(stream);
if(original == null){
core.getPluginLogger().warn("Cannot create Favicon. Unable to create BufferedImage.");
core.getPlugin().getPluginLogger().warn("Cannot create Favicon. Unable to create BufferedImage.");
return null;
}

@@ -115,8 +115,8 @@ private BufferedImage resolveImage(AdvancedServerList core, String input){

return favicon;
}catch(IOException ex){
core.getPluginLogger().warn("Unable to create Favicon. Encountered IOException during creation.");
core.getPluginLogger().warn("Cause: %s", ex.getMessage());
core.getPlugin().getPluginLogger().warn("Unable to create Favicon. Encountered IOException during creation.");
core.getPlugin().getPluginLogger().warn("Cause: %s", ex.getMessage());
return null;
}
}
@@ -130,8 +130,8 @@ private InputStream getFromUrl(AdvancedServerList core, String url){

return connection.getInputStream();
}catch(IOException ex){
core.getPluginLogger().warn("Error while connecting to %s for Favicon creation.", url);
core.getPluginLogger().warn("Cause: %s", ex.getMessage());
core.getPlugin().getPluginLogger().warn("Error while connecting to %s for Favicon creation.", url);
core.getPlugin().getPluginLogger().warn("Cause: %s", ex.getMessage());
return null;
}
}
Original file line number Diff line number Diff line change
@@ -46,24 +46,24 @@ public class PlayerHandler{

public PlayerHandler(AdvancedServerList core){
this.core = core;
this.cache = core.getPath().resolve("cache.data");
this.cache = core.getPlugin().getFolderPath().resolve("cache.data");
}

public void load(){
if(!cache.toFile().exists()){
core.getPluginLogger().info("No cache.data present. Skipping...");
core.getPlugin().getPluginLogger().info("No cache.data present. Skipping...");
return;
}
List<String> lines;
try{
lines = Files.readAllLines(cache);
}catch(IOException ex){
core.getPluginLogger().warn("Encountered IOException while trying to read cache.data", ex);
core.getPlugin().getPluginLogger().warn("Encountered IOException while trying to read cache.data", ex);
return;
}

if(lines.isEmpty()){
core.getPluginLogger().info("cache.data is empty. Skipping...");
core.getPlugin().getPluginLogger().info("cache.data is empty. Skipping...");
return;
}

@@ -78,12 +78,12 @@ public void load(){
players.add(parts[0], parts[1]);
}

core.getPluginLogger().info("Loaded " + players.size() + " players into cache!");
core.getPlugin().getPluginLogger().info("Loaded " + players.size() + " players into cache!");
}

public void save(){
if(players.isEmpty()){
core.getPluginLogger().info("No data to save. Skipping...");
core.getPlugin().getPluginLogger().info("No data to save. Skipping...");
return;
}

@@ -98,9 +98,9 @@ public void save(){
writer.write(joiner.toString());
writer.close();

core.getPluginLogger().info("Successfully saved cache.data file.");
core.getPlugin().getPluginLogger().info("Successfully saved cache.data file.");
}catch(IOException ex){
core.getPluginLogger().warn("Cannot save player data to cache.data file!", ex);
core.getPlugin().getPluginLogger().warn("Cannot save player data to cache.data file!", ex);
}
}

119 changes: 68 additions & 51 deletions core/src/main/resources/profiles/default.yml
Original file line number Diff line number Diff line change
@@ -28,62 +28,79 @@ motd:
- '<grey>Line 2'

#
# Sets if players should be hidden.
# When set to true will the player count be replaced with ???
# Additionally will the options 'players' and 'playerCount' be ignored by the plugin.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#hideplayers
#
hidePlayers: false

#
# Set the hover text that usually displays online players.
# Only normal colours such as <grey> or <aqua> are supported.
#
# Remove this option or set it to players: [] to not alter the hover text.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#players
#
players:
- '<grey>Line 1'
- '<grey>Line 2'
- '<grey>Line 3'

#
# Set the text that usually shows online players and max that can join.
# Only normal colours such as <grey> or <aqua> are supported.
#
# Remove this option or set it to playerCount: '' to not alter the player count text.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#playercount
#
playerCount: '<grey>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 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.
#
# 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.
# Allows you to set a favicon for this server list profile.
# You can use one of three possible options:
# 1. A file name matching a PNG file located in the 'favicons' folder (Needs to end with .png)
# 2. A URL pointing to a valid Image
# 3. ${player name} to display the player's head as favicon. Uses https://mc-heads.net for this
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#favicon
#
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
#
# Note that this will also manipulate the ${server playersMax} placeholder when used
# in motd, players or playerCount.
# Contains various options to manipulate the player count (Text displaying online and max players allowed).
#
# Remove this option or set it to -1 to not alter the max player count.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#xmore
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#playercount
#
xMore: -1
playerCount:
#
# Whether AdvancedServerList should hide the player count or not.
# When set to true will the player count display '???' instead of <online>/<max>
#
# Note that when this is enabled will all other options, except for 'extraPlayers', be ignored.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#hideplayers
#
hidePlayers: false
#
# Sets the text displayed when hovering over the player count.
# You can set this to 'hover: []' or remove the option to not modify the hover text.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#hover
#
hover:
- '<grey>Line 1'
- '<grey>Line 2'
- '<grey>Line 3'
#
# Modifies the text that usually displays '<online>/<max>'
#
# Note that when used, the Ping icon will display as 'outdated server'/'outdated client'.
# This is nothing that can be fixed or avoided and would require changes from Mojang.
#
# You can set this to 'count: ''' or remove the option to not modify the player count text.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#text
#
text: ''
#
# Allows you to modify the max player count displayed.
# Note that this will affect ${server playersMax} (Except for when used in conditions).
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#extraPlayers
#
extraPlayers:
#
# Enables/Disables the extraPlayers option.
# When enabled will the total amount of players allowed be modified to the current
# number of online players + 'amount'
#
# Defaults to false when not present.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#enabled
#
enabled: false
#
# Sets how much should be added/subtracted from the current number of online players
# to then use for the max player count.
#
# Example:
# When set to 1 while 10 players are online would the player count - if unmodified -
# display '10/11'. When -1 is used will it display '10/9'
#
# Defaults to 0 when not present.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#amount
#
amount: 0
Original file line number Diff line number Diff line change
@@ -26,22 +26,31 @@
package ch.andre601.advancedserverlist.paper;

import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginCore;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.interfaces.core.ServerCore;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.Placeholders;
import ch.andre601.advancedserverlist.paper.commands.CmdAdvancedServerList;
import ch.andre601.advancedserverlist.paper.events.JoinEvent;
import ch.andre601.advancedserverlist.paper.events.PingEvent;
import ch.andre601.advancedserverlist.paper.logging.PaperLogger;
import com.destroystokyo.paper.profile.PlayerProfile;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.CachedServerIcon;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class PaperCore extends JavaPlugin implements PluginCore<CachedServerIcon>{
public class PaperCore extends JavaPlugin implements ServerCore<CachedServerIcon, PlayerProfile, OfflinePlayer>{

private final PluginLogger logger = new PaperLogger(getLogger());

@@ -117,7 +126,7 @@ public AdvancedServerList getCore(){
}

@Override
public Path getPath(){
public Path getFolderPath(){
return getDataFolder().toPath();
}

@@ -147,4 +156,26 @@ public String getPlatformVersion(){
private void enable(){
this.core = new AdvancedServerList(this);
}

@Override
public List<PlayerProfile> createPlayers(List<String> lines, OfflinePlayer player, Placeholders... placeholders){
List<PlayerProfile> players = new ArrayList<>(lines.size());

for(String line : lines){
String parsed = ComponentParser.text(line)
.replacements(placeholders[0])
.replacements(placeholders[1])
.modifyText(text -> {
if(getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player, text);

return text;
})
.toString();

players.add(Bukkit.createProfile(UUID.randomUUID(), parsed));
}

return players;
}
}
Original file line number Diff line number Diff line change
@@ -29,13 +29,11 @@
import ch.andre601.advancedserverlist.core.profiles.ProfileManager;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
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;
import ch.andre601.advancedserverlist.paper.PaperCore;
import ch.andre601.advancedserverlist.paper.PaperPlayer;
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
import com.destroystokyo.paper.profile.PlayerProfile;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
@@ -44,9 +42,6 @@
import org.bukkit.util.CachedServerIcon;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class PingEvent implements Listener{

@@ -77,9 +72,9 @@ public void onServerPing(PaperServerListPingEvent event){

if(profile == null)
return;
if(profile.getXMore() >= 0){
max = online + profile.getXMore();

if(profile.isExtraPlayersEnabled()){
max = online + profile.getExtraPlayers();
event.setMaxPlayers(max);
}

@@ -100,10 +95,9 @@ public void onServerPing(PaperServerListPingEvent event){

if(profile.shouldHidePlayers()){
event.setHidePlayers(true);
return;
}

if(!profile.getPlayerCount().isEmpty()){
if(!profile.getPlayerCount().isEmpty() && !profile.shouldHidePlayers()){
event.setVersion(ComponentParser.text(profile.getPlayerCount())
.replacements(playerPlaceholders)
.replacements(serverPlaceholders)
@@ -117,10 +111,12 @@ public void onServerPing(PaperServerListPingEvent event){
event.setProtocolVersion(-1);
}

if(!profile.getPlayers().isEmpty()){
if(!profile.getPlayers().isEmpty() && !profile.shouldHidePlayers()){
event.getPlayerSample().clear();

event.getPlayerSample().addAll(getPlayers(profile.getPlayers(), playerPlaceholders, serverPlaceholders, player.getPlayer()));
event.getPlayerSample().addAll(
plugin.createPlayers(profile.getPlayers(), player.getPlayer(), playerPlaceholders, serverPlaceholders)
);
}

if(!profile.getFavicon().isEmpty()){
@@ -143,22 +139,6 @@ public void onServerPing(PaperServerListPingEvent event){
}
}

private List<PlayerProfile> getPlayers(List<String> lines, Placeholders playerPlaceholders, Placeholders serverPlaceholders, OfflinePlayer player){
List<PlayerProfile> players = new ArrayList<>();
lines.forEach(line -> players.add(Bukkit.createProfile(UUID.randomUUID(), ComponentParser.text(line)
.replacements(playerPlaceholders)
.replacements(serverPlaceholders)
.modifyText(text -> {
if(plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player, text);

return text;
})
.toString())));

return players;
}

private PaperPlayer resolvePlayer(InetSocketAddress address, int protocol){
String playerName = plugin.getCore().getPlayerHandler().getPlayerByIp(address.getHostString());
OfflinePlayer player = Bukkit.getPlayerExact(playerName);
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<plugin.version>1.5.3</plugin.version>
<plugin.version>1.6.0</plugin.version>
<plugin.description>Create multiple Server lists based on conditions.</plugin.description>

<maven.compiler.source>16</maven.compiler.source>
Original file line number Diff line number Diff line change
@@ -26,21 +26,29 @@
package ch.andre601.advancedserverlist.spigot;

import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginCore;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.interfaces.core.ServerCore;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.Placeholders;
import ch.andre601.advancedserverlist.spigot.commands.CmdAdvancedServerList;
import ch.andre601.advancedserverlist.spigot.events.LoadEvent;
import ch.andre601.advancedserverlist.spigot.logging.SpigotLogger;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedServerPing;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.java.JavaPlugin;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class SpigotCore extends JavaPlugin implements PluginCore<WrappedServerPing.CompressedImage>{
public class SpigotCore extends JavaPlugin implements ServerCore<WrappedServerPing.CompressedImage, WrappedGameProfile, OfflinePlayer>{

private final PluginLogger logger = new SpigotLogger(getLogger());

@@ -103,7 +111,7 @@ public AdvancedServerList getCore(){
}

@Override
public Path getPath(){
public Path getFolderPath(){
return getDataFolder().toPath();
}

@@ -130,11 +138,33 @@ public String getPlatformVersion(){
return getServer().getVersion();
}

@Override
public List<WrappedGameProfile> createPlayers(List<String> lines, OfflinePlayer player, Placeholders... placeholders){
List<WrappedGameProfile> players = new ArrayList<>(lines.size());

for(String line : lines){
String parsed = ComponentParser.text(line)
.replacements(placeholders[0])
.replacements(placeholders[1])
.modifyText(text -> {
if(getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player, text);

return text;
})
.toString();

players.add(new WrappedGameProfile(UUID.randomUUID(), parsed));
}

return players;
}

private void printPaperInfo(){
getPluginLogger().warn("======================================================================================");
getPluginLogger().warn("You are using the Spigot version of AdvancedServerList on a PaperMC server.");
getPluginLogger().warn("It is recommended to use the dedicated PaperMC version, to benefit from the");
getPluginLogger().warn("Following improvements:");
getPluginLogger().warn("You are using the Spigot version of AdvancedServerList on a Paper server.");
getPluginLogger().warn("It is recommended to use the dedicated Paper version, to benefit from the");
getPluginLogger().warn("following improvements:");
getPluginLogger().warn(" - No need to download external libraries already provided by PaperMC.");
getPluginLogger().warn(" - No dependency on ProtocolLib thanks to provided Events.");
getPluginLogger().warn("");
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ public void onServerLoad(ServerLoadEvent event){
PluginManager manager = plugin.getServer().getPluginManager();

if(!manager.isPluginEnabled("ProtocolLib")){
plugin.getCore().getPluginLogger().warn("ProtocolLib not found! AdvancedServerList requires it to work on Spigot!");
plugin.getCore().getPlugin().getPluginLogger().warn("ProtocolLib not found! AdvancedServerList requires it to work on Spigot!");

manager.disablePlugin(plugin);
return;
Original file line number Diff line number Diff line change
@@ -39,7 +39,6 @@
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.wrappers.AdventureComponentConverter;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedServerPing;
import me.clip.placeholderapi.PlaceholderAPI;
import net.kyori.adventure.text.Component;
@@ -101,9 +100,9 @@ public void onPacketSending(PacketEvent event){

if(profile == null)
return;
if(profile.getXMore() >= 0){
max = online + profile.getXMore();

if(profile.isExtraPlayersEnabled()){
max = online + profile.getExtraPlayers();
ping.setPlayersMaximum(max);
}

@@ -125,10 +124,9 @@ public void onPacketSending(PacketEvent event){

if(profile.shouldHidePlayers()){
ping.setPlayersVisible(false);
return;
}

if(!profile.getPlayerCount().isEmpty()){
if(!profile.getPlayerCount().isEmpty() && !profile.shouldHidePlayers()){
ping.setVersionName(ComponentParser.text(profile.getPlayerCount())
.replacements(playerPlaceholders)
.replacements(serverPlaceholders)
@@ -143,19 +141,10 @@ public void onPacketSending(PacketEvent event){
ping.setVersionProtocol(-1);
}

if(!profile.getPlayers().isEmpty()){
ping.setPlayers(getFakePlayers(
ComponentParser.list(profile.getPlayers())
.replacements(playerPlaceholders)
.replacements(serverPlaceholders)
.modifyText(text -> {
if(plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player.getPlayer(), text);

return text;
})
.toString()
));
if(!profile.getPlayers().isEmpty() && !profile.shouldHidePlayers()){
ping.setPlayers(
spigotPlugin.createPlayers(profile.getPlayers(), player.getPlayer(), playerPlaceholders, serverPlaceholders)
);
}

if(!profile.getFavicon().isEmpty()){
@@ -180,17 +169,6 @@ public void onPacketSending(PacketEvent event){
});
}

private List<WrappedGameProfile> getFakePlayers(String text){
String[] lines = text.split("\n");
List<WrappedGameProfile> profiles = new ArrayList<>();

for(String line : lines){
profiles.add(new WrappedGameProfile(UUID.randomUUID(), line));
}

return profiles;
}

private SpigotPlayer resolvePlayer(InetSocketAddress address, int protocol){
String playerName = plugin.getCore().getPlayerHandler().getPlayerByIp(address.getHostString());
OfflinePlayer player = Bukkit.getPlayerExact(playerName);
Original file line number Diff line number Diff line change
@@ -26,9 +26,11 @@
package ch.andre601.advancedserverlist.velocity;

import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginCore;
import ch.andre601.advancedserverlist.core.interfaces.core.ProxyCore;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.Placeholders;
import ch.andre601.advancedserverlist.velocity.commands.CmdAdvancedServerList;
import ch.andre601.advancedserverlist.velocity.events.JoinEvent;
import ch.andre601.advancedserverlist.velocity.events.PingEvent;
@@ -40,14 +42,18 @@
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.ServerPing;
import com.velocitypowered.api.util.Favicon;
import org.bstats.charts.SimplePie;
import org.bstats.velocity.Metrics;
import org.slf4j.LoggerFactory;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class VelocityCore implements PluginCore<Favicon>{
public class VelocityCore implements ProxyCore<Favicon, ServerPing.SamplePlayer>{

private final PluginLogger logger;
private final ProxyServer proxy;
@@ -110,7 +116,7 @@ public AdvancedServerList getCore(){
}

@Override
public Path getPath(){
public Path getFolderPath(){
return path;
}

@@ -127,6 +133,22 @@ public FaviconHandler<Favicon> getFaviconHandler(){
return faviconHandler;
}

@Override
public List<ServerPing.SamplePlayer> createPlayers(List<String> lines, Placeholders... placeholders){
List<ServerPing.SamplePlayer> players = new ArrayList<>(lines.size());

for(String line : lines){
String parsed = ComponentParser.text(line)
.replacements(placeholders[0])
.replacements(placeholders[1])
.toString();

players.add(new ServerPing.SamplePlayer(parsed, UUID.randomUUID()));
}

return players;
}

@Override
public String getPlatformName(){
return getProxy().getVersion().getName();
Original file line number Diff line number Diff line change
@@ -25,7 +25,6 @@

package ch.andre601.advancedserverlist.velocity.events;

import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.ProfileManager;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
@@ -76,9 +75,9 @@ public void onProxyPing(ProxyPingEvent event){

if(profile == null)
return;
if(profile.getXMore() >= 0){
max = online + profile.getXMore();

if(profile.isExtraPlayersEnabled()){
max = online + profile.getExtraPlayers();
builder.maximumPlayers(max);
}

@@ -95,12 +94,9 @@ public void onProxyPing(ProxyPingEvent event){

if(profile.shouldHidePlayers()){
builder.nullPlayers();

event.setPing(builder.build());
return;
}

if(!profile.getPlayerCount().isEmpty()){
if(!profile.getPlayerCount().isEmpty() && !profile.shouldHidePlayers()){
builder.version(new ServerPing.Version(
-1,
ComponentParser.text(profile.getPlayerCount())
@@ -110,13 +106,8 @@ public void onProxyPing(ProxyPingEvent event){
));
}

if(!profile.getPlayers().isEmpty()){
String players = ComponentParser.list(profile.getPlayers())
.replacements(playerPlaceholders)
.replacements(serverPlaceholders)
.toString();

ServerPing.SamplePlayer[] playerSamples = AdvancedServerList.getPlayers(ServerPing.SamplePlayer.class, players)
if(!profile.getPlayers().isEmpty() && !profile.shouldHidePlayers()){
ServerPing.SamplePlayer[] playerSamples = plugin.createPlayers(profile.getPlayers(), playerPlaceholders, serverPlaceholders)
.toArray(new ServerPing.SamplePlayer[0]);

if(playerSamples.length > 0)

0 comments on commit 99509b5

Please sign in to comment.