diff --git a/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java b/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java index 2d432ab..41aa933 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java @@ -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() { diff --git a/src/main/java/ru/windcorp/progressia/common/util/HashableVec3i.java b/src/main/java/ru/windcorp/progressia/common/util/HashableVec3i.java deleted file mode 100644 index f003555..0000000 --- a/src/main/java/ru/windcorp/progressia/common/util/HashableVec3i.java +++ /dev/null @@ -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 ; - } - -} diff --git a/src/main/java/ru/windcorp/progressia/common/world/Coordinates.java b/src/main/java/ru/windcorp/progressia/common/world/Coordinates.java index b43231b..634b05e 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/Coordinates.java +++ b/src/main/java/ru/windcorp/progressia/common/world/Coordinates.java @@ -162,5 +162,72 @@ public class Coordinates { public static boolean isOnChunkBorder(int blockInChunk) { 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; + } } diff --git a/src/main/java/ru/windcorp/progressia/common/world/DefaultChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/DefaultChunkData.java index 967fe4e..e11509b 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/DefaultChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/DefaultChunkData.java @@ -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; @@ -45,11 +45,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 transparent; private final Vec3i position = new Vec3i(); private final DefaultWorldData world; @@ -204,44 +199,6 @@ public class DefaultChunkData implements ChunkData { public void setGenerationHint(Object generationHint) { this.generationHint = generationHint; } - - public void computeOpaque() - { - for (int xyz=0;xyz 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) { diff --git a/src/main/java/ru/windcorp/progressia/server/world/generation/planet/PlanetFeatureGenerator.java b/src/main/java/ru/windcorp/progressia/server/world/generation/planet/PlanetFeatureGenerator.java index 205ea43..97e2424 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/generation/planet/PlanetFeatureGenerator.java +++ b/src/main/java/ru/windcorp/progressia/server/world/generation/planet/PlanetFeatureGenerator.java @@ -56,9 +56,6 @@ public class PlanetFeatureGenerator { generateBorderFeatures(server, chunk); } - chunk.computeEmpty(); - chunk.computeOpaque(); - chunk.setGenerationHint(true); } diff --git a/src/main/java/ru/windcorp/progressia/test/region/Region.java b/src/main/java/ru/windcorp/progressia/test/region/Region.java new file mode 100644 index 0000000..7afd862 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/region/Region.java @@ -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 . + */ +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 offsets = ChunkMaps.newHashMap(); + private final ChunkMap 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 used = new HashSet(); + 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; + } +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/test/region/TestWorldDiskIO.java b/src/main/java/ru/windcorp/progressia/test/region/TestWorldDiskIO.java index b391ec3..da0da50 100644 --- a/src/main/java/ru/windcorp/progressia/test/region/TestWorldDiskIO.java +++ b/src/main/java/ru/windcorp/progressia/test/region/TestWorldDiskIO.java @@ -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; - - public class RandomFileMapped { - public RandomAccessFile file; - public HashMap offsets; - public HashMap lengths; - - 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(); - } - } - } - - - 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)); - } + private static final boolean ENABLE = true; - public boolean hasLength(Vec3i pos) { - return lengths.containsKey(new HashableVec3i(pos)); - } - - public void putLength(Vec3i pos, int length) - { - lengths.put(new HashableVec3i(pos), length); - } + private static final String FILE_NAME_FORMAT = "region_%d_%d_%d.progressia_region"; + + private static final int BITS_IN_CHUNK_COORDS = 4; + public static final int REGION_DIAMETER = 1 << BITS_IN_CHUNK_COORDS; + + 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 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 regions = new HashMap(); - 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 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(); - 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 offsets, HashMap length) throws IOException - { - Set used = new HashSet(); - 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>= 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 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; + 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 static void readGenerationHint(DefaultChunkData chunk, DataInputStream input, Server server) + static void writeGenerationHint(DefaultChunkData chunk, DataOutputStream output, Server server) + throws IOException { + server.getWorld().getGenerator().writeGenerationHint(output, chunk.getGenerationHint()); + } + + 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"); + } } }