Formatted and broke the saving mechanism. I'm too tired to bugfix
- Refactored and formatted TestWorldDiskIO - Removed HashableVec3i - Added Coordinates methods for custom bit count - Properly reverted commit 98250cd - Known bugs: - Server shutdown close()s regions too early - Re-entering a world does not show saved changes
This commit is contained in:
parent
cd16334db8
commit
f4300558d5
@ -102,9 +102,7 @@ public class ChunkRender
|
||||
}
|
||||
|
||||
public synchronized void render(ShapeRenderHelper renderer) {
|
||||
if (!data.isEmpty) {
|
||||
model.render(renderer);
|
||||
}
|
||||
model.render(renderer);
|
||||
}
|
||||
|
||||
public synchronized void update() {
|
||||
|
@ -1,35 +0,0 @@
|
||||
package ru.windcorp.progressia.common.util;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
|
||||
public class HashableVec3i {
|
||||
|
||||
public Vec3i value;
|
||||
|
||||
public HashableVec3i(Vec3i inValue)
|
||||
{
|
||||
value = inValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() // Uses first 3 primes greater than 2**30
|
||||
{
|
||||
return 1073741827 * value.x + 1073741831 * value.y + 1073741833 * value.z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object comparee)
|
||||
{
|
||||
if (comparee == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (comparee.getClass() != HashableVec3i.class)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
HashableVec3i compareeCast = (HashableVec3i) comparee;
|
||||
return compareeCast.value.x == value.x && compareeCast.value.y == value.y && compareeCast.value.z == value.z ;
|
||||
}
|
||||
|
||||
}
|
@ -163,4 +163,71 @@ public class Coordinates {
|
||||
return blockInChunk == 0 || blockInChunk == BLOCKS_PER_CHUNK - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generalized versions
|
||||
*/
|
||||
|
||||
public static int convertGlobalToCell(int bits, int global) {
|
||||
return global >> bits;
|
||||
}
|
||||
|
||||
public static Vec3i convertGlobalToCell(
|
||||
int bits,
|
||||
Vec3i global,
|
||||
Vec3i output
|
||||
) {
|
||||
if (output == null)
|
||||
output = new Vec3i();
|
||||
|
||||
output.x = convertGlobalToCell(bits, global.x);
|
||||
output.y = convertGlobalToCell(bits, global.y);
|
||||
output.z = convertGlobalToCell(bits, global.z);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static int convertGlobalToInCell(int bits, int global) {
|
||||
int mask = (1 << bits) - 1;
|
||||
return global & mask;
|
||||
}
|
||||
|
||||
public static Vec3i convertGlobalToInCell(
|
||||
int bits,
|
||||
Vec3i global,
|
||||
Vec3i output
|
||||
) {
|
||||
if (output == null)
|
||||
output = new Vec3i();
|
||||
|
||||
output.x = convertGlobalToInCell(bits, global.x);
|
||||
output.y = convertGlobalToInCell(bits, global.y);
|
||||
output.z = convertGlobalToInCell(bits, global.z);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static int getGlobal(int bits, int cell, int inCell) {
|
||||
return inCell | (cell << bits);
|
||||
}
|
||||
|
||||
public static Vec3i getGlobal(
|
||||
int bits,
|
||||
Vec3i cell,
|
||||
Vec3i inCell,
|
||||
Vec3i output
|
||||
) {
|
||||
if (output == null)
|
||||
output = new Vec3i();
|
||||
|
||||
output.x = getGlobal(bits, cell.x, inCell.x);
|
||||
output.y = getGlobal(bits, cell.y, inCell.y);
|
||||
output.z = getGlobal(bits, cell.z, inCell.z);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static boolean isOnCellBorder(int bits, int inCell) {
|
||||
return inCell == 0 || inCell == (1 << bits) - 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
@ -46,11 +46,6 @@ public class DefaultChunkData implements ChunkData {
|
||||
public static final int BLOCKS_PER_CHUNK = Coordinates.CHUNK_SIZE;
|
||||
public static final int CHUNK_RADIUS = BLOCKS_PER_CHUNK / 2;
|
||||
|
||||
public boolean isEmpty = false;
|
||||
public boolean isOpaque = false;
|
||||
|
||||
public static HashSet<BlockData> transparent;
|
||||
|
||||
private final Vec3i position = new Vec3i();
|
||||
private final DefaultWorldData world;
|
||||
|
||||
@ -205,44 +200,6 @@ public class DefaultChunkData implements ChunkData {
|
||||
this.generationHint = generationHint;
|
||||
}
|
||||
|
||||
public void computeOpaque()
|
||||
{
|
||||
for (int xyz=0;xyz<BLOCKS_PER_CHUNK*BLOCKS_PER_CHUNK*BLOCKS_PER_CHUNK;xyz++)
|
||||
{
|
||||
if (transparent.contains( blocks[xyz]))
|
||||
{
|
||||
isOpaque = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
isOpaque = true;
|
||||
}
|
||||
|
||||
public boolean isOpaque()
|
||||
{
|
||||
return isOpaque;
|
||||
}
|
||||
|
||||
public void computeEmpty()
|
||||
{
|
||||
BlockData air = new BlockData("Test:Air");
|
||||
for (int xyz=0;xyz<BLOCKS_PER_CHUNK*BLOCKS_PER_CHUNK*BLOCKS_PER_CHUNK;xyz++)
|
||||
{
|
||||
if (blocks[xyz] != air)
|
||||
{
|
||||
isEmpty = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
isEmpty = true;
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return isEmpty;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of {@link TileDataStack} used internally by
|
||||
* {@link DefaultChunkData} to
|
||||
|
@ -20,8 +20,7 @@ package ru.windcorp.progressia.server.management.load;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.Units;
|
||||
import ru.windcorp.progressia.common.world.generic.ChunkMap;
|
||||
@ -45,8 +44,6 @@ public class ChunkRequestDaemon {
|
||||
private final ChunkSet toGenerate = ChunkSets.newHashSet();
|
||||
private final ChunkSet toRequestUnload = ChunkSets.newHashSet();
|
||||
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final Collection<Vec3i> buffer = new ArrayList<>();
|
||||
|
||||
private static class ChunkUnloadRequest {
|
||||
@ -116,16 +113,16 @@ public class ChunkRequestDaemon {
|
||||
}
|
||||
|
||||
private void processLoadQueues() {
|
||||
toRequestUnload.forEach((pos) -> executor.submit(() -> scheduleUnload(pos)));
|
||||
toRequestUnload.forEach(this::scheduleUnload);
|
||||
toRequestUnload.clear();
|
||||
|
||||
toLoad.forEach((pos) -> executor.submit(() -> getChunkManager().loadOrGenerateChunk(pos)));
|
||||
toLoad.forEach(getChunkManager()::loadOrGenerateChunk);
|
||||
toLoad.clear();
|
||||
|
||||
toGenerate.forEach((pos) -> executor.submit(() -> getChunkManager().loadOrGenerateChunk(pos)));
|
||||
toGenerate.forEach(getChunkManager()::loadOrGenerateChunk);
|
||||
toGenerate.clear();
|
||||
|
||||
executor.submit(() -> unloadScheduledChunks());
|
||||
unloadScheduledChunks();
|
||||
}
|
||||
|
||||
private void scheduleUnload(Vec3i chunkPos) {
|
||||
|
@ -56,9 +56,6 @@ public class PlanetFeatureGenerator {
|
||||
generateBorderFeatures(server, chunk);
|
||||
}
|
||||
|
||||
chunk.computeEmpty();
|
||||
chunk.computeOpaque();
|
||||
|
||||
chunk.setGenerationHint(true);
|
||||
}
|
||||
|
||||
|
249
src/main/java/ru/windcorp/progressia/test/region/Region.java
Normal file
249
src/main/java/ru/windcorp/progressia/test/region/Region.java
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.test.region;
|
||||
|
||||
import static ru.windcorp.progressia.test.region.TestWorldDiskIO.REGION_DIAMETER;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.state.IOContext;
|
||||
import ru.windcorp.progressia.common.world.DecodingException;
|
||||
import ru.windcorp.progressia.common.world.DefaultChunkData;
|
||||
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
||||
import ru.windcorp.progressia.common.world.generic.ChunkMap;
|
||||
import ru.windcorp.progressia.common.world.generic.ChunkMaps;
|
||||
import ru.windcorp.progressia.common.world.io.ChunkIO;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
|
||||
public class Region {
|
||||
|
||||
private static final boolean RESET_CORRUPTED = true;
|
||||
|
||||
// 1 MiB
|
||||
private static final int MAX_CHUNK_SIZE = 1024 * 1024;
|
||||
private static final int SECTOR_SIZE = MAX_CHUNK_SIZE / 256;
|
||||
|
||||
private static final int HEADER_SIZE = Integer.BYTES * REGION_DIAMETER * REGION_DIAMETER * REGION_DIAMETER;
|
||||
|
||||
private final RandomAccessFile file;
|
||||
|
||||
private final ChunkMap<Integer> offsets = ChunkMaps.newHashMap();
|
||||
private final ChunkMap<Integer> lengths = ChunkMaps.newHashMap();
|
||||
|
||||
public Region(RandomAccessFile file) throws IOException {
|
||||
this.file = file;
|
||||
|
||||
try {
|
||||
confirmHeaderHealth();
|
||||
} catch (IOException e) {
|
||||
|
||||
TestWorldDiskIO.LOG.debug("Uh the file broke");
|
||||
if (RESET_CORRUPTED) {
|
||||
byte headerBytes[] = new byte[HEADER_SIZE];
|
||||
Arrays.fill(headerBytes, (byte) 0);
|
||||
|
||||
try {
|
||||
file.write(headerBytes);
|
||||
} catch (IOException e1) {
|
||||
e.addSuppressed(e1);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public RandomAccessFile getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
this.file.close();
|
||||
}
|
||||
|
||||
public int getOffset(Vec3i chunkLoc) {
|
||||
return offsets.get(chunkLoc);
|
||||
}
|
||||
|
||||
public boolean hasOffset(Vec3i pos) {
|
||||
return offsets.containsKey(pos);
|
||||
}
|
||||
|
||||
public void putOffset(Vec3i pos, int offset) {
|
||||
offsets.put(pos, offset);
|
||||
}
|
||||
|
||||
public int getLength(Vec3i chunkLoc) {
|
||||
return lengths.get(chunkLoc);
|
||||
}
|
||||
|
||||
public boolean hasLength(Vec3i pos) {
|
||||
return lengths.containsKey(pos);
|
||||
}
|
||||
|
||||
public void putLength(Vec3i pos, int length) {
|
||||
lengths.put(pos, length);
|
||||
}
|
||||
|
||||
private void confirmHeaderHealth() throws IOException {
|
||||
|
||||
Set<Integer> used = new HashSet<Integer>();
|
||||
final int chunksPerRegion = REGION_DIAMETER * REGION_DIAMETER * REGION_DIAMETER;
|
||||
|
||||
file.seek(0);
|
||||
|
||||
if (file.length() < HEADER_SIZE) {
|
||||
throw new IOException("File is too short to contain a header");
|
||||
}
|
||||
|
||||
for (int i = 0; i < chunksPerRegion; i++) {
|
||||
int offset = file.readInt();
|
||||
|
||||
int sectorLength = file.read();
|
||||
if (sectorLength == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vec3i pos = new Vec3i();
|
||||
pos.x = i / REGION_DIAMETER / REGION_DIAMETER;
|
||||
pos.y = (i / REGION_DIAMETER) % REGION_DIAMETER;
|
||||
pos.z = i % REGION_DIAMETER;
|
||||
|
||||
offsets.put(pos, offset);
|
||||
lengths.put(pos, sectorLength);
|
||||
|
||||
for (int sector = 0; sector < sectorLength; sector++) {
|
||||
if (!used.add(offset + sector)) {
|
||||
throw new IOException("A sector is used twice");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void save(DefaultChunkData chunk, Server server) throws IOException {
|
||||
Vec3i pos = TestWorldDiskIO.getInRegionCoords(chunk.getPosition());
|
||||
int definitionOffset = Integer.BYTES * (pos.z + REGION_DIAMETER * (pos.y + REGION_DIAMETER * pos.x));
|
||||
|
||||
if (!hasOffset(pos)) {
|
||||
allocateChunk(definitionOffset, pos);
|
||||
}
|
||||
int dataOffset = getOffset(pos);
|
||||
|
||||
byte[] buffer = saveToBuffer(chunk, server);
|
||||
writeBuffer(buffer, definitionOffset, dataOffset, pos);
|
||||
}
|
||||
|
||||
private byte[] saveToBuffer(DefaultChunkData chunk, Server server) throws IOException {
|
||||
ByteArrayOutputStream arrayStream = new ByteArrayOutputStream();
|
||||
try (
|
||||
DataOutputStream dataStream = new DataOutputStream(
|
||||
new DeflaterOutputStream(
|
||||
new BufferedOutputStream(arrayStream)
|
||||
)
|
||||
)
|
||||
) {
|
||||
ChunkIO.save(chunk, dataStream, IOContext.SAVE);
|
||||
TestWorldDiskIO.writeGenerationHint(chunk, dataStream, server);
|
||||
}
|
||||
|
||||
return arrayStream.toByteArray();
|
||||
}
|
||||
|
||||
private void writeBuffer(byte[] buffer, int definitionOffset, int dataOffset, Vec3i pos) throws IOException {
|
||||
file.seek(HEADER_SIZE + SECTOR_SIZE * dataOffset);
|
||||
file.write(buffer);
|
||||
|
||||
file.seek(definitionOffset + Integer.BYTES - 1);
|
||||
|
||||
int sectors = buffer.length / SECTOR_SIZE + 1;
|
||||
file.write(sectors);
|
||||
|
||||
putLength(pos, sectors);
|
||||
}
|
||||
|
||||
private void allocateChunk(int definitionOffset, Vec3i pos) throws IOException {
|
||||
int outputLen = (int) file.length();
|
||||
|
||||
int dataOffset = (int) (outputLen - HEADER_SIZE) / SECTOR_SIZE + 1;
|
||||
|
||||
file.seek(definitionOffset);
|
||||
file.writeInt(dataOffset);
|
||||
|
||||
file.setLength(HEADER_SIZE + dataOffset * SECTOR_SIZE);
|
||||
putOffset(pos, dataOffset);
|
||||
}
|
||||
|
||||
public DefaultChunkData load(Vec3i chunkPos, DefaultWorldData world, Server server)
|
||||
throws IOException,
|
||||
DecodingException {
|
||||
|
||||
int dataOffset = 0;
|
||||
int sectorLength = 0;
|
||||
Vec3i pos = TestWorldDiskIO.getInRegionCoords(chunkPos);
|
||||
|
||||
if (hasOffset(pos)) {
|
||||
dataOffset = getOffset(pos);
|
||||
sectorLength = getLength(pos);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] buffer = readBuffer(dataOffset, sectorLength);
|
||||
DefaultChunkData result = loadFromBuffer(buffer, chunkPos, world, server);
|
||||
return result;
|
||||
}
|
||||
|
||||
private DefaultChunkData loadFromBuffer(byte[] buffer, Vec3i chunkPos, DefaultWorldData world, Server server)
|
||||
throws IOException,
|
||||
DecodingException {
|
||||
|
||||
DataInputStream dataStream = new DataInputStream(
|
||||
new InflaterInputStream(
|
||||
new BufferedInputStream(
|
||||
new ByteArrayInputStream(buffer)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
DefaultChunkData result = ChunkIO.load(world, chunkPos, dataStream, IOContext.SAVE);
|
||||
TestWorldDiskIO.readGenerationHint(result, dataStream, server);
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte[] readBuffer(int dataOffset, int sectorLength) throws IOException {
|
||||
file.seek(HEADER_SIZE + SECTOR_SIZE * dataOffset);
|
||||
|
||||
byte buffer[] = new byte[SECTOR_SIZE * sectorLength];
|
||||
file.read(buffer);
|
||||
return buffer;
|
||||
}
|
||||
}
|
@ -18,792 +18,149 @@
|
||||
|
||||
package ru.windcorp.progressia.test.region;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.state.IOContext;
|
||||
import ru.windcorp.progressia.common.util.HashableVec3i;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.common.world.DefaultChunkData;
|
||||
import ru.windcorp.progressia.common.world.Coordinates;
|
||||
import ru.windcorp.progressia.common.world.DecodingException;
|
||||
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
||||
import ru.windcorp.progressia.common.world.io.ChunkIO;
|
||||
import ru.windcorp.progressia.common.world.generic.ChunkMap;
|
||||
import ru.windcorp.progressia.common.world.generic.ChunkMaps;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
import ru.windcorp.progressia.server.world.io.WorldContainer;
|
||||
|
||||
public class TestWorldDiskIO implements WorldContainer {
|
||||
|
||||
private static final boolean resetCorrupted = true;
|
||||
private static final boolean ENABLE = true;
|
||||
|
||||
public class RandomFileMapped {
|
||||
public RandomAccessFile file;
|
||||
public HashMap<HashableVec3i, Integer> offsets;
|
||||
public HashMap<HashableVec3i, Integer> lengths;
|
||||
private static final String FILE_NAME_FORMAT = "region_%d_%d_%d.progressia_region";
|
||||
|
||||
public RandomFileMapped(RandomAccessFile inFile)
|
||||
{
|
||||
boolean check = false;
|
||||
offsets = new HashMap<>();
|
||||
lengths = new HashMap<>();
|
||||
try {
|
||||
check = confirmHeaderHealth(inFile, offsets, lengths);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!check)
|
||||
{
|
||||
LOG.debug("Uh the file broke");
|
||||
if (resetCorrupted) {
|
||||
byte headerBytes[] = new byte[4 * chunksPerRegion];
|
||||
for (int i = 0; i < 4 * chunksPerRegion; i++) {
|
||||
headerBytes[i] = (byte) 0;
|
||||
}
|
||||
try {
|
||||
inFile.write(headerBytes);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
private static final int BITS_IN_CHUNK_COORDS = 4;
|
||||
public static final int REGION_DIAMETER = 1 << BITS_IN_CHUNK_COORDS;
|
||||
|
||||
|
||||
file = inFile;
|
||||
}
|
||||
|
||||
public int getOffset(Vec3i chunkLoc)
|
||||
{
|
||||
return offsets.get(new HashableVec3i(chunkLoc));
|
||||
}
|
||||
|
||||
public boolean hasOffset(Vec3i pos) {
|
||||
return offsets.containsKey(new HashableVec3i(pos));
|
||||
}
|
||||
|
||||
public void putOffset(Vec3i pos, int offset)
|
||||
{
|
||||
offsets.put(new HashableVec3i(pos), offset);
|
||||
}
|
||||
|
||||
public int getLength(Vec3i chunkLoc)
|
||||
{
|
||||
return lengths.get(new HashableVec3i(chunkLoc));
|
||||
}
|
||||
|
||||
public boolean hasLength(Vec3i pos) {
|
||||
return lengths.containsKey(new HashableVec3i(pos));
|
||||
}
|
||||
|
||||
public void putLength(Vec3i pos, int length)
|
||||
{
|
||||
lengths.put(new HashableVec3i(pos), length);
|
||||
}
|
||||
public static Vec3i getRegionCoords(Vec3i chunkCoords) {
|
||||
return Coordinates.convertGlobalToCell(BITS_IN_CHUNK_COORDS, chunkCoords, null);
|
||||
}
|
||||
|
||||
private Path SAVE_DIR = Paths.get("tmp_world");
|
||||
private static final String formatFile = "world.format";
|
||||
private static final Logger LOG = LogManager.getLogger("TestWorldDiskIO");
|
||||
|
||||
private HashMap<HashableVec3i, RandomFileMapped> inOutMap;
|
||||
private static final boolean ENABLE = false;
|
||||
|
||||
private static int maxSize = 1048576;
|
||||
private static int sectorSize = maxSize / 256;
|
||||
|
||||
private static final int bestFormat = 65536;
|
||||
|
||||
// private Map<Vec3i,Vec3i> regions = new HashMap<Vec3i,Vec3i>();
|
||||
private Vec3i regionSize;
|
||||
private int chunksPerRegion;
|
||||
|
||||
private int currentFormat = -1;
|
||||
private String extension = ".null";
|
||||
|
||||
private static int natFromInt(int loc) {
|
||||
if (loc < 0)
|
||||
return -2*loc - 1;
|
||||
return 2*loc;
|
||||
public static Vec3i getInRegionCoords(Vec3i chunkCoords) {
|
||||
return Coordinates.convertGlobalToInCell(BITS_IN_CHUNK_COORDS, chunkCoords, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* private static int intFromNat(int loc) // Possibly unused
|
||||
* {
|
||||
* if ((loc & 1) == 1)
|
||||
* return -loc >> 1;
|
||||
* return loc >> 1;
|
||||
* }
|
||||
*/
|
||||
static final Logger LOG = LogManager.getLogger();
|
||||
|
||||
private Vec3i getRegion(Vec3i chunkLoc) {
|
||||
int x = chunkLoc.x;
|
||||
if (x<0)
|
||||
{
|
||||
x /= regionSize.x;
|
||||
x--;
|
||||
}
|
||||
else
|
||||
{
|
||||
x /= regionSize.x;
|
||||
}
|
||||
int y = chunkLoc.y;
|
||||
if (y<0)
|
||||
{
|
||||
y /= regionSize.y;
|
||||
y--;
|
||||
}
|
||||
else
|
||||
{
|
||||
y /= regionSize.y;
|
||||
}
|
||||
int z = chunkLoc.z;
|
||||
if (z<0)
|
||||
{
|
||||
z /= regionSize.z;
|
||||
z--;
|
||||
}
|
||||
else
|
||||
{
|
||||
z /= regionSize.z;
|
||||
}
|
||||
return new Vec3i(
|
||||
natFromInt(x),
|
||||
natFromInt(y),
|
||||
natFromInt(z)
|
||||
);
|
||||
}
|
||||
private final Path path;
|
||||
private final ChunkMap<Region> regions = ChunkMaps.newHashMap();
|
||||
|
||||
private static int mod(int a, int m) {
|
||||
return ((a % m) + m) % m;
|
||||
}
|
||||
|
||||
private Vec3i getRegionLoc(Vec3i chunkLoc) {
|
||||
return new Vec3i(mod(chunkLoc.x, regionSize.x), mod(chunkLoc.y, regionSize.y), mod(chunkLoc.z, regionSize.z));
|
||||
}
|
||||
|
||||
public TestWorldDiskIO(Path worldPath) {
|
||||
if (worldPath != null) {
|
||||
SAVE_DIR = worldPath;
|
||||
}
|
||||
|
||||
// regions.put(new Vec3i(0,0,0), new Vec3i(1,1,1));
|
||||
}
|
||||
|
||||
/*public static int getAvailableSector(MappedByteBuffer mbb)
|
||||
{
|
||||
int sectorsUsed = 0;
|
||||
for (int i=offsetBytes; i<(offsetBytes+1)*chunksPerRegion; i+= (offsetBytes+1))
|
||||
{
|
||||
|
||||
sectorsUsed += mbb.get(i);
|
||||
}
|
||||
return sectorsUsed;
|
||||
}*/
|
||||
|
||||
private void setRegionSize(int format) {
|
||||
inOutMap = new HashMap<HashableVec3i, RandomFileMapped>();
|
||||
switch (format) {
|
||||
case 65536:
|
||||
default:
|
||||
regionSize = new Vec3i(16);
|
||||
chunksPerRegion = 16 * 16 * 16;
|
||||
currentFormat = 65536;
|
||||
extension = ".progressia_region";
|
||||
break;
|
||||
case 65537:
|
||||
regionSize = new Vec3i(16);
|
||||
chunksPerRegion = 16 * 16 * 16;
|
||||
currentFormat = 65536;
|
||||
extension = ".progressia_regionx";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean confirmHeaderHealth(RandomAccessFile input, HashMap<HashableVec3i, Integer> offsets, HashMap<HashableVec3i, Integer> length) throws IOException
|
||||
{
|
||||
Set<Integer> used = new HashSet<Integer>();
|
||||
input.seek(0);
|
||||
if (input.length() < 4*chunksPerRegion)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i=0;i<4*chunksPerRegion;i+=4)
|
||||
{
|
||||
int offset = 0;
|
||||
for (int ii = 0; ii < 3; ii++) {
|
||||
offset *= 256;
|
||||
offset += input.read();
|
||||
}
|
||||
int sectorLength = input.read();
|
||||
if (sectorLength==0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int headerPos = i/4;
|
||||
int x = headerPos/regionSize.y/regionSize.z;
|
||||
int y = (headerPos/regionSize.z)%regionSize.y;
|
||||
int z = headerPos%regionSize.z;
|
||||
HashableVec3i key = new HashableVec3i(new Vec3i(x,y,z));
|
||||
offsets.put(key , offset);
|
||||
length.put(key, sectorLength);
|
||||
for (int ii=0;ii<sectorLength;ii++)
|
||||
{
|
||||
if (used.contains(offset+ii))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
used.add(offset+ii);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(DefaultChunkData chunk, DefaultWorldData world, Server server) {
|
||||
if (!ENABLE)
|
||||
return;
|
||||
|
||||
try {
|
||||
|
||||
if (currentFormat == 65536) {
|
||||
LOG.debug(
|
||||
"Saving {} {} {}",
|
||||
chunk.getPosition().x,
|
||||
chunk.getPosition().y,
|
||||
chunk.getPosition().z
|
||||
);
|
||||
|
||||
Files.createDirectories(SAVE_DIR);
|
||||
|
||||
Vec3i saveCoords = getRegion(chunk.getPosition());
|
||||
|
||||
Path path = SAVE_DIR.resolve(
|
||||
String.format(
|
||||
"%d_%d_%d" + extension,
|
||||
saveCoords.x,
|
||||
saveCoords.y,
|
||||
saveCoords.z
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
RandomFileMapped outputMap = inOutMap.get(new HashableVec3i(saveCoords));
|
||||
//LOG.info("saveCoords {},{},{}", saveCoords.x, saveCoords.y, saveCoords.z);
|
||||
if (outputMap == null)
|
||||
{
|
||||
outputMap = makeNew(path, new HashableVec3i(saveCoords));
|
||||
}
|
||||
RandomAccessFile output = outputMap.file;
|
||||
|
||||
Vec3i pos = getRegionLoc(chunk.getPosition());
|
||||
int shortOffset = 4 * (pos.z + regionSize.z * (pos.y + regionSize.y * pos.x));
|
||||
int fullOffset = 4 * (chunksPerRegion);
|
||||
int offset = 0;
|
||||
|
||||
if (outputMap.hasOffset(pos))
|
||||
{
|
||||
offset = outputMap.getOffset(pos);
|
||||
}
|
||||
else {
|
||||
output.seek(shortOffset);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
offset *= 256;
|
||||
offset += output.read();
|
||||
}
|
||||
int sectorLength = output.read();
|
||||
if (sectorLength == 0) {
|
||||
int outputLen = (int) output.length();
|
||||
offset = (int) (outputLen - fullOffset) / sectorSize + 1;
|
||||
int tempOffset = offset;
|
||||
output.seek(shortOffset);
|
||||
|
||||
byte readOffset[] = new byte[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
readOffset[2 - i] = (byte) (tempOffset % 256);
|
||||
tempOffset >>= 8;
|
||||
}
|
||||
output.write(readOffset);
|
||||
|
||||
output.setLength(fullOffset + offset * sectorSize);
|
||||
}
|
||||
outputMap.putOffset(pos, offset);
|
||||
}
|
||||
|
||||
ByteArrayOutputStream tempDataStream = new ByteArrayOutputStream();
|
||||
DataOutputStream trueOutput = new DataOutputStream(
|
||||
new DeflaterOutputStream(
|
||||
new BufferedOutputStream(tempDataStream)
|
||||
)
|
||||
);
|
||||
ChunkIO.save(chunk, trueOutput, IOContext.SAVE);
|
||||
writeGenerationHint(chunk, trueOutput, server);
|
||||
|
||||
trueOutput.close();
|
||||
|
||||
byte tempData[] = tempDataStream.toByteArray();
|
||||
|
||||
output.seek( fullOffset + sectorSize * offset);
|
||||
output.write(tempData);
|
||||
|
||||
output.seek(shortOffset + 3);
|
||||
output.write(tempData.length / sectorSize + 1);
|
||||
outputMap.putLength(pos, tempData.length / sectorSize + 1);
|
||||
// LOG.info("Used {} sectors",(int)
|
||||
// tempData.length/sectorSize + 1);
|
||||
|
||||
}
|
||||
else if (currentFormat == 65537) {
|
||||
LOG.debug(
|
||||
"Saving {} {} {}",
|
||||
chunk.getPosition().x,
|
||||
chunk.getPosition().y,
|
||||
chunk.getPosition().z
|
||||
);
|
||||
|
||||
Files.createDirectories(SAVE_DIR);
|
||||
|
||||
Vec3i saveCoords = getRegion(chunk.getPosition());
|
||||
|
||||
Path path = SAVE_DIR.resolve(
|
||||
String.format(
|
||||
"%d_%d_%d" + extension,
|
||||
saveCoords.x,
|
||||
saveCoords.y,
|
||||
saveCoords.z
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
RandomFileMapped outputMap = inOutMap.get(new HashableVec3i(saveCoords));
|
||||
//LOG.info("saveCoords {},{},{}", saveCoords.x, saveCoords.y, saveCoords.z);
|
||||
if (outputMap == null)
|
||||
{
|
||||
outputMap = makeNew(path, new HashableVec3i(saveCoords));
|
||||
}
|
||||
RandomAccessFile output = outputMap.file;
|
||||
|
||||
Vec3i pos = getRegionLoc(chunk.getPosition());
|
||||
int shortOffset = 4 * (pos.z + regionSize.z * (pos.y + regionSize.y * pos.x));
|
||||
int fullOffset = 4 * (chunksPerRegion);
|
||||
int offset = 0;
|
||||
|
||||
if (outputMap.hasOffset(pos))
|
||||
{
|
||||
offset = outputMap.getOffset(pos);
|
||||
}
|
||||
else {
|
||||
output.seek(shortOffset);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
offset *= 256;
|
||||
offset += output.read();
|
||||
}
|
||||
int sectorLength = output.read();
|
||||
if (sectorLength == 0) {
|
||||
int outputLen = (int) output.length();
|
||||
offset = (int) (outputLen - fullOffset) / sectorSize + 1;
|
||||
int tempOffset = offset;
|
||||
output.seek(shortOffset);
|
||||
|
||||
byte readOffset[] = new byte[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
readOffset[2 - i] = (byte) (tempOffset % 256);
|
||||
tempOffset >>= 8;
|
||||
}
|
||||
output.write(readOffset);
|
||||
|
||||
output.setLength(fullOffset + offset * sectorSize);
|
||||
}
|
||||
outputMap.putOffset(pos, offset);
|
||||
}
|
||||
|
||||
ByteArrayOutputStream tempDataStream = new ByteArrayOutputStream();
|
||||
DataOutputStream trueOutput = new DataOutputStream(
|
||||
new DeflaterOutputStream(
|
||||
new BufferedOutputStream(tempDataStream)
|
||||
)
|
||||
);
|
||||
ChunkIO.save(chunk, trueOutput, IOContext.SAVE);
|
||||
writeGenerationHint(chunk, trueOutput, server);
|
||||
|
||||
trueOutput.close();
|
||||
|
||||
byte tempData[] = tempDataStream.toByteArray();
|
||||
|
||||
output.seek( fullOffset + sectorSize * offset);
|
||||
|
||||
chunk.computeOpaque();
|
||||
chunk.computeEmpty();
|
||||
output.write((chunk.isOpaque() ? 1 : 0) << 1 + (chunk.isEmpty() ? 1 : 0)); //Writes extra flag byte of whether or not the chunk is empty or solid
|
||||
output.write(tempData);
|
||||
|
||||
output.seek(shortOffset + 3);
|
||||
output.write(tempData.length / sectorSize + 1);
|
||||
outputMap.putLength(pos, tempData.length / sectorSize + 1);
|
||||
// LOG.info("Used {} sectors",(int)
|
||||
// tempData.length/sectorSize + 1);
|
||||
|
||||
}
|
||||
// else if (currentFormat)
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private RandomFileMapped makeNew(Path path, Object hashObj) {
|
||||
try
|
||||
{
|
||||
RandomAccessFile raf = new RandomAccessFile(path.toFile(), "rw");
|
||||
//FileChannel fc = raf.getChannel();
|
||||
//MappedByteBuffer output = fc.map(FileChannel.MapMode.READ_WRITE, 0, maxSize*chunksPerRegion);
|
||||
//output.limit(maxSize*chunksPerRegion);
|
||||
RandomFileMapped rfm = new RandomFileMapped(raf);
|
||||
inOutMap.put((HashableVec3i) hashObj, rfm);
|
||||
return rfm;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.warn("bad things");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void writeGenerationHint(DefaultChunkData chunk, DataOutputStream output, Server server)
|
||||
throws IOException {
|
||||
server.getWorld().getGenerator().writeGenerationHint(output, chunk.getGenerationHint());
|
||||
public TestWorldDiskIO(Path path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultChunkData load(Vec3i chunkPos, DefaultWorldData world, Server server) {
|
||||
if (!ENABLE)
|
||||
if (!ENABLE) {
|
||||
return null;
|
||||
|
||||
if (currentFormat == -1) {
|
||||
Path formatPath = SAVE_DIR.resolve(formatFile);
|
||||
File format = formatPath.toFile();
|
||||
|
||||
if (format.exists()) {
|
||||
String data = null;
|
||||
try {
|
||||
Scanner reader = new Scanner(format);
|
||||
|
||||
data = reader.next();
|
||||
|
||||
reader.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
byte[] formatBytes = data.getBytes();
|
||||
int formatNum = formatBytes[0] * 256 * 256 * 256 + formatBytes[1] * 256 * 256 + formatBytes[2] * 256
|
||||
+ formatBytes[3];
|
||||
|
||||
setRegionSize(formatNum);
|
||||
} else {
|
||||
setRegionSize(bestFormat);
|
||||
|
||||
LOG.debug("Making new world with format {}", bestFormat);
|
||||
|
||||
BufferedWriter bw;
|
||||
try {
|
||||
bw = new BufferedWriter(new FileWriter(format));
|
||||
|
||||
int bfClone = bestFormat;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
bw.write(bfClone >> 24);
|
||||
LOG.debug(bfClone >> 24);
|
||||
bfClone = bfClone << 8;
|
||||
}
|
||||
|
||||
/*
|
||||
* bw.write(
|
||||
* new char[] {
|
||||
* (char) bestFormat / (256 * 256 * 256),
|
||||
* (char) (bestFormat % 256) / (256 * 256),
|
||||
* (char) (bestFormat % (256 * 256)) / (256),
|
||||
* (char) (bestFormat % (256 * 256 * 256)) }
|
||||
* );
|
||||
*/
|
||||
|
||||
bw.close();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (currentFormat == 65536) {
|
||||
Vec3i saveCoords = getRegion(chunkPos);
|
||||
try {
|
||||
|
||||
Path path = SAVE_DIR.resolve(
|
||||
Region region = getRegion(chunkPos, false);
|
||||
if (region == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DefaultChunkData result = region.load(chunkPos, world, server);
|
||||
return result;
|
||||
|
||||
} catch (IOException | DecodingException e) {
|
||||
LOG.warn(
|
||||
"Failed to load chunk {} {} {}",
|
||||
chunkPos.x,
|
||||
chunkPos.y,
|
||||
chunkPos.z
|
||||
);
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(DefaultChunkData chunk, DefaultWorldData world, Server server) {
|
||||
if (!ENABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
LOG.debug(
|
||||
"Saving {} {} {}",
|
||||
chunk.getPosition().x,
|
||||
chunk.getPosition().y,
|
||||
chunk.getPosition().z
|
||||
);
|
||||
|
||||
Region region = getRegion(chunk.getPosition(), true);
|
||||
region.save(chunk, server);
|
||||
} catch (IOException e) {
|
||||
LOG.warn(
|
||||
"Failed to save chunk {} {} {}",
|
||||
chunk.getPosition().x,
|
||||
chunk.getPosition().y,
|
||||
chunk.getPosition().z
|
||||
);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private Region getRegion(Vec3i position, boolean createIfMissing) throws IOException {
|
||||
if (regions.isEmpty()) {
|
||||
Files.createDirectories(getPath());
|
||||
}
|
||||
|
||||
Vec3i regionCoords = getRegionCoords(position);
|
||||
|
||||
Region region = regions.get(regionCoords);
|
||||
if (region == null) {
|
||||
|
||||
Path path = getPath().resolve(
|
||||
String.format(
|
||||
"%d_%d_%d" + extension,
|
||||
saveCoords.x,
|
||||
saveCoords.y,
|
||||
saveCoords.z
|
||||
FILE_NAME_FORMAT,
|
||||
regionCoords.x,
|
||||
regionCoords.y,
|
||||
regionCoords.z
|
||||
)
|
||||
);
|
||||
|
||||
if (!Files.exists(path)) {
|
||||
LOG.debug(
|
||||
"Not found {} {} {}",
|
||||
chunkPos.x,
|
||||
chunkPos.y,
|
||||
chunkPos.z
|
||||
);
|
||||
|
||||
if (!Files.exists(path) && !createIfMissing) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
DefaultChunkData result = loadRegion(path, chunkPos, world, server);
|
||||
|
||||
LOG.debug(
|
||||
"Loaded {} {} {}",
|
||||
chunkPos.x,
|
||||
chunkPos.y,
|
||||
chunkPos.z
|
||||
);
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
LOG.debug(
|
||||
"Could not load {} {} {}",
|
||||
chunkPos.x,
|
||||
chunkPos.y,
|
||||
chunkPos.z
|
||||
);
|
||||
return null;
|
||||
}
|
||||
region = openRegion(path, regionCoords);
|
||||
}
|
||||
else if (currentFormat == 65537) {
|
||||
Vec3i saveCoords = getRegion(chunkPos);
|
||||
|
||||
Path path = SAVE_DIR.resolve(
|
||||
String.format(
|
||||
"%d_%d_%d" + extension,
|
||||
saveCoords.x,
|
||||
saveCoords.y,
|
||||
saveCoords.z
|
||||
)
|
||||
);
|
||||
|
||||
if (!Files.exists(path)) {
|
||||
LOG.debug(
|
||||
"Not found {} {} {}",
|
||||
chunkPos.x,
|
||||
chunkPos.y,
|
||||
chunkPos.z
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
DefaultChunkData result = loadRegionX(path, chunkPos, world, server);
|
||||
|
||||
LOG.debug(
|
||||
"Loaded {} {} {}",
|
||||
chunkPos.x,
|
||||
chunkPos.y,
|
||||
chunkPos.z
|
||||
);
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
LOG.debug(
|
||||
"Could not load {} {} {}",
|
||||
chunkPos.x,
|
||||
chunkPos.y,
|
||||
chunkPos.z
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return region;
|
||||
}
|
||||
|
||||
private DefaultChunkData loadRegion(Path path, Vec3i chunkPos, DefaultWorldData world, Server server)
|
||||
throws IOException,
|
||||
DecodingException {
|
||||
int offset = 0;
|
||||
int sectorLength = 0;
|
||||
Vec3i pos;
|
||||
RandomFileMapped inputMap;
|
||||
int fullOffset = 4 * (chunksPerRegion);
|
||||
try
|
||||
{
|
||||
Vec3i streamCoords = getRegion(chunkPos);
|
||||
|
||||
inputMap = inOutMap.get(new HashableVec3i(streamCoords));
|
||||
//LOG.info("streamCoords {},{},{}", streamCoords.x,streamCoords.y,streamCoords.z);
|
||||
if (inputMap == null)
|
||||
{
|
||||
//input = new RandomAccessFile(path.toFile(), "rw");
|
||||
//input = Files.newByteChannel(path);
|
||||
inputMap = makeNew(path, new HashableVec3i(streamCoords));
|
||||
}
|
||||
|
||||
RandomAccessFile input = inputMap.file;
|
||||
|
||||
|
||||
pos = getRegionLoc(chunkPos);
|
||||
|
||||
if (inputMap.hasOffset(pos))
|
||||
{
|
||||
offset = inputMap.getOffset(pos);
|
||||
sectorLength = inputMap.getLength(pos);
|
||||
//LOG.info("{},{}", offset, sectorLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// LOG.info(path.toString());
|
||||
|
||||
int shortOffset = 4 * (pos.z + regionSize.z * (pos.y + regionSize.y * pos.x));
|
||||
|
||||
input.seek(shortOffset);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
offset *= 256;
|
||||
offset += input.read();
|
||||
}
|
||||
sectorLength = input.read();
|
||||
if (sectorLength == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
inputMap.putOffset(pos, offset);
|
||||
inputMap.putLength(pos, sectorLength);
|
||||
}
|
||||
input.seek(fullOffset + sectorSize * offset);
|
||||
|
||||
// LOG.info("Read {} sectors", sectorLength);
|
||||
|
||||
byte tempData[] = new byte[sectorSize * sectorLength];
|
||||
input.read(tempData);
|
||||
|
||||
DataInputStream trueInput = new DataInputStream(
|
||||
new InflaterInputStream(new BufferedInputStream(new ByteArrayInputStream(tempData)))
|
||||
);
|
||||
DefaultChunkData chunk = ChunkIO.load(world, chunkPos, trueInput, IOContext.SAVE);
|
||||
readGenerationHint(chunk, trueInput, server);
|
||||
return chunk;
|
||||
}
|
||||
catch (EOFException e)
|
||||
{
|
||||
LOG.warn("Reached end of file, offset was {}, sectors was {}", offset, sectorLength);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
private Region openRegion(Path path, Vec3i regionCoords) throws IOException {
|
||||
RandomAccessFile raf = new RandomAccessFile(path.toFile(), "rw");
|
||||
Region region = new Region(raf);
|
||||
regions.put(regionCoords, region);
|
||||
return region;
|
||||
}
|
||||
|
||||
private DefaultChunkData loadRegionX(Path path, Vec3i chunkPos, DefaultWorldData world, Server server)
|
||||
throws IOException,
|
||||
DecodingException {
|
||||
int offset = 0;
|
||||
int sectorLength = 0;
|
||||
Vec3i pos;
|
||||
RandomFileMapped inputMap;
|
||||
int fullOffset = 4 * (chunksPerRegion);
|
||||
try
|
||||
{
|
||||
Vec3i streamCoords = getRegion(chunkPos);
|
||||
|
||||
inputMap = inOutMap.get(new HashableVec3i(streamCoords));
|
||||
//LOG.info("streamCoords {},{},{}", streamCoords.x,streamCoords.y,streamCoords.z);
|
||||
if (inputMap == null)
|
||||
{
|
||||
//input = new RandomAccessFile(path.toFile(), "rw");
|
||||
//input = Files.newByteChannel(path);
|
||||
inputMap = makeNew(path, new HashableVec3i(streamCoords));
|
||||
}
|
||||
|
||||
RandomAccessFile input = inputMap.file;
|
||||
|
||||
|
||||
pos = getRegionLoc(chunkPos);
|
||||
|
||||
if (inputMap.hasOffset(pos))
|
||||
{
|
||||
offset = inputMap.getOffset(pos);
|
||||
sectorLength = inputMap.getLength(pos);
|
||||
//LOG.info("{},{}", offset, sectorLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// LOG.info(path.toString());
|
||||
|
||||
int shortOffset = 4 * (pos.z + regionSize.z * (pos.y + regionSize.y * pos.x));
|
||||
|
||||
input.seek(shortOffset);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
offset *= 256;
|
||||
offset += input.read();
|
||||
}
|
||||
sectorLength = input.read();
|
||||
if (sectorLength == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
inputMap.putOffset(pos, offset);
|
||||
inputMap.putLength(pos, sectorLength);
|
||||
}
|
||||
input.seek(fullOffset + sectorSize * offset);
|
||||
|
||||
int xByte = input.read();
|
||||
|
||||
// LOG.info("Read {} sectors", sectorLength);
|
||||
|
||||
byte tempData[] = new byte[sectorSize * sectorLength];
|
||||
input.read(tempData);
|
||||
|
||||
DataInputStream trueInput = new DataInputStream(
|
||||
new InflaterInputStream(new BufferedInputStream(new ByteArrayInputStream(tempData)))
|
||||
);
|
||||
DefaultChunkData chunk = ChunkIO.load(world, chunkPos, trueInput, IOContext.SAVE);
|
||||
readGenerationHint(chunk, trueInput, server);
|
||||
|
||||
chunk.isOpaque = (xByte & 2)==2;
|
||||
chunk.isEmpty = (xByte & 1)==1;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
catch (EOFException e)
|
||||
{
|
||||
LOG.warn("Reached end of file, offset was {}, sectors was {}", offset, sectorLength);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
static void writeGenerationHint(DefaultChunkData chunk, DataOutputStream output, Server server)
|
||||
throws IOException {
|
||||
server.getWorld().getGenerator().writeGenerationHint(output, chunk.getGenerationHint());
|
||||
}
|
||||
|
||||
private static void readGenerationHint(DefaultChunkData chunk, DataInputStream input, Server server)
|
||||
static void readGenerationHint(DefaultChunkData chunk, DataInputStream input, Server server)
|
||||
throws IOException,
|
||||
DecodingException {
|
||||
chunk.setGenerationHint(server.getWorld().getGenerator().readGenerationHint(input));
|
||||
@ -811,13 +168,18 @@ public class TestWorldDiskIO implements WorldContainer {
|
||||
|
||||
@Override
|
||||
public Path getPath() {
|
||||
return SAVE_DIR;
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
try {
|
||||
for (Region region : regions.values()) {
|
||||
region.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "Could not close region files");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user