diff --git a/core/src/main/java/com/meowster/mcquad/McQuad.java b/core/src/main/java/com/meowster/mcquad/McQuad.java
index d72d43c..29a26c0 100755
--- a/core/src/main/java/com/meowster/mcquad/McQuad.java
+++ b/core/src/main/java/com/meowster/mcquad/McQuad.java
@@ -23,7 +23,7 @@
public class McQuad {
/* This version is displayed in the masthead of the web app */
- private static final String MCQUAD_VERSION = "1.2.1";
+ private static final String MCQUAD_VERSION = "1.2.2";
private final ParsedArgs parsedArgs;
private final OutputUtils outputUtils;
diff --git a/core/src/main/java/com/meowster/mcquad/McaDump.java b/core/src/main/java/com/meowster/mcquad/McaDump.java
new file mode 100644
index 0000000..2a253fe
--- /dev/null
+++ b/core/src/main/java/com/meowster/mcquad/McaDump.java
@@ -0,0 +1,56 @@
+package com.meowster.mcquad;
+
+import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static com.meowster.util.StringUtils.printErr;
+import static java.lang.System.exit;
+
+/**
+ * Command-line based region file analysis program, for dumping information
+ * about MCA (Minecraft Anvil format) files.
+ *
+ * Usage: com.meowster.mcquad.McaDump {region file}.
+ *
+ */
+public class McaDump {
+
+ private static final Pattern RE_REGION_FILE =
+ Pattern.compile("^r\\.(-?\\d+)\\.(-?\\d+)\\.mca$");
+
+ private McaDump(File mca) {
+ Matcher m = RE_REGION_FILE.matcher(mca.getName());
+ if (m.matches()) {
+ int x = Integer.parseInt(m.group(1));
+ int z = Integer.parseInt(m.group(2));
+ Region region = new Region(x, z, mca);
+ RegionDataDump.analyze(region);
+ } else {
+ printErr("No match?? {}", mca.getName());
+ exit(1);
+ }
+ }
+
+ private static int usage() {
+ System.out.println("Usage: McaDump {region file}");
+ return 2;
+ }
+
+ /**
+ * Main entry point.
+ *
+ * @param args command line arguments.
+ */
+ public static void main(String[] args) {
+ if (args.length != 1) {
+ exit(usage());
+ }
+ File mca = new File(args[0]);
+ if (!mca.canRead()) {
+ printErr("Unable to read file: {}", args[0]);
+ exit(usage());
+ }
+ new McaDump(mca);
+ }
+}
diff --git a/core/src/main/java/com/meowster/mcquad/Region.java b/core/src/main/java/com/meowster/mcquad/Region.java
index 3208ea7..90461d0 100755
--- a/core/src/main/java/com/meowster/mcquad/Region.java
+++ b/core/src/main/java/com/meowster/mcquad/Region.java
@@ -33,7 +33,7 @@ class Region {
* @param f the region file
* @param mock file is a mock
*/
- public Region(int x, int z, File f, boolean mock) {
+ Region(int x, int z, File f, boolean mock) {
this.coord = new Coord(x, z);
this.f = f;
this.mock = mock;
@@ -51,6 +51,17 @@ public Region(int x, int z, File f, boolean mock) {
this(x, z, null, true);
}
+ /**
+ * Constructor for creating region file (not a mock).
+ *
+ * @param x the x-coord
+ * @param z the z-coord
+ * @param f the region file
+ */
+ Region(int x, int z, File f) {
+ this(x, z, f, false);
+ }
+
@Override
public String toString() {
return "Region{" + coord + ", mock=" + mock + ", f=" + f + '}';
@@ -79,6 +90,15 @@ File regionFile() {
return f;
}
+ /**
+ * Returns true if this is a mock (test) region.
+ *
+ * @return true if mock; false otherwise
+ */
+ boolean isMock() {
+ return mock;
+ }
+
/**
* Returns the last modified timestamp for the backing region file.
* If there is no backing file (for example, unit tests running in
@@ -123,6 +143,15 @@ DataInputStream getChunkDataStream(int cx, int cz) {
return rf == null ? null : rf.getChunkDataStream(cx, cz);
}
+ /**
+ * Returns the number of chunks contained in this region.
+ *
+ * @return the number of chunks in the region
+ */
+ int chunkCount() {
+ return rf == null ? 0 : rf.chunkCount();
+ }
+
/**
* Instructs the region to release any resources used to hold region data,
* since we have generated the image and the data is no longer required.
@@ -130,5 +159,4 @@ DataInputStream getChunkDataStream(int cx, int cz) {
void releaseResources() {
rf = null;
}
-
}
diff --git a/core/src/main/java/com/meowster/mcquad/RegionData.java b/core/src/main/java/com/meowster/mcquad/RegionData.java
index 5f76280..eb69e96 100755
--- a/core/src/main/java/com/meowster/mcquad/RegionData.java
+++ b/core/src/main/java/com/meowster/mcquad/RegionData.java
@@ -48,6 +48,18 @@ void load(Set regions) {
* @param r the region to load
*/
private void loadRegion(Region r) {
+ /*
+ * NOTE:
+ * There is a bug in the Minecraft server (observed in 1.10.2) that
+ * sometimes outputs region files containing a single chunk, with no
+ * bearing to where players have explored. We need to filter out these
+ * spurious regions.
+ */
+ if (!r.isMock() && r.chunkCount() < 2) {
+ printErr("Spurious region detected: {}", r);
+ return;
+ }
+
bounds.add(r.coord());
regionMap.put(r.coord(), r);
}
diff --git a/core/src/main/java/com/meowster/mcquad/RegionDataDump.java b/core/src/main/java/com/meowster/mcquad/RegionDataDump.java
new file mode 100644
index 0000000..6c323c8
--- /dev/null
+++ b/core/src/main/java/com/meowster/mcquad/RegionDataDump.java
@@ -0,0 +1,88 @@
+package com.meowster.mcquad;
+
+import java.io.DataInputStream;
+
+import static com.meowster.util.StringUtils.EOL;
+import static com.meowster.util.StringUtils.printOut;
+
+/**
+ * Analysis of a region file.
+ */
+class RegionDataDump {
+
+ private static final String HASH = " #";
+ private static final String DOT = " .";
+
+ private static final int NCHUNKS = 32; // chunk dimension of region
+ private static final int NBLOCKS = 16; // block dimension of chunk
+
+ private static final int CHUNKS_PER_REGION = NCHUNKS * NCHUNKS;
+
+
+ private static Region region;
+ private static int totalChunks;
+ private static ChunkStuff[][] data;
+ private static int singleX;
+ private static int singleZ;
+
+ /**
+ * Analyze the region and output results.
+ *
+ * @param r the region to analyze
+ */
+ static void analyze(Region r) {
+ region = r;
+ totalChunks = 0;
+ data = new ChunkStuff[NCHUNKS][NCHUNKS];
+
+ // iterate over the chunks in the region...
+ for (int cz = 0; cz < NCHUNKS; cz++) {
+ for (int cx = 0; cx < NCHUNKS; cx++) {
+ DataInputStream dis = region.getChunkDataStream(cx, cz);
+ if (dis == null)
+ continue;
+
+ totalChunks++;
+ singleX = cx;
+ singleZ = cz;
+
+// Chunk chunk = new Chunk(dis);
+ data[cz][cx] = new ChunkStuff(null);
+ }
+ }
+
+ outputResults();
+ }
+
+
+ private static void outputResults() {
+ printOut("Region: {}; Chunks: {}/{}",
+ region.regionFile().getName(),
+ totalChunks,
+ CHUNKS_PER_REGION
+ );
+ if (totalChunks == 1) {
+ printOut(" Chunk coords [{}, {}]", singleX, singleZ);
+ }
+// printOut(createMap());
+ }
+
+ private static String createMap() {
+ StringBuilder sb = new StringBuilder();
+ for (int cz = 0; cz < NCHUNKS; cz++) {
+ for (int cx = 0; cx < NCHUNKS; cx++) {
+ sb.append(data[cz][cx] != null ? HASH : DOT);
+ }
+ sb.append(EOL);
+ }
+ return sb.toString();
+ }
+
+
+ // retains info about a chunk
+ private static class ChunkStuff {
+ ChunkStuff(Chunk chunk) {
+ // capture any data we need for dumping...
+ }
+ }
+}
diff --git a/core/src/main/java/com/meowster/mcquad/RegionFile.java b/core/src/main/java/com/meowster/mcquad/RegionFile.java
index f2c6878..d880625 100755
--- a/core/src/main/java/com/meowster/mcquad/RegionFile.java
+++ b/core/src/main/java/com/meowster/mcquad/RegionFile.java
@@ -186,6 +186,54 @@ else if (version == VERSION_DEFLATE)
}
}
+ /**
+ * Returns the number of chunks available in this region.
+ *
+ * @return the chunk count
+ */
+ int chunkCount() {
+ int nChunks = 0;
+ for (int cz = 0; cz < NCHUNKS; cz++) {
+ for (int cx = 0; cx < NCHUNKS; cx++) {
+ if (chunkAvailableAt(cx, cz)) {
+ nChunks++;
+ }
+ }
+ }
+ return nChunks;
+ }
+
+ /**
+ * Returns true if there is chunk data at the specified coordinates.
+ *
+ * @param x chunk x-coord
+ * @param z chunk z-coord
+ * @return true if chunk data is here; false otherwise
+ */
+ boolean chunkAvailableAt(int x, int z) {
+ if (outOfBounds(x, z))
+ return false;
+
+ int offset = getOffset(x, z);
+ if (offset == 0)
+ return false;
+
+ final int sectorNumber = offset >> 8;
+ final int numSectors = offset & 0xff;
+
+ return sectorNumber + numSectors <= nSectors;
+ }
+
+ /**
+ * Returns the chunk timestamp for the specified coordinates.
+ *
+ * @param x chunk x-coord
+ * @param z chunk z-coord
+ * @return true if chunk data is here; false otherwise
+ */
+ int timeStampAt(int x, int z) {
+ return outOfBounds(x, z) ? 0 : chunkTimestamps[x + z * 32];
+ }
private boolean outOfBounds(int x, int z) {
return x < 0 || x >= NCHUNKS || z < 0 || z >= NCHUNKS;
diff --git a/pom.xml b/pom.xml
index 8d8102b..3885bd1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,7 +13,7 @@
4.0.0
- 1.2.1
+ 1.2.2
UTF-8
diff --git a/tools/src/bin/mcquad-config.sh b/tools/src/bin/mcquad-config.sh
index 7d18b58..36d5d98 100644
--- a/tools/src/bin/mcquad-config.sh
+++ b/tools/src/bin/mcquad-config.sh
@@ -36,5 +36,5 @@ export MCMAPS_CONTEXT=/mcmaps
##
# MCQUAD_VERSION should be set to correct version to pull out of maven repo
-export MCQUAD_VERSION=1.2.1
+export MCQUAD_VERSION=1.2.2
diff --git a/tools/src/dev/cpwww.sh b/tools/src/dev/cpwww.sh
index dbf479c..73f1cfa 100755
--- a/tools/src/dev/cpwww.sh
+++ b/tools/src/dev/cpwww.sh
@@ -3,7 +3,7 @@
BACK=$PWD
TARBALL=static.tar.gz
-VERSION=1.2.1
+VERSION=1.2.2
cd ../../../web/target/mcquad-web-${VERSION}/
echo BUILD TAR
diff --git a/tools/src/dev/mcadump.sh b/tools/src/dev/mcadump.sh
new file mode 100755
index 0000000..27ecbb1
--- /dev/null
+++ b/tools/src/dev/mcadump.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+MCQUAD_VERSION=1.2.2
+
+MAIN=com.meowster.mcquad.McaDump
+
+CP="/Users/simonh/dev/mcquad/McQuad/core/target/classes"
+
+MCSAVES="/Users/simonh/dev/mcquad/tmp"
+
+ls "$MCSAVES"
+
+JAVA=/usr/bin/java
+
+
+for FILE in $(ls "$MCSAVES");
+do
+ $JAVA -cp $CP $MAIN "$MCSAVES/$FILE"
+done
+
+