This commit is contained in:
Piegames 2018-11-14 14:09:14 +01:00
parent 56e6493c27
commit 8d4b49f99c
4 changed files with 267 additions and 660 deletions

View File

@ -1,36 +1,48 @@
package morlok8k.MinecraftLandGenerator;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joml.Vector2i;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Help.Visibility;
import picocli.CommandLine.HelpCommand;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import picocli.CommandLine.RunLast;
import java.nio.file.Path;
@Command(name = "mlg", subcommands = {
HelpCommand.class})
@Command(name = "mlg", subcommands = { HelpCommand.class })
public class CommandLineMain implements Runnable {
@Option(names = {"-v", "--verbose"}, description = "Be verbose.")
private static Log log = LogFactory.getLog(CommandLineMain.class);
@Option(names = { "-v", "--verbose" }, description = "Be verbose.")
private boolean verbose = false;
@Option(names = {"-r", "--region"}, description = "Regionfiles instead of chunks")
@Option(names = { "-r", "--region" }, description = "Regionfiles instead of chunks")
private boolean regionFile = false;
@Option(names = {"-s", "--customspawn"}, description = "Customized SpawnPoints")
@Option(names = { "-s", "--customspawn" }, description = "Customized SpawnPoints")
private String[] customSpawnPoints;
@Option(names = "-i", description = "override the iteration spawn offset increment", defaultValue = "380", showDefaultValue = CommandLine.Help.Visibility.ALWAYS)
@Option(names = "-i", description = "override the iteration spawn offset increment",
defaultValue = "380", showDefaultValue = CommandLine.Help.Visibility.ALWAYS)
private int increment = 380;
@Option(names = {"--x-offset", "-x"}, description = "set the X offset to generate land around")
@Option(names = { "--x-offset", "-x" },
description = "set the X offset to generate land around")
private int xOffset = 0;
@Option(names = {"--y-offset", "-y"}, description = "set the Z offset to generate land around")
@Option(names = { "--y-offset", "-y" },
description = "set the Z offset to generate land around")
private int zOffset = 0;
@Parameters(index = "0", description = "X-coordinate")
@ -39,15 +51,17 @@ public class CommandLineMain implements Runnable {
@Parameters(index = "1", description = "Z-coordinate")
private int Z;
@Option(names = {"-s","--serverFile"}, description = "path to the directory in which the server runs")
private Path serverPath;
@Option(names = { "-s", "--serverFile" }, description = "Path to the server's jar file.",
defaultValue = "server.jar", showDefaultValue = Visibility.ALWAYS)
private Path serverFile;
@Option(names= {"-w","--worldPath"}, description = "path to the to be generated world")
@Option(names = { "-w", "--worldPath" },
description = "Path to the world that should be generated. Defaults to the value in server.properties")
private Path worldPath;
@Option(names= {"-d","--workDir"}, description = "workDirectory")
private Path workDir;
@Option(names = { "--java-cmd" },
description = "Java command to launch the server. Defaults to `java -jar`.")
private String[] javaOpts;
public CommandLineMain() {
@ -55,7 +69,26 @@ public class CommandLineMain implements Runnable {
@Override
public void run() {
CommandLine.usage(this, System.err);
Server server = new Server(javaOpts, serverFile);
if (worldPath != null)
server.setWorld(worldPath);
else worldPath = server.getWorld();
if (worldPath == null || !Files.exists(worldPath)) {
log.warn(
"No world was specified or the world at the given path does not exist. Starting the server once to create one...");
server.runMinecraft();
worldPath = server.getWorld();
}
if (worldPath == null || !Files.exists(worldPath)) {
log.fatal("There is still no world, we cannot continue without world");
return;
}
for (Vector2i spawn : generateSpawnpoints(startX, startZ, width, height, maxInc,
generationRadius)) {
server.setSpawn(level, spawn);
server.runMinecraft();
}
server.restoreWorld();
}
public static void main(String[] args) {
@ -63,4 +96,32 @@ public class CommandLineMain implements Runnable {
cli.parseWithHandler(new RunLast(), args);
}
/**
* @param maxInc
* Maximum number of chunks between two spawn points, horizontally or vertically
* @param generationMargin
* The radius to each side that will be generated by the server (Not the diameter!)
*/
public static List<Vector2i> generateSpawnpoints(int startX, int startZ, int width, int height,
int maxInc, int generationRadius) {
if (width < generationRadius || height < generationRadius)
throw new IllegalArgumentException("Width and height must both be at least "
+ (generationRadius * 2 + 1) + ", but are " + width + " and " + height);
List<Integer> xPoints = generateLinearSpawnpoints(startX + generationRadius,
width - generationRadius * 2, maxInc);
List<Integer> zPoints = generateLinearSpawnpoints(startZ + generationRadius,
height - generationRadius * 2, maxInc);
List<Vector2i> spawnPoints = new ArrayList<>(xPoints.size() * zPoints.size());
for (int x : xPoints)
for (int z : zPoints)
spawnPoints.add(new Vector2i(x, z));
return spawnPoints;
}
private static List<Integer> generateLinearSpawnpoints(int start, int length, int maxStep) {
int stepCount = (int) Math.ceil((double) length / maxStep);
double realStep = length / stepCount;
return IntStream.range(0, stepCount).mapToObj(i -> start + (int) (realStep * i))
.collect(Collectors.toList());
}
}

View File

@ -1,480 +0,0 @@
/*
#######################################################################
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE #
# Version 2, December 2004 #
# #
# Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> #
# #
# Everyone is permitted to copy and distribute verbatim or modified #
# copies of this license document, and changing it is allowed as long #
# as the name is changed. #
# #
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE #
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION #
# #
# 0. You just DO WHAT THE FUCK YOU WANT TO. #
# #
#######################################################################
*/
/*
* The Computer Programmer's Lament: Program complexity grows until it exceeds the capability of the programmer who must maintain it.
*/
package morlok8k.MinecraftLandGenerator;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joml.Vector3i;
/**
* @author Corrodias, Morlok8k, pr0f1x
*/
public class Main {
//////////////////////////////////////////////////////////
// REMINDER: Because I always forget/mix up languages: //
// "static" in java means that there will only ever be ONE of it created, shared by all the instances of that class.//
// "final" means "constant" //
// public/private shows/hides between classes //
//////////////////////////////////////////////////////////
/**
* @param args
* the command line arguments
*/
private static Log log = LogFactory.getLog(Main.class);
public static void main(String[] args) {
Main.runCLI();
}
/**
* Start MinecraftLandGenerator (Command Line Interface)
*
* @author Corrodias, Morlok8k
*/
private static void runCLI() {
final File backupLevel;
final File serverLevel;
// Basic Program Initialization
Startup.initialStart();
if (Startup.programArguments()) {
log.error("Error in program arguments.");
return;
}
if (Startup.confFile()) {
log.error("Error in conf file.");
return;
}
try {
if (Setup.doSetup()) {
log.error("Error in setup.");
return;
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} // Checks for server.properties, checks for world, creates world if needed, checks old data}
// =====================================================================
// PROCESSING
// =====================================================================
log.info("Processing world \"" + var.worldPath + "\", in " + var.increment
+ " block increments, with: " + var.javaLine);
// out( MLG + "Processing \"" + worldName + "\"...");
log.info("");
try {
final long generationStartTimeTracking = System.currentTimeMillis(); //Start of time remaining calculations
final boolean serverLaunch = Server.runMinecraft(); //run server once at spawn point to make sure everything works.
//final boolean serverLaunch = true; //testing only
if (!(serverLaunch)) {
System.exit(1); // we got a warning or severe error
}
if ((var.xRange == 0) & (var.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;
}
FileWrite.AppendTxtFile(var.worldPath + var.fileSeparator + var.logFile,
"# " + var.PROG_NAME + " " + var.VERSION + " - " + SelfAware.JVMinfo()
+ var.newLine + "# " + var.MC_Server_Version + var.newLine
+ "# Started: " + var.dateFormat.format(generationStartTimeTracking)
+ var.newLine + "##Size: X" + var.xRange + "Z" + var.zRange
+ var.newLine);
log.info("");
serverLevel = new File(var.worldPath + var.fileSeparator + "level.dat");
backupLevel = new File(var.worldPath + var.fileSeparator + "level_backup.dat");
log.info("Backing up level.dat to level_backup.dat.\n");
Misc.copyFile(serverLevel, backupLevel);
final Vector3i spawn = SpawnPoint.getSpawn(serverLevel);
log.info("Spawn point detected: [X,Y,Z] " + spawn);
FileWrite.AppendTxtFile(var.worldPath + var.fileSeparator + var.logFile, "# Seed: "
+ var.randomSeed + var.newLine + "# Spawn: " + spawn.toString() + var.newLine);
boolean overridden = false;
if (var.xOffset == null) {
var.xOffset = spawn.x;
} else {
overridden = true;
}
if (var.zOffset == null) {
var.zOffset = spawn.z;
} else {
overridden = true;
}
double xR = 0, zR = 0, xO = 0, zO = 0;
if (var.useChunks) { // use Chunks or Regions
xR = var.xRange; //say xRange was entered as 1000. this changes it to be 1008, a multiple of 16. (the size of a chunk)
xR = xR / 16;
xR = Math.ceil(xR);
xR = xR * 16;
zR = var.zRange; //say zRange was entered as 2000. there is no change, as it already is a multiple of 16.
zR = zR / 16;
zR = Math.ceil(zR);
zR = zR * 16;
xO = var.xOffset;
xO = xO / 16;
xO = Math.round(xO); //round instead of Ceiling
xO = xO * 16;
zO = var.zOffset;
zO = zO / 16;
zO = Math.round(zO); //round instead of Ceiling
zO = zO * 16;
} else {
xR = var.xRange; //say xRange was entered as 1000. this changes it to be 1024, a multiple of 512. (the size of a region)
xR = xR / 512;
xR = Math.ceil(xR);
xR = xR * 512;
zR = var.zRange; //say zRange was entered as 2048. there is no change, as it already is a multiple of 512.
zR = zR / 512;
zR = Math.ceil(zR);
zR = zR * 512;
xO = var.xOffset;
xO = xO / 512;
xO = Math.round(xO); //round instead of Ceiling
xO = xO * 512;
zO = var.zOffset;
zO = zO / 512;
zO = Math.round(zO); //round instead of Ceiling
zO = zO * 512;
}
var.xRange = (int) Math.ceil(xR);
var.zRange = (int) Math.ceil(zR);
var.xOffset = (int) Math.ceil(xO);
var.zOffset = (int) Math.ceil(zO);
if (overridden) {
log.info("Centering land generation on [" + var.xOffset + ", " + var.zOffset
+ "] due to switches.");
} else {
log.info("Centering land generation on [" + var.xOffset + ", " + var.zOffset
+ "]\n");
}
double xLoops, zLoops;
long curXloops = 0;
long curZloops = 0;
double xRangeAdj = 0;
double zRangeAdj = 0;
// 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
xRangeAdj = var.xRange - var.incrementFull; //Adjusting our range of X
xRangeAdj = xRangeAdj / var.increment;
xRangeAdj = Math.ceil(xRangeAdj); //round up to find out!
xRangeAdj = xRangeAdj + 1; //add an additional increment, as we removed a full one above
xRangeAdj = xRangeAdj * var.increment;
double inc = var.increment * 2; //increment*2, and casts to double.
// thanks to e2pii (on reddit) for correcting my math here.
// http://www.reddit.com/r/learnmath/comments/2y14fq/what_am_i_overlooking_here/
xLoops = (var.xRange - var.incrementFull) / inc; //How many loops do we need?
xLoops = Math.ceil(xLoops);
xLoops = xLoops + xLoops + 1;
// Z
zRangeAdj = var.zRange - var.incrementFull; //Adjusting our range of Z
zRangeAdj = zRangeAdj / var.increment;
zRangeAdj = Math.ceil(zRangeAdj); //round up to find out!
zRangeAdj = zRangeAdj + 1; //add an additional increment, as we removed a full one above
zRangeAdj = zRangeAdj * var.increment;
zLoops = (var.zRange - var.incrementFull) / inc; //How many loops do we need?
zLoops = Math.ceil(zLoops);
zLoops = zLoops + zLoops + 1;
log.info("Calculating Spawn Points...");
// Perfect Squares Code:
/*
if (xLoops > 3) {
xLoops = xLoops + 1;
}
if (zLoops > 3) {
zLoops = zLoops + 1;
}
*/
long totalIterations = (long) (xLoops * zLoops);
log.info("Estimated Total Spawn Points: " + totalIterations);
if (totalIterations > Integer.MAX_VALUE) {
log.error(
"TOO BIG! Please reduce the world size. World Size can't be larger than 17609200 x 17609200"); //or 17794560 using -i384
backupLevel.delete();
log.info("Removed backup file.");
System.exit(0);
}
long currentIteration = 0;
long differenceTime = System.currentTimeMillis();
Long timeTracking;
ArrayList<Vector3i> launchList = new ArrayList<>(0);
try {
launchList = new ArrayList<>((int) totalIterations);
} catch (Exception e1) {
e1.printStackTrace();
log.error(
"TOO BIG! Your computer can't handle such a large map. The size is dependant on 32/64 bit and Memory.");
backupLevel.delete();
log.info("Removed backup file.");
System.exit(0);
}
// X - West to East
for (long currentX = 0; currentX <= (xRangeAdj / 2); currentX += var.increment) {
curXloops++;
boolean eastEdgeReached = false;
if (curXloops == 1) {
currentX = (((0 - var.xRange) / 2) + (var.incrementFull / 2)); // West Edge of map
} else if (currentX >= ((xRangeAdj / 2) - (var.increment / 2))) {
currentX = ((var.xRange / 2) - (var.incrementFull / 2)); // East Edge of map
eastEdgeReached = true;
}
// Z - North to South
for (long currentZ = 0; currentZ <= (zRangeAdj / 2); currentZ += var.increment) {
currentIteration++;
curZloops++;
boolean southEdgeReached = false;
if (curZloops == 1) {
currentZ = (((0 - var.zRange) / 2) + (var.incrementFull / 2)); // North Edge of map
} else if (currentZ >= ((zRangeAdj / 2) - (var.increment / 2))) {
currentZ = ((var.zRange / 2) - (var.incrementFull / 2)); // South Edge of map
southEdgeReached = true;
}
{ // Middle of Loop
if (currentIteration % 10000000 == 0) { //for long calculations, we output an update every 10,000,000 points
String percentDone = Double.toString(
(((double) currentIteration) / totalIterations) * 100);
final 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
log.info("Calculated: " + currentIteration + "/" + totalIterations
+ " spawn points. (" + percentDone + "% Done)");
}
// add Coordinates to arraylist here
final Vector3i tempCoords = new Vector3i((int) currentX + var.xOffset, 64,
(int) currentZ + var.zOffset);
launchList.add(tempCoords);
// Write the current Coordinates to log file!
//FileWrite.AppendTxtFile(var.worldPath + var.fileSeparator
// + "MLG_coordinate_list.log", tempCoords.toString() + var.newLine);
if (var.testing) {
System.out.println(tempCoords);
}
} // End of the Middle of Loop
if (curZloops == 1) { // We are at the North edge. We have special code for the North edge, so we need to change currentZ to be normal again.
currentZ = (long) ((Math.ceil((((0 - zRangeAdj) / 2) / var.increment)))
* var.increment);
}
if (southEdgeReached) {
currentZ = (long) zRangeAdj; // We reached the South edge, so we make sure that we exit the "for loop", bypassing the "1152 bug"
}
} // End Z
curZloops = 0;
if (curXloops == 1) { // We are at the West edge. We have special code for the West edge, so we need to change currentX to be normal again.
currentX = (long) ((Math.ceil((((0 - xRangeAdj) / 2) / var.increment)))
* var.increment);
}
if (eastEdgeReached) {
currentX = (long) xRangeAdj; // We reached the East edge, so we make sure that we exit the "for loop", bypassing the "1152 bug"
}
} // End X
String pD = Double.toString((((double) currentIteration) / totalIterations) * 100);
final int pI =
((pD.indexOf(".") + 3) > pD.length()) ? pD.length() : (pD.indexOf(".") + 3); //fix index on numbers like 12.3
pD = pD.substring(0, (pD.indexOf(".") == -1 ? pD.length() : pI)); //Trim output, unless whole number
log.info("Calculated: " + currentIteration + "/" + totalIterations + " spawn points. ("
+ pD + "% Done)");
//get existing list, and remove this list from launchList
final ArrayList<Vector3i> removeList =
FileRead.readArrayListCoordLog(var.worldPath + var.fileSeparator + var.logFile);
log.info("Removing known generated areas...");
if (!removeList.isEmpty()) {
launchList.removeAll(removeList);
}
removeList.clear(); // we are done with this now.
System.gc(); //run the garbage collector - hopefully free up some memory!
currentIteration = 0;
totalIterations = launchList.size();
Vector3i xyz = null;
final Iterator<Vector3i> coordArrayIterator = launchList.iterator();
while (coordArrayIterator.hasNext()) {
currentIteration++;
xyz = coordArrayIterator.next();
//////// Start server launch code
String percentDone =
Double.toString((((double) currentIteration - 1) / totalIterations) * 100);
final 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
log.info("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.
log.info("Estimated time remaining: " + String.format("%02d:%02d",
(differenceTime / 1000) / 60, (differenceTime / 1000) % 60)); // I've noticed it gets pretty accurate after about 8 launches!
// Set the spawn point
SpawnPoint.setSpawn(serverLevel, xyz);
// Launch the server
boolean serverSuccess = false;
serverSuccess = Server.runMinecraft();
log.info("");
//////// End server launch code
if (serverSuccess) {
// Write the current Coordinates to log file!
FileWrite.AppendTxtFile(var.worldPath + var.fileSeparator + var.logFile,
xyz.toString() + var.newLine);
} else {
System.exit(1); // we got a warning or severe error
}
}
if (currentIteration == 0) {
log.info("Nothing to generate!");
} else {
//TODO: write completion code to output.
// FileWrite.AppendTxtFile(var.worldPath + var.fileSeparator + var.logFile, ####### + var.newLine);
//
//add xrange, zrange, region/chunk, xoffset, zoffset, increment //anything else?
//
log.info("Finished generating chunks.");
}
Misc.copyFile(backupLevel, serverLevel);
backupLevel.delete();
log.info("Restored original level.dat.");
long completeIn = var.startTime - System.currentTimeMillis();
log.info("Generation complete in: " + String.format("%02d:%02d",
(completeIn / 1000) / 60, (completeIn / 1000) % 60));
// Time.waitTenSec(false);
if (var.webLaunch) { //if webLaunch is already false, don't check for these things
if (java.awt.GraphicsEnvironment.isHeadless()) {
var.webLaunch = false; //headless enviroment - cant bring up webpage!
}
final File web1 = new File("web");
final File web2 = new File("web.txt"); //user has put in the magical file to not launch the webpage
final File web3 = new File("web.txt.txt");
if (web2.exists() || (web1.exists() || web3.exists())) { //check for "web.txt", if not found, check for "web", and if still not found, check for "web.txt.txt"
var.webLaunch = false;
}
}
if (var.webLaunch && java.awt.Desktop.isDesktopSupported()) {
final URI splashPage = URI.create("http://adf.ly/520855/splashbanner");
try {
java.awt.Desktop.getDesktop().browse(splashPage);
} catch (final IOException e) {
log.error("Error displaying webpage... " + e.getLocalizedMessage());
}
} else {
log.info("Please Visit: http://adf.ly/520855/mlg");
log.info("Or: " + var.WEBSITE);
log.info("Thanks!");
}
} catch (final IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
}
}
}

View File

@ -8,12 +8,33 @@
package morlok8k.MinecraftLandGenerator;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jnbt.CompoundTag;
import org.jnbt.IntTag;
import org.jnbt.NBTInputStream;
import org.jnbt.NBTOutputStream;
import org.jnbt.Tag;
import org.joml.Vector2i;
import org.joml.Vector3i;
/**
*
@ -21,7 +42,170 @@ import org.apache.commons.logging.LogFactory;
*/
public class Server {
private static Log log = LogFactory.getLog(Main.class);
private static Log log = LogFactory.getLog(Server.class);
protected final ProcessBuilder builder;
protected final Path workDir;
public Server(String[] javaOpts, Path serverFile) {
List<String> opts = new ArrayList<>(
Arrays.asList(javaOpts != null ? javaOpts : new String[] { "java", "-jar" }));
opts.add(serverFile.toString());
opts.add("nogui");
builder = new ProcessBuilder(opts);
builder.redirectErrorStream(true);
workDir = serverFile.getParent();
builder.directory(workDir.toFile());
}
public void setWorld(Path worldPath) throws IOException {
Path propsFile = workDir.resolve("server.properties");
if (!Files.exists(propsFile)) {
Files.write(propsFile, "level-name=".concat(propsFile.toString()).getBytes());
} else {
/* Make a backup first*/
Files.copy(propsFile, propsFile.resolveSibling("server.properties.bak"));
Properties props = new Properties();
props.load(Files.newInputStream(propsFile));
props.put("level-name", worldPath.toString());
props.store(Files.newOutputStream(propsFile), null);
}
}
public Path getWorld() throws IOException {
Path propsFile = workDir.resolve("server.properties");
Properties props = new Properties();
if (!Files.exists(propsFile)) return null;
props.load(Files.newInputStream(propsFile));
return Paths.get(props.getProperty("level-name"));
}
public void restoreWorld() throws IOException {
Path propsFile = workDir.resolve("server.properties");
Files.move(propsFile.resolveSibling("server.properties.bak"), propsFile,
StandardCopyOption.REPLACE_EXISTING);
}
/**
* @param level
* @return
* @throws IOException
* @author Corrodias
*/
protected static Vector3i getSpawn(final File level) throws IOException {
try {
final NBTInputStream input = new NBTInputStream(new FileInputStream(level));
final CompoundTag originalTopLevelTag = (CompoundTag) input.readTag();
input.close();
final Map<String, Tag> 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.
final Map<String, Tag> 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.
final IntTag spawnX = (IntTag) newData.get("SpawnX");
final IntTag spawnY = (IntTag) newData.get("SpawnY");
final IntTag spawnZ = (IntTag) newData.get("SpawnZ");
final Vector3i ret =
new Vector3i(spawnX.getValue(), spawnY.getValue(), spawnZ.getValue());
return ret;
} catch (final ClassCastException ex) {
throw new IOException("Invalid level format.");
} catch (final NullPointerException ex) {
throw new IOException("Invalid level format.");
}
}
protected static void setSpawn(Path world, Vector2i chunkSpawn) throws IOException {
setSpawn(world.resolve("level.dat").toFile(),
new Vector3i(chunkSpawn.x << 4 | 7, 64, chunkSpawn.y << 4 | 8));
}
/**
* Changes the spawn point in the given Alpha/Beta level to the given coordinates.<br>
* Note that, in Minecraft levels, the Y coordinate is height.<br>
* (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(final File level, final Vector3i xyz) throws IOException {
try {
final NBTInputStream input = new NBTInputStream(new FileInputStream(level));
final 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.
/* <editor-fold defaultstate="collapsed" desc="structure">
* 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.
* </editor-fold>
*/
//@formatter:on
final Map<String, Tag> 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.
final Map<String, Tag> 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")
final IntTag spawnX = (IntTag) newData.get("SpawnX"); // we never use these... Its only here for potential debugging.
@SuppressWarnings("unused")
final IntTag spawnY = (IntTag) newData.get("SpawnY"); // but whatever... so I (Morlok8k) suppressed these warnings.
@SuppressWarnings("unused")
final 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.x)); // pulling the data out of the Coordinates,
newData.put("SpawnY", new IntTag("SpawnY", xyz.y)); // and putting it into our IntTag's
newData.put("SpawnZ", new IntTag("SpawnZ", xyz.z));
// Again, we can't modify the data map in the old Tag, so we have to make a new one.
final CompoundTag newDataTag = new CompoundTag("Data", newData);
final Map<String, Tag> newTopLevelMap = new HashMap<>(1);
newTopLevelMap.put("Data", newDataTag);
final CompoundTag newTopLevelTag = new CompoundTag("", newTopLevelMap);
final NBTOutputStream output = new NBTOutputStream(new FileOutputStream(level));
output.writeTag(newTopLevelTag);
output.close();
} catch (final ClassCastException ex) {
throw new IOException("Invalid level format.");
} catch (final NullPointerException ex) {
throw new IOException("Invalid level format.");
}
}
/**
* Starts the process in the given ProcessBuilder, monitors its output for a "Done" message, and sends it a "stop" message.
@ -31,9 +215,9 @@ public class Server {
* @throws InterruptedException
* @author Corrodias, Morlok8k, piegames
*/
protected static void runMinecraft() throws IOException, InterruptedException {
log.info("Starting server.");
final Process process = var.minecraft.start();
public void runMinecraft() throws IOException, InterruptedException {
log.info("Starting server");
final Process process = builder.start();
final BufferedReader pOut =
new BufferedReader(new InputStreamReader(process.getInputStream()));
@ -44,7 +228,7 @@ public class Server {
if (line.contains("Done")) {
PrintStream out = new PrintStream(process.getOutputStream());
log.info("Stopping server... ");
log.info("Stopping server...");
out.println("save-all");
out.flush();
out.println("stop");

View File

@ -1,158 +0,0 @@
/*
#######################################################################
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE #
# Version 2, December 2004 #
# #
# Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> #
# #
# Everyone is permitted to copy and distribute verbatim or modified #
# copies of this license document, and changing it is allowed as long #
# as the name is changed. #
# #
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE #
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION #
# #
# 0. You just DO WHAT THE FUCK YOU WANT TO. #
# #
#######################################################################
*/
package morlok8k.MinecraftLandGenerator;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author morlok8k
*/
//TODO : Remove var.worldPath entirely because storing that is useless.
public class Setup {
private static Log log = LogFactory.getLog(Main.class);
static boolean doSetup() throws IOException {
final File serverPathFile;
final BufferedReader serverPropertiesFile;
final File levelDat;
final File backupLevel;
String line;
//---------- Verify server path
serverPathFile = new File(var.serverPath);
if (!serverPathFile.exists() || !serverPathFile.isDirectory()) {
/*FileNotFoundException fileException =
new FileNotFoundException("The server directory is invalid: " + var.serverPath);
throw fileException;*/
log.error("The server directory is invalid: " + var.serverPath);
return true;
}
//---------- Verify server.properties
try {
serverPropertiesFile = new BufferedReader(new FileReader(
new File(var.serverPath + var.fileSeparator + "server.properties")));
} catch (IOException e) {
log.error("Could not open the server.properties file.");
return true;
}
//---------- Set world name
try {
line = serverPropertiesFile.readLine();
} catch (IOException e) {
serverPropertiesFile.close();
return true;
}
while (line != null) {
if (line.contains("level-name")) { // Yep, that line contains it
if (line.contains("#") && line.indexOf('=') < line.indexOf('#')) {
// Apparently, "#slf dghdsf #gdcfggh" is a perfectly valid world name.
// In the other cases, the line is commented or invalid.
var.worldName = line.substring(line.indexOf('=') + 1);
var.worldPath = var.serverPath + var.fileSeparator + var.worldName;
} else { // There is no comment on the line
var.worldName = line.substring(line.indexOf('=') + 1);
var.worldPath = var.serverPath + var.fileSeparator + var.worldName;
}
}
try {
line = serverPropertiesFile.readLine();
} catch (IOException e) {
serverPropertiesFile.close();
return true;
}
}
serverPropertiesFile.close();
if (var.worldName == null) { // If after all this we still don't have a proper world name, stop everything and throw an exception
/*NullPointerException noNameException = new NullPointerException("There is no world name defined in the server.properties file!");
throw noNameException;*/
log.error("There is no world name defined in the server.properties file!");
return true;
}
//---------- Verify that the world exists and restore level_backup.dat if it exists. If not, start server once to create the world.
levelDat = new File(var.worldPath + var.fileSeparator + "level.dat");
backupLevel = new File(var.worldPath + var.fileSeparator + "level_backup.dat");
// prepare our two ProcessBuilders
// minecraft = new ProcessBuilder(javaLine, "-Xms1024m", "-Xmx1024m", "-jar", jarFile, "nogui");
var.minecraft = new ProcessBuilder(var.javaLine.split("\\s")); // is this always going to work? i don't know. (most likely yes)
var.minecraft.directory(new File(var.serverPath));
var.minecraft.redirectErrorStream(true);
if (levelDat.exists() && levelDat.isFile()) {
if (backupLevel.exists()) {
log.error(
"There is a level_backup.dat file left over from a previous attempt that failed.");
log.info("Resuming...");
//use resume data
final File serverLevel = new File(var.worldPath + var.fileSeparator + "level.dat");
try {
Misc.copyFile(backupLevel, serverLevel);
} catch (final IOException e) {
e.printStackTrace();
}
backupLevel.delete();
//return;
FileRead.readArrayListCoordLog(var.worldPath + var.fileSeparator + var.logFile); // we read the .log just for any resume data, if any.
System.gc(); //run the garbage collector - hopefully free up some memory!
var.xRange = var.resumeX;
var.zRange = var.resumeZ;
}
} else {
/*FileNotFoundException fileException =
new FileNotFoundException("The currently configured world does not exist.");*/
log.error(
"The currently configured world does not exist! Launching the server once to create it...");
try {
var.minecraft = new ProcessBuilder(var.javaLine.split("\\s")); // is this always going to work? i don't know. (most likely yes)
var.minecraft.directory(new File(var.serverPath));
var.minecraft.redirectErrorStream(true);
Server.runMinecraft();
} catch (IOException | InterruptedException e) {
return true;
}
log.error("World created! Starting world generation...");
}
return false;
}
}