From 0be54d1fccaab17c6f54a63ed4953680f3d20908 Mon Sep 17 00:00:00 2001
From: Piegames <14054505+piegamesde@users.noreply.github.com>
Date: Wed, 14 Nov 2018 17:21:03 +0100
Subject: [PATCH] manual-spawnpoints now works
---
.classpath | 1 +
.settings/org.eclipse.jdt.core.prefs | 32 +++-
pom.xml | 7 +-
.../CommandLineMain.java | 158 +++++++++++++-----
.../MinecraftLandGenerator/Server.java | 133 +++++----------
.../MinecraftLandGenerator/World.java | 90 ++++++++++
src/main/resources/log4j2.properties | 13 ++
7 files changed, 299 insertions(+), 135 deletions(-)
create mode 100644 src/main/java/morlok8k/MinecraftLandGenerator/World.java
create mode 100644 src/main/resources/log4j2.properties
diff --git a/.classpath b/.classpath
index aa54b47..def0e4f 100644
--- a/.classpath
+++ b/.classpath
@@ -6,6 +6,7 @@
+
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index 1426f45..242ac95 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -7,6 +7,15 @@ org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
org.eclipse.jdt.core.circularClasspath=error
org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnull.secondary=
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary=
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullable.secondary=
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
@@ -15,6 +24,7 @@ org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.APILeak=warning
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
@@ -26,9 +36,10 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
-org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.fieldHiding=info
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
@@ -39,7 +50,9 @@ org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
@@ -48,12 +61,20 @@ org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warni
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
@@ -63,19 +84,27 @@ org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=enabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=enabled
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning
+org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning
+org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled
+org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedImport=warning
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
@@ -85,6 +114,7 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.release=disabled
diff --git a/pom.xml b/pom.xml
index 3a87875..8d4de58 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,7 +40,7 @@
commons-logging
commons-logging
- 1.1.1
+ 1.2
@@ -48,6 +48,11 @@
log4j-core
2.11.1
+
+ org.apache.logging.log4j
+ log4j-jcl
+ 2.11.1
+
org.joml
diff --git a/src/main/java/morlok8k/MinecraftLandGenerator/CommandLineMain.java b/src/main/java/morlok8k/MinecraftLandGenerator/CommandLineMain.java
index 1fe6e86..21d7227 100644
--- a/src/main/java/morlok8k/MinecraftLandGenerator/CommandLineMain.java
+++ b/src/main/java/morlok8k/MinecraftLandGenerator/CommandLineMain.java
@@ -1,6 +1,6 @@
package morlok8k.MinecraftLandGenerator;
-import java.nio.file.Files;
+import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
@@ -9,47 +9,33 @@ import java.util.stream.IntStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.config.Configurator;
import org.joml.Vector2i;
+import morlok8k.MinecraftLandGenerator.CommandLineMain.AutoSpawnpoints;
+import morlok8k.MinecraftLandGenerator.CommandLineMain.ManualSpawnpoints;
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;
-
-@Command(name = "mlg", subcommands = { HelpCommand.class })
+import picocli.CommandLine.ParentCommand;
+import picocli.CommandLine.RunAll;
+@Command(name = "MinecraftLandGenerator",
+ subcommands = { HelpCommand.class, ManualSpawnpoints.class, AutoSpawnpoints.class })
public class CommandLineMain implements Runnable {
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")
- private boolean regionFile = false;
- @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)
- private int increment = 380;
-
- @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")
- private int zOffset = 0;
-
- @Parameters(index = "0", description = "X-coordinate")
- private int X;
-
- @Parameters(index = "1", description = "Z-coordinate")
- private int Z;
+ @Option(names = { "--debug-server" },
+ description = "Print the Minecraft server log to stdout for debugging")
+ private boolean debugServer = false;
@Option(names = { "-s", "--serverFile" }, description = "Path to the server's jar file.",
defaultValue = "server.jar", showDefaultValue = Visibility.ALWAYS)
@@ -63,37 +49,117 @@ public class CommandLineMain implements Runnable {
description = "Java command to launch the server. Defaults to `java -jar`.")
private String[] javaOpts;
+ @Command(name = "auto-spawnpoints")
+ public static class AutoSpawnpoints implements Runnable {
+
+ @ParentCommand
+ private CommandLineMain parent;
+
+ @Option(names = "-i", description = "override the iteration spawn offset increment",
+ defaultValue = "380", showDefaultValue = CommandLine.Help.Visibility.ALWAYS)
+ private int increment = 380;
+
+ @Parameters(index = "0", description = "X-coordinate")
+ private int x;
+ @Parameters(index = "1", description = "Z-coordinate")
+ private int z;
+ @Parameters(index = "2", description = "Width")
+ private int w;
+ @Parameters(index = "3", description = "Height")
+ private int h;
+
+ public AutoSpawnpoints() {
+ }
+
+ @Override
+ public void run() {
+ Server server = new Server(parent.debugServer, parent.javaOpts, parent.serverFile);
+ World world;
+ try {
+ world = server.initWorld(parent.worldPath);
+ } catch (IOException | InterruptedException e) {
+ log.error("Could not initialize world", e);
+ return;
+ }
+
+ log.info("Generating world");
+ server.runMinecraft(world, generateSpawnpoints(x, z, w, h, increment, 12));
+ log.info("Cleaning up temporary files");
+ try {
+ world.resetSpawn();
+ server.restoreWorld();
+ } catch (IOException e) {
+ log.warn(
+ "Could not delete backup files (server.properties.bak and level.dat.bak). Please delete them manually",
+ e);
+ }
+ log.info("Done.");
+ }
+
+ }
+
+ @Command(name = "manual-spawnpoints")
+ public static class ManualSpawnpoints implements Runnable {
+
+ @ParentCommand
+ private CommandLineMain parent;
+
+// @Option(names = { "-s", "--customspawn" }, description = "Customized SpawnPoints")
+// private String[] customSpawnPoints;
+
+ public ManualSpawnpoints() {
+ }
+
+ @Override
+ public void run() {
+ Server server = new Server(parent.debugServer, parent.javaOpts, parent.serverFile);
+ World world;
+ try {
+ world = server.initWorld(parent.worldPath);
+ } catch (IOException | InterruptedException e) {
+ log.error("Could not initialize world", e);
+ return;
+ }
+ List spawnpoints = new ArrayList<>();
+ spawnpoints.add(new Vector2i(100, 100));
+ spawnpoints.add(new Vector2i(200, 100));
+ spawnpoints.add(new Vector2i(300, 100));
+ spawnpoints.add(new Vector2i(400, 100));
+ log.info("Generating world");
+ server.runMinecraft(world, spawnpoints);
+ log.info("Cleaning up temporary files");
+ try {
+ world.resetSpawn();
+ server.restoreWorld();
+ } catch (IOException e) {
+ log.warn(
+ "Could not delete backup files (server.properties.bak and level.dat.bak). Please delete them manually",
+ e);
+ }
+ log.info("Done.");
+ }
+ }
+
public CommandLineMain() {
}
@Override
public void run() {
- 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 (verbose) {
+ Configurator.setRootLevel(Level.DEBUG);
}
- 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) {
+ /* Without this, JOML will print vectors out in scientific notation which isn't the most human readable thing in the world */
+ System.setProperty("joml.format", "false");
+
+ args = new String[] { "-v", "--debug-server", "-s",
+ "/home/piegames/Documents/GitHub/MinecraftLandGenerator/testserver/server.jar",
+ "auto-spawnpoints", "200", "100", "200", "100" };
CommandLine cli = new CommandLine(new CommandLineMain());
- cli.parseWithHandler(new RunLast(), args);
+ cli.parseWithHandler(new RunAll(), args);
}
/**
diff --git a/src/main/java/morlok8k/MinecraftLandGenerator/Server.java b/src/main/java/morlok8k/MinecraftLandGenerator/Server.java
index 8772764..83ba8c8 100644
--- a/src/main/java/morlok8k/MinecraftLandGenerator/Server.java
+++ b/src/main/java/morlok8k/MinecraftLandGenerator/Server.java
@@ -8,35 +8,23 @@
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.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
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.joml.Vector2i;
-import org.joml.Vector3i;
-
-import com.flowpowered.nbt.CompoundMap;
-import com.flowpowered.nbt.CompoundTag;
-import com.flowpowered.nbt.IntTag;
-import com.flowpowered.nbt.Tag;
-import com.flowpowered.nbt.stream.NBTInputStream;
-import com.flowpowered.nbt.stream.NBTOutputStream;
/**
*
@@ -48,8 +36,10 @@ public class Server {
protected final ProcessBuilder builder;
protected final Path workDir;
+ protected final boolean debugServer;
- public Server(String[] javaOpts, Path serverFile) {
+ public Server(boolean debugServer, String[] javaOpts, Path serverFile) {
+ this.debugServer = debugServer;
List opts = new ArrayList<>(
Arrays.asList(javaOpts != null ? javaOpts : new String[] { "java", "-jar" }));
opts.add(serverFile.toString());
@@ -60,6 +50,23 @@ public class Server {
builder.directory(workDir.toFile());
}
+ public World initWorld(Path worldPath) throws IOException, InterruptedException {
+ if (worldPath != null)
+ setWorld(worldPath);
+ else worldPath = 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...");
+ runMinecraft();
+ worldPath = getWorld();
+ if (!worldPath.isAbsolute()) worldPath = workDir.resolve(worldPath);
+ }
+ if (worldPath == null || !Files.exists(worldPath))
+ throw new NoSuchFileException(String.valueOf(worldPath));
+ log.debug("Using world path " + worldPath);
+ return new World(worldPath);
+ }
+
public void setWorld(Path worldPath) throws IOException {
Path propsFile = workDir.resolve("server.properties");
if (!Files.exists(propsFile)) {
@@ -79,83 +86,32 @@ public class Server {
Properties props = new Properties();
if (!Files.exists(propsFile)) return null;
props.load(Files.newInputStream(propsFile));
- return Paths.get(props.getProperty("level-name"));
+ if (!props.containsKey("level-name")) return null;
+ log.info("Found level in server.properties: " + props.getProperty("level-name"));
+ Path p = Paths.get(props.getProperty("level-name"));
+ if (!p.isAbsolute()) p = workDir.resolve(p);
+ return p;
}
public void restoreWorld() throws IOException {
Path propsFile = workDir.resolve("server.properties");
- Files.move(propsFile.resolveSibling("server.properties.bak"), propsFile,
- StandardCopyOption.REPLACE_EXISTING);
+ if (Files.exists(propsFile.resolveSibling("server.properties.bak")))
+ 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 (NBTInputStream input = new NBTInputStream(new FileInputStream(level));) {
- final CompoundTag levelTag = (CompoundTag) input.readTag();
- final Map> levelData =
- ((CompoundTag) levelTag.getValue().get("Data")).getValue();
-
- final IntTag spawnX = (IntTag) levelData.get("SpawnX");
- final IntTag spawnY = (IntTag) levelData.get("SpawnY");
- final IntTag spawnZ = (IntTag) levelData.get("SpawnZ");
-
- return new Vector3i(spawnX.getValue(), spawnY.getValue(), spawnZ.getValue());
- } catch (final ClassCastException | 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.
- * 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(final File level, final Vector3i xyz) throws IOException {
- // TODO clean this up even more
- try (NBTInputStream input = new NBTInputStream(new FileInputStream(level));) {
- final CompoundTag originalTopLevelTag = (CompoundTag) input.readTag();
- input.close();
-
- final 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.
- final Map> newData = new LinkedHashMap<>(originalData);
-
- 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", new CompoundMap(newData));
- final Map> newTopLevelMap = new HashMap<>(1);
- newTopLevelMap.put("Data", newDataTag);
- final CompoundTag newTopLevelTag = new CompoundTag("", new CompoundMap(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.");
+ public void runMinecraft(World world, List spawnpoints) {
+ log.debug("All spawn points: " + spawnpoints);
+ for (int i = 0; i < spawnpoints.size(); i++) {
+ Vector2i spawn = spawnpoints.get(i);
+ try {
+ log.info("Processing " + i + "/" + spawnpoints.size() + ", spawn point " + spawn);
+ world.setSpawn(spawn);
+ runMinecraft();
+ } catch (IOException | InterruptedException e) {
+ log.warn("Could not process spawn point " + spawn
+ + " this part of the world don't be generated", e);
+ }
}
}
@@ -168,6 +124,9 @@ public class Server {
* @author Corrodias, Morlok8k, piegames
*/
public void runMinecraft() throws IOException, InterruptedException {
+ log.debug("Setting EULA");
+ Files.write(workDir.resolve("eula.txt"), "eula=true".getBytes(), StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
log.info("Starting server");
final Process process = builder.start();
@@ -175,7 +134,7 @@ public class Server {
new BufferedReader(new InputStreamReader(process.getInputStream()));
for (String line = pOut.readLine(); line != null; line = pOut.readLine()) {
line = line.trim();
- if (log.isDebugEnabled()) log.debug(line);
+ if (debugServer) System.out.println(line);
if (line.contains("Done")) {
PrintStream out = new PrintStream(process.getOutputStream());
diff --git a/src/main/java/morlok8k/MinecraftLandGenerator/World.java b/src/main/java/morlok8k/MinecraftLandGenerator/World.java
new file mode 100644
index 0000000..2217f87
--- /dev/null
+++ b/src/main/java/morlok8k/MinecraftLandGenerator/World.java
@@ -0,0 +1,90 @@
+package morlok8k.MinecraftLandGenerator;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.joml.Vector2i;
+import org.joml.Vector3i;
+
+import com.flowpowered.nbt.CompoundMap;
+import com.flowpowered.nbt.CompoundTag;
+import com.flowpowered.nbt.IntTag;
+import com.flowpowered.nbt.Tag;
+import com.flowpowered.nbt.stream.NBTInputStream;
+import com.flowpowered.nbt.stream.NBTOutputStream;
+
+public class World {
+ private static Log log = LogFactory.getLog(World.class);
+
+ public final Path world;
+
+ public World(Path world) throws IOException {
+ this.world = Objects.requireNonNull(world);
+ Files.copy(world.resolve("level.dat"), world.resolve("level.dat.bak"));
+ }
+
+ public void resetSpawn() throws IOException {
+ if (Files.exists(world.resolve("level.dat.bak"))) Files.move(world.resolve("level.dat.bak"),
+ world.resolve("level.dat"), StandardCopyOption.REPLACE_EXISTING);
+ }
+
+ public void setSpawn(Vector2i chunkSpawn) throws IOException {
+ setSpawn(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.
+ * 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
+ */
+ public void setSpawn(final Vector3i xyz) throws IOException {
+ log.debug("Setting spawn to " + xyz);
+ // TODO clean this up even more
+ try (NBTInputStream input =
+ new NBTInputStream(Files.newInputStream(world.resolve("level.dat")));) {
+ final CompoundTag originalTopLevelTag = (CompoundTag) input.readTag();
+ input.close();
+
+ final 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.
+ final Map> newData = new LinkedHashMap<>(originalData);
+
+ 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", new CompoundMap(newData));
+ final Map> newTopLevelMap = new HashMap<>(1);
+ newTopLevelMap.put("Data", newDataTag);
+ final CompoundTag newTopLevelTag = new CompoundTag("", new CompoundMap(newTopLevelMap));
+
+ try (NBTOutputStream output =
+ new NBTOutputStream(Files.newOutputStream(world.resolve("level.dat")));) {
+ 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.");
+ }
+ }
+}
diff --git a/src/main/resources/log4j2.properties b/src/main/resources/log4j2.properties
new file mode 100644
index 0000000..3554fbb
--- /dev/null
+++ b/src/main/resources/log4j2.properties
@@ -0,0 +1,13 @@
+appenders=xyz
+
+appender.xyz.type = Console
+appender.xyz.name = myOutput
+appender.xyz.layout.type = PatternLayout
+appender.xyz.layout.pattern = %d{yy-MM-dd HH:mm:ss} %-5p %c{1} [%L] - %m%n
+
+rootLogger.level = info
+
+rootLogger.appenderRefs = abc
+
+rootLogger.appenderRef.abc.ref = myOutput
+