@Override public void onEnable() { MineCloud.environmentSetup(); mongo = MineCloud.instance().mongo(); redis = MineCloud.instance().redis(); redis.addChannel( SimpleRedisChannel.create("server-start-notif", redis) .addCallback( (message) -> getProxy() .getScheduler() .schedule( this, () -> { if (message.type() != MessageType.BINARY) { return; } try { MessageInputStream stream = message.contents(); Server server = mongo.repositoryBy(Server.class).findFirst(stream.readString()); addServer(server); } catch (IOException ignored) { } }, 1, TimeUnit.SECONDS))); redis.addChannel( SimpleRedisChannel.create("server-shutdown-notif", redis) .addCallback( (message) -> { if (message.type() != MessageType.BINARY) { return; } MessageInputStream stream = message.contents(); removeServer(stream.readString()); })); redis.addChannel( SimpleRedisChannel.create("teleport", redis) .addCallback( (message) -> { if (message.type() != MessageType.BINARY) { return; } MessageInputStream stream = message.contents(); ProxiedPlayer player = getProxy().getPlayer(stream.readString()); if (player == null) { return; } String name = stream.readString(); ServerInfo info = getProxy().getServerInfo(name); if (info == null) { ServerRepository repository = mongo.repositoryBy(Server.class); Server server = repository.findOne("_id", name); if (server != null) { addServer(server); info = getProxy().getServerInfo(name); } } player.connect(info); })); redis.addChannel( SimpleRedisChannel.create("teleport-type", redis) .addCallback( (message) -> { if (message.type() != MessageType.BINARY) { return; } MessageInputStream stream = message.contents(); ProxiedPlayer player = getProxy().getPlayer(stream.readString()); if (player == null) { return; } ServerType type = mongo.repositoryBy(ServerType.class).findFirst(stream.readString()); if (type == null) { getLogger() .log(Level.SEVERE, "Received teleport message with invalid server type"); return; } ServerRepository repository = mongo.repositoryBy(Server.class); List<Server> servers = repository .find( repository .createQuery() .field("network") .equal(bungee().network()) .field("type") .equal(type) .field("port") .notEqual(-1) .field("ramUsage") .notEqual(-1)) .asList(); Collections.sort( servers, (a, b) -> a.onlinePlayers().size() - b.onlinePlayers().size()); Server server = servers.get(0); ServerInfo info = getProxy().getServerInfo(server.name()); if (info == null) { getLogger() .warning("Cannot find " + server.name() + " in ServerInfo store, adding."); addServer(server); info = getProxy().getServerInfo(server.name()); } player.connect(info); })); getProxy() .getScheduler() .schedule( this, () -> getProxy() .getScheduler() .runAsync( this, () -> { Bungee bungee = bungee(); if (bungee != null) { return; } getLogger().info("Bungee removed from database, going down..."); getProxy().stop(); // bye bye }), 2, 2, TimeUnit.SECONDS); BungeeType type = bungee().type(); File nContainer = new File("nplugins/"); nContainer.mkdirs(); type.plugins() .forEach( (plugin) -> { String version = plugin.version(); PluginType pluginType = plugin.type(); File pluginsContainer = new File("/mnt/minecloud/plugins/", pluginType.name() + "/" + version); List<File> plugins = new ArrayList<>(); getLogger().info("Loading " + pluginType.name() + "..."); if (validateFolder(pluginsContainer, pluginType, version)) return; for (File f : pluginsContainer.listFiles()) { if (f.isDirectory()) continue; // ignore directories File pl = new File(nContainer, f.getName()); try { Files.copy(f, pl); } catch (IOException ex) { getLogger() .log( Level.SEVERE, "Could not load " + pluginType.name() + ", printing stacktrace..."); ex.printStackTrace(); return; } plugins.add(pl); } File configs = new File( "/mnt/minecloud/configs/", pluginType.name() + "/" + (plugin.config() == null ? version : plugin.config())); File configContainer = new File(nContainer, pluginType.name()); if (!validateFolder(configs, pluginType, version)) copyFolder(configs, configContainer); }); getProxy() .getScheduler() .schedule( this, () -> { ServerRepository repository = mongo.repositoryBy(Server.class); List<Server> servers = repository .find(repository.createQuery().field("network").equal(bungee().network())) .asList(); servers.removeIf((s) -> s.port() == -1); servers.forEach(this::addServer); getProxy().setReconnectHandler(new ReconnectHandler(this)); getProxy().getPluginManager().registerListener(this, new MineCloudListener(this)); // release plugin manager lock try { Field f = PluginManager.class.getDeclaredField("toLoad"); f.setAccessible(true); f.set(getProxy().getPluginManager(), new HashMap<>()); } catch (NoSuchFieldException | IllegalAccessException ignored) { } getProxy().getPluginManager().detectPlugins(nContainer); getProxy().getPluginManager().loadPlugins(); getProxy() .getPluginManager() .getPlugins() .stream() .filter((p) -> !p.getDescription().getName().equals("MineCloud-Bungee")) .forEach(Plugin::onEnable); }, 0, TimeUnit.SECONDS); }
private MineCloudDaemon(Properties properties) { redis = MineCloud.instance().redis(); mongo = MineCloud.instance().mongo(); dockerClient = new DefaultDockerClient("unix:///var/run/docker.sock"); node = (String) properties.get("node-name"); instance = this; redis.addChannel( SimpleRedisChannel.create("server-create", redis) .addCallback( (message) -> { if (message.type() != MessageType.BINARY) { return; } MessageInputStream stream = message.contents(); if (!stream.readString().equalsIgnoreCase(node)) { return; } Network network = mongo.repositoryBy(Network.class).findFirst(stream.readString()); ServerType type = mongo.repositoryBy(ServerType.class).findFirst(stream.readString()); List<ServerMetadata> metadata = new ArrayList<>(); int size = stream.readVarInt32(); for (int i = 0; i < size; i++) { metadata.add(new ServerMetadata(stream.readString(), stream.readString())); } Deployer.deployServer(network, type, metadata); })); redis.addChannel( SimpleRedisChannel.create("server-kill", redis) .addCallback( (message) -> { if (message.type() != MessageType.BINARY) { return; } MessageInputStream stream = message.contents(); if (!stream.readString().equalsIgnoreCase(node)) return; Server server = mongo.repositoryBy(Server.class).findFirst(stream.readString()); if (!server.node().name().equals(node)) { MineCloud.logger() .log( Level.SEVERE, "Invalid request was sent to kill a server " + "not on the current node"); return; } try { dockerClient.killContainer(server.containerId()); MineCloud.logger() .info( "Killed server " + server.name() + " with container id " + server.containerId()); mongo.repositoryBy(Server.class).delete(server); } catch (DockerException | InterruptedException e) { MineCloud.logger().log(Level.SEVERE, "Was unable to kill a server", e); } })); redis.addChannel( SimpleRedisChannel.create("bungee-create", redis) .addCallback( (message) -> { if (message.type() != MessageType.BINARY) { return; } MessageInputStream stream = message.contents(); if (!stream.readString().equalsIgnoreCase(node)) return; Network network = mongo.repositoryBy(Network.class).findFirst(stream.readString()); BungeeType type = mongo.repositoryBy(BungeeType.class).findFirst(stream.readString()); Deployer.deployBungee(network, type); })); redis.addChannel( SimpleRedisChannel.create("bungee-kill", redis) .addCallback( (message) -> { if (message.type() != MessageType.BINARY) { return; } MessageInputStream stream = message.contents(); if (!stream.readString().equalsIgnoreCase(node)) return; Bungee bungee = mongo.repositoryBy(Bungee.class).findFirst(stream.readString()); if (!bungee.node().name().equals(node)) { MineCloud.logger() .log( Level.SEVERE, "Invalid request was sent to kill a bungee " + "not on the current node"); return; } try { dockerClient.killContainer(bungee.containerId()); MineCloud.logger() .info( "Killed bungee " + bungee.name() + " with container id " + bungee.containerId()); mongo.repositoryBy(Bungee.class).delete(bungee); } catch (DockerException | InterruptedException e) { MineCloud.logger().log(Level.SEVERE, "Was unable to kill a server", e); } })); redis.addChannel( SimpleRedisChannel.create("server-start-notif", redis) .addCallback( (message) -> { if (message.type() != MessageType.BINARY) return; MessageInputStream stream = message.contents(); Server server = mongo.repositoryBy(Server.class).findFirst(stream.readString()); if (!server.node().name().equals(node)) return; if (server.port() != -1) return; // don't even know how this would happen try { ContainerInfo info = dockerClient.inspectContainer(server.containerId()); info.networkSettings() .ports() .forEach( (s, l) -> { if (!s.contains("25565")) return; server.setPort(Integer.parseInt(l.get(0).hostPort())); MineCloud.logger() .log( Level.INFO, "Set " + server.name() + "'s port to " + server.port()); }); mongo.repositoryBy(Server.class).save(server); } catch (Exception e) { MineCloud.logger() .log(Level.SEVERE, "Was unable to set the port of a started server", e); } })); redis.addChannel(SimpleRedisChannel.create("server-shutdown-notif", redis)); new StatisticsWatcher().start(); while (!Thread.currentThread().isInterrupted()) { try { dockerClient .listContainers(DockerClient.ListContainersParam.allContainers()) .stream() .filter( (container) -> !container.status().toLowerCase().contains("up") && ((System.currentTimeMillis() / 1000L) - container.created()) > 5L) .forEach( (container) -> { try { String name = container.names() == null || container.names().isEmpty() ? "null" : container.names().get(0); dockerClient.removeContainer(container.id()); if (container.image().contains("minecloud")) { String type = container.image().substring(9); switch (type.toLowerCase()) { case "bungee": AbstractMongoRepository<Bungee> repository = mongo.repositoryBy(Bungee.class); Query<Bungee> query = repository.createQuery().field("_id").equal(node().publicIp()); repository.deleteByQuery(query); break; case "server": Server server = mongo .repositoryBy(Server.class) .findOne("containerId", container.names().get(0)); if (server != null) { mongo.repositoryBy(Server.class).delete(server); } break; } } MineCloud.logger() .info("Killed dead container " + container.id() + " (" + name + ")"); } catch (DockerException | InterruptedException e) { MineCloud.logger() .log( Level.SEVERE, "Was unable to kill exited container " + container.id(), e); } }); ServerRepository repository = mongo.repositoryBy(Server.class); Query<Server> query = repository .createQuery() .field("node") .equal(node()) .field("port") .notEqual(-1) .field("tps") .notEqual(-1); repository .find(query) .asList() .forEach( (server) -> { boolean exists; try { dockerClient.inspectContainer(server.containerId()); exists = true; } catch (DockerException | InterruptedException ex) { exists = false; } if (exists) { return; } repository.delete(server); MineCloud.logger() .info( "Removed " + server.containerId() + " from DB due to not existing as a container"); }); } catch (DockerException | InterruptedException e) { MineCloud.logger().log(Level.SEVERE, "Was unable to list containers for update", e); } try { Thread.sleep(2000L); } catch (InterruptedException ignored) { // I don't care } } }