diff --git a/src/corrodias/minecraft/landgenerator/MLG_Misc.java b/src/corrodias/minecraft/landgenerator/MLG_Misc.java new file mode 100644 index 0000000..9377b3c --- /dev/null +++ b/src/corrodias/minecraft/landgenerator/MLG_Misc.java @@ -0,0 +1,65 @@ +package corrodias.minecraft.landgenerator; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import morlok8k.minecraft.landgenerator.Coordinates; +import morlok8k.minecraft.landgenerator.MLG_FileRead; + +public class MLG_Misc { + + //TODO: add description + /** + * @return + */ + static boolean printSpawn() { + // ugh, sorry, this is an ugly hack, but it's a last-minute feature. this is a lot of duplicated code. + // - Fixed by Morlok8k + + MLG_FileRead.readConf(); + MLG_WorldVerify.verifyWorld(); + + File level = new File(Main.worldPath + Main.fileSeparator + "level.dat"); + try { + Coordinates spawn = MLG_SpawnPoint.getSpawn(level); + Main.out("The current spawn point is: [X,Y,Z] " + spawn); + return true; + } catch (IOException ex) { + Main.err("Error while reading " + level.getPath()); + return false; + } + } + + /** + * I'd love to use nio, but it requires Java 7.
+ * I could use Apache Commons, but i don't want to include a library for one little thing.
+ * Copies src file to dst file.
+ * If the dst file does not exist, it is created
+ * + * @author Corrodias + * @param src + * @param dst + * @throws IOException + */ + public static void copyFile(File src, File dst) throws IOException { + InputStream copyIn = new FileInputStream(src); + OutputStream copyOut = new FileOutputStream(dst); + + // Transfer bytes from in to out + byte[] buf = new byte[1024]; + int len; + while ((len = copyIn.read(buf)) >= 0) { + if (len > 0) { + copyOut.write(buf, 0, len); + } + } + copyIn.close(); + copyOut.flush(); + copyOut.close(); + } + +} diff --git a/src/corrodias/minecraft/landgenerator/MLG_Server.java b/src/corrodias/minecraft/landgenerator/MLG_Server.java new file mode 100644 index 0000000..e86e6db --- /dev/null +++ b/src/corrodias/minecraft/landgenerator/MLG_Server.java @@ -0,0 +1,300 @@ +package corrodias.minecraft.landgenerator; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; + +public class MLG_Server { + + /** + * Starts the process in the given ProcessBuilder, monitors its output for a "[INFO] Done!" message, and sends it a "stop\r\n" message. One message is printed to the console before launching and + * one is printed to the console when the Done! message is detected. If "verbose" is true, the process's output will also be printed to the console. + * + * @param minecraft + * + * @throws IOException + * @author Corrodias + */ + protected static boolean runMinecraft(boolean alternate) throws IOException { + if (Main.verbose) { + Main.out("Starting server."); + } + boolean serverSuccess = true; + boolean warning = false; + boolean warningsWeCanIgnore = false; + final boolean ignoreWarningsOriginal = Main.ignoreWarnings; + + // monitor output and print to console where required. + // STOP the server when it's done. + + if (alternate) { // Alternate - a replication (slightly stripped down) of MLG 1.3.0's code. simplest code possible. + Main.out("Alternate Launch"); + Process process = Main.minecraft.start(); + + byte[] stop = { 's', 't', 'o', 'p', '\r', '\n' }; + + // monitor output and print to console where required. + // STOP the server when it's done. + BufferedReader pOut = + new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = pOut.readLine()) != null) { + + line = line.trim(); //Trim spaces off the beginning and end, if any. + + System.out.println(line); + if (line.contains(Main.doneText)) { // EDITED By Morlok8k for Minecraft 1.3+ Beta + OutputStream outputStream = process.getOutputStream(); + + Main.out("Stopping server... (Please Wait...)"); + outputStream.write(stop); + outputStream.flush(); + + } + } + // readLine() returns null when the process exits + + } else { // start minecraft server normally! + Process process = Main.minecraft.start(); + if (Main.verbose) { + Main.out("Started Server."); + } + BufferedReader pOut = + new BufferedReader(new InputStreamReader(process.getInputStream())); + if (Main.verbose) { + Main.out("Accessing Server Output..."); + } + + String line = null; + String shortLine = null; + String outTmp = ""; + String outTmp2 = null; + + byte[] stop = { 's', 't', 'o', 'p', '\r', '\n' }; // Moved here, so this code wont run every loop, thus Faster! + // and no, i can't use a string here! + + byte[] saveAll = { 's', 'a', 'v', 'e', '-', 'a', 'l', 'l', '\r', '\n' }; + + boolean prepTextFirst = true; + + OutputStream outputStream = process.getOutputStream(); // moved here to remove some redundancy + + /* + 2012-02-29 03:50:28 [INFO] Converting map! + Scanning folders... + Total conversion count is 9 + 2012-02-29 03:50:29 [INFO] Converting... 8% + 2012-02-29 03:50:30 [INFO] Converting... 9% + 2012-02-29 03:50:31 [INFO] Converting... 10% + 2012-02-29 03:50:32 [INFO] Converting... 12% + 2012-02-29 03:50:33 [INFO] Converting... 13% + */ + + boolean convertedMapFormattingFlag = false; // This allows MLG to track if we converted a map to a new format (such as Chunk-file -> McRegion, or McRegion -> Anvil) + // just so it gets a line ending after the % output finishes + while ((line = pOut.readLine()) != null) { + + int posBracket = line.lastIndexOf("]"); + if (posBracket != -1) { + shortLine = line.substring(posBracket + 2); + shortLine = shortLine.trim(); + } else { + shortLine = line; + } + + line = line.trim(); + + if (Main.verbose) { + Main.outS(shortLine); + } else if (line.toLowerCase().contains("saving")) { + Main.outS(shortLine); + } else if (line.contains(Main.preparingText) || line.contains("Converting...")) { + if (line.contains("Converting...")) { + convertedMapFormattingFlag = true; + } + outTmp2 = line.substring(line.length() - 3, line.length()); + outTmp2 = outTmp2.trim(); //we are removing extra spaces here + if (outTmp.equals(outTmp2)) { + //instead of printing the same number, we add another dot + Main.outP("."); + } else { + outTmp = outTmp2; + + if (prepTextFirst) { + Main.outP(Main.MLG + outTmp + "..."); + prepTextFirst = false; + } else { + Main.outP(" " + outTmp + "..."); + } + + } + + } else if (line.contains(Main.preparingLevel)) { + prepTextFirst = true; + + if (convertedMapFormattingFlag == true) { + Main.outP(Main.newLine); + convertedMapFormattingFlag = false; + } + + if (line.contains("level 0")) { // "Preparing start region for level 0" + Main.outP(Main.MLG + Main.worldName + ": " + Main.level_0 + ":" + Main.newLine); + } else if (line.contains("level 1")) { // "Preparing start region for level 1" + Main.outP(Main.newLine + Main.MLG + Main.worldName + ": " + Main.level_1 + ":" + Main.newLine); + } else if (line.contains("level 2")) { // "Preparing start region for level 2" + Main.outP(Main.newLine + Main.MLG + Main.worldName + ": " + Main.level_2 + ":" + Main.newLine); + } else if (line.contains("level 3")) { // "Preparing start region for level 3" + Main.outP(Main.newLine + Main.MLG + Main.worldName + ": " + Main.level_3 + ":" + Main.newLine); + } else if (line.contains("level 4")) { // "Preparing start region for level 4" + Main.outP(Main.newLine + Main.MLG + Main.worldName + ": " + Main.level_4 + ":" + Main.newLine); + } else if (line.contains("level 5")) { // "Preparing start region for level 5" + Main.outP(Main.newLine + Main.MLG + Main.worldName + ": " + Main.level_5 + ":" + Main.newLine); + } else if (line.contains("level 6")) { // "Preparing start region for level 6" + Main.outP(Main.newLine + Main.MLG + Main.worldName + ": " + Main.level_6 + ":" + Main.newLine); + } else if (line.contains("level 7")) { // "Preparing start region for level 7" + Main.outP(Main.newLine + Main.MLG + Main.worldName + ": " + Main.level_7 + ":" + Main.newLine); + } else if (line.contains("level 8")) { // "Preparing start region for level 8" + Main.outP(Main.newLine + Main.MLG + Main.worldName + ": " + Main.level_8 + ":" + Main.newLine); + } else if (line.contains("level 9")) { // "Preparing start region for level 9" + Main.outP(Main.newLine + Main.MLG + Main.worldName + ": " + Main.level_9 + ":" + Main.newLine); + } else { + Main.outP(Main.newLine + Main.MLG + shortLine); + } + } else if (line.contains("server version") || line.contains("Converting map!")) { //TODO: add to .conf + Main.outS(shortLine); + + if (line.contains("server version") && Main.MC_Server_Version.isEmpty()) { + // if server version, save string to variable, for use in arraylist save file. + Main.MC_Server_Version = shortLine; + } + + } + + if (line.contains(Main.doneText)) { // now this is configurable! + + Main.outP(Main.newLine); + Main.outS(line.substring(line.lastIndexOf("]") + 2, line.indexOf("!"))); + if (Main.waitSave) { + Main.out("Waiting 30 seconds to save..."); + + int count = 1; + while (count <= 30) { + Main.outP("."); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + count += 1; + } + Main.out(""); + } + Main.out("Saving server data..."); + outputStream.write(saveAll); + outputStream.flush(); + + Main.out("Stopping server... (Please Wait...)"); + // OutputStream outputStream = process.getOutputStream(); + outputStream.write(stop); + outputStream.flush(); + // outputStream.close(); + + if (Main.waitSave) { + Main.out("Waiting 10 seconds to save."); + int count = 1; + while (count <= 10) { + Main.outP("."); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + count += 1; + } + Main.out(""); + } + } + + //Here we want to ignore the most common warning: "Can't keep up!" + if (line.contains("Can't keep up!")) { //TODO: add to .conf + warningsWeCanIgnore = true; //[WARNING] Can't keep up! Did the system time change, or is the server overloaded? + Main.ignoreWarnings = true; + } else if (line.contains("[WARNING] To start the server with more ram")) { + if (Main.verbose == false) { // If verbose is true, we already displayed it. + Main.outS(line); + } + warningsWeCanIgnore = true; //we can safely ignore this... + Main.ignoreWarnings = true; + } else if (line.contains("Error occurred during initialization of VM") + || line.contains("Could not reserve enough space for object heap")) { + if (Main.verbose == false) { // If verbose is true, we already displayed it. + Main.outP("[Java Error] " + line); + } + warning = true; + } + + if (Main.ignoreWarnings == false) { + if (line.contains("[WARNING]")) { // If we have a warning, stop... + Main.out(""); + Main.out("Warning found: Stopping " + Main.PROG_NAME); + if (Main.verbose == false) { // If verbose is true, we already displayed it. + Main.outS(line); + } + Main.out(""); + Main.out("Forcing Save..."); + outputStream.write(saveAll); + outputStream.flush(); + // OutputStream outputStream = process.getOutputStream(); + outputStream.write(stop); // if the warning was a fail to bind to port, we may need to write stop twice! + outputStream.flush(); + outputStream.write(stop); + outputStream.flush(); + // outputStream.close(); + warning = true; + // System.exit(1); + } + if (line.contains("[SEVERE]")) { // If we have a severe error, stop... + Main.out(""); + Main.out("Severe error found: Stopping server."); + if (Main.verbose == false) { // If verbose is true, we already displayed it. + Main.outS(line); + } + Main.out(""); + Main.out("Forcing Save..."); + outputStream.write(saveAll); + outputStream.flush(); + // OutputStream outputStream = process.getOutputStream(); + outputStream.write(stop); + outputStream.flush(); + outputStream.write(stop); // sometimes we need to do stop twice... + outputStream.flush(); + // outputStream.close(); + warning = true; + // System.exit(1); + // Quit! + } + } + + if (warningsWeCanIgnore) { + Main.ignoreWarnings = ignoreWarningsOriginal; + } + } + + if (warning == true) { // in 1.4.4 we had a issue. tried to write stop twice, but we had closed the stream already. this, and other lines should fix this. + outputStream.flush(); + //outputStream.close(); + //System.exit(1); + serverSuccess = false; + } + + outputStream.close(); + } + + // readLine() returns null when the process exits + return serverSuccess; + } + +} diff --git a/src/corrodias/minecraft/landgenerator/MLG_SpawnPoint.java b/src/corrodias/minecraft/landgenerator/MLG_SpawnPoint.java new file mode 100644 index 0000000..e54f337 --- /dev/null +++ b/src/corrodias/minecraft/landgenerator/MLG_SpawnPoint.java @@ -0,0 +1,143 @@ +package corrodias.minecraft.landgenerator; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import morlok8k.minecraft.landgenerator.Coordinates; + +import org.jnbt.CompoundTag; +import org.jnbt.IntTag; +import org.jnbt.LongTag; +import org.jnbt.NBTInputStream; +import org.jnbt.NBTOutputStream; +import org.jnbt.Tag; + +public class MLG_SpawnPoint { + + /** + * Changes the spawn point in the given Alpha/Beta level to the given coordinates.
+ * Note that, in Minecraft levels, the Y coordinate is height.
+ * (We picture maps from above, but the game was made from a different perspective) + * + * @param level + * the level file to change the spawn point in + * @param xyz + * the Coordinates of the spawn point + * @throws IOException + * if there are any problems reading/writing the file + * @author Corrodias + */ + protected static void setSpawn(File level, Coordinates xyz) throws IOException { + + try { + NBTInputStream input = new NBTInputStream(new FileInputStream(level)); + CompoundTag originalTopLevelTag = (CompoundTag) input.readTag(); + input.close(); + + //@formatter:off + + //Note: The Following Information is Old (from 2010), compared to the Data inside a current "level.dat". + //However, What we look at (SpawnX,Y,Z and RandomSeed) have not changed. + + /* + * Structure: + * + *TAG_Compound("Data"): World data. + * * TAG_Long("Time"): Stores the current "time of day" in ticks. There are 20 ticks per real-life second, and 24000 ticks per Minecraft day, making the day length 20 minutes. 0 appears to be sunrise, 12000 sunset and 24000 sunrise again. + * * TAG_Long("LastPlayed"): Stores the Unix time stamp (in milliseconds) when the player saved the game. + * * TAG_Compound("Player"): Player entity information. See Entity Format and Mob Entity Format for details. Has additional elements: + * o TAG_List("Inventory"): Each TAG_Compound in this list defines an item the player is carrying, holding, or wearing as armor. + * + TAG_Compound: Inventory item data + * # TAG_Short("id"): Item or Block ID. + * # TAG_Short("Damage"): The amount of wear each item has suffered. 0 means undamaged. When the Damage exceeds the item's durability, it breaks and disappears. Only tools and armor accumulate damage normally. + * # TAG_Byte("Count"): Number of items stacked in this inventory slot. Any item can be stacked, including tools, armor, and vehicles. Range is 1-255. Values above 127 are not displayed in-game. + * # TAG_Byte("Slot"): Indicates which inventory slot this item is in. + * o TAG_Int("Score"): Current score, doesn't appear to be implemented yet. Always 0. + * * TAG_Int("SpawnX"): X coordinate of the player's spawn position. Default is 0. + * * TAG_Int("SpawnY"): Y coordinate of the player's spawn position. Default is 64. + * * TAG_Int("SpawnZ"): Z coordinate of the player's spawn position. Default is 0. + * * TAG_Byte("SnowCovered"): 1 enables, 0 disables, see Winter Mode + * * TAG_Long("SizeOnDisk"): Estimated size of the entire world in bytes. + * * TAG_Long("RandomSeed"): Random number providing the Random Seed for the terrain. + * + */ + + //@formatter:on + + Map originalData = + ((CompoundTag) originalTopLevelTag.getValue().get("Data")).getValue(); + // This is our map of data. It is an unmodifiable map, for some reason, so we have to make a copy. + Map newData = new LinkedHashMap(originalData); + + // .get() a couple of values, just to make sure we're dealing with a valid level file, here. Good for debugging, too. + @SuppressWarnings("unused") + IntTag spawnX = (IntTag) newData.get("SpawnX"); // we never use these... Its only here for potential debugging. + @SuppressWarnings("unused") + IntTag spawnY = (IntTag) newData.get("SpawnY"); // but whatever... so I (Morlok8k) suppressed these warnings. + @SuppressWarnings("unused") + IntTag spawnZ = (IntTag) newData.get("SpawnZ"); // I don't want to remove existing code, either by myself (Morlok8k) or Corrodias + + newData.put("SpawnX", new IntTag("SpawnX", xyz.getX())); // pulling the data out of the Coordinates, + newData.put("SpawnY", new IntTag("SpawnY", xyz.getY())); // and putting it into our IntTag's + newData.put("SpawnZ", new IntTag("SpawnZ", xyz.getZ())); + + // Again, we can't modify the data map in the old Tag, so we have to make a new one. + CompoundTag newDataTag = new CompoundTag("Data", newData); + Map newTopLevelMap = new HashMap(1); + newTopLevelMap.put("Data", newDataTag); + CompoundTag newTopLevelTag = new CompoundTag("", newTopLevelMap); + + NBTOutputStream output = new NBTOutputStream(new FileOutputStream(level)); + output.writeTag(newTopLevelTag); + output.close(); + } catch (ClassCastException ex) { + throw new IOException("Invalid level format."); + } catch (NullPointerException ex) { + throw new IOException("Invalid level format."); + } + } + + //TODO: update this + /** + * @param level + * @return + * @throws IOException + * @author Corrodias + */ + protected static Coordinates getSpawn(File level) throws IOException { + try { + NBTInputStream input = new NBTInputStream(new FileInputStream(level)); + CompoundTag originalTopLevelTag = (CompoundTag) input.readTag(); + input.close(); + + Map originalData = + ((CompoundTag) originalTopLevelTag.getValue().get("Data")).getValue(); + // This is our map of data. It is an unmodifiable map, for some + // reason, so we have to make a copy. + Map newData = new LinkedHashMap(originalData); + // .get() a couple of values, just to make sure we're dealing with a + // valid level file, here. Good for debugging, too. + IntTag spawnX = (IntTag) newData.get("SpawnX"); + IntTag spawnY = (IntTag) newData.get("SpawnY"); + IntTag spawnZ = (IntTag) newData.get("SpawnZ"); + + LongTag Seed = (LongTag) newData.get("RandomSeed"); + Main.randomSeed = Seed.getValue(); + Main.out("Seed: " + Main.randomSeed); // lets output the seed, cause why not? + + Coordinates ret = + new Coordinates(spawnX.getValue(), spawnY.getValue(), spawnZ.getValue()); + return ret; + } catch (ClassCastException ex) { + throw new IOException("Invalid level format."); + } catch (NullPointerException ex) { + throw new IOException("Invalid level format."); + } + } + +} diff --git a/src/corrodias/minecraft/landgenerator/MLG_WorldVerify.java b/src/corrodias/minecraft/landgenerator/MLG_WorldVerify.java new file mode 100644 index 0000000..37e2af4 --- /dev/null +++ b/src/corrodias/minecraft/landgenerator/MLG_WorldVerify.java @@ -0,0 +1,117 @@ +package corrodias.minecraft.landgenerator; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class MLG_WorldVerify { + + /** + * + */ + static void verifyWorld() { + //TODO: element comment + + // verify that we ended up with a good server path, either from the file or from an argument. + File file = new File(Main.serverPath); + if (!file.exists() || !file.isDirectory()) { + Main.err("The server directory is invalid: " + Main.serverPath); + return; + } + + try { + // read the name of the current world from the server.properties file + BufferedReader props = + new BufferedReader(new FileReader(new File(Main.serverPath + Main.fileSeparator + + "server.properties"))); + String line; + while ((line = props.readLine()) != null) { + String property = ""; + String value = ""; + + int pos = line.indexOf('='); + + int end = line.lastIndexOf('#'); // comments, ignored lines + + if (end == -1) { // If we have no hash sign, then we read till the end of the line + end = line.length(); + + } + if (end <= (pos + 1)) { // If hash is before the '=', we may have an issue... it should be fine, cause we check for issues next, but lets make sure. + end = line.length(); + pos = -1; + } + + if (end == 0) { //hash is first char, meaning entire line is a comment + end = line.length(); + pos = 0; + } + + if (pos != -1) { + if (line.length() == 0) { + property = ""; + value = ""; + } else { + property = line.substring(0, pos).toLowerCase(); + value = line.substring(pos + 1); + } + + if (property.equals("level-name")) { + Main.worldPath = Main.serverPath + Main.fileSeparator + value; + Main.worldName = value; + } + if (Main.useRCON) { + if (property.equals("enable-rcon")) { + + if (value.contains("true")) { + Main.rcon_Enabled = true; + Main.out("RCON is set to be Enabled on the server."); + } else { + Main.rcon_Enabled = false; + Main.useRCON = false; + Main.err("RCON is not Enabled on the server."); + } + } else if (property.equals("rcon.password")) { + Main.rcon_Password = value; + if (Main.rcon_Password.isEmpty()) { + Main.useRCON = false; + Main.err("RCON Needs a password!."); + } + Main.out("RCON Password:" + Main.rcon_Password); + } else if (property.equals("rcon.port")) { + Main.rcon_Port = value; + Main.out("RCON Port:" + Main.rcon_Port); + } else if (property.equals("server-ip")) { + String IP = value; + if (IP.isEmpty()) { + IP = "0.0.0.0"; + } + Main.rcon_IPaddress = IP; + + } + } + + } + } + + } catch (FileNotFoundException ex) { + Main.err("Could not open " + Main.serverPath + Main.fileSeparator + "server.properties"); + return; + } catch (IOException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + return; + } + + File level = new File(Main.worldPath + Main.fileSeparator + "level.dat"); + if (!level.exists() || !level.isFile()) { + Main.err("The currently-configured world does not exist. Please launch the server once, first."); + return; + } + + } + +} diff --git a/src/corrodias/minecraft/landgenerator/Main.java b/src/corrodias/minecraft/landgenerator/Main.java index bba1d98..3f4f393 100644 --- a/src/corrodias/minecraft/landgenerator/Main.java +++ b/src/corrodias/minecraft/landgenerator/Main.java @@ -1,44 +1,34 @@ package corrodias.minecraft.landgenerator; import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.FileReader; -import java.io.FileWriter; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; import java.security.NoSuchAlgorithmException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; -import java.util.LinkedHashMap; +import java.util.Iterator; import java.util.Locale; -import java.util.Map; import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; +import morlok8k.minecraft.landgenerator.Coordinates; +import morlok8k.minecraft.landgenerator.MLG_ArrayList; import morlok8k.minecraft.landgenerator.MLG_DownloadFile; +import morlok8k.minecraft.landgenerator.MLG_FileRead; +import morlok8k.minecraft.landgenerator.MLG_FileWrite; import morlok8k.minecraft.landgenerator.MLG_MD5; import morlok8k.minecraft.landgenerator.MLG_Readme_and_HelpInfo; +import morlok8k.minecraft.landgenerator.MLG_SelfAware; import morlok8k.minecraft.landgenerator.MLG_StringArrayParse; +import morlok8k.minecraft.landgenerator.MLG_Time; import morlok8k.minecraft.landgenerator.MLG_Update; import morlok8k.minecraft.landgenerator.MLG_input_CLI; -import org.jnbt.CompoundTag; -import org.jnbt.IntTag; -import org.jnbt.LongTag; -import org.jnbt.NBTInputStream; -import org.jnbt.NBTOutputStream; -import org.jnbt.Tag; - /** * * @author Corrodias, Morlok8k, pr0f1x @@ -52,12 +42,14 @@ public class Main { public static boolean testing = false; // display more output when debugging public static final String PROG_NAME = "Minecraft Land Generator"; // Program Name - public static final String VERSION = "1.6.9 (1.7pre)"; // Version Number! + public static final String VERSION = "1.6.99 (1.7.0 test3)"; // Version Number! public static final String AUTHORS = "Corrodias, Morlok8k, pr0f1x"; // Authors public static final String fileSeparator = System.getProperty("file.separator"); public static final String newLine = System.getProperty("line.separator"); + public static String[] originalArgs = {}; + public static String MLG = "[MLG] "; public static String MLGe = "[MLG-ERROR] "; @@ -69,51 +61,41 @@ public class Main { public static final String MinecraftLandGeneratorConf = "MinecraftLandGenerator.conf"; public static final String defaultReadmeFile = "_MLG_Readme.txt"; - // - // - //Private Vars: - private static int MinecraftServerChunkPlayerCache = 625; - private static int increment = (int) (Math.sqrt(MinecraftServerChunkPlayerCache) * 16) - 20; //private int increment = 380; - private static boolean incrementSwitch = false; + public static String doneText = null; + public static String preparingText = null; + public static String preparingLevel = null; - private static ProcessBuilder minecraft = null; - private static String javaLine = null; - private static final String defaultJavaLine = + public static String level_0 = null; // the world + public static String level_1 = null; // the nether + public static String level_2 = null; // the end + public static String level_3 = null; // future worlds + public static String level_4 = null; + public static String level_5 = null; + public static String level_6 = null; + public static String level_7 = null; + public static String level_8 = null; + public static String level_9 = null; + + public static ProcessBuilder minecraft = null; + public static String javaLine = null; + public static final String defaultJavaLine = "java -Djava.awt.headless=true -Djline.terminal=jline.UnsupportedTerminal -Duser.language=en" + " -Xms1024m -Xmx1024m -Xincgc -jar minecraft_server.jar nogui"; - private static String serverPath = null; - private static String worldPath = null; - private static String worldName = null; - private static String doneText = null; - private static String preparingText = null; - private static String preparingLevel = null; + public static String serverPath = null; + public static String worldPath = null; + public static String worldName = null; - private static String level_0 = null; // the world - private static String level_1 = null; // the nether - private static String level_2 = null; // the end - private static String level_3 = null; // future worlds - private static String level_4 = null; - private static String level_5 = null; - private static String level_6 = null; - private static String level_7 = null; - private static String level_8 = null; - private static String level_9 = null; - - private int xRange = 0; - private int zRange = 0; - private Integer xOffset = null; - private Integer zOffset = null; public static boolean verbose = false; - private boolean alternate = false; - private static boolean dontWait = false; - private static boolean waitSave = false; - private static boolean ignoreWarnings = false; - private static LongTag randomSeed = null; - private static DateFormat dateFormat = new SimpleDateFormat( // Lets get a nice Date format for display + public static boolean dontWait = false; + public static boolean waitSave = false; + public static boolean ignoreWarnings = false; + public static Long randomSeed = (long) 0; + + public static DateFormat dateFormat = new SimpleDateFormat( // Lets get a nice Date format for display "EEEE, MMMM d, yyyy 'at' h:mm a zzzz", Locale.ENGLISH); - private static Date date = null; // date stuff + public static Date date = null; // date stuff public static Long MLG_Last_Modified_Long = 0L; public static final Class cls = Main.class; @@ -126,6 +108,8 @@ public class Main { public static int inf_loop_protect_BuildID = 0; public static boolean flag_downloadedBuildID = false; + public static String MC_Server_Version = ""; + public static ArrayList timeStamps = new ArrayList(); public static final String MLG_JarFile = "MinecraftLandGenerator.jar"; @@ -136,19 +120,31 @@ public class Main { public static final String github_MLG_BuildID_URL = github_URL + buildIDFile; public static final String github_MLG_jar_URL = github_URL + MLG_JarFile; + // + // + //Private Vars: + private static int MinecraftServerChunkPlayerCache = 625; //You see this number when you first launch the server in GUI mode, after the world is loaded, but before anyone has connected. + private static int increment = (int) (Math.sqrt(MinecraftServerChunkPlayerCache) * 16) - 20; //private int increment = 380; + + private int xRange = 0; + private int zRange = 0; + private Integer xOffset = null; + private Integer zOffset = null; + + private boolean alternate = false; + private static Boolean recheckFlag = false; private static long startTime = 0L; + private static boolean assertsEnabled = false; //debugging use... use java -ea -jar MinecraftlandGenerator.jar... + // RCON Stuff (not currently used yet...) - private static boolean useRCON = false; //use RCON to communicate with server. ***Experimental*** - @SuppressWarnings("unused") - private static boolean rcon_Enabled = false; //is server is set to use RCON? + public static boolean useRCON = false; //use RCON to communicate with server. ***Experimental*** + public static boolean rcon_Enabled = false; //is server is set to use RCON? public static String rcon_IPaddress = "0.0.0.0"; //default is 0.0.0.0 public static String rcon_Port = "25575"; //default is 25575, we are just initializing here. public static String rcon_Password = "test"; //default is "", but a password must be entered. - private static boolean assertsEnabled = false; //future debugging use... - ////////////////////////////////////////////////////////// // REMINDER: Because I always forget/mix up languages: // // "static" in java means "global" to this class // @@ -164,6 +160,8 @@ public class Main { public static void main(String[] args) { startTime = System.currentTimeMillis(); + originalArgs = args; + // This is really just here for debugging... // I plan on adding more asserts later, but for now, this will do. // to enable this, run: @@ -238,7 +236,7 @@ public class Main { // Lets get the date, and our BuildID date = new Date(); - readBuildID(); + MLG_Update.readBuildID(); // The following displays no matter what happens, so we needed this date stuff to happen first. @@ -280,7 +278,7 @@ public class Main { if (args[0].equalsIgnoreCase("-version") || args[0].equalsIgnoreCase("-help") || args[0].equals("/?")) { - showHelp(true); + MLG_Readme_and_HelpInfo.showHelp(true); return; } @@ -294,7 +292,8 @@ public class Main { if (args.length == 2) { if (args[1].equalsIgnoreCase("download")) { - boolean fileSuccess = downloadFile(github_MLG_Conf_URL, testing); + boolean fileSuccess = + MLG_DownloadFile.downloadFile(github_MLG_Conf_URL, testing); if (fileSuccess) { out(MinecraftLandGeneratorConf + " file downloaded."); return; @@ -302,35 +301,35 @@ public class Main { } } - saveConf(true); //new conf file + MLG_FileWrite.saveConf(true); //new conf file return; } else if (args[0].equalsIgnoreCase("-ps") || args[0].equalsIgnoreCase("-printspawn")) { // okay, sorry, this is an ugly hack, but it's just a last-minute feature. - printSpawn(); - waitTenSec(false); + MLG_Misc.printSpawn(); + MLG_Time.waitTenSec(false); return; } else if (args[0].equalsIgnoreCase("-build")) { - buildID(false); + MLG_Update.buildID(false); return; } else if (args[0].equalsIgnoreCase("-update")) { - updateMLG(); - waitTenSec(false); + MLG_Update.updateMLG(); + MLG_Time.waitTenSec(false); return; } else if (args[0].equalsIgnoreCase("-readme")) { if (args.length == 2) { - readMe(args[1]); + MLG_Readme_and_HelpInfo.readMe(args[1]); } else { - readMe(null); + MLG_Readme_and_HelpInfo.readMe(null); } return; } else if (args[0].equalsIgnoreCase("-downloadfile")) { if (args.length == 2) { - downloadFile(args[1], true); + MLG_DownloadFile.downloadFile(args[1], true); } else { out("No File to Download!"); - waitTenSec(false); + MLG_Time.waitTenSec(false); } return; @@ -343,7 +342,7 @@ public class Main { try { File config = new File(args[1]); try { - origMD5 = fileMD5(config.toString()); + origMD5 = MLG_MD5.fileMD5(config.toString()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } @@ -353,14 +352,14 @@ public class Main { if (line.contains("###RECHECK###")) { recheckFlag = !recheckFlag; } else { - downloadFile(line, true); + MLG_DownloadFile.downloadFile(line, true); } } in.close(); if (recheckFlag == true) { try { - recheckMD5 = fileMD5(config.toString()); + recheckMD5 = MLG_MD5.fileMD5(config.toString()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } @@ -372,7 +371,7 @@ public class Main { if (line_recheck.contains("###RECHECK###")) { recheckFlag = !recheckFlag; } else { - downloadFile(line_recheck, true); + MLG_DownloadFile.downloadFile(line_recheck, true); } } in_recheck.close(); @@ -382,26 +381,26 @@ public class Main { } catch (FileNotFoundException ex) { System.err.println(args[1] + " - File not found"); - waitTenSec(false); + MLG_Time.waitTenSec(false); return; } catch (IOException ex) { System.err.println(args[1] + " - Could not read file."); - waitTenSec(false); + MLG_Time.waitTenSec(false); return; } } else { out("No File with links!"); - waitTenSec(false); + MLG_Time.waitTenSec(false); } return; } else if (args.length == 1) { out("For help, use java -jar " + MLGFileNameShort + " -help"); - waitTenSec(false); + MLG_Time.waitTenSec(false); return; } - readConf(); + MLG_FileRead.readConf(); boolean oldConf = false; // This next section checks to see if we have a old configuration file (or none!) @@ -433,9 +432,9 @@ public class Main { if (oldConf) { err("Old Version of " + MinecraftLandGeneratorConf + " found. Updating..."); - saveConf(false); //old conf + MLG_FileWrite.saveConf(false); //old conf - waitTenSec(false); + MLG_Time.waitTenSec(false); return; } @@ -466,7 +465,6 @@ public class Main { } else if (nextSwitch.startsWith("-i")) { increment = Integer.parseInt(args[i + 2].substring(2)); - incrementSwitch = true; out("Notice: Non-Default Increment: " + increment); } else if (nextSwitch.startsWith("-w")) { @@ -495,7 +493,7 @@ public class Main { out("Notice: Z Offset: " + zOffset); if (nextSwitch.startsWith("-y")) { out("Notice: MLG now uses Z instead of Y. Please use the -z switch instead"); - waitTenSec(false); + MLG_Time.waitTenSec(false); } } else { @@ -508,7 +506,7 @@ public class Main { return; } - verifyWorld(); + MLG_WorldVerify.verifyWorld(); { File backupLevel = new File(worldPath + fileSeparator + "level_backup.dat"); @@ -516,7 +514,10 @@ public class Main { err("There is a level_backup.dat file left over from a previous attempt that failed. You should go determine whether to keep the current level.dat" + " or restore the backup."); err("You most likely will want to restore the backup!"); - waitTenSec(false); + MLG_Time.waitTenSec(false); + + //TODO: use resume data + return; } } @@ -533,7 +534,7 @@ public class Main { // prepare our two ProcessBuilders // minecraft = new ProcessBuilder(javaLine, "-Xms1024m", "-Xmx1024m", "-jar", jarFile, "nogui"); - minecraft = new ProcessBuilder(javaLine.split("\\s")); // is this always going to work? i don't know. + minecraft = new ProcessBuilder(javaLine.split("\\s")); // is this always going to work? i don't know. (most likely yes) minecraft.directory(new File(serverPath)); minecraft.redirectErrorStream(true); @@ -542,12 +543,18 @@ public class Main { long generationStartTimeTracking = System.currentTimeMillis(); //Start of time remaining calculations. - runMinecraft(alternate); + MLG_Server.runMinecraft(alternate); if ((xRange == 0) & (zRange == 0)) { //If the server is launched with an X and a Z of zero, then we just shutdown MLG after the initial launch. return; } + MLG_FileWrite.AppendTxtFile( + worldPath + fileSeparator + "MinecraftLandGenerator.log", + "# " + PROG_NAME + " " + VERSION + " - " + MLG_SelfAware.JVMinfo() + newLine + + "# " + MC_Server_Version + newLine + "# Started: " + + dateFormat.format(generationStartTimeTracking) + newLine); + xRange = (int) (Math.ceil(((double) xRange) / ((double) 16))) * 16; //say xRange was entered as 1000. this changes it to be 1008, a multiple of 16. (the size of a chunk) zRange = (int) (Math.ceil(((double) zRange) / ((double) 16))) * 16; //say zRange was entered as 2000. there is no change, as it already is a multiple of 16. @@ -557,21 +564,24 @@ public class Main { File backupLevel = new File(worldPath + fileSeparator + "level_backup.dat"); out("Backing up level.dat to level_backup.dat."); - copyFile(serverLevel, backupLevel); + MLG_Misc.copyFile(serverLevel, backupLevel); out(""); - Integer[] spawn = getSpawn(serverLevel); - out("Spawn point detected: [X,Y,Z] [" + spawn[0] + ", " + spawn[1] + ", " + spawn[2] - + "]"); + Coordinates spawn = MLG_SpawnPoint.getSpawn(serverLevel); + out("Spawn point detected: [X,Y,Z] " + spawn); + + MLG_FileWrite.AppendTxtFile(worldPath + fileSeparator + "MinecraftLandGenerator.log", + "# Seed: " + randomSeed + newLine + "# Spawn: " + spawn.toString() + newLine); + { boolean overridden = false; if (xOffset == null) { - xOffset = spawn[0]; + xOffset = spawn.getX(); } else { overridden = true; } if (zOffset == null) { - zOffset = spawn[2]; + zOffset = spawn.getZ(); } else { overridden = true; } @@ -582,70 +592,22 @@ public class Main { } out(""); - // new code to optimize the locations - Integer incrementX, incrementZ; - Double blah; double xLoops, zLoops; - //TODO: add code to check for user input for increment, and use that instead! (this should take priority) - - //TODO: have main loop make an arraylist of spawnpoints + // have main loop make an arraylist of spawnpoints // read from a file if MLG has run before on this world. save to arraylist // remove existing points from new list. // run mlg on remaining list of spawn points. // X - blah = ((double) xRange / (double) increment); //How many loops do we need? - xLoops = Math.ceil(blah); //round up to find out! - blah = Math.floor(xRange / xLoops); //optimal distance calculations here - incrementX = blah.intValue(); //save to an int - blah = Math.floor(xRange / Math.ceil((xRange / ((double) increment + 20)))); - if (blah < increment) { //should we use 380 or 400 as our original increment? This decides it. - incrementX = blah.intValue(); - } - if (blah.isInfinite()) { - incrementX = 0; // An Infinity error. this should never be less than (increment/2)! - } else if (incrementX < (increment / 2)) { - incrementX = 0; // Should never happen except for the Infinity error - } else if (incrementX > increment) { - incrementX = increment; // Should never happen. Just in case! - } + xLoops = ((double) xRange / (double) increment); //How many loops do we need? + xLoops = Math.ceil(xLoops); //round up to find out! // Z - blah = ((double) zRange / (double) increment); //How many loops do we need? - zLoops = Math.ceil(blah); //round up to find out! - blah = Math.floor(zRange / zLoops); //optimal distance calculations here - incrementZ = blah.intValue(); //save to an int - blah = Math.floor(zRange / Math.ceil((zRange / ((double) increment + 20)))); - if (blah < increment) { //should we use 380 or 400 as our original increment? This decides it. - incrementZ = blah.intValue(); - } - if (blah.isInfinite()) { - incrementZ = 0; // An Infinity error. this should never be less than (increment/2)! - } else if (incrementZ < (increment / 2)) { - incrementZ = 0; // Should never happen except for the Infinity error - } else if (incrementZ > increment) { - incrementZ = increment; // Should never happen. Just in case! - } + zLoops = ((double) zRange / (double) increment); //How many loops do we need? + zLoops = Math.ceil(zLoops); //round up to find out! - blah = null; // I'm done with this temporary variable now. I used it to make my code simplier, - // and so I wouldn't need to constantly use casting - // (as java will do it if one of the numbers is already a double) - - if (incrementSwitch) { //user input takes priority over any calculations we make! - incrementX = increment; - incrementZ = increment; - } - - if (verbose) { - if (incrementX != increment) { - out("Optimized X increments from: " + increment + " to: " + incrementX); - } - if (incrementZ != increment) { - out("Optimized Z increments from: " + increment + " to: " + incrementZ); - } - } - // end new code for location optimizations + out("Calculating Spawn Points..."); int totalIterations = (int) (xLoops * zLoops); int currentIteration = 0; @@ -657,8 +619,10 @@ public class Main { Long timeTracking = 0L; - for (int currentX = (((0 - xRange) / 2) + (incrementX / 2)); currentX <= (xRange / 2); currentX += - incrementX) { + ArrayList launchList = new ArrayList(totalIterations); + + for (int currentX = (int) ((Math.ceil((((0 - xRange) / 2) / increment))) * increment); currentX <= (xRange / 2); currentX += + increment) { curXloops++; if (curXloops == 1) { currentX = (((0 - xRange) / 2) + (increment / 2) + 16); @@ -666,8 +630,9 @@ public class Main { currentX = (xRange / 2) - (increment / 2); } - for (int currentZ = (((0 - zRange) / 2) + (incrementZ / 2)); currentZ <= (zRange / 2); currentZ += - incrementZ) { + for (int currentZ = + (int) ((Math.ceil((((0 - zRange) / 2) / increment))) * increment); currentZ <= (zRange / 2); currentZ += + increment) { currentIteration++; curZloops++; @@ -677,932 +642,111 @@ public class Main { currentZ = (zRange / 2) - (increment / 2); } - String curX = Integer.toString(currentX + xOffset); - //String curY = "64"; //Y is always set to 64 - String curZ = Integer.toString(currentZ + zOffset); - String percentDone = - Double.toString(((double) currentIteration / (double) totalIterations) * 100); - int percentIndex = - ((percentDone.indexOf(".") + 3) > percentDone.length()) ? percentDone - .length() : (percentDone.indexOf(".") + 3); //fix index on numbers like 12.3 - percentDone = - percentDone.substring(0, - (percentDone.indexOf(".") == -1 ? percentDone.length() - : percentIndex)); //Trim output, unless whole number - - out("Setting spawn to (X,Y,Z): [" + curX + ", 64, " + curZ + "] (" - + currentIteration + "/" + totalIterations + ") " + percentDone - + "% Done"); // Time Remaining estimate - - if (testing) { - outD("X:" + curXloops + ", Z:" + curZloops); - } - - timeTracking = System.currentTimeMillis(); - - //NEW CODE: - differenceTime = - (timeTracking - generationStartTimeTracking) / (currentIteration + 1); // Updated. we now count all runs, instead of the last 4. - differenceTime *= 1 + (totalIterations - currentIteration); // this should provide a more accurate result. - out("Estimated time remaining: " + displayTime(differenceTime)); // I've noticed it gets pretty accurate after about 8 launches! - - // Set the spawn point - setSpawn(serverLevel, currentX + xOffset, 64, currentZ + zOffset); - - // Launch the server - runMinecraft(alternate); - out(""); + // add coords to arraylist here + Coordinates tempCoords = + new Coordinates(currentX + xOffset, 64, currentZ + zOffset); + launchList.add(tempCoords); if (curZloops == 1) { - currentZ = (((0 - zRange) / 2) + (incrementZ / 2)); + currentZ = + (int) ((Math.ceil((((0 - zRange) / 2) / increment))) * increment); } } curZloops = 0; if (curXloops == 1) { - currentX = (((0 - xRange) / 2) + (incrementX / 2)); + currentX = (int) ((Math.ceil((((0 - xRange) / 2) / increment))) * increment); } } - out("Finished generating chunks."); - copyFile(backupLevel, serverLevel); + //get existing list, and remove this list from launchList + ArrayList removeList = + MLG_FileRead.readArrayListCoordLog(worldPath + fileSeparator + + "MinecraftLandGenerator.log"); + + if (!(removeList.isEmpty())) { + MLG_ArrayList.arrayListRemove(launchList, removeList); + } + + int numRemoved = totalIterations - launchList.size(); + if (numRemoved > 0) { + out("Reduced number of server launches by: " + numRemoved); + } + + removeList.clear(); // we are done with this now. + + System.gc(); //run the garbage collector - hopefully free up some memory! + + currentIteration = 0; + totalIterations = launchList.size(); + Coordinates xyz = null; + Iterator coordArrayIterator = launchList.iterator(); + while (coordArrayIterator.hasNext()) { + currentIteration++; + xyz = coordArrayIterator.next(); + + //////// Start server launch code + + String percentDone = + Double.toString(((double) currentIteration / (double) totalIterations) * 100); + int percentIndex = + ((percentDone.indexOf(".") + 3) > percentDone.length()) ? percentDone + .length() : (percentDone.indexOf(".") + 3); //fix index on numbers like 12.3 + percentDone = + percentDone.substring(0, + (percentDone.indexOf(".") == -1 ? percentDone.length() + : percentIndex)); //Trim output, unless whole number + + out("Setting spawn to [X,Y,Z]: " + xyz + " (" + currentIteration + " of " + + totalIterations + ") " + percentDone + "% Done"); // Time Remaining estimate + + timeTracking = System.currentTimeMillis(); + + //NEW CODE: + differenceTime = + (timeTracking - generationStartTimeTracking) / (currentIteration + 1); // Updated. we now count all runs, instead of the last 4. + differenceTime *= 1 + (totalIterations - currentIteration); // this should provide a more accurate result. + out("Estimated time remaining: " + MLG_Time.displayTime(differenceTime)); // I've noticed it gets pretty accurate after about 8 launches! + + // Set the spawn point + MLG_SpawnPoint.setSpawn(serverLevel, xyz); + + // Launch the server + boolean serverSuccess = false; + + serverSuccess = MLG_Server.runMinecraft(alternate); + out(""); + + //////// End server launch code + + if (serverSuccess) { + // Write the current Coordinates to log file! + MLG_FileWrite.AppendTxtFile(worldPath + fileSeparator + + "MinecraftLandGenerator.log", xyz.toString() + newLine); + } else { + System.exit(1); // we got a warning or severe error + } + + } + + if (currentIteration == 0) { + out("Nothing to generate!"); + } else { + out("Finished generating chunks."); + } + + MLG_Misc.copyFile(backupLevel, serverLevel); backupLevel.delete(); out("Restored original level.dat."); //finishedImage(); //disabled, because I didn't care for it - it didn't flow well with MLG - out("Generation complete in: " + displayTime(startTime, System.currentTimeMillis())); - waitTenSec(false); + out("Generation complete in: " + + MLG_Time.displayTime(startTime, System.currentTimeMillis())); + MLG_Time.waitTenSec(false); } catch (IOException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } } - //TODO: update this - /** - * @param level - * @return - * @throws IOException - * @author Corrodias - */ - protected static Integer[] getSpawn(File level) throws IOException { - try { - NBTInputStream input = new NBTInputStream(new FileInputStream(level)); - CompoundTag originalTopLevelTag = (CompoundTag) input.readTag(); - input.close(); - - Map originalData = - ((CompoundTag) originalTopLevelTag.getValue().get("Data")).getValue(); - // This is our map of data. It is an unmodifiable map, for some - // reason, so we have to make a copy. - Map newData = new LinkedHashMap(originalData); - // .get() a couple of values, just to make sure we're dealing with a - // valid level file, here. Good for debugging, too. - IntTag spawnX = (IntTag) newData.get("SpawnX"); - IntTag spawnY = (IntTag) newData.get("SpawnY"); - IntTag spawnZ = (IntTag) newData.get("SpawnZ"); - - randomSeed = (LongTag) newData.get("RandomSeed"); - out("Seed: " + randomSeed.getValue()); // lets output the seed, cause why not? - - Integer[] ret = - new Integer[] { spawnX.getValue(), spawnY.getValue(), spawnZ.getValue() }; - return ret; - } catch (ClassCastException ex) { - throw new IOException("Invalid level format."); - } catch (NullPointerException ex) { - throw new IOException("Invalid level format."); - } - } - - /** - * Changes the spawn point in the given Alpha/Beta level to the given coordinates.
- * Note that, in Minecraft levels, the Y coordinate is height.
- * (We picture maps from above, but the game was made from a different perspective) - * - * @param level - * the level file to change the spawn point in - * @param x - * the new X value - * @param y - * the new Y value - * @param z - * the new Z value - * @throws IOException - * if there are any problems reading/writing the file - * @author Corrodias - */ - protected static void setSpawn(File level, Integer x, Integer y, Integer z) throws IOException { - try { - NBTInputStream input = new NBTInputStream(new FileInputStream(level)); - CompoundTag originalTopLevelTag = (CompoundTag) input.readTag(); - input.close(); - - //@formatter:off - - //Note: The Following Information is Old (from 2010), compared to the Data inside a current "level.dat". - //However, What we look at (SpawnX,Y,Z and RandomSeed) have not changed. - - /* - * Structure: - * - *TAG_Compound("Data"): World data. - * * TAG_Long("Time"): Stores the current "time of day" in ticks. There are 20 ticks per real-life second, and 24000 ticks per Minecraft day, making the day length 20 minutes. 0 appears to be sunrise, 12000 sunset and 24000 sunrise again. - * * TAG_Long("LastPlayed"): Stores the Unix time stamp (in milliseconds) when the player saved the game. - * * TAG_Compound("Player"): Player entity information. See Entity Format and Mob Entity Format for details. Has additional elements: - * o TAG_List("Inventory"): Each TAG_Compound in this list defines an item the player is carrying, holding, or wearing as armor. - * + TAG_Compound: Inventory item data - * # TAG_Short("id"): Item or Block ID. - * # TAG_Short("Damage"): The amount of wear each item has suffered. 0 means undamaged. When the Damage exceeds the item's durability, it breaks and disappears. Only tools and armor accumulate damage normally. - * # TAG_Byte("Count"): Number of items stacked in this inventory slot. Any item can be stacked, including tools, armor, and vehicles. Range is 1-255. Values above 127 are not displayed in-game. - * # TAG_Byte("Slot"): Indicates which inventory slot this item is in. - * o TAG_Int("Score"): Current score, doesn't appear to be implemented yet. Always 0. - * * TAG_Int("SpawnX"): X coordinate of the player's spawn position. Default is 0. - * * TAG_Int("SpawnY"): Y coordinate of the player's spawn position. Default is 64. - * * TAG_Int("SpawnZ"): Z coordinate of the player's spawn position. Default is 0. - * * TAG_Byte("SnowCovered"): 1 enables, 0 disables, see Winter Mode - * * TAG_Long("SizeOnDisk"): Estimated size of the entire world in bytes. - * * TAG_Long("RandomSeed"): Random number providing the Random Seed for the terrain. - * - */ - - //@formatter:on - - Map originalData = - ((CompoundTag) originalTopLevelTag.getValue().get("Data")).getValue(); - // This is our map of data. It is an unmodifiable map, for some reason, so we have to make a copy. - Map newData = new LinkedHashMap(originalData); - // .get() a couple of values, just to make sure we're dealing with a valid level file, here. Good for debugging, too. - - @SuppressWarnings("unused") - IntTag spawnX = (IntTag) newData.get("SpawnX"); // we never use these... Its only here for potential debugging. - @SuppressWarnings("unused") - IntTag spawnY = (IntTag) newData.get("SpawnY"); // but whatever... so I (Morlok8k) suppressed these warnings. - @SuppressWarnings("unused") - IntTag spawnZ = (IntTag) newData.get("SpawnZ"); // I don't want to remove existing code, either by myself (Morlok8k) or Corrodias - - newData.put("SpawnX", new IntTag("SpawnX", x)); - newData.put("SpawnY", new IntTag("SpawnY", y)); - newData.put("SpawnZ", new IntTag("SpawnZ", z)); - - // Again, we can't modify the data map in the old Tag, so we have to make a new one. - CompoundTag newDataTag = new CompoundTag("Data", newData); - Map newTopLevelMap = new HashMap(1); - newTopLevelMap.put("Data", newDataTag); - CompoundTag newTopLevelTag = new CompoundTag("", newTopLevelMap); - - NBTOutputStream output = new NBTOutputStream(new FileOutputStream(level)); - output.writeTag(newTopLevelTag); - output.close(); - } catch (ClassCastException ex) { - throw new IOException("Invalid level format."); - } catch (NullPointerException ex) { - throw new IOException("Invalid level format."); - } - } - - /** - * Starts the process in the given ProcessBuilder, monitors its output for a "[INFO] Done!" message, and sends it a "stop\r\n" message. One message is printed to the console before launching and - * one is printed to the console when the Done! message is detected. If "verbose" is true, the process's output will also be printed to the console. - * - * @param minecraft - * - * @throws IOException - * @author Corrodias - */ - protected static void runMinecraft(boolean alternate) throws IOException { - if (verbose) { - out("Starting server."); - } - - boolean warning = false; - boolean warningsWeCanIgnore = false; - final boolean ignoreWarningsOriginal = ignoreWarnings; - - // monitor output and print to console where required. - // STOP the server when it's done. - - if (alternate) { // Alternate - a replication (slightly stripped down) of MLG 1.3.0's code. simplest code possible. - out("Alternate Launch"); - Process process = minecraft.start(); - - byte[] stop = { 's', 't', 'o', 'p', '\r', '\n' }; - - // monitor output and print to console where required. - // STOP the server when it's done. - BufferedReader pOut = - new BufferedReader(new InputStreamReader(process.getInputStream())); - String line; - while ((line = pOut.readLine()) != null) { - - line = line.trim(); //Trim spaces off the beginning and end, if any. - - System.out.println(line); - if (line.contains(doneText)) { // EDITED By Morlok8k for Minecraft 1.3+ Beta - OutputStream outputStream = process.getOutputStream(); - - out("Stopping server... (Please Wait...)"); - outputStream.write(stop); - outputStream.flush(); - - } - } - // readLine() returns null when the process exits - - } else { // start minecraft server normally! - Process process = minecraft.start(); - if (verbose) { - out("Started Server."); - } - BufferedReader pOut = - new BufferedReader(new InputStreamReader(process.getInputStream())); - if (verbose) { - out("Accessing Server Output..."); - } - - String line = null; - String shortLine = null; - String outTmp = ""; - String outTmp2 = null; - - byte[] stop = { 's', 't', 'o', 'p', '\r', '\n' }; // Moved here, so this code wont run every loop, thus Faster! - // and no, i can't use a string here! - - byte[] saveAll = { 's', 'a', 'v', 'e', '-', 'a', 'l', 'l', '\r', '\n' }; - - boolean prepTextFirst = true; - - OutputStream outputStream = process.getOutputStream(); // moved here to remove some redundancy - - //TODO: add converting section: - /* - 2012-02-29 03:50:28 [INFO] Converting map! - Scanning folders... - Total conversion count is 9 - 2012-02-29 03:50:29 [INFO] Converting... 8% - 2012-02-29 03:50:30 [INFO] Converting... 9% - 2012-02-29 03:50:31 [INFO] Converting... 10% - 2012-02-29 03:50:32 [INFO] Converting... 12% - 2012-02-29 03:50:33 [INFO] Converting... 13% - */ - - boolean convertedMapFormattingFlag = false; // This allows MLG to track if we converted a map to a new format (such as Chunk-file -> McRegion, or McRegion -> Anvil) - // just so it gets a line ending after the % output finishes - while ((line = pOut.readLine()) != null) { - - int posBracket = line.lastIndexOf("]"); - if (posBracket != -1) { - shortLine = line.substring(posBracket + 2); - } else { - shortLine = line; - } - - line = line.trim(); - - if (verbose) { - outS(shortLine); - } else if (line.toLowerCase().contains("saving")) { - outS(shortLine); - } else if (line.contains(preparingText) || line.contains("Converting...")) { - if (line.contains("Converting...")) { - convertedMapFormattingFlag = true; - } - outTmp2 = line.substring(line.length() - 3, line.length()); - outTmp2 = outTmp2.trim(); //we are removing extra spaces here - if (outTmp.equals(outTmp2)) { - //instead of printing the same number, we add another dot - outP("."); - } else { - outTmp = outTmp2; - - if (prepTextFirst) { - outP(MLG + outTmp + "..."); - prepTextFirst = false; - } else { - outP(" " + outTmp + "..."); - } - - } - - } else if (line.contains(preparingLevel)) { - prepTextFirst = true; - - if (convertedMapFormattingFlag == true) { - outP(newLine); - convertedMapFormattingFlag = false; - } - - if (line.contains("level 0")) { // "Preparing start region for level 0" - outP(MLG + worldName + ": " + level_0 + ":" + newLine); - } else if (line.contains("level 1")) { // "Preparing start region for level 1" - outP(newLine + MLG + worldName + ": " + level_1 + ":" + newLine); - } else if (line.contains("level 2")) { // "Preparing start region for level 2" - outP(newLine + MLG + worldName + ": " + level_2 + ":" + newLine); - } else if (line.contains("level 3")) { // "Preparing start region for level 3" - outP(newLine + MLG + worldName + ": " + level_3 + ":" + newLine); - } else if (line.contains("level 4")) { // "Preparing start region for level 4" - outP(newLine + MLG + worldName + ": " + level_4 + ":" + newLine); - } else if (line.contains("level 5")) { // "Preparing start region for level 5" - outP(newLine + MLG + worldName + ": " + level_5 + ":" + newLine); - } else if (line.contains("level 6")) { // "Preparing start region for level 6" - outP(newLine + MLG + worldName + ": " + level_6 + ":" + newLine); - } else if (line.contains("level 7")) { // "Preparing start region for level 7" - outP(newLine + MLG + worldName + ": " + level_7 + ":" + newLine); - } else if (line.contains("level 8")) { // "Preparing start region for level 8" - outP(newLine + MLG + worldName + ": " + level_8 + ":" + newLine); - } else if (line.contains("level 9")) { // "Preparing start region for level 9" - outP(newLine + MLG + worldName + ": " + level_9 + ":" + newLine); - } else { - outP(newLine + MLG + shortLine); - } - } else if (line.contains("server version") || line.contains("Converting map!")) { //TODO: add to .conf - outS(shortLine); - //TODO: if server version, save string to some variable, for use in arraylist save file. - } - - if (line.contains(doneText)) { // now this is configurable! - - outP(newLine); - outS(line.substring(line.lastIndexOf("]") + 2, line.indexOf("!"))); - if (waitSave) { - out("Waiting 30 seconds to save..."); - - int count = 1; - while (count <= 30) { - outP("."); - - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - count += 1; - } - out(""); - } - out("Saving server data..."); - outputStream.write(saveAll); - outputStream.flush(); - - out("Stopping server... (Please Wait...)"); - // OutputStream outputStream = process.getOutputStream(); - outputStream.write(stop); - outputStream.flush(); - // outputStream.close(); - - if (waitSave) { - out("Waiting 10 seconds to save."); - int count = 1; - while (count <= 10) { - outP("."); - - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - count += 1; - } - out(""); - } - } - - //Here we want to ignore the most common warning: "Can't keep up!" - if (line.contains("Can't keep up!")) { //TODO: add to .conf - warningsWeCanIgnore = true; //[WARNING] Can't keep up! Did the system time change, or is the server overloaded? - ignoreWarnings = true; - } else if (line.contains("[WARNING] To start the server with more ram")) { - if (verbose == false) { // If verbose is true, we already displayed it. - outS(line); - } - warningsWeCanIgnore = true; //we can safely ignore this... - ignoreWarnings = true; - } else if (line.contains("Error occurred during initialization of VM") - || line.contains("Could not reserve enough space for object heap")) { - if (verbose == false) { // If verbose is true, we already displayed it. - outP("[Java Error] " + line); - } - warning = true; - } - - if (ignoreWarnings == false) { - if (line.contains("[WARNING]")) { // If we have a warning, stop... - out(""); - out("Warning found: Stopping " + PROG_NAME); - if (verbose == false) { // If verbose is true, we already displayed it. - outS(line); - } - out(""); - out("Forcing Save..."); - outputStream.write(saveAll); - outputStream.flush(); - // OutputStream outputStream = process.getOutputStream(); - outputStream.write(stop); // if the warning was a fail to bind to port, we may need to write stop twice! - outputStream.flush(); - outputStream.write(stop); - outputStream.flush(); - // outputStream.close(); - warning = true; - // System.exit(1); - } - if (line.contains("[SEVERE]")) { // If we have a severe error, stop... - out(""); - out("Severe error found: Stopping server."); - if (verbose == false) { // If verbose is true, we already displayed it. - outS(line); - } - out(""); - out("Forcing Save..."); - outputStream.write(saveAll); - outputStream.flush(); - // OutputStream outputStream = process.getOutputStream(); - outputStream.write(stop); - outputStream.flush(); - outputStream.write(stop); // sometimes we need to do stop twice... - outputStream.flush(); - // outputStream.close(); - warning = true; - // System.exit(1); - // Quit! - } - } - - if (warningsWeCanIgnore) { - ignoreWarnings = ignoreWarningsOriginal; - } - } - - if (warning == true) { // in 1.4.4 we had a issue. tried to write stop twice, but we had closed the stream already. this, and other lines should fix this. - outputStream.flush(); - outputStream.close(); - System.exit(1); - } - - outputStream.close(); - } - - // readLine() returns null when the process exits - } - - /** - * I'd love to use nio, but it requires Java 7.
- * I could use Apache Commons, but i don't want to include a library for one little thing.
- * Copies src file to dst file.
- * If the dst file does not exist, it is created
- * - * @author Corrodias - * @param src - * @param dst - * @throws IOException - */ - public static void copyFile(File src, File dst) throws IOException { - InputStream copyIn = new FileInputStream(src); - OutputStream copyOut = new FileOutputStream(dst); - - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len; - while ((len = copyIn.read(buf)) >= 0) { - if (len > 0) { - copyOut.write(buf, 0, len); - } - } - copyIn.close(); - copyOut.flush(); - copyOut.close(); - } - - //TODO: add description - /** - * @return - */ - private static boolean printSpawn() { - // ugh, sorry, this is an ugly hack, but it's a last-minute feature. this is a lot of duplicated code. - // - Fixed by Morlok8k - - readConf(); - verifyWorld(); - - File level = new File(worldPath + fileSeparator + "level.dat"); - try { - Integer[] spawn = getSpawn(level); - out("The current spawn point is: [X,Y,Z] [" + spawn[0] + ", " + spawn[1] + ", " - + spawn[2] + "]"); - return true; - } catch (IOException ex) { - err("Error while reading " + level.getPath()); - return false; - } - } - - /** - * Saves a Readme file. - * - * @param readmeFile - * @author Morlok8k - * - */ - private static void readMe(String readmeFile) { - - MLG_Readme_and_HelpInfo.readMe(readmeFile); - - } - - /** - * @author Morlok8k - * @param URL - * URL in a String - * @param Output - * Displays output if true - * @return Boolean: true if download was successful, false if download wasn't - */ - private static boolean downloadFile(String URL, boolean Output) { - //This exists so I don't need to type "MLG_DownloadFile.downloadFile" every time. - return MLG_DownloadFile.downloadFile(URL, Output); - } - - /** - * This is an "undocumented" function to create a BuildID file. It should only be used right after compiling a .jar file
- * The resulting BuildID file is uploaded to github, and also distributed with the program.
- * THE FILE SHOULD ONLY BE MADE FROM SCRATCH AT THE TIME OF PUBLISHING!
- * (Otherwise, the purpose is defeated!)
- *
- * The problem is that when a .jar file is downloaded from the internet, it gets a new date stamp - the time that it was downloaded. Only the original copy that was compiled on the original - * computer will have the correct time stamp. (or possibly a copy from the original computer)
- *
- * This saves the hash and the timestamp (now known as the BuildID) - * - * @author Morlok8k - */ - private static void buildID(boolean downloadOnly) { - MLG_Update.buildID(downloadOnly); - } - - /** - * Gets the BuildID for MLG - * - * @author Morlok8k - * - */ - private static void readBuildID() { - MLG_Update.readBuildID(); - } - - /** - * Updates MLG to the Latest Version - * - * @author Morlok8k - * - */ - private static void updateMLG() { - - MLG_Update.updateMLG(); - } - - /** - * This gets the MLG_MD5 of a file
- * - * @author Morlok8k - */ - public static String fileMD5(String fileName) throws NoSuchAlgorithmException, - FileNotFoundException { - return MLG_MD5.fileMD5(fileName); - } - - /** - * Displays or returns Help information - * - * @param SysOut - *
- * Set TRUE to display info to System.out. (Returns null)
- * Set FALSE to return info as String. - * @author Morlok8k - */ - private static String showHelp(boolean SysOut) { - - return MLG_Readme_and_HelpInfo.showHelp(SysOut); - } - - /** - * .zip file Get Modification Time
- * - * Takes a string of a path to a .zip file (or .jar), and and returns a Long of the latest "Last Time Modified".
- *
- * - * Thanks to the following:
- * http://www.java-examples.com/get-modification-time-zip-entry-example
- * http://www.java-examples.com/get-crc-32-checksum-zip-entry-example - * - * @param zipFile - * @param timeBuildID - * @author Morlok8k - */ - public static Long ZipGetModificationTime(String zipFile) { - return MLG_Update.ZipGetModificationTime(zipFile); - } - - /** - * - */ - private static void readConf() { - //TODO: element comment - //String errorMsg = ""; - - try { - File config = new File(MinecraftLandGeneratorConf); - BufferedReader in = new BufferedReader(new FileReader(config)); - String line = ""; - String property = ""; - String value = ""; - - while ((line = in.readLine()) != null) { - int pos = line.indexOf('='); - - int end = line.lastIndexOf('#'); // comments, ignored lines - - if (end == -1) { // If we have no hash sign, then we read till the end of the line - end = line.length(); - - } - if (end <= (pos + 1)) { // If hash is before the '=', we may have an issue... it should be fine, cause we check for issues next, but lets make sure. - end = line.length(); - pos = -1; - } - - if (end == 0) { //hash is first char, meaning entire line is a comment - end = line.length(); - pos = 0; - } - - if (pos != -1) { - if (line.length() == 0) { - property = ""; - value = ""; - } else { - property = line.substring(0, pos).toLowerCase(); - value = line.substring(pos + 1, end); - } - - if (property.equals("serverpath")) { - serverPath = value; - } else if (property.equals("java")) { - javaLine = value; - } else if (property.equals("done_text")) { - doneText = value; - } else if (property.equals("preparing_text")) { - preparingText = value; - } else if (property.equals("preparing_level")) { - preparingLevel = value; - } else if (property.equals("level-0")) { - level_0 = value; - } else if (property.equals("level-1")) { - level_1 = value; - } else if (property.equals("level-2")) { - level_2 = value; - } else if (property.equals("level-3")) { - level_3 = value; - } else if (property.equals("level-4")) { - level_4 = value; - } else if (property.equals("level-5")) { - level_5 = value; - } else if (property.equals("level-6")) { - level_6 = value; - } else if (property.equals("level-7")) { - level_7 = value; - } else if (property.equals("level-8")) { - level_8 = value; - } else if (property.equals("level-9")) { - level_9 = value; - } else if (property.equals("waitsave")) { - if (value.toLowerCase().equals("true")) { - waitSave = true; - } else { - waitSave = false; - } - } - } - } - in.close(); - - if (testing) { - outD("Test Output: Reading of Config File "); - outD(" serverPath: " + serverPath); - outD(" javaLine: " + javaLine); - outD(" doneText: " + doneText); - outD(" preparingText: " + preparingText); - outD("preparingLevel: " + preparingLevel); - outD(" level_0: " + level_0); - outD(" level_1: " + level_1); - outD(" level_2: " + level_2); - outD(" level_3: " + level_3); - outD(" level_4: " + level_4); - outD(" level_5: " + level_5); - outD(" level_6: " + level_6); - outD(" level_7: " + level_7); - outD(" level_8: " + level_8); - outD(" level_9: " + level_9); - outD(" waitSave: " + waitSave); - } - } catch (FileNotFoundException ex) { - out("Could not find " - + MinecraftLandGeneratorConf - + ". It is recommended that you run the application with the -conf option to create it."); - return; - } catch (IOException ex) { - err("Could not read " + MinecraftLandGeneratorConf + "."); - return; - } - } - - /** - * Generates a Config File. - * - * @param newConf - * true: Uses Default values. false: uses existing values - * @author Morlok8k - */ - private static void saveConf(boolean newConf) { - - String jL = null; //javaLine - String sP = null; //serverPath - - if (newConf) { - jL = defaultJavaLine; // reads the default from a constant, makes it easier! - sP = "."; // - } else { - jL = javaLine; // we read these values from an existing Conf File. - sP = serverPath; // - } - - String txt = null; - //@formatter:off - txt = "#" + PROG_NAME + " Configuration File: Version: " + VERSION + newLine - + "#Authors: " + AUTHORS + newLine - + "#Auto-Generated: " + dateFormat.format(date) + newLine - + newLine - + "#Line to run server:" + newLine - + "Java=" + jL // reads the default from a constant, makes it easier! - + newLine - + newLine - + "#Location of server. use \".\" for the same folder as MLG" + newLine - + "ServerPath=" + sP - + newLine - + newLine - + "#Strings read from the server" + newLine - + "Done_Text=[INFO] Done" + newLine - + "Preparing_Text=[INFO] Preparing spawn area:" + newLine - + "Preparing_Level=[INFO] Preparing start region for" + newLine - + "Level-0=The Overworld" + newLine - + "Level-1=The Nether" + newLine - + "Level-2=The End" + newLine - + "Level-3=Level 3 (Future Level)" + newLine - + "Level-4=Level 4 (Future Level)" + newLine - + "Level-5=Level 5 (Future Level)" + newLine - + "Level-6=Level 6 (Future Level)" + newLine - + "Level-7=Level 7 (Future Level)" + newLine - + "Level-8=Level 8 (Future Level)" + newLine - + "Level-9=Level 9 (Future Level)" + newLine - + newLine - + "#Optional: Wait a few seconds after saving." + newLine + "WaitSave=false"; - //@formatter:on - - writeTxtFile(MinecraftLandGeneratorConf, txt); - - return; - - } - - /** - * - */ - private static void verifyWorld() { - //TODO: element comment - - // verify that we ended up with a good server path, either from the file or from an argument. - File file = new File(serverPath); - if (!file.exists() || !file.isDirectory()) { - err("The server directory is invalid: " + serverPath); - return; - } - - try { - // read the name of the current world from the server.properties file - BufferedReader props = - new BufferedReader(new FileReader(new File(serverPath + fileSeparator - + "server.properties"))); - String line; - while ((line = props.readLine()) != null) { - String property = ""; - String value = ""; - - int pos = line.indexOf('='); - - int end = line.lastIndexOf('#'); // comments, ignored lines - - if (end == -1) { // If we have no hash sign, then we read till the end of the line - end = line.length(); - - } - if (end <= (pos + 1)) { // If hash is before the '=', we may have an issue... it should be fine, cause we check for issues next, but lets make sure. - end = line.length(); - pos = -1; - } - - if (end == 0) { //hash is first char, meaning entire line is a comment - end = line.length(); - pos = 0; - } - - if (pos != -1) { - if (line.length() == 0) { - property = ""; - value = ""; - } else { - property = line.substring(0, pos).toLowerCase(); - value = line.substring(pos + 1); - } - - if (property.equals("level-name")) { - worldPath = serverPath + fileSeparator + value; - worldName = value; - } - if (useRCON) { - if (property.equals("enable-rcon")) { - - if (value.contains("true")) { - rcon_Enabled = true; - out("RCON is set to be Enabled on the server."); - } else { - rcon_Enabled = false; - useRCON = false; - err("RCON is not Enabled on the server."); - } - } else if (property.equals("rcon.password")) { - rcon_Password = value; - if (rcon_Password.isEmpty()) { - useRCON = false; - err("RCON Needs a password!."); - } - out("RCON Password:" + rcon_Password); - } else if (property.equals("rcon.port")) { - rcon_Port = value; - out("RCON Port:" + rcon_Port); - } else if (property.equals("server-ip")) { - String IP = value; - if (IP.isEmpty()) { - IP = "0.0.0.0"; - } - rcon_IPaddress = IP; - - } - } - - } - } - - } catch (FileNotFoundException ex) { - err("Could not open " + serverPath + fileSeparator + "server.properties"); - return; - } catch (IOException ex) { - Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); - return; - } - - File level = new File(worldPath + fileSeparator + "level.dat"); - if (!level.exists() || !level.isFile()) { - err("The currently-configured world does not exist. Please launch the server once, first."); - return; - } - - } - - /** - * @param file - * @param txt - */ - public static void writeTxtFile(String file, String txt) { - //TODO: element comment - - /* - * NOTE: I don't include a generic readTxtFile method, as that code depends on what I'm reading. - * For things like that I make a special method for it, if its used in more than one place. - * Like reading the config file. - */ - - try { - File oFile = new File(file); - BufferedWriter outFile = new BufferedWriter(new FileWriter(oFile)); - outFile.write(txt); - outFile.newLine(); - outFile.close(); - out(file + " file created."); - return; - } catch (IOException ex) { - err("Could not create " + MinecraftLandGeneratorConf + "."); - ex.printStackTrace(); - return; - } - - } - /** * Outputs a formatted string to System.out as a line. * @@ -1643,7 +787,7 @@ public class Main { * String to display and format * @author Morlok8k */ - private static void outS(String str) { + static void outS(String str) { System.out.println("[Server] " + str); } @@ -1658,100 +802,4 @@ public class Main { System.out.println(MLG + "[DEBUG] " + str); } - /** - * waits ten seconds. outputs 10%, 20%, etc after each second. - * - * @author Morlok8k - */ - private static void waitTenSec(boolean output) { - - if (dontWait) { return; } //Don't wait! - - if (output) { - outP(MLG); //here we wait 10 sec. - } - - int count = 0; - while (count <= 100) { - if (output) { - outP(count + "% "); - } - - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - count += 10; - } - if (output) { - outP(newLine); - } - return; - - } - - /** - * Returns the time in a readable format between two points of time given in Millis. - * - * @param startTimeMillis - * @param endTimeMillis - * @author Morlok8k - * @return String of Readable Time - */ - public static String displayTime(long startTimeMillis, long endTimeMillis) { - - long millis = (endTimeMillis - startTimeMillis); - //I just duplicated displayTime to have a start & end times, because it just made things simpler to code. - return (displayTime(millis)); - } - - /** - * Returns the time in a readable format given a time in Millis. - * - * @param timeMillis - * @author Morlok8k - * @return String of Readable Time - */ - public static String displayTime(long timeMillis) { - - long seconds = timeMillis / 1000; - long minutes = seconds / 60; - long hours = minutes / 60; - long days = hours / 24; - long years = days / 365; - - String took = - (years > 0 ? String.format("%d " + ((years) == 1 ? "Year, " : "Years, "), years) - : "") - + (days > 0 ? String.format("%d " - + ((days % 365) == 1 ? "Day, " : "Days, "), days % 365) : "") - + (hours > 0 ? String.format("%d " - + ((hours % 24) == 1 ? "Hour, " : "Hours, "), hours % 24) : "") - + (minutes > 0 ? String.format("%d " - + ((minutes % 60) == 1 ? "Minute, " : "Minutes, "), minutes % 60) - : "") - + String.format("%d " + ((seconds % 60) == 1 ? "Second" : "Seconds"), - seconds % 60); - - if (!(verbose)) { - int commaFirst = took.indexOf(","); - int commaSecond = took.substring((commaFirst + 1), took.length()).indexOf(","); - int end = (commaFirst + 1 + commaSecond); - - if (commaSecond == -1) { - end = commaFirst; - } - - if (commaFirst == -1) { - end = took.length(); - } - - took = took.substring(0, end); - } - - took.trim(); - return (took); - } - } diff --git a/src/morlok8k/minecraft/landgenerator/Coordinates.java b/src/morlok8k/minecraft/landgenerator/Coordinates.java index 63c35b1..e60299a 100644 --- a/src/morlok8k/minecraft/landgenerator/Coordinates.java +++ b/src/morlok8k/minecraft/landgenerator/Coordinates.java @@ -10,7 +10,7 @@ package morlok8k.minecraft.landgenerator; */ public class Coordinates { //FYI: int's (Integer's) are good enough for Minecraft. They have a range of -2,147,483,648 to 2,147,483,647 - // Minecraft starts failing around ± 12,550,820 and ends at either ± 30,000,000 or ± 32,000,000 (depending on the version). + // Minecraft starts failing around (+/-) 12,550,820 and ends at either (+/-) 30,000,000 or (+/-) 32,000,000 (depending on the version). // See: http://www.minecraftwiki.net/wiki/Far_Lands for more info. public int X = 0; @@ -89,7 +89,7 @@ public class Coordinates { public static Coordinates parseString(String StringOfCoords) { //parse out string - StringOfCoords.trim(); + StringOfCoords = StringOfCoords.trim(); int x = 0, y = 0, z = 0; @@ -108,13 +108,13 @@ public class Coordinates { firstComma = StringOfCoords.indexOf(","); secComma = StringOfCoords.lastIndexOf(","); - System.out.println(start + " " + end + " " + firstComma + " " + secComma); + //System.out.println(start + " " + end + " " + firstComma + " " + secComma); sX = StringOfCoords.substring(start + 1, firstComma); sY = StringOfCoords.substring(firstComma + 1, secComma); sZ = StringOfCoords.substring(secComma + 1, end); - System.out.println(sX + " " + sY + " " + sZ); + //System.out.println(sX + " " + sY + " " + sZ); x = Integer.parseInt(sX); y = Integer.parseInt(sY); diff --git a/src/morlok8k/minecraft/landgenerator/MLG_ArrayList.java b/src/morlok8k/minecraft/landgenerator/MLG_ArrayList.java index 3367a07..82af88b 100644 --- a/src/morlok8k/minecraft/landgenerator/MLG_ArrayList.java +++ b/src/morlok8k/minecraft/landgenerator/MLG_ArrayList.java @@ -26,8 +26,4 @@ public class MLG_ArrayList { return list; } - //TODO: add read arraylist file - - //TODO: add save arraylist file (save this file only after generation is complete) - } diff --git a/src/morlok8k/minecraft/landgenerator/MLG_DownloadFile.java b/src/morlok8k/minecraft/landgenerator/MLG_DownloadFile.java index a666bab..fe7c3d6 100644 --- a/src/morlok8k/minecraft/landgenerator/MLG_DownloadFile.java +++ b/src/morlok8k/minecraft/landgenerator/MLG_DownloadFile.java @@ -88,7 +88,7 @@ public class MLG_DownloadFile { timeTracking[1] = System.currentTimeMillis(); //differenceTime = (timeTracking[1] - timeTracking[0]); if (Output) { - Main.out("Elapsed Time: " + Main.displayTime(timeTracking[0], timeTracking[1])); + Main.out("Elapsed Time: " + MLG_Time.displayTime(timeTracking[0], timeTracking[1])); } } catch (FileNotFoundException e) { e.printStackTrace(); diff --git a/src/morlok8k/minecraft/landgenerator/MLG_FileRead.java b/src/morlok8k/minecraft/landgenerator/MLG_FileRead.java new file mode 100644 index 0000000..9e0d841 --- /dev/null +++ b/src/morlok8k/minecraft/landgenerator/MLG_FileRead.java @@ -0,0 +1,170 @@ +package morlok8k.minecraft.landgenerator; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; + +import corrodias.minecraft.landgenerator.Main; + +public class MLG_FileRead { + + public static ArrayList readArrayListCoordLog(String file) { + + ArrayList Return = new ArrayList(); + + try { + BufferedReader in = new BufferedReader(new FileReader(new File(file))); + String line = ""; + + while ((line = in.readLine()) != null) { + int end = line.indexOf('#'); // comments, ignored lines + boolean ignoreLine = false; + Coordinates c = new Coordinates(); + + if (end == -1) { // If we have no hash sign, then we read till the end of the line + end = line.length(); + } + + if (end == 0) { //hash is first char, meaning entire line is a comment + ignoreLine = true; + } + + if (!(ignoreLine)) { + c = Coordinates.parseString(line.substring(0, end)); + Return.add(c); + } /* else { + // other future stuff may go here. + } + */ + + } + in.close(); + + } catch (FileNotFoundException ex) { + Main.out("Could not find " + file + "."); + return Return; + } catch (IOException ex) { + Main.err("Could not read " + file + "."); + return Return; + } + + return Return; + } + + /** + * + */ + public static void readConf() { + //TODO: element comment + //String errorMsg = ""; + + try { + File config = new File(Main.MinecraftLandGeneratorConf); + BufferedReader in = new BufferedReader(new FileReader(config)); + String line = ""; + String property = ""; + String value = ""; + + while ((line = in.readLine()) != null) { + int pos = line.indexOf('='); + + int end = line.lastIndexOf('#'); // comments, ignored lines + + if (end == -1) { // If we have no hash sign, then we read till the end of the line + end = line.length(); + + } + if (end <= (pos + 1)) { // If hash is before the '=', we may have an issue... it should be fine, cause we check for issues next, but lets make sure. + end = line.length(); + pos = -1; + } + + if (end == 0) { //hash is first char, meaning entire line is a comment + end = line.length(); + pos = 0; + } + + if (pos != -1) { + if (line.length() == 0) { + property = ""; + value = ""; + } else { + property = line.substring(0, pos).toLowerCase(); + value = line.substring(pos + 1, end); + } + + if (property.equals("serverpath")) { + Main.serverPath = value; + } else if (property.equals("java")) { + Main.javaLine = value; + } else if (property.equals("done_text")) { + Main.doneText = value; + } else if (property.equals("preparing_text")) { + Main.preparingText = value; + } else if (property.equals("preparing_level")) { + Main.preparingLevel = value; + } else if (property.equals("level-0")) { + Main.level_0 = value; + } else if (property.equals("level-1")) { + Main.level_1 = value; + } else if (property.equals("level-2")) { + Main.level_2 = value; + } else if (property.equals("level-3")) { + Main.level_3 = value; + } else if (property.equals("level-4")) { + Main.level_4 = value; + } else if (property.equals("level-5")) { + Main.level_5 = value; + } else if (property.equals("level-6")) { + Main.level_6 = value; + } else if (property.equals("level-7")) { + Main.level_7 = value; + } else if (property.equals("level-8")) { + Main.level_8 = value; + } else if (property.equals("level-9")) { + Main.level_9 = value; + } else if (property.equals("waitsave")) { + if (value.toLowerCase().equals("true")) { + Main.waitSave = true; + } else { + Main.waitSave = false; + } + } + } + } + in.close(); + + if (Main.testing) { + Main.outD("Test Output: Reading of Config File "); + Main.outD(" serverPath: " + Main.serverPath); + Main.outD(" javaLine: " + Main.javaLine); + Main.outD(" doneText: " + Main.doneText); + Main.outD(" preparingText: " + Main.preparingText); + Main.outD("preparingLevel: " + Main.preparingLevel); + Main.outD(" level_0: " + Main.level_0); + Main.outD(" level_1: " + Main.level_1); + Main.outD(" level_2: " + Main.level_2); + Main.outD(" level_3: " + Main.level_3); + Main.outD(" level_4: " + Main.level_4); + Main.outD(" level_5: " + Main.level_5); + Main.outD(" level_6: " + Main.level_6); + Main.outD(" level_7: " + Main.level_7); + Main.outD(" level_8: " + Main.level_8); + Main.outD(" level_9: " + Main.level_9); + Main.outD(" waitSave: " + Main.waitSave); + } + } catch (FileNotFoundException ex) { + Main.out("Could not find " + + Main.MinecraftLandGeneratorConf + + ". It is recommended that you run the application with the -conf option to create it."); + return; + } catch (IOException ex) { + Main.err("Could not read " + Main.MinecraftLandGeneratorConf + "."); + return; + } + } + +} diff --git a/src/morlok8k/minecraft/landgenerator/MLG_FileWrite.java b/src/morlok8k/minecraft/landgenerator/MLG_FileWrite.java new file mode 100644 index 0000000..db4b560 --- /dev/null +++ b/src/morlok8k/minecraft/landgenerator/MLG_FileWrite.java @@ -0,0 +1,123 @@ +package morlok8k.minecraft.landgenerator; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import corrodias.minecraft.landgenerator.Main; + +/** + * http://www.roseindia.net/java/example/java/io/java-append-to-file.shtml
+ * Append To File - Java Tutorial + */ +public class MLG_FileWrite { + + public static final String newLine = Main.newLine; + + /** + * @param file + * @param appendTxt + */ + public static void AppendTxtFile(String file, String appendTxt) { + try { + // Create file + FileWriter fstream = new FileWriter(file, true); + BufferedWriter out = new BufferedWriter(fstream); + //String output = "Hello Java" + newLine; + out.write(appendTxt); + //Close the output stream + out.close(); + } catch (Exception e) {//Catch exception if any + System.err.println("Error: " + e.getMessage()); + } + } + + /** + * @param file + * @param txt + */ + public static void writeTxtFile(String file, String txt) { + //TODO: element comment + + /* + * NOTE: I don't include a generic readTxtFile method, as that code depends on what I'm reading. + * For things like that I make a special method for it, if its used in more than one place. + * Like reading the config file. + */ + + try { + File oFile = new File(file); + BufferedWriter outFile = new BufferedWriter(new FileWriter(oFile)); + outFile.write(txt); + outFile.newLine(); + outFile.close(); + Main.out(file + " file created."); + return; + } catch (IOException ex) { + Main.err("Could not create " + Main.MinecraftLandGeneratorConf + "."); + ex.printStackTrace(); + return; + } + + } + + /** + * Generates a Config File. + * + * @param newConf + * true: Uses Default values. false: uses existing values + * @author Morlok8k + */ + public static void saveConf(boolean newConf) { + + String jL = null; //javaLine + String sP = null; //serverPath + + if (newConf) { + jL = Main.defaultJavaLine; // reads the default from a constant, makes it easier! + sP = "."; // + } else { + jL = Main.javaLine; // we read these values from an existing Conf File. + sP = Main.serverPath; // + } + + String txt = null; + //@formatter:off + txt = "#" + Main.PROG_NAME + " Configuration File: Version: " + Main.VERSION + Main.newLine + + "#Authors: " + Main.AUTHORS + Main.newLine + + "#Auto-Generated: " + Main.dateFormat.format(Main.date) + Main.newLine + + Main.newLine + + "#Line to run server:" + Main.newLine + + "Java=" + jL // reads the default from a constant, makes it easier! + + Main.newLine + + Main.newLine + + "#Location of server. use \".\" for the same folder as MLG" + Main.newLine + + "ServerPath=" + sP + + Main.newLine + + Main.newLine + + "#Strings read from the server" + Main.newLine + + "Done_Text=[INFO] Done" + Main.newLine + + "Preparing_Text=[INFO] Preparing spawn area:" + Main.newLine + + "Preparing_Level=[INFO] Preparing start region for" + Main.newLine + + "Level-0=The Overworld" + Main.newLine + + "Level-1=The Nether" + Main.newLine + + "Level-2=The End" + Main.newLine + + "Level-3=Level 3 (Future Level)" + Main.newLine + + "Level-4=Level 4 (Future Level)" + Main.newLine + + "Level-5=Level 5 (Future Level)" + Main.newLine + + "Level-6=Level 6 (Future Level)" + Main.newLine + + "Level-7=Level 7 (Future Level)" + Main.newLine + + "Level-8=Level 8 (Future Level)" + Main.newLine + + "Level-9=Level 9 (Future Level)" + Main.newLine + + Main.newLine + + "#Optional: Wait a few seconds after saving." + Main.newLine + "WaitSave=false"; + //@formatter:on + + writeTxtFile(Main.MinecraftLandGeneratorConf, txt); + + return; + + } + +} diff --git a/src/morlok8k/minecraft/landgenerator/MLG_Readme_and_HelpInfo.java b/src/morlok8k/minecraft/landgenerator/MLG_Readme_and_HelpInfo.java index e5ff115..06bc444 100644 --- a/src/morlok8k/minecraft/landgenerator/MLG_Readme_and_HelpInfo.java +++ b/src/morlok8k/minecraft/landgenerator/MLG_Readme_and_HelpInfo.java @@ -201,7 +201,7 @@ public class MLG_Readme_and_HelpInfo { + newLine; //@formatter:on - Main.writeTxtFile(readmeFile, ReadMeText + showHelpSTR); + MLG_FileWrite.writeTxtFile(readmeFile, ReadMeText + showHelpSTR); } @@ -304,4 +304,5 @@ public class MLG_Readme_and_HelpInfo { return returnString; } + } diff --git a/src/morlok8k/minecraft/landgenerator/MLG_SelfAware.java b/src/morlok8k/minecraft/landgenerator/MLG_SelfAware.java new file mode 100644 index 0000000..4a11a56 --- /dev/null +++ b/src/morlok8k/minecraft/landgenerator/MLG_SelfAware.java @@ -0,0 +1,36 @@ +package morlok8k.minecraft.landgenerator; + +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.util.List; + +import corrodias.minecraft.landgenerator.Main; + +public class MLG_SelfAware { + + public static String JVMinfo() { + + String Return = ""; + + RuntimeMXBean RuntimemxBean = ManagementFactory.getRuntimeMXBean(); + List aList = RuntimemxBean.getInputArguments(); + + for (int i = 0; i < aList.size(); i++) { + Return = Return + (aList.get(i)) + " "; + } + + Return = Return.trim(); + + Return = + "Launch info: JVM: " + Return + " JAR: " + System.getProperty("sun.java.command") + + " ARGS: "; + + for (int i = 0; i < Main.originalArgs.length; i++) { + Return = Return + (Main.originalArgs[i]) + " "; + } + + Return = Return.trim(); + return Return; + } + +} diff --git a/src/morlok8k/minecraft/landgenerator/MLG_Time.java b/src/morlok8k/minecraft/landgenerator/MLG_Time.java new file mode 100644 index 0000000..ecbb09b --- /dev/null +++ b/src/morlok8k/minecraft/landgenerator/MLG_Time.java @@ -0,0 +1,103 @@ +package morlok8k.minecraft.landgenerator; + +import corrodias.minecraft.landgenerator.Main; + +public class MLG_Time { + + /** + * waits ten seconds. outputs 10%, 20%, etc after each second. + * + * @author Morlok8k + */ + public static void waitTenSec(boolean output) { + + if (Main.dontWait) { return; } //Don't wait! + + if (output) { + Main.outP(Main.MLG); //here we wait 10 sec. + } + + int count = 0; + while (count <= 100) { + if (output) { + Main.outP(count + "% "); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + count += 10; + } + if (output) { + Main.outP(Main.newLine); + } + return; + + } + + /** + * Returns the time in a readable format between two points of time given in Millis. + * + * @param startTimeMillis + * @param endTimeMillis + * @author Morlok8k + * @return String of Readable Time + */ + public static String displayTime(long startTimeMillis, long endTimeMillis) { + + long millis = (endTimeMillis - startTimeMillis); + //I just duplicated displayTime to have a start & end times, because it just made things simpler to code. + return (MLG_Time.displayTime(millis)); + } + + /** + * Returns the time in a readable format given a time in Millis. + * + * @param timeMillis + * @author Morlok8k + * @return String of Readable Time + */ + public static String displayTime(long timeMillis) { + + long seconds = timeMillis / 1000; + long minutes = seconds / 60; + long hours = minutes / 60; + long days = hours / 24; + long years = days / 365; + + String took = + (years > 0 ? String.format("%d " + ((years) == 1 ? "Year, " : "Years, "), years) + : "") + + (days > 0 ? String.format("%d " + + ((days % 365) == 1 ? "Day, " : "Days, "), days % 365) : "") + + (hours > 0 ? String.format("%d " + + ((hours % 24) == 1 ? "Hour, " : "Hours, "), hours % 24) : "") + + (minutes > 0 ? String.format("%d " + + ((minutes % 60) == 1 ? "Minute, " : "Minutes, "), minutes % 60) + : "") + + String.format("%d " + ((seconds % 60) == 1 ? "Second" : "Seconds"), + seconds % 60); + + if (!(Main.verbose)) { + int commaFirst = took.indexOf(","); + int commaSecond = took.substring((commaFirst + 1), took.length()).indexOf(","); + int end = (commaFirst + 1 + commaSecond); + + if (commaSecond == -1) { + end = commaFirst; + } + + if (commaFirst == -1) { + end = took.length(); + } + + took = took.substring(0, end); + } + + took = took.trim(); + return (took); + } + +} diff --git a/src/morlok8k/minecraft/landgenerator/MLG_Update.java b/src/morlok8k/minecraft/landgenerator/MLG_Update.java index d37bd27..ec0352c 100644 --- a/src/morlok8k/minecraft/landgenerator/MLG_Update.java +++ b/src/morlok8k/minecraft/landgenerator/MLG_Update.java @@ -16,6 +16,7 @@ import java.util.Iterator; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import corrodias.minecraft.landgenerator.MLG_Misc; import corrodias.minecraft.landgenerator.Main; public class MLG_Update { @@ -67,7 +68,7 @@ public class MLG_Update { if (Main.MLG_Current_Hash == null) { try { - Main.MLG_Current_Hash = Main.fileMD5(Main.MLGFileName); + Main.MLG_Current_Hash = MLG_MD5.fileMD5(Main.MLGFileName); // out(hash + " " + MLGFileName); } catch (Exception e) { Main.out("Error: MLG_MD5 from file failed"); @@ -127,7 +128,7 @@ public class MLG_Update { Main.out("\"" + Main.buildIDFile + "\" file not Found. Generating New \"" + Main.buildIDFile + "\" File"); - Main.writeTxtFile(Main.buildIDFile, + MLG_FileWrite.writeTxtFile(Main.buildIDFile, Main.MLG_Current_Hash + "=" + String.valueOf(time.getTime()) + "#MLG v" + Main.VERSION + INFO); @@ -173,7 +174,7 @@ public class MLG_Update { if (Main.MLG_Current_Hash == null) { try { - Main.MLG_Current_Hash = Main.fileMD5(Main.MLGFileName); + Main.MLG_Current_Hash = MLG_MD5.fileMD5(Main.MLGFileName); // out(hash + " " + MLGFileName); } catch (Exception e) { Main.out("Error: MLG_MD5 from file failed"); @@ -238,7 +239,8 @@ public class MLG_Update { new Long(line.substring(pos + 1, end)); Main.MLG_Last_Modified_Date = new Date(Main.MLG_Last_Modified_Long); - Long highestModTime = Main.ZipGetModificationTime(Main.MLGFileName); + Long highestModTime = + MLG_Update.ZipGetModificationTime(Main.MLGFileName); long tCalc = Main.MLG_Last_Modified_Long - highestModTime; if (Main.testing) { @@ -339,7 +341,7 @@ public class MLG_Update { e1.printStackTrace(); try { - Main.copyFile(new File(Main.MLG_JarFile), new File(Main.MLG_JarFile + MLG_Misc.copyFile(new File(Main.MLG_JarFile), new File(Main.MLG_JarFile + ".old")); File fileDelete = new File(Main.MLG_JarFile); fileDelete.delete();