Skip to content

Commit

Permalink
Merge pull request #49 from prydin/prydin-partial-load
Browse files Browse the repository at this point in the history
Added support for partial chunk/region load
  • Loading branch information
Querz authored Jun 8, 2020
2 parents 0755fe5 + 49e613b commit 795f1f2
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 58 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,13 @@ local.properties
# IDEA folder
.idea/

# .iml files
*.iml

# ignore javadoc for now
doc/

# ignore out
out/

*.patch
*.patch
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
3 changes: 1 addition & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#Wed Mar 29 16:28:10 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
59 changes: 36 additions & 23 deletions gradlew
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
#!/usr/bin/env sh

#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

##############################################################################
##
## Gradle start up script for UN*X
Expand Down Expand Up @@ -28,16 +44,16 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn ( ) {
warn () {
echo "$*"
}

die ( ) {
die () {
echo
echo "$*"
echo
Expand Down Expand Up @@ -66,6 +82,7 @@ esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar


# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
Expand Down Expand Up @@ -109,10 +126,11 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

JAVACMD=`cygpath --unix "$JAVACMD"`

# We build the pattern for arguments to be converted via cygpath
Expand All @@ -138,35 +156,30 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $i + 1`
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi

# Escape application args
save ( ) {
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
APP_ARGS=`save "$@"`

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"
22 changes: 21 additions & 1 deletion gradlew.bat
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem

@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
Expand All @@ -13,8 +29,11 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
Expand Down Expand Up @@ -65,6 +84,7 @@ set CMD_LINE_ARGS=%*

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

Expand Down
96 changes: 72 additions & 24 deletions src/main/java/net/querz/mca/Chunk.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import static net.querz.mca.LoadFlags.*;

public class Chunk {

public static final int DEFAULT_DATA_VERSION = 1628;

private boolean partial;

private int lastMCAUpdate;

private CompoundTag data;
Expand Down Expand Up @@ -49,47 +52,78 @@ public class Chunk {
*/
public Chunk(CompoundTag data) {
this.data = data;
initReferences();
initReferences(ALL_DATA);
}

private void initReferences() {
private void initReferences(long loadFlags) {
if (data == null) {
throw new NullPointerException("data cannot be null");
}
CompoundTag level;
if ((level = data.getCompoundTag("Level")) == null) {
throw new IllegalArgumentException("data does not contain \"Level\" tag");
}
this.dataVersion = data.getInt("DataVersion");
this.inhabitedTime = level.getLong("InhabitedTime");
this.lastUpdate = level.getLong("LastUpdate");
this.biomes = level.getIntArray("Biomes");
this.heightMaps = level.getCompoundTag("Heightmaps");
this.carvingMasks = level.getCompoundTag("CarvingMasks");
this.entities = level.containsKey("Entities") ? level.getListTag("Entities").asCompoundTagList() : null;
this.tileEntities = level.containsKey("TileEntities") ? level.getListTag("TileEntities").asCompoundTagList() : null;
this.tileTicks = level.containsKey("TileTicks") ? level.getListTag("TileTicks").asCompoundTagList() : null;
this.liquidTicks = level.containsKey("LiquidTicks") ? level.getListTag("LiquidTicks").asCompoundTagList() : null;
this.lights = level.containsKey("Lights") ? level.getListTag("Lights").asListTagList() : null;
this.liquidsToBeTicked = level.containsKey("LiquidsToBeTicked") ? level.getListTag("LiquidsToBeTicked").asListTagList() : null;
this.toBeTicked = level.containsKey("ToBeTicked") ? level.getListTag("ToBeTicked").asListTagList() : null;
this.postProcessing = level.containsKey("PostProcessing") ? level.getListTag("PostProcessing").asListTagList() : null;
this.status = level.getString("Status");
this.structures = level.getCompoundTag("Structures");
if (level.containsKey("Sections")) {
dataVersion = data.getInt("DataVersion");
inhabitedTime = level.getLong("InhabitedTime");
lastUpdate = level.getLong("LastUpdate");
if ((loadFlags & BIOMES) != 0) {
biomes = level.getIntArray("Biomes");
}
if ((loadFlags & HEIGHTMAPS) != 0) {
heightMaps = level.getCompoundTag("Heightmaps");
}
if ((loadFlags & CARVING_MASKS) != 0) {
carvingMasks = level.getCompoundTag("CarvingMasks");
}
if ((loadFlags & ENTITIES) != 0) {
entities = level.containsKey("Entities") ? level.getListTag("Entities").asCompoundTagList() : null;
}
if ((loadFlags & TILE_ENTITIES) != 0) {
tileEntities = level.containsKey("TileEntities") ? level.getListTag("TileEntities").asCompoundTagList() : null;
}
if ((loadFlags & TILE_TICKS) != 0) {
tileTicks = level.containsKey("TileTicks") ? level.getListTag("TileTicks").asCompoundTagList() : null;
}
if ((loadFlags & LIQUID_TICKS) != 0) {
liquidTicks = level.containsKey("LiquidTicks") ? level.getListTag("LiquidTicks").asCompoundTagList() : null;
}
if ((loadFlags & LIGHTS) != 0) {
lights = level.containsKey("Lights") ? level.getListTag("Lights").asListTagList() : null;
}
if ((loadFlags & LIQUIDS_TO_BE_TICKED) != 0) {
liquidsToBeTicked = level.containsKey("LiquidsToBeTicked") ? level.getListTag("LiquidsToBeTicked").asListTagList() : null;
}
if ((loadFlags & TO_BE_TICKED) != 0) {
toBeTicked = level.containsKey("ToBeTicked") ? level.getListTag("ToBeTicked").asListTagList() : null;
}
if ((loadFlags & POST_PROCESSING) != 0) {
postProcessing = level.containsKey("PostProcessing") ? level.getListTag("PostProcessing").asListTagList() : null;
}
status = level.getString("Status");
if ((loadFlags & STRUCTURES) != 0) {
structures = level.getCompoundTag("Structures");
}
if ((loadFlags & (BLOCK_LIGHTS|BLOCK_STATES|SKY_LIGHT)) != 0 && level.containsKey("Sections")) {
for (CompoundTag section : level.getListTag("Sections").asCompoundTagList()) {
int sectionIndex = section.getByte("Y");
if (sectionIndex > 15 || sectionIndex < 0) {
continue;
}
Section newSection = new Section(section, dataVersion);
Section newSection = new Section(section, dataVersion, loadFlags);
if (newSection.isEmpty()) {
continue;
}

this.sections[sectionIndex] = newSection;
sections[sectionIndex] = newSection;
}
}

// If we haven't requested the full set of data we can drop the underlying raw data to let the GC handle it.
if (loadFlags != ALL_DATA) {
data = null;
partial = true;
} else {
partial = false;
}
}

/**
Expand All @@ -98,9 +132,13 @@ private void initReferences() {
* @param xPos The x-coordinate of the chunk.
* @param zPos The z-coodrinate of the chunk.
* @return The amount of bytes written to the RandomAccessFile.
* @throws UnsupportedOperationException When something went wrong during writing.
* @throws IOException When something went wrong during writing.
*/
public int serialize(RandomAccessFile raf, int xPos, int zPos) throws IOException {
if (partial) {
throw new UnsupportedOperationException("Partially loaded chunks cannot be serialized");
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
try (BufferedOutputStream nbtOut = new BufferedOutputStream(CompressionType.ZLIB.compress(baos))) {
new NBTSerializer(false).toStream(new NamedTag(null, updateHandle(xPos, zPos)), nbtOut);
Expand All @@ -118,6 +156,16 @@ public int serialize(RandomAccessFile raf, int xPos, int zPos) throws IOExceptio
* @throws IOException When something went wrong during reading.
*/
public void deserialize(RandomAccessFile raf) throws IOException {
deserialize(raf, ALL_DATA);
}

/**
* Reads chunk data from a RandomAccessFile. The RandomAccessFile must already be at the correct position.
* @param raf The RandomAccessFile to read the chunk data from.
* @param loadFlags A logical or of {@link LoadFlags} constants indicating what data should be loaded
* @throws IOException When something went wrong during reading.
*/
public void deserialize(RandomAccessFile raf, long loadFlags) throws IOException {
byte compressionTypeByte = raf.readByte();
CompressionType compressionType = CompressionType.getFromID(compressionTypeByte);
if (compressionType == null) {
Expand All @@ -127,7 +175,7 @@ public void deserialize(RandomAccessFile raf) throws IOException {
NamedTag tag = new NBTDeserializer(false).fromStream(dis);
if (tag != null && tag.getTag() instanceof CompoundTag) {
data = (CompoundTag) tag.getTag();
initReferences();
initReferences(loadFlags);
} else {
throw new IOException("invalid data tag: " + (tag == null ? "null" : tag.getClass().getName()));
}
Expand Down Expand Up @@ -571,7 +619,7 @@ public CompoundTag updateHandle(int xPos, int zPos) {
} else {
if (biomes != null && biomes.length == 1024) level.putIntArray("Biomes", biomes);
}
if (heightMaps != null) level.put("HeightMaps", heightMaps);
if (heightMaps != null) level.put("Heightmaps", heightMaps);
if (carvingMasks != null) level.put("CarvingMasks", carvingMasks);
if (entities != null) level.put("Entities", entities);
if (tileEntities != null) level.put("TileEntities", tileEntities);
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/net/querz/mca/LoadFlags.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package net.querz.mca;

public class LoadFlags {

public static long BIOMES = 0x0001;
public static long HEIGHTMAPS = 0x0002;
public static long CARVING_MASKS = 0x0004;
public static long ENTITIES = 0x0008;
public static long TILE_ENTITIES = 0x0010;
public static long TILE_TICKS = 0x0040;
public static long LIQUID_TICKS = 0x0080;
public static long TO_BE_TICKED = 0x0100;
public static long POST_PROCESSING = 0x0200;
public static long STRUCTURES = 0x0400;
public static long BLOCK_LIGHTS = 0x0800;
public static long BLOCK_STATES = 0x1000;
public static long SKY_LIGHT = 0x2000;
public static long LIGHTS = 0x4000;
public static long LIQUIDS_TO_BE_TICKED = 0x8000;

public static long ALL_DATA = 0xffffffffffffffffL;


}
13 changes: 12 additions & 1 deletion src/main/java/net/querz/mca/MCAFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ public MCAFile(int regionX, int regionZ) {
* @throws IOException If something went wrong during deserialization.
* */
public void deserialize(RandomAccessFile raf) throws IOException {
deserialize(raf, LoadFlags.ALL_DATA);
}

/**
* Reads an .mca file from a {@code RandomAccessFile} into this object.
* This method does not perform any cleanups on the data.
* @param raf The {@code RandomAccessFile} to read from.
* @param loadFlags A logical or of {@link LoadFlags} constants indicating what data should be loaded
* @throws IOException If something went wrong during deserialization.
* */
public void deserialize(RandomAccessFile raf, long loadFlags) throws IOException {
chunks = new Chunk[1024];
for (int i = 0; i < 1024; i++) {
raf.seek(i * 4);
Expand All @@ -47,7 +58,7 @@ public void deserialize(RandomAccessFile raf) throws IOException {
int timestamp = raf.readInt();
Chunk chunk = new Chunk(timestamp);
raf.seek(4096 * offset + 4); //+4: skip data size
chunk.deserialize(raf);
chunk.deserialize(raf, loadFlags);
chunks[i] = chunk;
}
}
Expand Down
Loading

0 comments on commit 795f1f2

Please sign in to comment.