package tc.oc.commons.bungee.servers;
import java.net.InetSocketAddress;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.config.Configuration;
import tc.oc.api.docs.Server;
import tc.oc.api.docs.virtual.ServerDoc;
import tc.oc.api.model.ModelListener;
import tc.oc.api.servers.ServerStore;
import tc.oc.commons.core.logging.Loggers;
import tc.oc.commons.core.plugin.PluginFacet;
/**
* Keeps Bungee's list of {@link ServerInfo}s in sync with the {@link Server}s
* in the {@link ServerStore}, and provides conversions between them.
*/
@Singleton
public class ServerTracker implements ModelListener, PluginFacet {
protected final Logger logger;
protected final ProxyServer proxy;
protected final boolean interDatacenter;
protected final Server localServer;
protected final Provider<ServerStore> serverStore; // Circular dependency
@Inject ServerTracker(Loggers loggers, Configuration configuration, ProxyServer proxy, Server localServer, Provider<ServerStore> serverStore) {
this.localServer = localServer;
this.logger = loggers.get(getClass());
this.proxy = proxy;
this.interDatacenter = configuration.getBoolean("inter-datacenter", true);
this.serverStore = serverStore;
}
/**
* Return the {@link Server} document for the given proxy record
*/
public Server byInfo(ServerInfo info) {
return serverStore.get().byBungeeName(info.getName());
}
/**
* Return the {@link Server} document for the server that the given player is currently connected to
*/
public Server byPlayer(ProxiedPlayer player) {
return byInfo(player.getServer().getInfo());
}
/**
* If the given {@link Server} is connectable from this proxy, return it's {@link ServerInfo}
*/
public Optional<ServerInfo> serverInfo(@Nullable ServerDoc.Identity server) {
final Server complete = serverStore.get().byId(server._id());
if(isConnectable(complete)) {
ServerInfo info = proxy.getServerInfo(server.bungee_name());
if(!isInfoCorrect(complete, info)) {
logger.fine("Registering " + server.bungee_name());
info = proxy.constructServerInfo(
server.bungee_name(),
new InetSocketAddress(complete.ip(), complete.current_port()),
"",
false
);
proxy.getConfig().addServer(info);
}
return Optional.of(info);
} else {
if(proxy.getConfig().removeServerNamed(server.bungee_name()) != null) {
logger.fine("Unregistering " + server.bungee_name());
}
return Optional.empty();
}
}
private boolean isConnectable(Server server) {
return server.alive() &&
server.online() &&
server.ip() != null &&
server.current_port() != null &&
server.bungee_name() != null && server.bungee_name().length() != 0 &&
(Objects.equals(server.datacenter(), localServer.datacenter()) || interDatacenter);
}
private boolean isInfoCorrect(Server server, @Nullable ServerInfo info) {
return info != null &&
server.ip().equals(info.getAddress().getHostString()) &&
server.current_port().equals(info.getAddress().getPort());
}
@HandleModel
public void serverUpdated(@Nullable Server before, @Nullable Server after, Server latest) {
serverInfo(latest);
}
}