package net.ME1312.SubServers.Proxy.Host.Internal; import net.ME1312.SubServers.Proxy.Event.SubCreateEvent; import net.ME1312.SubServers.Proxy.Host.Executable; import net.ME1312.SubServers.Proxy.Host.Host; import net.ME1312.SubServers.Proxy.Host.SubCreator; import net.ME1312.SubServers.Proxy.Library.Config.YAMLSection; import net.ME1312.SubServers.Proxy.Library.Container; import net.ME1312.SubServers.Proxy.Library.UniversalFile; import net.ME1312.SubServers.Proxy.Library.Util; import net.ME1312.SubServers.Proxy.Library.Version.Version; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.*; import java.net.URL; import java.nio.charset.Charset; import java.sql.SQLException; import java.sql.Statement; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; import java.util.UUID; public class InternalSubCreator extends SubCreator { private InternalHost host; private String gitBash; private Thread thread = null; public InternalSubCreator(Host host, String gitBash) { this.host = (InternalHost) host; this.gitBash = gitBash; } private void GenerateEULA(File dir) throws FileNotFoundException, UnsupportedEncodingException { PrintWriter writer = new PrintWriter(new File(dir, "eula.txt"), "UTF-8"); writer.println("#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://account.mojang.com/documents/minecraft_eula)."); writer.println("#" + new SimpleDateFormat("EEE MMM d HH:mm:ss z yyyy").format(Calendar.getInstance().getTime())); writer.println("eula=true"); writer.close(); } private void GenerateProperties(File dir, int port) throws FileNotFoundException, UnsupportedEncodingException { PrintWriter writer = new PrintWriter(new File(dir, "server.properties"), "UTF-8"); writer.println("#Minecraft server properties"); writer.println("#" + new SimpleDateFormat("EEE MMM d HH:mm:ss z yyyy").format(Calendar.getInstance().getTime())); writer.println("generator-settings="); writer.println("op-permission-level=4"); writer.println("allow-nether=true"); writer.println("resource-pack-hash="); writer.println("level-name=world"); writer.println("enable-query=true"); writer.println("allow-flight=false"); writer.println("announce-player-achievements=false"); writer.println("server-port=" + port); writer.println("max-world-size=29999984"); writer.println("level-type=DEFAULT"); writer.println("enable-rcon=false"); writer.println("level-seed="); writer.println("force-gamemode=false"); writer.println("server-ip=" + host.getAddress().toString().substring(1)); writer.println("network-compression-threshold=-1"); writer.println("max-build-height=256"); writer.println("spawn-npcs=true"); writer.println("white-list=false"); writer.println("spawn-animals=true"); writer.println("snooper-enabled=true"); writer.println("online-mode=false"); writer.println("resource-pack="); writer.println("pvp=true"); writer.println("difficulty=1"); writer.println("enable-command-block=true"); writer.println("gamemode=0"); writer.println("player-idle-timeout=0"); writer.println("max-players=20"); writer.println("max-tick-time=60000"); writer.println("spawn-monsters=true"); writer.println("generate-structures=true"); writer.println("view-distance=10"); writer.println("motd=A Generated SubServer"); writer.close(); } private void GenerateSpigotYAML(File dir) throws FileNotFoundException, UnsupportedEncodingException { PrintWriter writer = new PrintWriter(new File(dir, "spigot.yml"), "UTF-8"); writer.println("# This is the main configuration file for Spigot."); writer.println("# As you can see, there's tons to configure. Some options may impact gameplay, so use"); writer.println("# with caution, and make sure you know what each option does before configuring."); writer.println("# For a reference for any variable inside this file, check out the Spigot wiki at"); writer.println("# http://www.spigotmc.org/wiki/spigot-configuration/"); writer.println("#"); writer.println("# If you need help with the configuration or have any questions related to Spigot,"); writer.println("# join us at the IRC or drop by our forums and leave a post."); writer.println("#"); writer.println("# IRC: #spigot @ irc.spi.gt ( http://www.spigotmc.org/pages/irc/ )"); writer.println("# Forums: http://www.spigotmc.org/"); writer.println(); writer.println("config-version: 8"); writer.println("settings:"); writer.println(" debug: false"); writer.println(" save-user-cache-on-stop-only: false"); writer.println(" bungeecord: true"); writer.println(" late-bind: false"); writer.println(" sample-count: 12"); writer.println(" player-shuffle: 0"); writer.println(" filter-creative-items: true"); writer.println(" user-cache-size: 1000"); writer.println(" int-cache-limit: 1024"); writer.println(" moved-wrongly-threshold: 0.0625"); writer.println(" moved-too-quickly-threshold: 100.0"); writer.println(" timeout-time: 60"); writer.println(" restart-on-crash: false"); writer.println(" restart-script: ./start.sh"); writer.println(" netty-threads: 4"); writer.println(" attribute:"); writer.println(" maxHealth:"); writer.println(" max: 2048.0"); writer.println(" movementSpeed:"); writer.println(" max: 2048.0"); writer.println(" attackDamage:"); writer.println(" max: 2048.0"); writer.println("commands:"); writer.println(" tab-complete: 0"); writer.println(" log: true"); writer.println(" spam-exclusions:"); writer.println(" - /skill"); writer.println(" silent-commandblock-console: true"); writer.println(" replace-commands:"); writer.println(" - setblock"); writer.println(" - summon"); writer.println(" - testforblock"); writer.println(" - tellraw"); writer.println("messages:"); writer.println(" whitelist: You are not whitelisted on this server!"); writer.println(" unknown-command: Unknown command. Type \"/help\" for help."); writer.println(" server-full: The server is full!"); writer.println(" outdated-client: Outdated client! Please use {0}"); writer.println(" outdated-server: Outdated server! I'm still on {0}"); writer.println(" restart: Server is restarting"); writer.println("stats:"); writer.println(" disable-saving: false"); writer.println(" forced-stats: {}"); writer.println("world-settings:"); writer.println(" default:"); writer.println(" verbose: true"); writer.println(" wither-spawn-sound-radius: 0"); writer.println(" view-distance: 10"); writer.println(" item-despawn-rate: 6000"); writer.println(" merge-radius:"); writer.println(" item: 2.5"); writer.println(" exp: 3.0"); writer.println(" arrow-despawn-rate: 1200"); writer.println(" enable-zombie-pigmen-portal-spawns: true"); writer.println(" zombie-aggressive-towards-villager: true"); writer.println(" hanging-tick-frequency: 100"); writer.println(" max-bulk-chunks: 10"); writer.println(" max-entity-collisions: 8"); writer.println(" random-light-updates: false"); writer.println(" save-structure-info: true"); writer.println(" mob-spawn-range: 4"); writer.println(" anti-xray:"); writer.println(" enabled: true"); writer.println(" engine-mode: 1"); writer.println(" hide-blocks:"); writer.println(" - 14"); writer.println(" - 15"); writer.println(" - 16"); writer.println(" - 21"); writer.println(" - 48"); writer.println(" - 49"); writer.println(" - 54"); writer.println(" - 56"); writer.println(" - 73"); writer.println(" - 74"); writer.println(" - 82"); writer.println(" - 129"); writer.println(" - 130"); writer.println(" replace-blocks:"); writer.println(" - 1"); writer.println(" - 5"); writer.println(" dragon-death-sound-radius: 0"); writer.println(" seed-village: 10387312"); writer.println(" seed-feature: 14357617"); writer.println(" hunger:"); writer.println(" walk-exhaustion: 0.2"); writer.println(" sprint-exhaustion: 0.8"); writer.println(" combat-exhaustion: 0.3"); writer.println(" regen-exhaustion: 3.0"); writer.println(" max-tnt-per-tick: 100"); writer.println(" max-tick-time:"); writer.println(" tile: 50"); writer.println(" entity: 50"); writer.println(" entity-activation-range:"); writer.println(" animals: 32"); writer.println(" monsters: 32"); writer.println(" misc: 16"); writer.println(" entity-tracking-range:"); writer.println(" players: 48"); writer.println(" animals: 48"); writer.println(" monsters: 48"); writer.println(" misc: 32"); writer.println(" other: 64"); writer.println(" ticks-per:"); writer.println(" hopper-transfer: 8"); writer.println(" hopper-check: 8"); writer.println(" hopper-amount: 1"); writer.println(" growth:"); writer.println(" cactus-modifier: 100"); writer.println(" cane-modifier: 100"); writer.println(" melon-modifier: 100"); writer.println(" mushroom-modifier: 100"); writer.println(" pumpkin-modifier: 100"); writer.println(" sapling-modifier: 100"); writer.println(" wheat-modifier: 100"); writer.println(" netherwart-modifier: 100"); writer.println(" nerf-spawner-mobs: false"); writer.println(" chunks-per-tick: 650"); writer.println(" clear-tick-list: false"); writer.println(); writer.close(); } private void GenerateSpongeConf(File dir) throws FileNotFoundException, UnsupportedEncodingException { new File(dir, "config" + File.separator + "sponge").mkdirs(); PrintWriter writer = new PrintWriter(new File(dir, "config" + File.separator + "sponge" + File.separator + "global.conf"), "UTF-8"); writer.println("# 1.0"); writer.println("#"); writer.println("# # If you need help with the configuration or have any questions related to Sponge,"); writer.println("# # join us at the IRC or drop by our forums and leave a post."); writer.println("#"); writer.println("# # IRC: #sponge @ irc.esper.net ( http://webchat.esper.net/?channel=sponge )"); writer.println("# # Forums: https://forums.spongepowered.org/"); writer.println("#"); writer.println(); writer.println("sponge {"); writer.println(" block-tracking {"); writer.println(" # If enabled, adds player tracking support for block positions. Note: This should only be disabled if you do not care who caused a block to change."); writer.println(" enabled=true"); writer.println(" }"); writer.println(" bungeecord {"); writer.println(" # If enabled, allows BungeeCord to forward IP address, UUID, and Game Profile to this server"); writer.println(" ip-forwarding=true"); writer.println(" }"); writer.println(" commands {}"); writer.println(" debug {"); writer.println(" # Dump chunks in the event of a deadlock"); writer.println(" dump-chunks-on-deadlock=false"); writer.println(" # Dump the heap in the event of a deadlock"); writer.println(" dump-heap-on-deadlock=false"); writer.println(" # Dump the server thread on deadlock warning"); writer.println(" dump-threads-on-warn=false"); writer.println(" # Enable Java's thread contention monitoring for thread dumps"); writer.println(" thread-contention-monitoring=false"); writer.println(" }"); writer.println(" entity {"); writer.println(" # Number of colliding entities in one spot before logging a warning. Set to 0 to disable"); writer.println(" collision-warn-size=200"); writer.println(" # Number of entities in one dimension before logging a warning. Set to 0 to disable"); writer.println(" count-warn-size=0"); writer.println(" # Number of ticks before a painting is respawned on clients when their art is changed"); writer.println(" entity-painting-respawn-delay=2"); writer.println(" # Number of ticks before the fake player entry of a human is removed from the tab list (range of 0 to 100 ticks)."); writer.println(" human-player-list-remove-delay=10"); writer.println(" # Controls the time in ticks for when an item despawns."); writer.println(" item-despawn-rate=6000"); writer.println(" # Max size of an entity's bounding box before removing it. Set to 0 to disable"); writer.println(" max-bounding-box-size=1000"); writer.println(" # Square of the max speed of an entity before removing it. Set to 0 to disable"); writer.println(" max-speed=100"); writer.println(" }"); writer.println(" entity-activation-range {"); writer.println(" ambient-activation-range=32"); writer.println(" aquatic-activation-range=32"); writer.println(" creature-activation-range=32"); writer.println(" minecraft {"); writer.println(" creature {"); writer.println(" entityhorse=true"); writer.println(" pig=true"); writer.println(" sheep=true"); writer.println(" }"); writer.println(" enabled=true"); writer.println(" misc {"); writer.println(" item=true"); writer.println(" minecartchest=true"); writer.println(" }"); writer.println(" monster {"); writer.println(" guardian=true"); writer.println(" }"); writer.println(" }"); writer.println(" misc-activation-range=16"); writer.println(" monster-activation-range=32"); writer.println(" }"); writer.println(" general {"); writer.println(" # Forces Chunk Loading on provide requests (speedup for mods that don't check if a chunk is loaded)"); writer.println(" chunk-load-override=false"); writer.println(" # Disable warning messages to server admins"); writer.println(" disable-warnings=false"); writer.println(" }"); writer.println(" logging {"); writer.println(" # Log when blocks are broken"); writer.println(" block-break=false"); writer.println(" # Log when blocks are modified"); writer.println(" block-modify=false"); writer.println(" # Log when blocks are placed"); writer.println(" block-place=false"); writer.println(" # Log when blocks are populated in a chunk"); writer.println(" block-populate=false"); writer.println(" # Log when blocks are placed by players and tracked"); writer.println(" block-tracking=false"); writer.println(" # Log when chunks are loaded"); writer.println(" chunk-load=false"); writer.println(" # Log when chunks are unloaded"); writer.println(" chunk-unload=false"); writer.println(" # Whether to log entity collision/count checks"); writer.println(" entity-collision-checks=false"); writer.println(" # Log when living entities are destroyed"); writer.println(" entity-death=false"); writer.println(" # Log when living entities are despawned"); writer.println(" entity-despawn=false"); writer.println(" # Log when living entities are spawned"); writer.println(" entity-spawn=false"); writer.println(" # Whether to log entity removals due to speed"); writer.println(" entity-speed-removal=false"); writer.println(" # Add stack traces to dev logging"); writer.println(" log-stacktraces=false"); writer.println(" }"); writer.println(" modules {"); writer.println(" bungeecord=true"); writer.println(" entity-activation-range=true"); writer.println(" timings=true"); writer.println(" }"); writer.println(" # Configuration options related to the Sql service, including connection aliases etc"); writer.println(" sql {}"); writer.println(" timings {"); writer.println(" enabled=true"); writer.println(" hidden-config-entries=["); writer.println(" \"sponge.sql\""); writer.println(" ]"); writer.println(" history-interval=300"); writer.println(" history-length=3600"); writer.println(" server-name-privacy=false"); writer.println(" verbose=false"); writer.println(" }"); writer.println(" world {"); writer.println(" # Lava behaves like vanilla water when source block is removed"); writer.println(" flowing-lava-decay=false"); writer.println(" # Vanilla water source behavior - is infinite"); writer.println(" infinite-water-source=false"); writer.println(" }"); writer.println("}"); writer.println(); writer.close(); } private void run(String name, ServerType type, Version version, int memory, int port) { Executable exec = null; UniversalFile dir = new UniversalFile(new File(host.getDirectory()), name); dir.mkdirs(); if (type == ServerType.SPIGOT) { exec = new Executable("java -Xmx" + memory + "M -Djline.terminal=jline.UnsupportedTerminal -Dcom.mojang.eula.agree=true -jar Spigot.jar"); try { GenerateSpigotYAML(dir); GenerateProperties(dir, port); } catch (IOException e) { e.printStackTrace(); } } else if (type == ServerType.VANILLA) { exec = new Executable("java -Xmx" + memory + "M -jar Vanilla.jar nogui"); try { GenerateEULA(dir); GenerateProperties(dir, port); } catch (FileNotFoundException | UnsupportedEncodingException e) { e.printStackTrace(); } } else if (type == ServerType.SPONGE) { try { Document spongexml = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(Util.readAll(new BufferedReader(new InputStreamReader(new URL("http://files.minecraftforge.net/maven/org/spongepowered/spongeforge/maven-metadata.xml").openStream(), Charset.forName("UTF-8"))))))); Document forgexml = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(Util.readAll(new BufferedReader(new InputStreamReader(new URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/maven-metadata.xml").openStream(), Charset.forName("UTF-8"))))))); NodeList spnodeList = spongexml.getElementsByTagName("version"); Version spversion = null; for (int i = 0; i < spnodeList.getLength(); i++) { Node node = spnodeList.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { if (node.getTextContent().startsWith(version.toString() + '-') && (spversion == null || new Version(node.getTextContent()).compareTo(spversion) >= 0)) { spversion = new Version(node.getTextContent()); } } } NodeList mcfnodeList = forgexml.getElementsByTagName("version"); Version mcfversion = null; for (int i = 0; i < mcfnodeList.getLength(); i++) { Node node = mcfnodeList.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { if (node.getTextContent().contains(spversion.toString().split("\\-")[1]) && (mcfversion == null || new Version(node.getTextContent()).compareTo(mcfversion) >= 0)) { mcfversion = new Version(node.getTextContent()); } } } exec = new Executable("java -Xmx" + memory + "M -jar Forge.jar"); version = new Version(mcfversion.toString() + "::" + spversion.toString()); GenerateEULA(dir); GenerateProperties(dir, port); GenerateSpongeConf(dir); } catch (ParserConfigurationException | IOException | SAXException | NullPointerException e) { e.printStackTrace(); } } try { InputStream input = null; OutputStream output = null; try { input = new FileInputStream(new UniversalFile(host.plugin.dir, "SubServers:build.sh")); output = new FileOutputStream(new File(dir, "build-subserver.sh")); byte[] buf = new byte[1024]; int bytesRead; while ((bytesRead = input.read(buf)) > 0) { output.write(buf, 0, bytesRead); } } finally { if (input != null) input.close(); if (output != null) output.close(); } if (!(new File(dir, "build-subserver.sh").exists())) { System.out.println("SubCreator > Problem Copying build-subserver.sh!"); } else { File gitBash = new File(this.gitBash, "bin" + File.separatorChar + "bash.exe"); if (!(System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)) { Process process1 = Runtime.getRuntime().exec("chmod +x build-subserver.sh", null, dir); try { process1.waitFor(); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (process1.exitValue() != 0) { System.out.println("SubCreator > Problem Setting Executable Permissions for build-subserver.sh"); System.out.println("SubCreator > This may cause errors in the Build Process"); } } Process process2 = Runtime.getRuntime().exec((System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)? "\"" + gitBash + "\" --login -i -c \"bash build-subserver.sh " + version.toString() + " " + type.toString().toLowerCase() + "\"" :("bash build-subserver.sh " + version.toString() + " " + type.toString().toLowerCase() + " " + System.getProperty("user.home")), null, dir); InternalSubLogger read = new InternalSubLogger(process2.getInputStream(), host.getName() + "/Creator", new Container(host.plugin.config.get().getSection("Settings").getBoolean("Log-Creator")), new File(dir, "SubCreator-" + type.toString() + "-" + version.toString().replace("::", "@") + ".log")); read.start(); try { process2.waitFor(); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (process2.exitValue() == 0) { host.addSubServer(name, true, port, "&aThis is a SubServer", true, dir.getPath(), exec, "stop", true, false, false, false); YAMLSection server = new YAMLSection(); server.set("Enabled", true); server.set("Host", host.getName()); server.set("Port", port); server.set("Motd", "&aThis is a SubServer"); server.set("Log", true); server.set("Directory", dir.getPath()); server.set("Executable", exec.toString()); server.set("Stop-Command", "stop"); server.set("Run-On-Launch", false); server.set("Auto-Restart", false); server.set("Auto-Restart", false); server.set("Restricted", false); host.plugin.config.get().getSection("Servers").set(name, server); host.plugin.config.save(); } else { System.out.println("SubCreator > build-subserver.sh exited with an errors. Please try again."); } } } catch (IOException e) { e.printStackTrace(); } } @Override public void create(UUID player, String name, ServerType type, Version version, int memory, int port) { if (!isBusy()) { final SubCreateEvent event = new SubCreateEvent(player, host, name, type, version, memory, port); host.plugin.getPluginManager().callEvent(event); if (!event.isCancelled()) { (thread = new Thread(() -> { InternalSubCreator.this.run(name, event.getType(), event.getVersion(), event.getMemory(), port); })).start(); } } } @Override public void waitFor() throws InterruptedException { while (thread != null && thread.isAlive()) { Thread.sleep(250); } } @Override public Host getHost() { return host; } @Override public String getGitBashDirectory() { return gitBash; } @Override public boolean isBusy() { return thread != null && thread.isAlive(); } }