From ef928b382f92ad89f9bfd232dd356857da7e2697 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Wed, 30 Dec 2020 14:22:34 +0300 Subject: [PATCH] Implemented TestChunkCodec and removed chunk generation from client --- .../progressia/common/util/DataBuffer.java | 2 +- .../progressia/common/world/ChunkData.java | 18 ++ .../common/world/tile/TileDataStack.java | 10 + .../progressia/test/TestChunkCodec.java | 210 +++++++++++++++++- 4 files changed, 230 insertions(+), 10 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java b/src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java index e2527ea..ab5dbc8 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java +++ b/src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java @@ -25,7 +25,7 @@ public class DataBuffer { @Override public int read() throws IOException { if (DataBuffer.this.position >= buffer.size()) return -1; - int result = buffer.getQuick(DataBuffer.this.position); + int result = buffer.getQuick(DataBuffer.this.position) & 0xFF; ++DataBuffer.this.position; return result; } diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index 2e454e0..af6c9c7 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -393,6 +393,24 @@ implements GenericChunk< report(null, tile); } + + @Override + public void load(TileData tile, int tag) { + addFarthest(tile); + + int assignedTag = getIndexByTag(tag); + + if (assignedTag == tag) return; + if (assignedTag == -1) { + throw new IllegalArgumentException("Tag " + tag + " already used by tile at index " + getIndexByTag(tag)); + } + + indicesByTag[tagsByIndex[size() - 1]] = -1; + tagsByIndex[size() - 1] = tag; + indicesByTag[tag] = size() - 1; + + assert checkConsistency(); + } @Override public TileData remove(int index) { diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java b/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java index f0f7609..9f72116 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java @@ -25,6 +25,16 @@ extends GenericTileStack< */ @Override public abstract void add(int index, TileData tile); + + /** + * Adds the specified tile at the end of this stack assigning it the provided tag. + * This method is useful for copying stacks when preserving tags is necessary. + * @param tile the tile to add + * @param tag the tag to assign the new tile + * @throws IllegalArgumentException if this stack already contains a tile with the + * provided tag + */ + public abstract void load(TileData tile, int tag); /** * Replaces the tile at the specified position in this stack with the specified tile. diff --git a/src/main/java/ru/windcorp/progressia/test/TestChunkCodec.java b/src/main/java/ru/windcorp/progressia/test/TestChunkCodec.java index 5b00683..8e0a190 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestChunkCodec.java +++ b/src/main/java/ru/windcorp/progressia/test/TestChunkCodec.java @@ -1,36 +1,228 @@ package ru.windcorp.progressia.test; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; import glm.vec._3.i.Vec3i; +import gnu.trove.map.TObjectIntMap; +import gnu.trove.map.hash.TObjectIntHashMap; +import ru.windcorp.jputil.functions.ThrowingConsumer; import ru.windcorp.progressia.common.io.ChunkCodec; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.DecodingException; import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.block.BlockData; +import ru.windcorp.progressia.common.world.block.BlockDataRegistry; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.tile.TileData; +import ru.windcorp.progressia.common.world.tile.TileDataRegistry; public class TestChunkCodec extends ChunkCodec { + private static class Palette { + private final List nidToElement = new ArrayList<>(); + private final TObjectIntMap elementToNid = new TObjectIntHashMap<>(); + + public void add(E element) { + if (elementToNid.containsKey(element)) return; + + nidToElement.add(element); + elementToNid.put(element, elementToNid.size()); + } + + public E getByNid(int nid) { + return nidToElement.get(nid); + } + + public int getNid(E element) { + return elementToNid.get(element); + } + + public int size() { + return nidToElement.size(); + } + } + public TestChunkCodec() { super("Test:TestCodec", 0x00); } - @Override - public ChunkData decode(WorldData world, Vec3i position, InputStream data) throws DecodingException, IOException { - ChunkData chunk = new ChunkData(position, world); - TestContent.generateChunk(chunk); - return chunk; - } - @Override public boolean shouldEncode(ChunkData chunk) { return true; } + + /* + * Decoding + */ @Override - public void encode(ChunkData chunk, OutputStream output) throws IOException { - // Do nothing. Heh. + public ChunkData decode(WorldData world, Vec3i position, InputStream inputStream) throws DecodingException, IOException { + DataInput input = new DataInputStream(inputStream); + + BlockData[] blockPalette = readBlockPalette(input); + TileData[] tilePalette = readTilePalette(input); + + ChunkData chunk = new ChunkData(position, world); + readBlocks(input, blockPalette, chunk); + readTiles(input, tilePalette, chunk); + + return chunk; + } + + private BlockData[] readBlockPalette(DataInput input) throws IOException { + BlockData[] palette = new BlockData[input.readInt()]; + + for (int nid = 0; nid < palette.length; ++nid) { + String id = input.readUTF(); + palette[nid] = BlockDataRegistry.getInstance().get(id); + } + + return palette; + } + + private TileData[] readTilePalette(DataInput input) throws IOException { + TileData[] palette = new TileData[input.readInt()]; + + for (int nid = 0; nid < palette.length; ++nid) { + String id = input.readUTF(); + palette[nid] = TileDataRegistry.getInstance().get(id); + } + + return palette; + } + + private void readBlocks(DataInput input, BlockData[] blockPalette, ChunkData chunk) throws IOException { + try { + chunk.forEachBiC(guard(v -> { + chunk.setBlock(v, blockPalette[input.readInt()], false); + })); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + } + + private void readTiles(DataInput input, TileData[] tilePalette, ChunkData chunk) throws IOException { + Vec3i bic = new Vec3i(); + + while (true) { + int xOrEndMarker = input.readByte() & 0xFF; + if (xOrEndMarker == 0xFF) break; + + bic.set(xOrEndMarker, input.readByte() & 0xFF, input.readByte() & 0xFF); + BlockFace face = BlockFace.getFaces().get(input.readByte() & 0xFF); + + int tiles = input.readByte() & 0xFF; + + for (int i = 0; i < tiles; ++i) { + TileData tile = tilePalette[input.readInt()]; + int tag = input.readInt(); + chunk.getTiles(bic, face).load(tile, tag); + } + } + } + + /* + * Encoding + */ + + @Override + public void encode(ChunkData chunk, OutputStream outputStream) throws IOException { + + DataOutput output = new DataOutputStream(outputStream); + + Palette blockPalette = createBlockPalette(chunk); + Palette tilePalette = createTilePalette(chunk); + + writeBlockPalette(blockPalette, output); + writeTilePalette(tilePalette, output); + + writeBlocks(chunk, blockPalette, output); + writeTiles(chunk, tilePalette, output); + } + + private Palette createBlockPalette(ChunkData chunk) { + Palette blockPalette = new Palette<>(); + chunk.forEachBiC(v -> blockPalette.add(chunk.getBlock(v))); + return blockPalette; + } + + private Palette createTilePalette(ChunkData chunk) { + Palette tilePalette = new Palette<>(); + chunk.forEachTile((ts, t) -> tilePalette.add(t)); + return tilePalette; + } + + private void writeBlockPalette(Palette blockPalette, DataOutput output) throws IOException { + output.writeInt(blockPalette.size()); + for (int nid = 0; nid < blockPalette.size(); ++nid) { + BlockData block = blockPalette.getByNid(nid); + output.writeUTF(block.getId()); + } + } + + private void writeTilePalette(Palette tilePalette, DataOutput output) throws IOException { + output.writeInt(tilePalette.size()); + for (int nid = 0; nid < tilePalette.size(); ++nid) { + TileData tile = tilePalette.getByNid(nid); + output.writeUTF(tile.getId()); + } + } + + private void writeBlocks(ChunkData chunk, Palette blockPalette, DataOutput output) throws IOException { + try { + chunk.forEachBiC(guard(v -> { + output.writeInt(blockPalette.getNid(chunk.getBlock(v))); + })); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + } + + private void writeTiles(ChunkData chunk, Palette tilePalette, DataOutput output) throws IOException { + Vec3i bic = new Vec3i(); + + try { + chunk.forEachTileStack(guard(ts -> { + if (ts.isEmpty()) return; + + ts.getBlockInChunk(bic); + output.writeByte(bic.x); + output.writeByte(bic.y); + output.writeByte(bic.z); + + output.writeByte(ts.getFace().getId()); + output.writeByte(ts.size()); + + for (int index = 0; index < ts.size(); ++index) { + output.writeInt(tilePalette.getNid(ts.get(index))); + output.writeInt(ts.getTagByIndex(index)); + } + })); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + + output.writeByte(0xFF); + } + + private static Consumer guard(ThrowingConsumer action) { + return v -> { + try { + action.accept(v); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }; } }