Update MCAFile.java

This commit is contained in:
Jesse Boyd 2019-11-02 08:40:11 +01:00
parent ed7df341b4
commit 0b2bd862a0
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F

View File

@ -3,7 +3,6 @@ package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.beta.Trimable; import com.boydti.fawe.beta.Trimable;
import com.boydti.fawe.jnbt.streamer.StreamDelegate; import com.boydti.fawe.jnbt.streamer.StreamDelegate;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.RunnableVal4; import com.boydti.fawe.object.RunnableVal4;
import com.boydti.fawe.object.collection.CleanableThreadLocal; import com.boydti.fawe.object.collection.CleanableThreadLocal;
import com.boydti.fawe.object.io.BufferedRandomAccessFile; import com.boydti.fawe.object.io.BufferedRandomAccessFile;
@ -14,6 +13,7 @@ import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -23,7 +23,6 @@ import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -33,6 +32,7 @@ import java.util.zip.InflaterInputStream;
/** /**
* Chunk format: http://minecraft.gamepedia.com/Chunk_format#Entity_format * Chunk format: http://minecraft.gamepedia.com/Chunk_format#Entity_format
* e.g.: `.Level.Entities.#` (Starts with a . as the root tag is unnamed) * e.g.: `.Level.Entities.#` (Starts with a . as the root tag is unnamed)
* Note: This class isn't thread safe. You can use it in an async thread, but not multiple at the same time
*/ */
public class MCAFile implements Trimable { public class MCAFile implements Trimable {
@ -61,6 +61,7 @@ public class MCAFile implements Trimable {
private int X, Z; private int X, Z;
private MCAChunk[] chunks; private MCAChunk[] chunks;
private boolean[] chunkInitialized; private boolean[] chunkInitialized;
private Object[] locks;
final ThreadLocal<byte[]> byteStore1 = new ThreadLocal<byte[]>() { final ThreadLocal<byte[]> byteStore1 = new ThreadLocal<byte[]>() {
@Override @Override
@ -86,6 +87,10 @@ public class MCAFile implements Trimable {
this.locations = new byte[4096]; this.locations = new byte[4096];
this.chunks = new MCAChunk[32 * 32]; this.chunks = new MCAChunk[32 * 32];
this.chunkInitialized = new boolean[this.chunks.length]; this.chunkInitialized = new boolean[this.chunks.length];
this.locks = new Object[this.chunks.length];
for (int i = 0; i < locks.length; i++) {
locks[i] = new Object();
}
} }
@Override @Override
@ -106,34 +111,29 @@ public class MCAFile implements Trimable {
public MCAFile init(File file) throws FileNotFoundException { public MCAFile init(File file) throws FileNotFoundException {
String[] split = file.getName().split("\\."); String[] split = file.getName().split("\\.");
X = Integer.parseInt(split[1]); int X = Integer.parseInt(split[1]);
Z = Integer.parseInt(split[2]); int Z = Integer.parseInt(split[2]);
return init(file, X, Z); return init(file, X, Z);
} }
public MCAFile init(File file, int mcrX, int mcrZ) throws FileNotFoundException { public MCAFile init(File file, int mcrX, int mcrZ) throws FileNotFoundException {
if (raf != null) { if (raf != null) {
synchronized (raf) { flush(pool);
if (raf != null) { for (int i = 0; i < 4096; i++) {
flush(pool); locations[i] = 0;
for (int i = 0; i < 4096; i++) {
locations[i] = 0;
}
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
raf = null;
deleted = false;
Arrays.fill(chunkInitialized, false);
readLocations = false;
}
} }
this.X = mcrX; try {
this.Z = mcrZ; raf.close();
} catch (IOException e) {
e.printStackTrace();
}
raf = null;
} }
deleted = false;
Arrays.fill(chunkInitialized, false);
readLocations = false;
this.X = mcrX;
this.Z = mcrZ;
this.file = file; this.file = file;
if (!file.exists()) { if (!file.exists()) {
throw new FileNotFoundException(file.getName()); throw new FileNotFoundException(file.getName());
@ -178,9 +178,9 @@ public class MCAFile implements Trimable {
e.printStackTrace(); e.printStackTrace();
} }
} }
synchronized (chunks) { deleted = false;
Arrays.fill(chunkInitialized, false); readLocations = false;
} Arrays.fill(chunkInitialized, false);
} }
@Override @Override
@ -235,14 +235,24 @@ public class MCAFile implements Trimable {
int pair = getIndex(cx, cz); int pair = getIndex(cx, cz);
MCAChunk chunk = chunks[pair]; MCAChunk chunk = chunks[pair];
if (chunk == null) { if (chunk == null) {
chunk = new MCAChunk(); Object lock = locks[pair];
chunk.setPosition(cx, cz); synchronized (lock) {
chunks[pair] = chunk; chunk = chunks[pair];
if (chunk == null) {
chunk = new MCAChunk();
chunk.setPosition(cx, cz);
chunks[pair] = chunk;
}
}
} else if (chunkInitialized[pair]) { } else if (chunkInitialized[pair]) {
return chunk; return chunk;
} }
readChunk(chunk, pair); synchronized (chunk) {
chunkInitialized[pair] = true; if (!chunkInitialized[pair]) {
readChunk(chunk, pair);
chunkInitialized[pair] = true;
}
}
return chunk; return chunk;
} }
@ -493,13 +503,11 @@ public class MCAFile implements Trimable {
if (isDeleted()) { if (isDeleted()) {
return true; return true;
} }
synchronized (chunks) { for (int i = 0; i < chunks.length; i++) {
for (int i = 0; i < chunks.length; i++) { MCAChunk chunk = chunks[i];
MCAChunk chunk = chunks[i]; if (chunk != null && this.chunkInitialized[i]) {
if (chunk != null && this.chunkInitialized[i]) { if (chunk.isModified() || chunk.isDeleted()) {
if (chunk.isModified() || chunk.isDeleted()) { return true;
return true;
}
} }
} }
} }
@ -508,9 +516,9 @@ public class MCAFile implements Trimable {
/** /**
* Write the chunk to the file * Write the chunk to the file
* @param pool * @param wait - If the flush method needs to wait for the pool
*/ */
public void flush(ForkJoinPool pool) { public void flush(boolean wait) {
synchronized (raf) { synchronized (raf) {
// If the file is marked as deleted, nothing is written // If the file is marked as deleted, nothing is written
if (isDeleted()) { if (isDeleted()) {
@ -519,12 +527,6 @@ public class MCAFile implements Trimable {
return; return;
} }
boolean wait; // If the flush method needs to wait for the pool
if (pool == null) {
wait = true;
pool = new ForkJoinPool();
} else wait = false;
// Chunks that need to be relocated // Chunks that need to be relocated
Int2ObjectOpenHashMap<byte[]> relocate = new Int2ObjectOpenHashMap<>(); Int2ObjectOpenHashMap<byte[]> relocate = new Int2ObjectOpenHashMap<>();
// The position of each chunk // The position of each chunk
@ -533,42 +535,40 @@ public class MCAFile implements Trimable {
final Int2ObjectOpenHashMap<byte[]> compressedMap = new Int2ObjectOpenHashMap<>(); final Int2ObjectOpenHashMap<byte[]> compressedMap = new Int2ObjectOpenHashMap<>();
// The data of each chunk that needs to be moved // The data of each chunk that needs to be moved
final Int2ObjectOpenHashMap<byte[]> append = new Int2ObjectOpenHashMap<>(); final Int2ObjectOpenHashMap<byte[]> append = new Int2ObjectOpenHashMap<>();
boolean modified = false; boolean[] modified = new boolean[1];
// Get the current time for the chunk timestamp // Get the current time for the chunk timestamp
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
// Load the chunks into the append or compressed map // Load the chunks into the append or compressed map
for (MCAChunk chunk : getCachedChunks()) { final ForkJoinPool finalPool = this.pool;
forEachCachedChunk(chunk -> {
if (chunk.isModified() || chunk.isDeleted()) { if (chunk.isModified() || chunk.isDeleted()) {
modified = true; modified[0] = true;
chunk.setLastUpdate(now); chunk.setLastUpdate(now);
if (!chunk.isDeleted()) { if (!chunk.isDeleted()) {
pool.submit(new Runnable() { MCAFile.this.pool.submit(() -> {
@Override try {
public void run() { byte[] compressed = toBytes(chunk);
try { int pair = MathMan.pair((short) (chunk.getX() & 31), (short) (chunk.getZ() & 31));
byte[] compressed = toBytes(chunk); Int2ObjectOpenHashMap map;
int pair = MathMan.pair((short) (chunk.getX() & 31), (short) (chunk.getZ() & 31)); if (getOffset(chunk.getX(), chunk.getZ()) == 0) {
Int2ObjectOpenHashMap map; map = append;
if (getOffset(chunk.getX(), chunk.getZ()) == 0) { } else {
map = append; map = compressedMap;
} else {
map = compressedMap;
}
synchronized (map) {
map.put(pair, compressed);
}
} catch (Throwable e) {
e.printStackTrace();
} }
synchronized (map) {
map.put(pair, compressed);
}
} catch (Throwable e) {
e.printStackTrace();
} }
}); });
} }
} }
} });
// If any changes were detected // If any changes were detected
if (modified) { if (modified[0]) {
file.setLastModified(now); file.setLastModified(now);
// Load the offset data into the offset map // Load the offset data into the offset map
@ -700,13 +700,9 @@ public class MCAFile implements Trimable {
e.printStackTrace(); e.printStackTrace();
} }
if (wait) { if (wait) {
pool.shutdown();
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
} }
} }
} }
CleanableThreadLocal.clean(byteStore1);
CleanableThreadLocal.clean(byteStore2);
CleanableThreadLocal.clean(byteStore3);
} }
} }