Java tutorial
/* * _____ __ _ _ _____ _ * | __|___ ___ _ _ ___ ___| | |_|___| |_| _ | |_ _ ___ * |__ | -_| _| | | -_| _| |__| |_ -| _| __| | | |_ -| * |_____|___|_| \_/|___|_| |_____|_|___|_| |__| |_|___|___| * * ServerListPlus - http://git.io/slp * > The most customizable server status ping plugin for Minecraft! * Copyright (c) 2014, Minecrell <https://github.com/Minecrell> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.minecrell.serverlistplus.bungee; import net.minecrell.serverlistplus.core.ServerListPlusCore; import net.minecrell.serverlistplus.core.ServerListPlusException; import net.minecrell.serverlistplus.core.config.PluginConf; import net.minecrell.serverlistplus.core.config.storage.InstanceStorage; import net.minecrell.serverlistplus.core.favicon.FaviconHelper; import net.minecrell.serverlistplus.core.favicon.FaviconSource; import net.minecrell.serverlistplus.core.logging.JavaServerListPlusLogger; import net.minecrell.serverlistplus.core.logging.ServerListPlusLogger; import net.minecrell.serverlistplus.core.plugin.ScheduledTask; import net.minecrell.serverlistplus.core.plugin.ServerListPlusPlugin; import net.minecrell.serverlistplus.core.plugin.ServerType; import net.minecrell.serverlistplus.core.status.ResponseFetcher; import net.minecrell.serverlistplus.core.status.StatusManager; import net.minecrell.serverlistplus.core.status.StatusRequest; import net.minecrell.serverlistplus.core.status.StatusResponse; import net.minecrell.serverlistplus.core.util.Helper; import net.minecrell.serverlistplus.core.util.Randoms; import java.awt.image.BufferedImage; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; import com.google.common.base.Optional; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilderSpec; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import net.md_5.bungee.api.AbstractReconnectHandler; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.Favicon; import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.ProxyPingEvent; import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.TabExecutor; import net.md_5.bungee.event.EventHandler; import net.minecrell.metrics.BungeeMetricsLite; import static net.minecrell.serverlistplus.core.logging.Logger.*; public class BungeePlugin extends BungeePluginBase implements ServerListPlusPlugin { private ServerListPlusCore core; private Listener connectionListener, pingListener; private BungeeMetricsLite metrics; // Favicon cache private final CacheLoader<FaviconSource, Optional<Favicon>> faviconLoader = new CacheLoader<FaviconSource, Optional<Favicon>>() { @Override public Optional<Favicon> load(FaviconSource source) throws Exception { // Try loading the favicon BufferedImage image = FaviconHelper.loadSafely(core, source); if (image == null) return Optional.absent(); // Favicon loading failed else return Optional.of(Favicon.create(image)); // Success! } }; private LoadingCache<FaviconSource, Optional<Favicon>> faviconCache; @Override public void onEnable() { try { // Load the core first this.core = new ServerListPlusCore(this); getLogger().log(INFO, "Successfully loaded!"); } catch (ServerListPlusException e) { getLogger().log(INFO, "Please fix the error before restarting the server!"); return; } catch (Exception e) { getLogger().log(ERROR, "An internal error occurred while loading the core.", e); return; } // Register commands getProxy().getPluginManager().registerCommand(this, new ServerListPlusCommand()); } @Override public void onDisable() { try { core.stop(); } catch (ServerListPlusException ignored) { } } // Commands public final class ServerListPlusCommand extends Command implements TabExecutor { private ServerListPlusCommand() { super("serverlistplus", null, "serverlist+", "serverlist", "slp", "sl+", "s++", "serverping+", "serverping", "spp", "slus"); } @Override public void execute(CommandSender sender, String[] args) { core.executeCommand(new BungeeCommandSender(sender), getName(), args); } @Override public Iterable<String> onTabComplete(CommandSender sender, String[] args) { return core.tabComplete(new BungeeCommandSender(sender), getName(), args); } } // Player tracking public final class ConnectionListener implements Listener { private ConnectionListener() { } @EventHandler public void onPlayerLogin(LoginEvent event) { handleConnection(event.getConnection()); } @EventHandler public void onPlayerLogout(PlayerDisconnectEvent event) { handleConnection(event.getPlayer().getPendingConnection()); } private void handleConnection(PendingConnection con) { core.updateClient(con.getAddress().getAddress(), con.getUniqueId(), con.getName()); } } // Status listener public final class PingListener implements Listener { private PingListener() { } @EventHandler public void onProxyPing(ProxyPingEvent event) { if (event.getResponse() == null) return; // Check if response is not empty PendingConnection con = event.getConnection(); StatusRequest request = core.createRequest(con.getAddress().getAddress()); request.setProtocolVersion(con.getVersion()); InetSocketAddress host = con.getVirtualHost(); if (host != null) { ServerInfo forcedHost = AbstractReconnectHandler.getForcedHost(con); request.setTarget(host, forcedHost != null ? forcedHost.getName() : null); } final ServerPing ping = event.getResponse(); final ServerPing.Players players = ping.getPlayers(); final ServerPing.Protocol version = ping.getVersion(); StatusResponse response = request.createResponse(core.getStatus(), // Return unknown player counts if it has been hidden new ResponseFetcher() { @Override public Integer getOnlinePlayers() { return players != null ? players.getOnline() : null; } @Override public Integer getMaxPlayers() { return players != null ? players.getMax() : null; } @Override public int getProtocolVersion() { return version != null ? version.getProtocol() : 0; } }); // Description String message = response.getDescription(); if (message != null) ping.setDescription(message); if (version != null) { // Version name message = response.getVersion(); if (message != null) version.setName(message); // Protocol version Integer protocol = response.getProtocolVersion(); if (protocol != null) version.setProtocol(protocol); } // Favicon FaviconSource favicon = response.getFavicon(); if (favicon != null) { Optional<Favicon> icon = faviconCache.getUnchecked(favicon); if (icon.isPresent()) ping.setFavicon(icon.get()); } if (players != null) { if (response.hidePlayers()) { ping.setPlayers(null); } else { // Online players Integer count = response.getOnlinePlayers(); if (count != null) players.setOnline(count); // Max players count = response.getMaxPlayers(); if (count != null) players.setMax(count); // Player hover message = response.getPlayerHover(); if (message != null) { if (response.useMultipleSamples()) { count = response.getDynamicSamples(); List<String> lines = count != null ? Helper.splitLinesCached(message, count) : Helper.splitLinesCached(message); ServerPing.PlayerInfo[] sample = new ServerPing.PlayerInfo[lines.size()]; for (int i = 0; i < sample.length; i++) sample[i] = new ServerPing.PlayerInfo(lines.get(i), StatusManager.EMPTY_UUID); players.setSample(sample); } else players.setSample(new ServerPing.PlayerInfo[] { new ServerPing.PlayerInfo(message, StatusManager.EMPTY_UUID) }); } } } } } @Override public ServerListPlusCore getCore() { return core; } @Override public ServerType getServerType() { return ServerType.BUNGEE; } @Override public String getServerImplementation() { return getProxy().getVersion() + " (MC: " + getProxy().getGameVersion() + ')'; } @Override public Integer getOnlinePlayers(String location) { ServerInfo server = getProxy().getServerInfo(location); return server != null ? server.getPlayers().size() : null; } @Override public Iterator<String> getRandomPlayers() { return getRandomPlayers(getProxy().getPlayers()); } @Override public Iterator<String> getRandomPlayers(String location) { ServerInfo server = getProxy().getServerInfo(location); return server != null ? getRandomPlayers(server.getPlayers()) : null; } private static Iterator<String> getRandomPlayers(Collection<ProxiedPlayer> players) { if (Helper.isNullOrEmpty(players)) return null; List<String> result = new ArrayList<>(players.size()); for (ProxiedPlayer player : players) { result.add(player.getName()); } return Randoms.shuffle(result).iterator(); } @Override public Cache<?, ?> getRequestCache() { return null; } @Override public LoadingCache<FaviconSource, Optional<Favicon>> getFaviconCache() { return faviconCache; } @Override public void runAsync(Runnable task) { getProxy().getScheduler().runAsync(this, task); } @Override public ScheduledTask scheduleAsync(Runnable task, long repeat, TimeUnit unit) { return new ScheduledBungeeTask(getProxy().getScheduler().schedule(this, task, repeat, repeat, unit)); } @Override public String colorize(String s) { return ChatColor.translateAlternateColorCodes('&', s); } @Override public ServerListPlusLogger createLogger(ServerListPlusCore core) { return new JavaServerListPlusLogger(core, getLogger()); } @Override public void initialize(ServerListPlusCore core) { } @Override public void reloadCaches(ServerListPlusCore core) { } @Override public void reloadFaviconCache(CacheBuilderSpec spec) { if (spec != null) { this.faviconCache = CacheBuilder.from(spec).build(faviconLoader); } else { // Delete favicon cache faviconCache.invalidateAll(); faviconCache.cleanUp(); this.faviconCache = null; } } @Override public void configChanged(InstanceStorage<Object> confs) { // Player tracking if (confs.get(PluginConf.class).PlayerTracking.Enabled) { if (connectionListener == null) { registerListener(this.connectionListener = new ConnectionListener()); getLogger().log(DEBUG, "Registered proxy player tracking listener."); } } else if (connectionListener != null) { unregisterListener(connectionListener); this.connectionListener = null; getLogger().log(DEBUG, "Unregistered proxy player tracking listener."); } // Plugin statistics if (confs.get(PluginConf.class).Stats) { if (metrics == null) try { this.metrics = new BungeeMetricsLite(this); metrics.start(); } catch (Throwable e) { getLogger().log(DEBUG, "Failed to enable plugin statistics: " + Helper.causedException(e)); } } else if (metrics != null) try { metrics.stop(); this.metrics = null; } catch (Throwable e) { getLogger().log(DEBUG, "Failed to disable plugin statistics: " + Helper.causedException(e)); } } @Override public void statusChanged(StatusManager status) { // Status listener if (status.hasChanges()) { if (pingListener == null) { registerListener(this.pingListener = new PingListener()); getLogger().log(DEBUG, "Registered proxy ping listener."); } } else if (pingListener != null) { unregisterListener(pingListener); this.pingListener = null; getLogger().log(DEBUG, "Unregistered proxy ping listener."); } } }