package tc.oc.commons.bukkit.teleport; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; import org.bukkit.configuration.Configuration; import tc.oc.api.docs.Server; import tc.oc.api.docs.virtual.ServerDoc; import tc.oc.api.model.ModelDispatcher; import tc.oc.api.model.ModelListener; import tc.oc.api.servers.ServerStore; import tc.oc.commons.core.plugin.PluginFacet; import static java.util.Comparator.comparing; @Singleton public class FeaturedServerTracker implements PluginFacet, ModelListener { private final Configuration config; private final Server localServer; private final ServerStore servers; private final Map<String, Server> featuredServersByFamily = new HashMap<>(); // Order servers by player count, as long as they aren't full private final Comparator<Server> featuredServerOrder = Comparator .<Server>nullsLast(null) .thenComparing(server -> !server.online()) // Avoid offline servers .thenComparing(this::isAlmostEmpty) // Avoid empty servers .thenComparing(this::isAlmostFull) // Avoid full servers .thenComparing(comparing(Server::num_online).reversed()); // Choose the fullest server @Inject FeaturedServerTracker(Configuration config, Server localServer, ServerStore servers, ModelDispatcher modelDispatcher) { this.config = config; this.localServer = localServer; this.servers = servers; modelDispatcher.subscribe(this); } @Override public void enable() { servers.all().forEach(this::refreshServer); } public String localDatacenter() { return config.getString("local-datacenter-override", localServer.datacenter()); } public boolean isMappable(Server server) { return server != null && server.alive() && server.running() && server.bungee_name() != null && server.visibility() == ServerDoc.Visibility.PUBLIC && server.datacenter().equals(localDatacenter()); } public @Nullable Server featuredServerForFamily(String family) { return featuredServersByFamily.get(family); } /** * Server is "almost full" if free space is less than 10% or 3 slots, whichever is greater */ public boolean isAlmostFull(Server server) { return server.num_participating() > Math.min(server.max_players() * 0.9, server.max_players() - 3); } public boolean isAlmostEmpty(Server server) { return server.num_online() <= 1; } private void refreshServer(Server changed) { Server featured = featuredServersByFamily.get(changed.family()); if(changed.equals(featured)) { // If featured server changed, check entire family for a better server refreshFamily(changed.family()); } else if(isMappable(changed) && featuredServerOrder.compare(changed, featured) < 0) { // Otherwise, just check if the changed server should replace the current featured one featuredServersByFamily.put(changed.family(), changed); } } private void refreshFamily(final String family) { final Server server = servers.first(featuredServerOrder, s -> isMappable(s) && family.equals(s.family())); if(server != null) { featuredServersByFamily.put(family, server); } else { featuredServersByFamily.remove(family); } } @HandleModel public void serverUpdated(@Nullable Server before, @Nullable Server after, Server latest) { refreshServer(latest); } }