Skip to content

Commit

Permalink
Add MOTD system for bungee, fixed up some spigot MOTD features
Browse files Browse the repository at this point in the history
  • Loading branch information
CyberedCake committed Jul 6, 2022
1 parent 7348f4f commit b92e7b1
Show file tree
Hide file tree
Showing 19 changed files with 1,144 additions and 20 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ static int getTargetJavaVersion() { return 17; }

// version info
def major = '3'
def minor = '4'
def minor = '5'



Expand Down
12 changes: 11 additions & 1 deletion bungee/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ repositories {
name = 'sonatype'
url = 'https://oss.sonatype.org/content/groups/public/'
}
maven {
name = 'exceptionflug'
url = 'https://mvn.exceptionflug.de/repository/exceptionflug-public/'
}
}

dependencies {
Expand All @@ -13,13 +17,19 @@ dependencies {
// bungeecord and plugin hooks
compileOnly 'net.md-5:bungeecord-api:1.19-R0.1-SNAPSHOT'
compileOnly 'net.luckperms:api:5.4'
compileOnly 'dev.simplix:protocolize-api:2.2.1'

// utils
implementation 'org.reflections:reflections:0.10.2'
implementation 'org.apache.commons:commons-lang3:3.12.0'
}

shadowJar {
relocate 'javassist', 'net.cybercake.cyberapi.dependencies.javassist'
relocate 'javax.annotation', 'net.cybercake.cyberapi.dependencies.annotations'
relocate 'org.reflections', 'net.cybercake.cyberapi.dependencies.reflections'
relocate 'com.google.gson', 'net.cybercake.cyberapi.dependencies.google.gson'
relocate 'org.apache.commons.lang3', 'net.cybercake.cyberapi.dependencies.apache.commons'
relocate 'org.intellij.lang.annotations', 'net.cybercake.cyberapi.dependencies.annotations.intellij'
relocate 'org.jetbrains.annotations', 'net.cybercake.cyberapi.dependencies.annotations.jetbrains'
relocate 'javax.annotation', 'net.cybercake.cyberapi.dependencies.annotations.javax'
}
39 changes: 38 additions & 1 deletion bungee/src/main/java/net/cybercake/cyberapi/bungee/CyberAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import net.cybercake.cyberapi.bungee.player.BungeeTitle;
import net.cybercake.cyberapi.bungee.player.CyberPlayer;
import net.cybercake.cyberapi.bungee.server.commands.CommandManager;
import net.cybercake.cyberapi.bungee.server.serverlist.ServerListInfo;
import net.cybercake.cyberapi.bungee.server.serverlist.ServerListInfoListener;
import net.cybercake.cyberapi.common.CommonManager;
import net.cybercake.cyberapi.common.basic.Time;
import net.cybercake.cyberapi.common.builders.player.UserHeadSettings;
Expand Down Expand Up @@ -94,6 +96,9 @@ protected CyberAPI startCyberAPI(@Nullable Settings settings) {
// load all support variables, so they're not 'null'
// automatically sets the variable in these methods, so returned values are not used
getLuckPermsSupport();
getProtocolizeSupport();

registerListener(new ServerListInfoListener());

reflectionsConsoleFilter(); // deprecated because I don't want anyone else using it
CommandManager.commandManager().init(settings.getCommandsPath());
Expand Down Expand Up @@ -123,6 +128,7 @@ protected CyberAPI startCyberAPI(@Nullable Settings settings) {
private FeatureSupport miniMessageSupport = null;
private FeatureSupport luckPermsSupport = null;
private FeatureSupport protocolLibSupport = null;
private FeatureSupport protocolizeSupport = null;

/**
* Gets the settings CyberAPI is using to determine the developer's preferences
Expand Down Expand Up @@ -332,6 +338,28 @@ public FeatureSupport getProtocolLibSupport() {
return this.protocolLibSupport;
}

/**
* Gets the Protocolize support. This method assumes the best of the developer as if they have marked Protocolize support as {@link FeatureSupport#SUPPORTED}, it will allow use of it.
* @return the {@link FeatureSupport} enum of the value
* @since 3.5
*/
public FeatureSupport getProtocolizeSupport() {
if(protocolizeSupport == null) {
protocolizeSupport = settings.supportsProtocolize();

if(protocolizeSupport.equals(FeatureSupport.AUTO)) {
try {
Class.forName("dev.simplix.protocolize");
this.protocolizeSupport = FeatureSupport.SUPPORTED;
} catch (Exception exception) {
this.protocolizeSupport = FeatureSupport.UNSUPPORTED;
}
log.verbose("Protocolize support was set to auto, detected: " + protocolizeSupport.name());
}
}
return this.protocolizeSupport;
}

/**
* Sends a title to a player with specified settings
* @param player the player to send the title to
Expand Down Expand Up @@ -391,6 +419,15 @@ public void performCommand(CommandSender sender, String command) {
ProxyServer.getInstance().getPluginManager().dispatchCommand(sender, command.substring(1));
}

/**
* Returns an instance of {@link ServerListInfo}, which allows you to change things like the MOTD, player count, icon, etc.
* @return the {@link ServerListInfo} instance
* @since 3.5
*/
public ServerListInfo getServerListInfo() {
return ServerListInfo.serverListInfo();
}

/**
* Returns a list of online players in forms of {@link ProxiedPlayer} objects
* @return the online players in {@link ProxiedPlayer} objects
Expand Down Expand Up @@ -465,7 +502,7 @@ protected APILog() {}
public void verbose(String message) { verbose(StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass().getCanonicalName(), message); }
public void verbose(String canonical, String message) {
if(getSettings().isVerbose())
log(Level.INFO, ChatColor.DARK_GRAY + " [" + ChatColor.GRAY + "VERBOSE/" + canonical + ChatColor.DARK_GRAY + " ] " + ChatColor.RESET + message);
log(Level.INFO, ChatColor.DARK_GRAY + " [" + ChatColor.GRAY + "VERBOSE/" + canonical + ChatColor.DARK_GRAY + "] " + ChatColor.RESET + message);
}

public void verboseException(Throwable throwable) {
Expand Down
10 changes: 10 additions & 0 deletions bungee/src/main/java/net/cybercake/cyberapi/bungee/Validators.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,14 @@ public static void validateLuckPermsHook() {
if(!CyberAPI.getInstance().getLuckPermsSupport().equals(FeatureSupport.SUPPORTED)) throw serverHook("LuckPerms");
}

/**
* --{@literal >} <b>MAINLY FOR USE INSIDE CYBERAPI ONLY</b> {@literal <}--
* <br><br>
* Validates that Protocolize is supported and working
*/
public static void validateProtocolizeHook() {
validateIsNotAuto(CyberAPI.getInstance().getProtocolizeSupport());
if(!CyberAPI.getInstance().getProtocolizeSupport().equals(FeatureSupport.SUPPORTED)) throw serverHook("Protocolize");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public enum TextType {
* <br> <br>
* Used for the MOTD of the server on the server list
* <br> <br>
* <b>45 characters {@literal <}- default</b>
* <b>60 characters {@literal <}- default</b>
*/
MOTD(45);
MOTD(60);

private final int length;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package net.cybercake.cyberapi.bungee.server.serverlist;

import net.cybercake.cyberapi.bungee.server.serverlist.managers.MOTDManager;
import net.cybercake.cyberapi.bungee.server.serverlist.managers.PlayerListManager;
import net.cybercake.cyberapi.bungee.server.serverlist.managers.ProtocolManager;

public class ServerListInfo {

/**
* @deprecated Please use {@link ServerListInfo#serverListInfo()} or
*/
@SuppressWarnings({"all"})
@Deprecated
public ServerListInfo() { }

private static ServerListInfo serverListInfo = null;

/**
* Gets an instance of {@link ServerListInfo}
* @return the {@link ServerListInfo} instance
* @since 3.5
*/
public static ServerListInfo serverListInfo() {
if(serverListInfo == null) serverListInfo = new ServerListInfo();
return new ServerListInfo();
}

/**
* Gets an instance of MOTD manager, please use this method instead of instantiating {@link MOTDManager}
* <br>
* This class if for managing the MOTD, allowing you to modify it to what you please
* @return the {@link MOTDManager} instance
* @since 3.5
*/
public MOTDManager getMOTDManager() {
return MOTDManager.motdManager();
}

/**
* Gets an instance of Player List manager, please use this method instead of instantiating {@link PlayerListManager}
* <br>
* This class is for managing the player list, including player count, max player count, and players online hover
* @return the {@link PlayerListManager} instance
* @since 3.5
*/
public PlayerListManager getPlayerListManager() {
return PlayerListManager.playerListManager();
}

/**
* Gets an instance of Protocol manager, please use this method instead of instantiating {@link ProtocolManager}
* <br>
* This class is for managing the protocol number and version name, for example, if a server is outdated, it'll show "Paper 1.18" or something, and you can change that by using this class
* @return the {@link ProtocolManager} instance
* @since 3.5
*/
public ProtocolManager getProtocolManager() { return ProtocolManager.protocolManager(); }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package net.cybercake.cyberapi.bungee.server.serverlist;

import net.cybercake.cyberapi.bungee.CyberAPI;
import net.cybercake.cyberapi.bungee.chat.UChat;
import net.cybercake.cyberapi.bungee.server.serverlist.motd.MOTD;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.Favicon;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.event.ProxyPingEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;

public class ServerListInfoListener implements Listener {

private static final HashMap<File, Favicon> faviconsFromFile = new HashMap<>();
private static final HashMap<URL, Favicon> faviconsFromURL = new HashMap<>();

@EventHandler
public void onPing(ProxyPingEvent event) {
ServerPing ping = handlePing(event.getConnection(), event.getResponse());
if(ping == null) return;
event.setResponse(ping);
}

private ServerPing handlePing(PendingConnection address, ServerPing ping) {
try {
ServerListInfo info = CyberAPI.getInstance().getServerListInfo();
ServerListPingEvent serverListPingEvent =
new ServerListPingEvent(
address,
info.getProtocolManager().getVersionName(),
info.getProtocolManager().getProtocolNumber(),
info.getPlayerListManager().shouldShowPlayers(),
info.getProtocolManager().shouldAlwaysShowVersion(),
info.getMOTDManager().getRandomMOTD(),
info.getPlayerListManager().getMaxPlayers(),
info.getPlayerListManager().getOnlinePlayers(),
info.getPlayerListManager().getCustomOnlinePlayers()
);
if(serverListPingEvent.isCancelled()) return null;
ProxyServer.getInstance().getPluginManager().callEvent(serverListPingEvent);

// MOTD
MOTD motd = (serverListPingEvent.getMOTD() == null ? MOTD.builder("default_temp").build() : serverListPingEvent.getMOTD());
ping.setDescriptionComponent(UChat.bComponent(motd.getFormattedMOTD()));
try {
Favicon image = null;
switch(motd.getMOTDIconType()) {
case FILE -> {
// image = Favicon.create(Base64.getEncoder().encodeToString(Files.readAllBytes(motd.getFileIcon().toPath())));
if(motd.getFileIcon() != null && faviconsFromFile.get(motd.getFileIcon()) == null) {
CyberAPI.getInstance().getAPILogger().verbose("New icon found (type=FILE)! Caching into temporary storage...");
CyberAPI.getInstance().getAPILogger().verbose("This may take a second, and Bungee will likely provide a 'Listener took <x>ms...', just ignore it!");
image = Favicon.create(ImageIO.read(motd.getFileIcon()));
faviconsFromFile.put(motd.getFileIcon(), image);
} else if(faviconsFromFile.get(motd.getFileIcon()) != null) {
image = faviconsFromFile.get(motd.getFileIcon());
}
}
case URL -> {
if(motd.getURLIcon() != null && faviconsFromURL.get(motd.getURLIcon()) == null) {
CyberAPI.getInstance().getAPILogger().verbose("New icon found (type=URL)! Caching into temporary storage...");
CyberAPI.getInstance().getAPILogger().verbose("This may take a second, and Bungee will likely provide a 'Listener took <x>ms...', just ignore it!");
HttpURLConnection connection = (HttpURLConnection) motd.getURLIcon().openConnection();
connection.connect();
BufferedImage bufferedImage = ImageIO.read(connection.getInputStream());
connection.disconnect();
image = Favicon.create(bufferedImage);
faviconsFromURL.put(motd.getURLIcon(), image);
} else if(faviconsFromURL.get(motd.getURLIcon()) != null) {
image = faviconsFromURL.get(motd.getURLIcon());
}
}
}
if(image != null) ping.setFavicon(image);
} catch (Exception exception) {
CyberAPI.getInstance().getAPILogger().error("An exception occurred whilst creating the Favicon for the server: " + ChatColor.DARK_GRAY + exception);
CyberAPI.getInstance().getAPILogger().verboseException(exception);
}

// player count
ArrayList<ServerPing.PlayerInfo> profiles = new ArrayList<>();
for(String name : serverListPingEvent.getOnlinePlayers()) {
profiles.add(new ServerPing.PlayerInfo(name, UUID.randomUUID()));
}
ping.setPlayers(new ServerPing.Players(serverListPingEvent.getMaxPlayers(), (serverListPingEvent.isPlayerListVisible() ? serverListPingEvent.getOnlinePlayerCount() : Integer.MIN_VALUE), (serverListPingEvent.isPlayerListVisible() ? profiles.toArray(new ServerPing.PlayerInfo[]{}) : null)));

ping.setVersion(new ServerPing.Protocol(UChat.chat(serverListPingEvent.getVersionName()), (serverListPingEvent.isVersionNameAlwaysVisible() ? 0 : (serverListPingEvent.getProtocolVersion() == Integer.MIN_VALUE ? ProxyServer.getInstance().getProtocolVersion() : serverListPingEvent.getProtocolVersion()))));

return ping;
} catch (Exception exception){
CyberAPI.getInstance().getAPILogger().error("An exception occurred whilst sending server ping packet: " + ChatColor.DARK_GRAY + exception);
CyberAPI.getInstance().getAPILogger().verboseException(exception);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package net.cybercake.cyberapi.bungee.server.serverlist;

import net.cybercake.cyberapi.bungee.CyberAPI;
import net.cybercake.cyberapi.bungee.server.serverlist.motd.MOTD;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.api.plugin.Event;

import java.util.ArrayList;
import java.util.List;

/**
* The server list ping event for CyberAPI, an alternative way to change the server list. It is, however, still recommended that you use {@link CyberAPI#getServerListInfo()} instead!
*/
@SuppressWarnings({"unused"})
public class ServerListPingEvent extends Event implements Cancellable {
private boolean cancelled;

private final PendingConnection connection;

private String versionName;
private int protocolVersion;
private boolean versionNameAlwaysVisible;
private boolean playerListVisible;
private MOTD motd;
private int maxPlayers;
private int onlinePlayerCount;
private List<String> onlinePlayers;

public ServerListPingEvent(PendingConnection connection, String versionName, int protocolVersion, boolean playerListVisible, boolean versionNameAlwaysVisible, MOTD motd, int maxPlayers, int onlinePlayerCount, List<String> onlinePlayers) {
this.connection = connection;

this.versionName = versionName;
this.protocolVersion = protocolVersion;
this.playerListVisible = playerListVisible;
this.versionNameAlwaysVisible = versionNameAlwaysVisible;
this.motd = motd;
this.maxPlayers = maxPlayers;
this.onlinePlayerCount = onlinePlayerCount;
this.onlinePlayers = onlinePlayers;
this.cancelled = false;
}

public void setVersionName(String versionName) { this.versionName = versionName; }
public void setProtocolVersion(int protocolVersion) { this.protocolVersion = protocolVersion; }
public void setPlayerListVisible(boolean playerListVisible) { this.playerListVisible = playerListVisible; }
public void setVersionNameAlwaysVisible(boolean versionNameAlwaysVisible) { this.versionNameAlwaysVisible = versionNameAlwaysVisible; }
public void setMOTD(MOTD motd) { this.motd = motd; }
public void setMaxPlayers(int maxPlayers) { this.maxPlayers = maxPlayers; }
public void setOnlinePlayerCount(int onlinePlayerCount) { this.onlinePlayerCount = onlinePlayerCount; }
public void setOnlinePlayers(List<String> onlinePlayers) { this.onlinePlayers = onlinePlayers; }
public void setOnlinePlayers(String... onlinePlayers) { this.onlinePlayers = new ArrayList<>(List.of(onlinePlayers)); }

public PendingConnection getPendingConnection() { return connection; }

public String getVersionName() { return this.versionName; }
public int getProtocolVersion() { return this.protocolVersion; }
public boolean isPlayerListVisible() { return this.playerListVisible; }
public boolean isVersionNameAlwaysVisible() { return this.versionNameAlwaysVisible; }
public MOTD getMOTD() { return this.motd; }
public int getMaxPlayers() { return this.maxPlayers; }
public int getOnlinePlayerCount() { return this.onlinePlayerCount; }
public List<String> getOnlinePlayers() { return onlinePlayers; }

@Override public boolean isCancelled() { return cancelled; }
@Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; }
}
Loading

0 comments on commit b92e7b1

Please sign in to comment.