diff --git a/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizerCube.java b/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizerCube.java index 89ca03f..4ee0bae 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizerCube.java +++ b/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizerCube.java @@ -18,7 +18,7 @@ package ru.windcorp.progressia.client.world.cro; import static ru.windcorp.progressia.common.world.ChunkData.BLOCKS_PER_CHUNK; -import static ru.windcorp.progressia.common.world.ChunkData.TILES_PER_FACE; +import static ru.windcorp.progressia.common.world.tile.GenericTileStack.TILES_PER_FACE; import static ru.windcorp.progressia.common.world.block.BlockFace.BLOCK_FACE_COUNT; import java.util.ArrayList; @@ -72,18 +72,6 @@ public class ChunkRenderOptimizerCube extends ChunkRenderOptimizer { private static final Vec3 COLOR_MULTIPLIER = new Vec3(1, 1, 1); -// private final OpaqueCube[][][] blocks = -// new OpaqueCube[BLOCKS_PER_CHUNK] -// [BLOCKS_PER_CHUNK] -// [BLOCKS_PER_CHUNK]; -// -// private final OpaqueTile[][][][][] tiles = -// new OpaqueTile[BLOCKS_PER_CHUNK] -// [BLOCKS_PER_CHUNK] -// [BLOCKS_PER_CHUNK] -// [BLOCK_FACE_COUNT] -// [TILES_PER_FACE]; - private final BlockInfo[][][] data = new BlockInfo[BLOCKS_PER_CHUNK] [BLOCKS_PER_CHUNK] diff --git a/src/main/java/ru/windcorp/progressia/client/world/tile/TileLocation.java b/src/main/java/ru/windcorp/progressia/client/world/tile/TileLocation.java deleted file mode 100644 index 9590454..0000000 --- a/src/main/java/ru/windcorp/progressia/client/world/tile/TileLocation.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.windcorp.progressia.client.world.tile; - -import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.world.block.BlockFace; - -public class TileLocation { - - public final Vec3i pos = new Vec3i(); - public BlockFace face; - public int layer; - - public TileLocation() { - // Do nothing - } - - public TileLocation(TileLocation src) { - this.pos.set(src.pos.x, src.pos.y, src.pos.z); - this.face = src.face; - this.layer = src.layer; - } - -} 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 86b226c..863d990 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -20,32 +20,27 @@ package ru.windcorp.progressia.common.world; import static ru.windcorp.progressia.common.world.block.BlockFace.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Consumer; -import com.google.common.collect.Lists; - -import glm.vec._2.Vec2; -import glm.vec._3.Vec3; import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.client.world.tile.TileLocation; -import ru.windcorp.progressia.common.util.SizeLimitedList; import ru.windcorp.progressia.common.util.VectorUtil; 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.entity.EntityData; -import ru.windcorp.progressia.common.world.entity.EntityDataRegistry; import ru.windcorp.progressia.common.world.tile.TileData; -import ru.windcorp.progressia.common.world.tile.TileDataRegistry; +import ru.windcorp.progressia.common.world.tile.TileDataStack; +import ru.windcorp.progressia.common.world.tile.TileReference; +import ru.windcorp.progressia.common.world.tile.TileStackIsFullException; public class ChunkData { public static final int BLOCKS_PER_CHUNK = Coordinates.CHUNK_SIZE; - public static final int TILES_PER_FACE = 8; private final Vec3i position = new Vec3i(); private final WorldData world; @@ -54,8 +49,7 @@ public class ChunkData { BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK ]; - @SuppressWarnings("unchecked") - private final List[] tiles = (List[]) new List[ + private final TileDataStack[] tiles = new TileDataStack[ BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCK_FACE_COUNT ]; @@ -69,95 +63,8 @@ public class ChunkData { public ChunkData(Vec3i position, WorldData world) { this.position.set(position.x, position.y, position.z); this.world = world; - - tmp_generate(); } - private void tmp_generate() { - BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt"); - BlockData stone = BlockDataRegistry.getInstance().get("Test:Stone"); - BlockData air = BlockDataRegistry.getInstance().get("Test:Air"); - - TileData grass = TileDataRegistry.getInstance().get("Test:Grass"); - TileData stones = TileDataRegistry.getInstance().get("Test:Stones"); - TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers"); - TileData sand = TileDataRegistry.getInstance().get("Test:Sand"); - - Vec3i aPoint = new Vec3i(5, 0, BLOCKS_PER_CHUNK + BLOCKS_PER_CHUNK/2).sub(getPosition()); - Vec3i pos = new Vec3i(); - - for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) { - for (int y = 0; y < BLOCKS_PER_CHUNK; ++y) { - for (int z = 0; z < BLOCKS_PER_CHUNK; ++z) { - - pos.set(x, y, z); - float f = aPoint.sub(pos, pos).length(); - pos.set(x, y, z); - - if (f > 17) { - setBlock(pos, stone, false); - } else if (f > 14) { - setBlock(pos, dirt, false); - } else { - setBlock(pos, air, false); - } - - } - } - } - - for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) { - for (int y = 0; y < BLOCKS_PER_CHUNK; ++y) { - pos.set(x, y, 0); - - for (pos.z = BLOCKS_PER_CHUNK - 1; pos.z >= 0 && getBlock(pos) == air; --pos.z); - - getTiles(pos, BlockFace.TOP).add(grass); - for (BlockFace face : BlockFace.getFaces()) { - if (face.getVector().z != 0) continue; - pos.add(face.getVector()); - - if (!isInBounds(pos) || (getBlock(pos) == air)) { - pos.sub(face.getVector()); - getTiles(pos, face).add(grass); - } else { - pos.sub(face.getVector()); - } - } - - int hash = x*x * 19 ^ y*y * 41 ^ pos.z*pos.z * 147; - if (hash % 5 == 0) { - getTiles(pos, BlockFace.TOP).add(sand); - } - - hash = x*x * 13 ^ y*y * 37 ^ pos.z*pos.z * 129; - if (hash % 5 == 0) { - getTiles(pos, BlockFace.TOP).add(stones); - } - - hash = x*x * 17 ^ y*y * 39 ^ pos.z*pos.z * 131; - if (hash % 9 == 0) { - getTiles(pos, BlockFace.TOP).add(flowers); - } - } - } - - if (!getPosition().any()) { - EntityData player = EntityDataRegistry.getInstance().create("Test:Player"); - player.setEntityId(0x42); - player.setPosition(new Vec3(-6, -6, 20)); - player.setDirection(new Vec2( - (float) Math.toRadians(40), (float) Math.toRadians(45) - )); - getEntities().add(player); - - EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie"); - statie.setEntityId(0xDEADBEEF); - statie.setPosition(new Vec3(0, 15, 16)); - getEntities().add(statie); - } - } - public BlockData getBlock(Vec3i posInChunk) { return blocks[getBlockIndex(posInChunk)]; } @@ -174,7 +81,7 @@ public class ChunkData { } } - public List getTilesOrNull(Vec3i blockInChunk, BlockFace face) { + public TileDataStack getTilesOrNull(Vec3i blockInChunk, BlockFace face) { return tiles[getTileIndex(blockInChunk, face)]; } @@ -186,7 +93,7 @@ public class ChunkData { */ protected void setTiles( Vec3i blockInChunk, BlockFace face, - List tiles + TileDataStack tiles ) { this.tiles[getTileIndex(blockInChunk, face)] = tiles; } @@ -195,53 +102,31 @@ public class ChunkData { return getTilesOrNull(blockInChunk, face) != null; } - public List getTiles(Vec3i blockInChunk, BlockFace face) { + public TileDataStack getTiles(Vec3i blockInChunk, BlockFace face) { int index = getTileIndex(blockInChunk, face); if (tiles[index] == null) { - createTileContainer(blockInChunk, face); + createTileStack(blockInChunk, face); } return tiles[index]; } - private void createTileContainer(Vec3i blockInChunk, BlockFace face) { - if (isBorder(blockInChunk, face)) { - createBorderTileContainer(blockInChunk, face); - } else { - createNormalTileContainer(blockInChunk, face); + private void createTileStack(Vec3i blockInChunk, BlockFace face) { + Vec3i independentBlockInChunk = conjureIndependentBlockInChunkVec3i(blockInChunk); + TileDataStackImpl stack = new TileDataStackImpl(independentBlockInChunk, face); + setTiles(blockInChunk, face, stack); + } + + private Vec3i conjureIndependentBlockInChunkVec3i(Vec3i blockInChunk) { + for (int i = 0; i < BlockFace.BLOCK_FACE_COUNT; ++i) { + TileDataStack stack = getTilesOrNull(blockInChunk, BlockFace.getFaces().get(i)); + if (stack instanceof TileDataStackImpl) { + return ((TileDataStackImpl) stack).blockInChunk; + } } - } - - private void createNormalTileContainer(Vec3i blockInChunk, BlockFace face) { - List primaryList = - SizeLimitedList.wrap( - new ChunkDataReportingList( - new ArrayList<>(TILES_PER_FACE), - this, blockInChunk, face - ), - TILES_PER_FACE - ); - List secondaryList = Lists.reverse(primaryList); - - Vec3i cursor = new Vec3i(blockInChunk.x, blockInChunk.y, blockInChunk.z); - - face = face.getPrimaryAndMoveCursor(cursor); - setTiles(cursor, face, primaryList); - - face = face.getSecondaryAndMoveCursor(cursor); - setTiles(cursor, face, secondaryList); - } - - private void createBorderTileContainer(Vec3i blockInChunk, BlockFace face) { - // TODO cooperate with neighbours - setTiles( - blockInChunk, face, - SizeLimitedList.wrap( - new ArrayList<>(TILES_PER_FACE), TILES_PER_FACE - ) - ); + return new Vec3i(blockInChunk); } private static int getBlockIndex(Vec3i posInChunk) { @@ -272,16 +157,15 @@ public class ChunkData { } } - private static boolean isInBounds(Vec3i posInChunk) { + public static boolean isInBounds(Vec3i posInChunk) { return posInChunk.x >= 0 && posInChunk.x < BLOCKS_PER_CHUNK && posInChunk.y >= 0 && posInChunk.y < BLOCKS_PER_CHUNK && posInChunk.z >= 0 && posInChunk.z < BLOCKS_PER_CHUNK; } - private boolean isBorder(Vec3i blockInChunk, BlockFace face) { + public boolean isBorder(Vec3i blockInChunk, BlockFace face) { final int min = 0, max = BLOCKS_PER_CHUNK - 1; - return (blockInChunk.x == min && face == SOUTH ) || (blockInChunk.x == max && face == NORTH ) || @@ -299,32 +183,24 @@ public class ChunkData { ); } + public void forEachTileStack(Consumer action) { + forEachBlock(blockInChunk -> { + for (BlockFace face : BlockFace.getFaces()) { + TileDataStack stack = getTilesOrNull(blockInChunk, face); + if (stack == null) continue; + action.accept(stack); + } + }); + } + /** - * Iterates over all tiles in this chunk. Tiles are referenced using their - * primary block (so that the face is - * {@linkplain BlockFace#isPrimary() primary}). + * Iterates over all tiles in this chunk. * * @param action the action to perform. {@code TileLocation} refers to each * tile using its primary block */ - public void forEachTile(BiConsumer action) { - TileLocation loc = new TileLocation(); - - forEachBlock(blockInChunk -> { - loc.pos.set(blockInChunk.x, blockInChunk.y, blockInChunk.z); - - for (BlockFace face : BlockFace.getPrimaryFaces()) { - List list = getTilesOrNull(blockInChunk, face); - if (list == null) continue; - - loc.face = face; - - for (loc.layer = 0; loc.layer < list.size(); ++loc.layer) { - TileData tile = list.get(loc.layer); - action.accept(loc, tile); - } - } - }); + public void forEachTile(BiConsumer action) { + forEachTileStack(stack -> stack.forEach(tileData -> action.accept(stack, tileData))); } public void forEachEntity(Consumer action) { @@ -374,5 +250,293 @@ public class ChunkData { protected void beforeUnloaded() { getListeners().forEach(l -> l.beforeChunkUnloaded(this)); } + + /** + * Implementation of {@link TileDataStack} used internally by {@link ChunkData} to + * actually store the tiles. This is basically an array wrapper with reporting + * capabilities. + * @author javapony + */ + private class TileDataStackImpl extends TileDataStack { + private class TileReferenceImpl implements TileReference { + private int index; + + public TileReferenceImpl(int index) { + this.index = index; + } + + public void incrementIndex() { + this.index++; + } + + public void decrementIndex() { + this.index--; + } + + public void invalidate() { + this.index = 0; + } + + @Override + public TileData get() { + if (!isValid()) return null; + return TileDataStackImpl.this.get(this.index); + } + + @Override + public int getIndex() { + return index; + } + + @Override + public TileDataStack getStack() { + return TileDataStackImpl.this; + } + + @Override + public boolean isValid() { + return this.index >= 0; + } + } + + private final TileData[] tiles = new TileData[TILES_PER_FACE]; + private int size = 0; + + private final TileReferenceImpl[] references = new TileReferenceImpl[tiles.length]; + private final int[] indicesByTag = new int[tiles.length]; + private final int[] tagsByIndex = new int[tiles.length]; + + { + Arrays.fill(indicesByTag, -1); + Arrays.fill(tagsByIndex, -1); + } + + /* + * Potentially shared + */ + private final Vec3i blockInChunk; + private final BlockFace face; + + public TileDataStackImpl(Vec3i blockInChunk, BlockFace face) { + this.blockInChunk = blockInChunk; + this.face = face; + } + + @Override + public Vec3i getBlockInChunk(Vec3i output) { + if (output == null) output = new Vec3i(); + output.set(blockInChunk.x, blockInChunk.y, blockInChunk.z); + return output; + } + + @Override + public BlockFace getFace() { + return face; + } + + @Override + public ChunkData getChunk() { + return ChunkData.this; + } + + @Override + public int size() { + return size; + } + + @Override + public TileData get(int index) { + checkIndex(index, false); + + return tiles[index]; + } + + @Override + public TileData set(int index, TileData tile) { + Objects.requireNonNull(tile, "tile"); + TileData previous = get(index); // checks index + + tiles[index] = tile; + + if (references[index] != null) { + references[index].invalidate(); + references[index] = null; + } + + assert checkConsistency(); + + report(previous, tile); + return previous; + } + + @Override + public void add(int index, TileData tile) { + Objects.requireNonNull(tile, "tile"); + checkIndex(index, true); + + if (index != size()) { + System.arraycopy(tiles, index + 1, tiles, index + 2, size - index); + + for (int i = index; i < size; ++i) { + if (references[i] != null) { + references[i].incrementIndex(); + } + + indicesByTag[tagsByIndex[i]]++; + } + + System.arraycopy(references, index + 1, references, index + 2, size - index); + System.arraycopy(tagsByIndex, index + 1, tagsByIndex, index + 2, size - index); + } + + size++; + tiles[index] = tile; + references[index] = null; + + for (int tag = 0; tag < indicesByTag.length; ++tag) { + if (tagsByIndex[tag] == -1) { + indicesByTag[tag] = index; + tagsByIndex[index] = tag; + break; + } + } + + modCount++; + assert checkConsistency(); + + report(null, tile); + } + + @Override + public TileData remove(int index) { + TileData previous = get(index); // checks index + + if (references[index] != null) { + references[index].invalidate(); + } + + indicesByTag[tagsByIndex[index]] = -1; + + if (index != size() - 1) { + System.arraycopy(tiles, index + 1, tiles, index, size - index - 1); + + for (int i = index + 1; i < size; ++i) { + if (references[i] != null) { + references[i].decrementIndex(); + } + + indicesByTag[tagsByIndex[i]]--; + } + + System.arraycopy(references, index + 1, references, index, size - index - 1); + System.arraycopy(tagsByIndex, index + 1, tagsByIndex, index, size - index - 1); + } + + size--; + tiles[size] = null; + references[size] = null; + tagsByIndex[size] = -1; + + modCount++; + assert checkConsistency(); + + report(previous, null); + return previous; + } + + @Override + public TileReference getReference(int index) { + checkIndex(index, false); + + if (references[index] == null) { + references[index] = new TileReferenceImpl(index); + } + + return references[index]; + } + + @Override + public int getIndexByTag(int tag) { + return indicesByTag[tag]; + } + + @Override + public int getTagByIndex(int index) { + checkIndex(index, false); + return tagsByIndex[index]; + } + + @Override + public void clear() { + while (!isEmpty()) { + removeFarthest(); + } + } + + private void checkIndex(int index, boolean isSizeAllowed) { + if (isSizeAllowed ? (index > size()) : (index >= size())) + throw new IndexOutOfBoundsException("Index " + index + " is out of bounds: size is " + size); + + if (index < 0) + throw new IndexOutOfBoundsException("Index " + index + " is out of bounds: index cannot be negative"); + + if (index >= TILES_PER_FACE) + throw new TileStackIsFullException("Index " + index + " is out of bounds: maximum tile stack size is " + TILES_PER_FACE); + } + + private void report(TileData previous, TileData current) { + ChunkData.this.getListeners().forEach(l -> { + if (previous != null) { + l.onChunkTilesChanged(ChunkData.this, blockInChunk, face, previous, false); + } + + if (current != null) { + l.onChunkTilesChanged(ChunkData.this, blockInChunk, face, current, true); + } + + l.onChunkChanged(ChunkData.this); + }); + } + + private boolean checkConsistency() { + int index; + + for (index = 0; index < size(); ++index) { + if (get(index) == null) + throw new AssertionError("get(index) is null"); + + if (references[index] != null) { + TileReference ref = getReference(index); + if (ref == null) + throw new AssertionError("references[index] is not null but getReference(index) is"); + if (!ref.isValid()) + throw new AssertionError("Reference is not valid"); + if (ref.get() != get(index)) + throw new AssertionError("Reference points to " + (ref.get() == null ? "null" : "wrong tile")); + if (ref.getIndex() != index) + throw new AssertionError("Reference has invalid index"); + if (ref.getStack() != this) + throw new AssertionError("Reference has invalid TDS"); + } + + if (index != indicesByTag[tagsByIndex[index]]) + throw new AssertionError("Tag mapping is inconsistent"); + if (index != getIndexByTag(getTagByIndex(index))) + throw new AssertionError("Tag methods are inconsistent with tag mapping"); + } + + for (; index < tiles.length; ++index) { + if (tiles[index] != null) + throw new AssertionError("Leftover tile detected"); + if (references[index] != null) + throw new AssertionError("Leftover reference detected"); + if (tagsByIndex[index] != -1) + throw new AssertionError("Leftover tags detected"); + } + + return true; + } + + } } diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkDataReportingList.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkDataReportingList.java deleted file mode 100644 index bbe16c4..0000000 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkDataReportingList.java +++ /dev/null @@ -1,69 +0,0 @@ -package ru.windcorp.progressia.common.world; - -import java.util.AbstractList; -import java.util.List; - -import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.world.block.BlockFace; -import ru.windcorp.progressia.common.world.tile.TileData; - -class ChunkDataReportingList extends AbstractList { - - private final List parent; - private final ChunkData reportTo; - private final Vec3i blockInChunk; - private final BlockFace face; - - public ChunkDataReportingList(List parent, ChunkData reportTo, Vec3i blockInChunk, BlockFace face) { - super(); - this.parent = parent; - this.reportTo = reportTo; - this.blockInChunk = new Vec3i(blockInChunk); - this.face = face; - } - - @Override - public TileData get(int index) { - return parent.get(index); - } - - @Override - public int size() { - return parent.size(); - } - - @Override - public TileData set(int index, TileData element) { - TileData previous = parent.set(index, element); - report(previous, element); - return previous; - } - - @Override - public void add(int index, TileData element) { - parent.add(index, element); - report(null, element); - } - - @Override - public TileData remove(int index) { - TileData previous = parent.remove(index); - report(previous, null); - return previous; - } - - private void report(TileData previous, TileData current) { - reportTo.getListeners().forEach(l -> { - if (previous != null) { - l.onChunkTilesChanged(reportTo, blockInChunk, face, previous, false); - } - - if (current != null) { - l.onChunkTilesChanged(reportTo, blockInChunk, face, current, true); - } - - l.onChunkChanged(reportTo); - }); - } - -} diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java index 30aac1f..64e4b9c 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -29,6 +29,7 @@ import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.util.CoordinatePacker; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.test.TestContent; public class WorldData { @@ -60,6 +61,7 @@ public class WorldData { for (cursor.x = -(size / 2); cursor.x <= (size / 2); ++cursor.x) { for (cursor.y = -(size / 2); cursor.y <= (size / 2); ++cursor.y) { ChunkData chunk = new ChunkData(cursor, this); + TestContent.generateChunk(chunk); addChunkListeners(chunk); addChunk(chunk); } diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/GenericTileStack.java b/src/main/java/ru/windcorp/progressia/common/world/tile/GenericTileStack.java new file mode 100644 index 0000000..e22cc8b --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/GenericTileStack.java @@ -0,0 +1,90 @@ +package ru.windcorp.progressia.common.world.tile; + +import java.util.AbstractList; +import java.util.Objects; +import java.util.RandomAccess; +import java.util.function.Consumer; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; +import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.common.world.block.BlockFace; + +public abstract class GenericTileStack +extends AbstractList +implements RandomAccess { + + public static interface TSConsumer { + void accept(int layer, T tile); + } + + public static final int TILES_PER_FACE = 8; + + public abstract Vec3i getBlockInChunk(Vec3i output); + protected abstract Vec3i getChunkPos(); + public abstract C getChunk(); + public abstract BlockFace getFace(); + + public Vec3i getBlockInWorld(Vec3i output) { + // This is safe + return Coordinates.getInWorld(getChunkPos(), getBlockInChunk(output), output); + } + + public boolean isFull() { + return size() >= TILES_PER_FACE; + } + + public T getClosest() { + return get(0); + } + + public T getFarthest() { + return get(size() - 1); + } + + public void forEach(TSConsumer action) { + Objects.requireNonNull(action, "action"); + for (int i = 0; i < size(); ++i) { + action.accept(i, get(i)); + } + } + + @Override + public void forEach(Consumer action) { + Objects.requireNonNull(action, "action"); + for (int i = 0; i < size(); ++i) { + action.accept(get(i)); + } + } + + public T findClosest(String id) { + Objects.requireNonNull(id, "id"); + + for (int i = 0; i < size(); ++i) { + T tile = get(i); + if (tile.getId().equals(id)) { + return tile; + } + } + + return null; + } + + public T findFarthest(String id) { + Objects.requireNonNull(id, "id"); + + for (int i = 0; i < size(); ++i) { + T tile = get(i); + if (tile.getId().equals(id)) { + return tile; + } + } + + return null; + } + + public boolean contains(String id) { + return findClosest(id) != null; + } + +} 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 new file mode 100644 index 0000000..489a5ca --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java @@ -0,0 +1,130 @@ +package ru.windcorp.progressia.common.world.tile; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.block.BlockData; + +public abstract class TileDataStack extends GenericTileStack { + + /** + * Inserts the specified tile at the specified position in this stack. + * Shifts the tile currently at that position (if any) and any tiles above to + * the top (adds one to their indices). + * @param index index at which the specified tile is to be inserted + * @param tile tile to be inserted + * @throws TileStackIsFullException if this stack is {@linkplain #isFull() full} + */ + /* + * Impl note: AbstractList provides a useless implementation of this method, + * make sure to override it in subclass + */ + @Override + public abstract void add(int index, TileData tile); + + /** + * Replaces the tile at the specified position in this stack with the specified tile. + * @param index index of the tile to replace + * @param tile tile to be stored at the specified position + * @return the tile previously at the specified position + */ + /* + * Impl note: AbstractList provides a useless implementation of this method, + * make sure to override it in subclass + */ + @Override + public abstract TileData set(int index, TileData tile); + + /** + * Removes the tile at the specified position in this list. Shifts any subsequent tiles + * to the left (subtracts one from their indices). Returns the tile that was removed + * from the list. + * @param index the index of the tile to be removed + * @return the tile previously at the specified position + */ + /* + * Impl note: AbstractList provides a useless implementation of this method, + * make sure to override it in subclass + */ + @Override + public abstract TileData remove(int index); + + public abstract TileReference getReference(int index); + + public abstract int getIndexByTag(int tag); + + public abstract int getTagByIndex(int index); + + /* + * Implementation + */ + + @Override + public Vec3i getChunkPos() { + return getChunk().getPosition(); + } + + /* + * Aliases and overloads + */ + + public void addClosest(TileData tile) { + add(0, tile); + } + + public void addFarthest(TileData tile) { + add(size(), tile); + } + + /** + * Attempts to {@link #add(int, TileData) add} the provided {@code tile} + * at {@code index}. If the stack is {@linkplain #isFull() full}, does nothing. + * @param index the index to insert the tile at + * @param tile the tile to try to add + * @return {@code true} iff this stack has changed + */ + public boolean offer(int index, TileData tile) { + if (isFull()) return false; + add(index, tile); + return true; + } + + public boolean offerClosest(TileData tile) { + return offer(0, tile); + } + + public boolean offerFarthest(TileData tile) { + return offer(size(), tile); + } + + public TileData removeClosest() { + return remove(0); + } + + public TileData removeFarthest() { + return remove(size() - 1); + } + + public TileData poll(int index) { + if (size() <= index) return null; + return remove(index); + } + + public TileData pollClosest() { + return poll(0); + } + + public TileData pollFarthest() { + return poll(size() - 1); + } + + @Override + public boolean add(TileData tile) { + addFarthest(tile); + return true; + } + + public BlockData getHost() { + return getChunk().getBlock(getBlockInChunk(null)); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/TileReference.java b/src/main/java/ru/windcorp/progressia/common/world/tile/TileReference.java new file mode 100644 index 0000000..bcdc18d --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/TileReference.java @@ -0,0 +1,13 @@ +package ru.windcorp.progressia.common.world.tile; + +public interface TileReference { + + TileData get(); + int getIndex(); + TileDataStack getStack(); + + default boolean isValid() { + return get() != null; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/TileStackIsFullException.java b/src/main/java/ru/windcorp/progressia/common/world/tile/TileStackIsFullException.java new file mode 100644 index 0000000..55cd402 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/TileStackIsFullException.java @@ -0,0 +1,28 @@ +package ru.windcorp.progressia.common.world.tile; + +public class TileStackIsFullException extends RuntimeException { + + private static final long serialVersionUID = 6665942370305610231L; + + public TileStackIsFullException() { + + } + + public TileStackIsFullException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public TileStackIsFullException(String message, Throwable cause) { + super(message, cause); + } + + public TileStackIsFullException(String message) { + super(message); + } + + public TileStackIsFullException(Throwable cause) { + super(cause); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java b/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java index d51a4b2..51de5d4 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java @@ -3,20 +3,17 @@ package ru.windcorp.progressia.server.world; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.function.BiConsumer; -import com.google.common.collect.Lists; - import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.client.world.tile.TileLocation; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.Coordinates; import ru.windcorp.progressia.common.world.block.BlockFace; import ru.windcorp.progressia.common.world.entity.EntityData; -import ru.windcorp.progressia.common.world.tile.TileData; +import ru.windcorp.progressia.common.world.tile.TileDataStack; +import ru.windcorp.progressia.common.world.tile.TileReference; import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.block.BlockLogicRegistry; import ru.windcorp.progressia.server.world.block.TickableBlock; @@ -27,6 +24,7 @@ import ru.windcorp.progressia.server.world.ticking.TickingPolicy; import ru.windcorp.progressia.server.world.tile.TickableTile; import ru.windcorp.progressia.server.world.tile.TileLogic; import ru.windcorp.progressia.server.world.tile.TileLogicRegistry; +import ru.windcorp.progressia.server.world.tile.TileLogicStack; public class ChunkLogic { @@ -34,11 +32,11 @@ public class ChunkLogic { private final ChunkData data; private final Collection tickingBlocks = new ArrayList<>(); - private final Collection tickingTiles = new ArrayList<>(); + private final Collection tickingTiles = new ArrayList<>(); private final TickChunk tickTask = new TickChunk(this); - private final Map, List> tileLogicLists = + private final Map tileLogicLists = Collections.synchronizedMap(new WeakHashMap<>()); public ChunkLogic(WorldLogic world, ChunkData data) { @@ -49,42 +47,26 @@ public class ChunkLogic { } private void generateTickLists() { - MutableBlockTickContext blockTickContext = - new MutableBlockTickContext(); + ChunkTickContext context = TickContextMutable.start().withChunk(this).build(); - MutableTileTickContext tileTickContext = - new MutableTileTickContext(); - - data.forEachBlock(blockInChunk -> { - BlockLogic block = getBlock(blockInChunk); + context.forEachBlock(bctxt -> { + BlockLogic block = bctxt.getBlock(); - if (block instanceof TickableBlock) { - blockTickContext.init( - getWorld().getServer(), - Coordinates.getInWorld(getData().getPosition(), blockInChunk, null) - ); - - if (((TickableBlock) block).getTickingPolicy(blockTickContext) == TickingPolicy.REGULAR) { - tickingBlocks.add(new Vec3i(blockInChunk)); - } - } - }); - - data.forEachTile((loc, tileData) -> { - TileLogic tile = TileLogicRegistry.getInstance().get(tileData.getId()); + if (!(block instanceof TickableBlock)) return; - if (tile instanceof TickableTile) { - tileTickContext.init( - getWorld().getServer(), - Coordinates.getInWorld(getData().getPosition(), loc.pos, null), - loc.face, - loc.layer - ); - - if (((TickableTile) tile).getTickingPolicy(tileTickContext) == TickingPolicy.REGULAR) { - tickingTiles.add(new TileLocation(loc)); - } + if (((TickableBlock) block).getTickingPolicy(bctxt) == TickingPolicy.REGULAR) { + tickingBlocks.add(Coordinates.convertInWorldToInChunk(bctxt.getBlockInWorld(), null)); } + + bctxt.forEachFace(fctxt -> fctxt.forEachTile(tctxt -> { + TileLogic tile = tctxt.getTile(); + + if (!(tile instanceof TickableTile)) return; + + if (((TickableTile) tile).getTickingPolicy(tctxt) == TickingPolicy.REGULAR) { + tickingTiles.add(tctxt.getReference()); + } + })); }); } @@ -114,11 +96,11 @@ public class ChunkLogic { }); } - public void forEachTickingTile(BiConsumer action) { - tickingTiles.forEach(location -> { + public void forEachTickingTile(BiConsumer action) { + tickingTiles.forEach(ref -> { action.accept( - location, - getTilesOrNull(location.pos, location.face).get(location.layer) + ref, + TileLogicRegistry.getInstance().get(ref.get().getId()) ); }); } @@ -138,32 +120,70 @@ public class ChunkLogic { ); } - public List getTiles(Vec3i blockInChunk, BlockFace face) { - return wrapTileList(getData().getTiles(blockInChunk, face)); + public TileLogicStack getTiles(Vec3i blockInChunk, BlockFace face) { + return getTileStackWrapper(getData().getTiles(blockInChunk, face)); } - public List getTilesOrNull(Vec3i blockInChunk, BlockFace face) { - List tiles = getData().getTilesOrNull(blockInChunk, face); + public TileLogicStack getTilesOrNull(Vec3i blockInChunk, BlockFace face) { + TileDataStack tiles = getData().getTilesOrNull(blockInChunk, face); if (tiles == null) return null; - return wrapTileList(tiles); + return getTileStackWrapper(tiles); } - private List wrapTileList(List tileDataList) { + private TileLogicStack getTileStackWrapper(TileDataStack tileDataList) { return tileLogicLists.computeIfAbsent( tileDataList, - ChunkLogic::createWrapper - ); - } - - private static List createWrapper(List tileDataList) { - return Lists.transform( - tileDataList, - data -> TileLogicRegistry.getInstance().get(data.getId()) + TileLogicStackImpl::new ); } public TickChunk getTickTask() { return tickTask; } + + private class TileLogicStackImpl extends TileLogicStack { + + private final TileDataStack parent; + + public TileLogicStackImpl(TileDataStack parent) { + this.parent = parent; + } + + @Override + public Vec3i getBlockInChunk(Vec3i output) { + return parent.getBlockInChunk(output); + } + + @Override + public Vec3i getChunkPos() { + return ChunkLogic.this.getPosition(); + } + + @Override + public ChunkLogic getChunk() { + return ChunkLogic.this; + } + + @Override + public BlockFace getFace() { + return parent.getFace(); + } + + @Override + public TileLogic get(int index) { + return TileLogicRegistry.getInstance().get(parent.get(index).getId()); + } + + @Override + public int size() { + return parent.size(); + } + + @Override + public TileDataStack getData() { + return parent; + } + + } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/ChunkTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/ChunkTickContext.java index e995974..b8816a0 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/ChunkTickContext.java +++ b/src/main/java/ru/windcorp/progressia/server/world/ChunkTickContext.java @@ -1,13 +1,31 @@ package ru.windcorp.progressia.server.world; +import java.util.function.Consumer; + +import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.server.world.block.BlockTickContext; public interface ChunkTickContext extends TickContext { - ChunkLogic getChunk(); + Vec3i getChunk(); + + default ChunkLogic getChunkLogic() { + return getWorld().getChunk(getChunk()); + } default ChunkData getChunkData() { - return getChunk().getData(); + ChunkLogic chunkLogic = getChunkLogic(); + return chunkLogic == null ? null : chunkLogic.getData(); + } + + default void forEachBlock(Consumer action) { + TickContextMutable context = TickContextMutable.uninitialized(); + + getChunkData().forEachBlock(blockInChunk -> { + context.rebuild().withServer(getServer()).withChunk(getChunk()).withBlockInChunk(blockInChunk).build(); + action.accept(context); + }); } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/MutableBlockTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/MutableBlockTickContext.java deleted file mode 100644 index 893d1e8..0000000 --- a/src/main/java/ru/windcorp/progressia/server/world/MutableBlockTickContext.java +++ /dev/null @@ -1,38 +0,0 @@ -package ru.windcorp.progressia.server.world; - -import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.server.Server; -import ru.windcorp.progressia.server.world.block.BlockTickContext; - -public class MutableBlockTickContext -extends MutableTickContext -implements BlockTickContext { - - private final Vec3i blockInWorld = new Vec3i(); - private ChunkLogic chunk; - - @Override - public Vec3i getBlockInWorld() { - return this.blockInWorld; - } - - public void setCoordsInWorld(Vec3i blockInWorld) { - getBlockInWorld().set(blockInWorld.x, blockInWorld.y, blockInWorld.z); - setChunk(getWorld().getChunkByBlock(blockInWorld)); - } - - @Override - public ChunkLogic getChunk() { - return chunk; - } - - public void setChunk(ChunkLogic chunk) { - this.chunk = chunk; - } - - public void init(Server server, Vec3i blockInWorld) { - setServer(server); - if (blockInWorld != null) setCoordsInWorld(blockInWorld); - } - -} diff --git a/src/main/java/ru/windcorp/progressia/server/world/MutableTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/MutableTickContext.java deleted file mode 100644 index 3476db5..0000000 --- a/src/main/java/ru/windcorp/progressia/server/world/MutableTickContext.java +++ /dev/null @@ -1,43 +0,0 @@ -package ru.windcorp.progressia.server.world; - -import ru.windcorp.progressia.server.Server; - -public class MutableTickContext implements TickContext { - - private double tickLength; - private Server server; - private WorldLogic world; - - public MutableTickContext() { - super(); - } - - public double getTickLength() { - return tickLength; - } - - public void setTickLength(double tickLength) { - this.tickLength = tickLength; - } - - @Override - public Server getServer() { - return server; - } - - public void setServer(Server server) { - this.server = server; - this.setTickLength(server.getTickLength()); - setWorld(server.getWorld()); - } - - @Override - public WorldLogic getWorld() { - return world; - } - - public void setWorld(WorldLogic world) { - this.world = world; - } - -} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/server/world/MutableTileTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/MutableTileTickContext.java deleted file mode 100644 index 162659c..0000000 --- a/src/main/java/ru/windcorp/progressia/server/world/MutableTileTickContext.java +++ /dev/null @@ -1,59 +0,0 @@ -package ru.windcorp.progressia.server.world; - -import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.world.block.BlockFace; -import ru.windcorp.progressia.server.Server; -import ru.windcorp.progressia.server.world.tile.TileTickContext; - -public class MutableTileTickContext -extends MutableTickContext -implements TileTickContext { - - private final Vec3i currentBlockInWorld = new Vec3i(); - private final Vec3i counterBlockInWorld = new Vec3i(); - - private BlockFace face; - private int layer; - - @Override - public Vec3i getCurrentBlockInWorld() { - return this.currentBlockInWorld; - } - - @Override - public Vec3i getCounterBlockInWorld() { - return this.counterBlockInWorld; - } - - public void setCoordsInWorld(Vec3i currentBlockInWorld) { - getCurrentBlockInWorld().set(currentBlockInWorld.x, currentBlockInWorld.y, currentBlockInWorld.z); - getCounterBlockInWorld().set(currentBlockInWorld.x, currentBlockInWorld.y, currentBlockInWorld.z).add(getCurrentFace().getVector()); - } - - @Override - public BlockFace getCurrentFace() { - return face; - } - - public void setFace(BlockFace face) { - this.face = face; - setCoordsInWorld(getCurrentBlockInWorld()); - } - - @Override - public int getCurrentLayer() { - return layer; - } - - public void setLayer(int layer) { - this.layer = layer; - } - - public void init(Server server, Vec3i blockInWorld, BlockFace face, int layer) { - setServer(server); - setFace(face); - setCoordsInWorld(blockInWorld); - setLayer(layer); - } - -} diff --git a/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java b/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java index 534cf97..f260675 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java +++ b/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java @@ -1,7 +1,5 @@ package ru.windcorp.progressia.server.world; -import java.util.List; - import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.block.BlockFace; @@ -32,7 +30,7 @@ public class TickAndUpdateUtil { BlockLogic block = world.getBlock(blockInWorld); if (!(block instanceof TickableBlock)) return; // also checks nulls - BlockTickContext tickContext = getBlockTickContext(world.getServer(), blockInWorld); + BlockTickContext tickContext = TickContextMutable.start().withWorld(world).withBlock(blockInWorld).build(); tickBlock((TickableBlock) block, tickContext); } @@ -48,23 +46,17 @@ public class TickAndUpdateUtil { TileLogic tile = world.getTile(blockInWorld, face, layer); if (!(tile instanceof TickableTile)) return; - TileTickContext tickContext = getTileTickContext(world.getServer(), blockInWorld, face, layer); + TileTickContext tickContext = TickContextMutable.start().withWorld(world).withBlock(blockInWorld).withFace(face).withLayer(layer); tickTile((TickableTile) tile, tickContext); } public static void tickTiles(WorldLogic world, Vec3i blockInWorld, BlockFace face) { - List tiles = world.getTilesOrNull(blockInWorld, face); - if (tiles == null || tiles.isEmpty()) return; - - MutableTileTickContext tickContext = new MutableTileTickContext(); - - for (int layer = 0; layer < tiles.size(); ++layer) { - TileLogic tile = tiles.get(layer); - if (!(tile instanceof TickableTile)) return; - - tickContext.init(world.getServer(), blockInWorld, face, layer); - tickTile((TickableTile) tile, tickContext); - } + TickContextMutable.start().withWorld(world).withBlock(blockInWorld).withFace(face).build().forEachTile(context -> { + TileLogic tile = context.getTile(); + if (tile instanceof TickableTile) { + tickTile((TickableTile) tile, context); + } + }); } public static void updateBlock(UpdateableBlock block, BlockTickContext context) { @@ -79,7 +71,7 @@ public class TickAndUpdateUtil { BlockLogic block = world.getBlock(blockInWorld); if (!(block instanceof UpdateableBlock)) return; // also checks nulls - BlockTickContext tickContext = getBlockTickContext(world.getServer(), blockInWorld); + BlockTickContext tickContext = TickContextMutable.start().withWorld(world).withBlock(blockInWorld).build(); updateBlock((UpdateableBlock) block, tickContext); } @@ -95,23 +87,17 @@ public class TickAndUpdateUtil { TileLogic tile = world.getTile(blockInWorld, face, layer); if (!(tile instanceof UpdateableTile)) return; - TileTickContext tickContext = getTileTickContext(world.getServer(), blockInWorld, face, layer); + TileTickContext tickContext = TickContextMutable.start().withWorld(world).withBlock(blockInWorld).withFace(face).withLayer(layer); updateTile((UpdateableTile) tile, tickContext); } public static void updateTiles(WorldLogic world, Vec3i blockInWorld, BlockFace face) { - List tiles = world.getTilesOrNull(blockInWorld, face); - if (tiles == null || tiles.isEmpty()) return; - - MutableTileTickContext tickContext = new MutableTileTickContext(); - - for (int layer = 0; layer < tiles.size(); ++layer) { - TileLogic tile = tiles.get(layer); - if (!(tile instanceof UpdateableTile)) return; - - tickContext.init(world.getServer(), blockInWorld, face, layer); - updateTile((UpdateableTile) tile, tickContext); - } + TickContextMutable.start().withWorld(world).withBlock(blockInWorld).withFace(face).build().forEachTile(context -> { + TileLogic tile = context.getTile(); + if (tile instanceof UpdateableTile) { + updateTile((UpdateableTile) tile, context); + } + }); } public static void tickEntity(EntityLogic logic, EntityData data, TickContext context) { @@ -123,32 +109,51 @@ public class TickAndUpdateUtil { } public static void tickEntity(EntityData data, Server server) { - tickEntity(EntityLogicRegistry.getInstance().get(data.getId()), data, getTickContext(server)); + tickEntity(EntityLogicRegistry.getInstance().get(data.getId()), data, TickContextMutable.start().withServer(server).build()); } - public static BlockTickContext getBlockTickContext( - Server server, - Vec3i blockInWorld - ) { - MutableBlockTickContext result = new MutableBlockTickContext(); - result.init(server, blockInWorld); - return result; - } - - public static TileTickContext getTileTickContext( - Server server, - Vec3i blockInWorld, - BlockFace face, - int layer - ) { - MutableTileTickContext result = new MutableTileTickContext(); - result.init(server, blockInWorld, face, layer); - return result; - } - - public static TickContext getTickContext(Server server) { - return getBlockTickContext(server, null); - } +// public static BlockTickContext getBlockTickContext( +// Server server, +// Vec3i blockInWorld +// ) { +// MutableBlockTickContext result = new MutableBlockTickContext(); +// result.init(server, blockInWorld); +// return result; +// } +// +// public static TileTickContext getTileTickContext( +// Server server, +// Vec3i blockInWorld, +// BlockFace face, +// int layer +// ) { +// MutableTileTickContext result = new MutableTileTickContext(); +// result.init(server, blockInWorld, face, layer); +// return result; +// } +// +// public static TileTickContext getTileTickContext( +// Server server, +// TileDataStack stack, +// int index +// ) { +// MutableTileTickContext result = new MutableTileTickContext(); +// result.init(server, stack, index); +// return result; +// } +// +// public static TileTickContext getTileTickContext( +// Server server, +// TileReference ref +// ) { +// MutableTileTickContext result = new MutableTileTickContext(); +// result.init(server, ref); +// return result; +// } +// +// public static TickContext getTickContext(Server server) { +// return getBlockTickContext(server, null); +// } private TickAndUpdateUtil() {} diff --git a/src/main/java/ru/windcorp/progressia/server/world/TickContext.java b/src/main/java/ru/windcorp/progressia/server/world/TickContext.java index 82b18e0..58b6880 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/TickContext.java +++ b/src/main/java/ru/windcorp/progressia/server/world/TickContext.java @@ -8,7 +8,7 @@ import ru.windcorp.progressia.server.world.tasks.WorldAccessor; public interface TickContext { - double getTickLength(); + float getTickLength(); Server getServer(); diff --git a/src/main/java/ru/windcorp/progressia/server/world/TickContextMutable.java b/src/main/java/ru/windcorp/progressia/server/world/TickContextMutable.java new file mode 100644 index 0000000..bc97593 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/TickContextMutable.java @@ -0,0 +1,455 @@ +package ru.windcorp.progressia.server.world; + +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.tile.GenericTileStack; +import ru.windcorp.progressia.common.world.tile.TileDataStack; +import ru.windcorp.progressia.common.world.tile.TileReference; +import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.world.block.BlockTickContext; +import ru.windcorp.progressia.server.world.tile.TSTickContext; +import ru.windcorp.progressia.server.world.tile.TileTickContext; + +public abstract class TickContextMutable implements BlockTickContext, TSTickContext, TileTickContext { + + private static enum Role { + NONE, WORLD, CHUNK, BLOCK, TILE_STACK, TILE; + } + + /* + * TickContextMutable interface + */ + + // Only TickContextMutable.Impl can extend; extend Impl if need be + private TickContextMutable() {} + + public abstract Builder.Empty rebuild(); + + /* + * Static methods + */ + + public static TickContextMutable uninitialized() { + return new Impl(); + } + + public static Builder.Empty start() { + return uninitialized().rebuild(); + } + + public static Builder.World copyWorld(TickContext context) { + return start().withServer(context.getServer()); + } + + public static Builder.Chunk copyChunk(ChunkTickContext context) { + return start().withChunk(context.getChunkLogic()); + } + + public static Builder.Block copyBlock(BlockTickContext context) { + return copyWorld(context).withBlock(context.getBlockInWorld()); + } + + public static Builder.TileStack copyTS(TSTickContext context) { + return copyBlock(context).withFace(context.getFace()); + } + + public static TileTickContext copyTile(TileTickContext context) { + return copyTS(context).withLayer(context.getLayer()); + } + + /* + * Builder interfaces + */ + + public static interface Builder { + TickContextMutable build(); + + public static interface Empty /*does not extend Builder*/ { + World withServer(Server server); + + default Builder.World withWorld(WorldLogic world) { + Objects.requireNonNull(world, "world"); + return withServer(world.getServer()); + } + + default Builder.Chunk withChunk(ChunkLogic chunk) { + Objects.requireNonNull(chunk, "chunk"); + return withWorld(chunk.getWorld()).withChunk(chunk.getPosition()); + } + } + + public static interface World extends Builder { + Chunk withChunk(Vec3i chunk); + Block withBlock(Vec3i blockInWorld); + TileStack withTS(GenericTileStack tileStack); + + default Builder.Chunk withChunk(ChunkData chunk) { + Objects.requireNonNull(chunk, "chunk"); + return withChunk(chunk.getPosition()); + } + + default TileTickContext withTile(TileReference ref) { + Objects.requireNonNull(ref, "ref"); + return withTS(ref.getStack()).withLayer(ref.getIndex()); + } + } + + public static interface Chunk extends Builder { + Builder.Block withBlockInChunk(Vec3i blockInChunk); + } + + public static interface Block extends Builder { + Builder.TileStack withFace(BlockFace face); + } + + public static interface TileStack extends Builder { + TickContextMutable withLayer(int layer); + } + } + + /* + * Impl + */ + + public static class Impl + extends TickContextMutable + implements Builder.Empty, Builder.World, Builder.Chunk, Builder.Block, Builder.TileStack + { + + protected Impl() {} + + protected Server server; + protected final Vec3i chunk = new Vec3i(); + protected final Vec3i blockInWorld = new Vec3i(); + protected BlockFace face; + protected int layer; + + protected Role role = Role.NONE; + protected boolean isBeingBuilt = false; + + /* + * TickContextMutable + */ + + @Override + public Server getServer() { + checkContextState(Role.WORLD); + return this.server; + } + + @Override + public float getTickLength() { + checkContextState(Role.WORLD); + return (float) this.server.getTickLength(); + } + + @Override + public Vec3i getChunk() { + checkContextState(Role.CHUNK); + return this.chunk; + } + + @Override + public Vec3i getBlockInWorld() { + checkContextState(Role.BLOCK); + return this.blockInWorld; + } + + @Override + public BlockFace getFace() { + checkContextState(Role.TILE_STACK); + return this.face; + } + + @Override + public int getLayer() { + checkContextState(Role.TILE); + return this.layer; + } + + @Override + public Builder.Empty rebuild() { + this.role = Role.NONE; + this.isBeingBuilt = true; + + this.server = null; + this.chunk.set(0); + this.blockInWorld.set(0); + this.face = null; + this.layer = -1; + + return this; + } + + /* + * Builder + * memo: do NOT use Context getters, they throw ISEs + */ + + @Override + public TickContextMutable build() { + checkBuilderState(null); + this.isBeingBuilt = false; + return this; + } + + @Override + public World withServer(Server server) { + Objects.requireNonNull(server, "server"); + checkBuilderState(Role.NONE); + this.server = server; + this.role = Role.WORLD; + return this; + } + + @Override + public Chunk withChunk(Vec3i chunk) { + Objects.requireNonNull(chunk, "chunk"); + checkBuilderState(Role.WORLD); + + this.chunk.set(chunk.x, chunk.y, chunk.z); + + this.role = Role.CHUNK; + return this; + } + + @Override + public Block withBlock(Vec3i blockInWorld) { + Objects.requireNonNull(blockInWorld, "blockInWorld"); + checkBuilderState(Role.WORLD); + + this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); + Coordinates.convertInWorldToChunk(blockInWorld, this.chunk); + + this.role = Role.BLOCK; + return this; + } + + @Override + public TileStack withTS(GenericTileStack tileStack) { + Objects.requireNonNull(tileStack, "tileStack"); + + return withBlock(tileStack.getBlockInWorld(this.blockInWorld)).withFace(tileStack.getFace()); + // ^^^^^^^^^^^^^^^^^ This is safe + } + + @Override + public Block withBlockInChunk(Vec3i blockInChunk) { + Objects.requireNonNull(blockInChunk, "blockInChunk"); + checkBuilderState(Role.CHUNK); + + Coordinates.getInWorld(this.chunk, blockInChunk, this.blockInWorld); + + this.role = Role.BLOCK; + return this; + } + + @Override + public TileStack withFace(BlockFace face) { + Objects.requireNonNull(face, "face"); + checkBuilderState(Role.BLOCK); + + this.face = face; + + this.role = Role.TILE_STACK; + return this; + } + + @Override + public TickContextMutable withLayer(int layer) { + checkBuilderState(Role.TILE); + + this.layer = layer; + + this.role = Role.TILE; + return build(); + } + + /* + * Optimization + */ + + @Override + public void forEachBlock(Consumer action) { + checkContextState(Role.CHUNK); + + Vec3i v = this.blockInWorld; + + int previousX = v.x; + int previousY = v.y; + int previousZ = v.z; + Role previousRole = this.role; + + this.role = Role.BLOCK; + + final int minX = Coordinates.getInWorld(chunk.x, 0); + final int minY = Coordinates.getInWorld(chunk.y, 0); + final int minZ = Coordinates.getInWorld(chunk.z, 0); + final int size = ChunkData.BLOCKS_PER_CHUNK; + + for (v.x = minX; v.x < minX + size; ++v.x) { + for (v.y = minY; v.y < minY + size; ++v.y) { + for (v.z = minZ; v.z < minZ + size; ++v.z) { + action.accept(this); + } + } + } + + this.role = previousRole; + blockInWorld.set(previousX, previousY, previousZ); + } + + @Override + public void forEachFace(Consumer action) { + checkContextState(Role.BLOCK); + BlockFace previousFace = this.face; + Role previousRole = this.role; + + this.role = Role.TILE_STACK; + for (int i = 0; i < BlockFace.BLOCK_FACE_COUNT; ++i) { + this.face = BlockFace.getFaces().get(i); + action.accept(this); + } + + this.role = previousRole; + this.face = previousFace; + } + + @Override + public R evalNeighbor(Vec3i direction, Function action) { + this.blockInWorld.add(direction); + R result = action.apply(this); + this.blockInWorld.sub(direction); + + return result; + } + + @Override + public void forNeighbor(Vec3i direction, Consumer action) { + this.blockInWorld.add(direction); + action.accept(this); + this.blockInWorld.sub(direction); + } + + @Override + public boolean forEachTile(Consumer action) { + checkContextState(Role.TILE_STACK); + int previousLayer = this.layer; + Role previousRole = this.role; + + this.role = Role.TILE; + TileDataStack stack = getTDSOrNull(); + if (stack == null || stack.isEmpty()) return false; + + for (this.layer = 0; this.layer < stack.size(); ++this.layer) { + action.accept(this); + } + + this.role = previousRole; + this.layer = previousLayer; + return true; + } + + @Override + public R evalComplementary(Function action) { + Objects.requireNonNull(action, "action"); + checkContextState(Role.TILE_STACK); + + this.blockInWorld.add(this.face.getVector()); + this.face = this.face.getCounter(); + R result = action.apply(this); + this.face = this.face.getCounter(); + this.blockInWorld.sub(this.face.getVector()); + + return result; + } + + @Override + public void forComplementary(Consumer action) { + Objects.requireNonNull(action, "action"); + checkContextState(Role.TILE_STACK); + + this.blockInWorld.add(this.face.getVector()); + this.face = this.face.getCounter(); + action.accept(this); + this.face = this.face.getCounter(); + this.blockInWorld.sub(this.face.getVector()); + } + + /* + * Misc + */ + + protected void checkContextState(Role requiredRole) { + if (isBeingBuilt) { + throw new IllegalStateException("This context is still being built"); + } + + if ((role == null) || (requiredRole.compareTo(role) > 0)) { + throw new IllegalStateException("This context is currently initialized as " + role + "; requested " + requiredRole); + } + } + + protected void checkBuilderState(Role requiredRole) { + if (!isBeingBuilt) { + throw new IllegalStateException("This context is already built"); + } + + if (requiredRole == null) { + if (role == Role.NONE) { + throw new IllegalStateException("This context is currently not initialized"); + } + } else { + if (role != requiredRole) { + throw new IllegalStateException("This context is currently initialized as " + role + "; requested " + requiredRole); + } + } + } + + @Override + public String toString() { + final String format; + + switch (this.role) { + case WORLD: + format = "TickContext"; + break; + case CHUNK: + format = "(%2$d; %3$d; %4$d)"; + break; + case BLOCK: + format = "(%5$d; %6$d; %7$d)"; + break; + case TILE_STACK: + format = "((%5$d; %6$d; %7$d); %8$6s)"; + break; + case TILE: + format = "((%5$d; %6$d; %7$d); %8$6s; %9$d)"; + break; + case NONE: + default: + format = "Uninitialized TickContextMutable"; + break; + } + + return String.format( + format, + this.chunk.x, + this.chunk.y, + this.chunk.z, + this.blockInWorld.x, + this.blockInWorld.y, + this.blockInWorld.z, + this.face, + this.layer + ); + } + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/block/BlockTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/block/BlockTickContext.java index d537b60..372c403 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/block/BlockTickContext.java +++ b/src/main/java/ru/windcorp/progressia/server/world/block/BlockTickContext.java @@ -1,8 +1,16 @@ package ru.windcorp.progressia.server.world.block; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; + import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.block.BlockData; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.block.BlockRelation; import ru.windcorp.progressia.server.world.ChunkTickContext; +import ru.windcorp.progressia.server.world.TickContextMutable; +import ru.windcorp.progressia.server.world.tile.TSTickContext; public interface BlockTickContext extends ChunkTickContext { @@ -19,6 +27,53 @@ public interface BlockTickContext extends ChunkTickContext { default BlockData getBlockData() { return getWorldData().getBlock(getBlockInWorld()); } + + default void forEachFace(Consumer action) { + Objects.requireNonNull(action, "action"); + TickContextMutable context = TickContextMutable.uninitialized(); + + for (BlockFace face : BlockFace.getFaces()) { + context.rebuild().withServer(getServer()).withBlock(getBlockInWorld()).withFace(face).build(); + action.accept(context); + } + } + + default BlockTickContext getNeighbor(Vec3i direction) { + Objects.requireNonNull(direction, "direction"); + return TickContextMutable.copyWorld(this).withBlock(getBlockInWorld().add_(direction)).build(); + } + + default BlockTickContext getNeighbor(BlockRelation relation) { + Objects.requireNonNull(relation, "relation"); + return getNeighbor(relation.getVector()); + } + + default R evalNeighbor(Vec3i direction, Function action) { + Objects.requireNonNull(action, "action"); + Objects.requireNonNull(direction, "direction"); + return action.apply(getNeighbor(direction)); + } + + default R evalNeighbor(BlockRelation relation, Function action) { + Objects.requireNonNull(action, "action"); + Objects.requireNonNull(relation, "relation"); + return evalNeighbor(relation.getVector(), action); + } + + default void forNeighbor(Vec3i direction, Consumer action) { + Objects.requireNonNull(action, "action"); + Objects.requireNonNull(direction, "direction"); + evalNeighbor(direction, (Function) ctxt -> { + action.accept(ctxt); + return null; + }); + } + + default void forNeighbor(BlockRelation relation, Consumer action) { + Objects.requireNonNull(action, "action"); + Objects.requireNonNull(relation, "relation"); + forNeighbor(relation.getVector(), action); + } /* * Convenience methods - changes diff --git a/src/main/java/ru/windcorp/progressia/server/world/block/ForwardingBlockTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/block/ForwardingBlockTickContext.java deleted file mode 100644 index ae289cb..0000000 --- a/src/main/java/ru/windcorp/progressia/server/world/block/ForwardingBlockTickContext.java +++ /dev/null @@ -1,43 +0,0 @@ -package ru.windcorp.progressia.server.world.block; - -import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.server.Server; -import ru.windcorp.progressia.server.world.ChunkLogic; - -public class ForwardingBlockTickContext implements BlockTickContext { - - private BlockTickContext parent; - - public ForwardingBlockTickContext(BlockTickContext parent) { - setParent(parent); - } - - public BlockTickContext getParent() { - return parent; - } - - public void setParent(BlockTickContext parent) { - this.parent = parent; - } - - @Override - public ChunkLogic getChunk() { - return parent.getChunk(); - } - - @Override - public double getTickLength() { - return parent.getTickLength(); - } - - @Override - public Server getServer() { - return parent.getServer(); - } - - @Override - public Vec3i getBlockInWorld() { - return parent.getBlockInWorld(); - } - -} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/AddOrRemoveTile.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/AddTile.java similarity index 74% rename from src/main/java/ru/windcorp/progressia/server/world/tasks/AddOrRemoveTile.java rename to src/main/java/ru/windcorp/progressia/server/world/tasks/AddTile.java index 60b1a7b..4402999 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/AddOrRemoveTile.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/AddTile.java @@ -1,6 +1,5 @@ package ru.windcorp.progressia.server.world.tasks; -import java.util.List; import java.util.function.Consumer; import glm.vec._3.i.Vec3i; @@ -8,23 +7,21 @@ import ru.windcorp.progressia.common.world.Coordinates; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.block.BlockFace; import ru.windcorp.progressia.common.world.tile.TileData; +import ru.windcorp.progressia.common.world.tile.TileDataStack; -class AddOrRemoveTile extends CachedWorldChange { +class AddTile extends CachedWorldChange { private final Vec3i blockInWorld = new Vec3i(); private BlockFace face; private TileData tile; - private boolean shouldAdd; - - public AddOrRemoveTile(Consumer disposer) { - super(disposer, "Core:AddOrRemoveTile"); + public AddTile(Consumer disposer) { + super(disposer, "Core:AddTile"); } public void initialize( Vec3i position, BlockFace face, - TileData tile, - boolean shouldAdd + TileData tile ) { if (this.tile != null) throw new IllegalStateException("Payload is not null. Current: " + this.tile + "; requested: " + tile); @@ -32,20 +29,15 @@ class AddOrRemoveTile extends CachedWorldChange { this.blockInWorld.set(position.x, position.y, position.z); this.face = face; this.tile = tile; - this.shouldAdd = shouldAdd; } @Override protected void affectCommon(WorldData world) { - List tiles = world + TileDataStack tiles = world .getChunkByBlock(blockInWorld) .getTiles(Coordinates.convertInWorldToInChunk(blockInWorld, null), face); - if (shouldAdd) { - tiles.add(tile); - } else { - tiles.remove(tile); - } + tiles.add(tile); } @Override diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/BlockTriggeredUpdate.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/BlockTriggeredUpdate.java index d0234e4..e56e233 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/BlockTriggeredUpdate.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/BlockTriggeredUpdate.java @@ -27,6 +27,7 @@ class BlockTriggeredUpdate extends CachedEvaluation { TickAndUpdateUtil.updateTiles(world, cursor, face); cursor.add(face.getVector()); TickAndUpdateUtil.updateBlock(world, cursor); + TickAndUpdateUtil.updateTiles(world, cursor, face.getCounter()); cursor.sub(face.getVector()); } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/RemoveTile.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/RemoveTile.java new file mode 100644 index 0000000..7601a83 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/RemoveTile.java @@ -0,0 +1,44 @@ +package ru.windcorp.progressia.server.world.tasks; + +import java.util.function.Consumer; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.tile.TileDataStack; + +class RemoveTile extends CachedWorldChange { + + private final Vec3i blockInWorld = new Vec3i(); + private BlockFace face; + private int tag; + + public RemoveTile(Consumer disposer) { + super(disposer, "Core:RemoveTile"); + } + + public void initialize( + Vec3i position, BlockFace face, + int tag + ) { + this.blockInWorld.set(position.x, position.y, position.z); + this.face = face; + this.tag = tag; + } + + @Override + protected void affectCommon(WorldData world) { + TileDataStack tiles = world + .getChunkByBlock(blockInWorld) + .getTiles(Coordinates.convertInWorldToInChunk(blockInWorld, null), face); + + tiles.remove(tiles.getIndexByTag(tag)); + } + + @Override + public void getRelevantChunk(Vec3i output) { + Coordinates.convertInWorldToChunk(blockInWorld, output); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/TickChunk.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/TickChunk.java index 8975894..76570b5 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/TickChunk.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/TickChunk.java @@ -1,5 +1,6 @@ package ru.windcorp.progressia.server.world.tasks; +import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.function.Consumer; @@ -9,23 +10,19 @@ import com.google.common.collect.ImmutableList; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.util.FloatMathUtils; import ru.windcorp.progressia.common.world.ChunkData; -import ru.windcorp.progressia.common.world.Coordinates; import ru.windcorp.progressia.common.world.block.BlockFace; -import ru.windcorp.progressia.common.world.tile.TileData; +import ru.windcorp.progressia.common.world.tile.TileDataStack; import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.world.ChunkLogic; -import ru.windcorp.progressia.server.world.MutableBlockTickContext; -import ru.windcorp.progressia.server.world.MutableTileTickContext; import ru.windcorp.progressia.server.world.TickAndUpdateUtil; +import ru.windcorp.progressia.server.world.TickContextMutable; import ru.windcorp.progressia.server.world.block.BlockLogic; -import ru.windcorp.progressia.server.world.block.BlockTickContext; import ru.windcorp.progressia.server.world.block.TickableBlock; import ru.windcorp.progressia.server.world.ticking.Evaluation; import ru.windcorp.progressia.server.world.ticking.TickingPolicy; +import ru.windcorp.progressia.server.world.tile.TSTickContext; import ru.windcorp.progressia.server.world.tile.TickableTile; import ru.windcorp.progressia.server.world.tile.TileLogic; -import ru.windcorp.progressia.server.world.tile.TileLogicRegistry; - import static ru.windcorp.progressia.common.world.ChunkData.BLOCKS_PER_CHUNK; public class TickChunk extends Evaluation { @@ -35,12 +32,18 @@ public class TickChunk extends Evaluation { ChunkData.BLOCKS_PER_CHUNK * ChunkData.BLOCKS_PER_CHUNK; - private final List> randomTickMethods = ImmutableList.of( - s -> this.tickRandomBlock(s), - s -> this.tickRandomTile(s, BlockFace.NORTH), - s -> this.tickRandomTile(s, BlockFace.TOP), - s -> this.tickRandomTile(s, BlockFace.WEST) - ); + private final List> randomTickMethods; + + { + List> randomTickMethods = new ArrayList<>(); + randomTickMethods.add(this::tickRandomBlock); + + for (BlockFace face : BlockFace.getFaces()) { + randomTickMethods.add(s -> this.tickRandomTile(s, face)); + } + + this.randomTickMethods = ImmutableList.copyOf(randomTickMethods); + } private final ChunkLogic chunk; @@ -63,30 +66,22 @@ public class TickChunk extends Evaluation { private void tickRegularBlocks(Server server) { if (!chunk.hasTickingBlocks()) return; - MutableBlockTickContext context = new MutableBlockTickContext(); - Vec3i blockInWorld = new Vec3i(); + TickContextMutable context = TickContextMutable.uninitialized(); chunk.forEachTickingBlock((blockInChunk, block) -> { - - Coordinates.getInWorld(chunk.getPosition(), blockInChunk, blockInWorld); - context.init(server, blockInWorld); + context.rebuild().withChunk(chunk).withBlockInChunk(blockInChunk).build(); ((TickableBlock) block).tick(context); - }); } private void tickRegularTiles(Server server) { if (!chunk.hasTickingTiles()) return; - MutableTileTickContext context = new MutableTileTickContext(); - Vec3i blockInWorld = new Vec3i(); + TickContextMutable context = TickContextMutable.uninitialized(); - chunk.forEachTickingTile((loc, tile) -> { - - Coordinates.getInWorld(chunk.getPosition(), loc.pos, blockInWorld); - context.init(server, blockInWorld, loc.face, loc.layer); + chunk.forEachTickingTile((ref, tile) -> { + context.rebuild().withServer(server).withTile(ref); ((TickableTile) tile).tick(context); - }); } @@ -130,11 +125,9 @@ public class TickChunk extends Evaluation { if (!(block instanceof TickableBlock)) return; TickableBlock tickable = (TickableBlock) block; - - BlockTickContext context = TickAndUpdateUtil.getBlockTickContext( - server, - Coordinates.getInWorld(this.chunk.getPosition(), blockInChunk, null) - ); + + TickContextMutable context = + TickContextMutable.start().withChunk(chunk).withBlockInChunk(blockInChunk).build(); if (tickable.getTickingPolicy(context) != TickingPolicy.RANDOM) return; tickable.tick(context); @@ -149,24 +142,19 @@ public class TickChunk extends Evaluation { random.nextInt(BLOCKS_PER_CHUNK) ); - List tiles = this.chunk.getData().getTilesOrNull(blockInChunk, face); - if (tiles == null) return; + TileDataStack tiles = this.chunk.getData().getTilesOrNull(blockInChunk, face); + if (tiles == null || tiles.isEmpty()) return; - MutableTileTickContext context = new MutableTileTickContext(); - Vec3i blockInWorld = Coordinates.getInWorld(this.chunk.getPosition(), blockInChunk, null); + TSTickContext context = TickContextMutable.start().withServer(server).withTS(tiles).build(); - for (int layer = 0; layer < tiles.size(); ++layer) { - TileData data = tiles.get(layer); - - TileLogic logic = TileLogicRegistry.getInstance().get(data.getId()); + context.forEachTile(tctxt -> { + TileLogic logic = tctxt.getTile(); if (!(logic instanceof TickableTile)) return; TickableTile tickable = (TickableTile) logic; - - context.init(server, blockInWorld, face, layer); - if (tickable.getTickingPolicy(context) != TickingPolicy.RANDOM) return; - tickable.tick(context); - } + if (tickable.getTickingPolicy(tctxt) != TickingPolicy.RANDOM) return; + tickable.tick(tctxt); + }); } private float computeRandomTicks(Server server) { diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/TileTriggeredUpdate.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/TileTriggeredUpdate.java index bedd4c3..bef9a55 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/TileTriggeredUpdate.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/TileTriggeredUpdate.java @@ -24,10 +24,11 @@ class TileTriggeredUpdate extends CachedEvaluation { WorldLogic world = server.getWorld(); - TickAndUpdateUtil.tickTiles(world, cursor, face); // Tick facemates (also self) - TickAndUpdateUtil.tickBlock(world, cursor); // Tick block on one side + TickAndUpdateUtil.updateTiles(world, cursor, face); // Update facemates (also self) + TickAndUpdateUtil.updateBlock(world, cursor); // Update block on one side cursor.add(face.getVector()); - TickAndUpdateUtil.tickBlock(world, cursor); // Tick block on the other side + TickAndUpdateUtil.updateBlock(world, cursor); // Update block on the other side + TickAndUpdateUtil.updateTiles(world, cursor, face.getCounter()); // Update complement } public void init(Vec3i blockInWorld, BlockFace face) { diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/WorldAccessor.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/WorldAccessor.java index 206d426..ff073fc 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/WorldAccessor.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/WorldAccessor.java @@ -22,7 +22,8 @@ public class WorldAccessor { cache = mloc .addClass(SetBlock.class, () -> new SetBlock(disposer)) - .addClass(AddOrRemoveTile.class, () -> new AddOrRemoveTile(disposer)) + .addClass(AddTile.class, () -> new AddTile(disposer)) + .addClass(RemoveTile.class, () -> new RemoveTile(disposer)) .addClass(ChangeEntity.class, () -> new ChangeEntity(disposer)) .addClass(BlockTriggeredUpdate.class, () -> new BlockTriggeredUpdate(disposer)) @@ -46,8 +47,8 @@ public class WorldAccessor { } public void addTile(Vec3i blockInWorld, BlockFace face, TileData tile) { - AddOrRemoveTile change = cache.grab(AddOrRemoveTile.class); - change.initialize(blockInWorld, face, tile, true); + AddTile change = cache.grab(AddTile.class); + change.initialize(blockInWorld, face, tile); server.requestChange(change); } @@ -55,9 +56,9 @@ public class WorldAccessor { addTile(blockInWorld, face, TileDataRegistry.getInstance().get(id)); } - public void removeTile(Vec3i blockInWorld, BlockFace face, TileData tile) { - AddOrRemoveTile change = cache.grab(AddOrRemoveTile.class); - change.initialize(blockInWorld, face, tile, false); + public void removeTile(Vec3i blockInWorld, BlockFace face, int tag) { + RemoveTile change = cache.grab(RemoveTile.class); + change.initialize(blockInWorld, face, tag); server.requestChange(change); } diff --git a/src/main/java/ru/windcorp/progressia/server/world/ticking/Ticker.java b/src/main/java/ru/windcorp/progressia/server/world/ticking/Ticker.java index 1ae5111..32502ab 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/ticking/Ticker.java +++ b/src/main/java/ru/windcorp/progressia/server/world/ticking/Ticker.java @@ -147,7 +147,7 @@ class Ticker { try { task.run(srv); } catch (Exception e) { - CrashReports.report(e, "Could not run {} task {}", task.getClass().getSimpleName(), task); + CrashReports.report(e, "Could not run %s task %s", task.getClass().getSimpleName(), task); } tasksCompleted++; diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/EdgeTileLogic.java b/src/main/java/ru/windcorp/progressia/server/world/tile/EdgeTileLogic.java deleted file mode 100644 index 516d6a5..0000000 --- a/src/main/java/ru/windcorp/progressia/server/world/tile/EdgeTileLogic.java +++ /dev/null @@ -1,39 +0,0 @@ -package ru.windcorp.progressia.server.world.tile; - -import ru.windcorp.progressia.common.world.block.BlockFace; -import ru.windcorp.progressia.server.world.block.BlockLogic; -import ru.windcorp.progressia.server.world.block.BlockTickContext; - -public class EdgeTileLogic extends TileLogic implements UpdateableTile { - - public EdgeTileLogic(String id) { - super(id); - } - - @Override - public void update(TileTickContext context) { - if (!canOccupyFace(context)) { - context.removeThisTile(); - } - } - - @Override - public boolean canOccupyFace(TileTickContext context) { - boolean canOccupyCurrent = canOccupyFace(context, context.getCurrentFace(), context.getCurrentBlockContext()); - boolean canOccupyCounter = canOccupyFace(context, context.getCounterFace(), context.getCounterBlockContext()); - - return (canOccupyCurrent != canOccupyCounter) || (canOccupyCurrent && canOccupyCounter && canBeSquashed(context)); - } - - public boolean canOccupyFace(TileTickContext ownContext, BlockFace blockFace, BlockTickContext blockContext) { - BlockLogic block = blockContext.getBlock(); - if (block == null) return false; - - return block.isSolid(blockContext, ownContext.getCurrentFace()); - } - - public boolean canBeSquashed(TileTickContext context) { - return false; - } - -} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/ForwardingTileTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/tile/ForwardingTileTickContext.java deleted file mode 100644 index 0f385db..0000000 --- a/src/main/java/ru/windcorp/progressia/server/world/tile/ForwardingTileTickContext.java +++ /dev/null @@ -1,59 +0,0 @@ -package ru.windcorp.progressia.server.world.tile; - -import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.world.block.BlockFace; -import ru.windcorp.progressia.server.Server; -import ru.windcorp.progressia.server.world.WorldLogic; - -public class ForwardingTileTickContext implements TileTickContext { - - private TileTickContext parent; - - public ForwardingTileTickContext(TileTickContext parent) { - this.parent = parent; - } - - public TileTickContext getParent() { - return parent; - } - - public void setParent(TileTickContext parent) { - this.parent = parent; - } - - @Override - public double getTickLength() { - return parent.getTickLength(); - } - - @Override - public Server getServer() { - return parent.getServer(); - } - - @Override - public WorldLogic getWorld() { - return parent.getWorld(); - } - - @Override - public Vec3i getCurrentBlockInWorld() { - return parent.getCurrentBlockInWorld(); - } - - @Override - public Vec3i getCounterBlockInWorld() { - return parent.getCounterBlockInWorld(); - } - - @Override - public BlockFace getCurrentFace() { - return parent.getCurrentFace(); - } - - @Override - public int getCurrentLayer() { - return parent.getCurrentLayer(); - } - -} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/HangingTileLogic.java b/src/main/java/ru/windcorp/progressia/server/world/tile/HangingTileLogic.java new file mode 100644 index 0000000..8d11430 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/HangingTileLogic.java @@ -0,0 +1,37 @@ +package ru.windcorp.progressia.server.world.tile; + +import ru.windcorp.progressia.server.world.block.BlockLogic; + +public class HangingTileLogic extends TileLogic implements UpdateableTile { + + public HangingTileLogic(String id) { + super(id); + } + + @Override + public void update(TileTickContext context) { + if (!canOccupyFace(context)) { + context.removeThisTile(); + } + } + + @Override + public boolean canOccupyFace(TileTickContext context) { + BlockLogic host = context.getBlock(); + if (host == null) return false; + + if (!host.isSolid(context, context.getFace())) return false; + + if (canBeSquashed(context)) return true; + + return context.evalComplementary(ctxt -> { + BlockLogic complHost = ctxt.getBlock(); + return complHost == null || !complHost.isSolid(ctxt, context.getFace()); + }); + } + + public boolean canBeSquashed(TileTickContext context) { + return false; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TSTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TSTickContext.java new file mode 100644 index 0000000..c5b9ada --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TSTickContext.java @@ -0,0 +1,90 @@ +package ru.windcorp.progressia.server.world.tile; + +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; + +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.tile.TileDataStack; +import ru.windcorp.progressia.server.world.ChunkLogic; +import ru.windcorp.progressia.server.world.TickContextMutable; +import ru.windcorp.progressia.server.world.block.BlockTickContext; + +public interface TSTickContext extends BlockTickContext { + + /* + * Specifications + */ + + BlockFace getFace(); + + /* + * Getters + */ + + default TileLogicStack getTLSOrNull() { + ChunkLogic chunkLogic = getChunkLogic(); + if (chunkLogic == null) return null; + + return chunkLogic.getTilesOrNull(getBlockInWorld(), getFace()); + } + + default TileLogicStack getTLS() { + return getChunkLogic().getTiles(getBlockInWorld(), getFace()); + } + + default TileDataStack getTDSOrNull() { + ChunkData chunkData = getChunkData(); + if (chunkData == null) return null; + + return chunkData.getTilesOrNull(getBlockInWorld(), getFace()); + } + + default TileDataStack getTDS() { + return getChunkData().getTiles(getBlockInWorld(), getFace()); + } + + /* + * Contexts + */ + + default TileTickContext forLayer(int layer) { + return TickContextMutable.start().withServer(getServer()).withBlock(getBlockInWorld()).withFace(getFace()).withLayer(layer); + } + + default boolean forEachTile(Consumer action) { + TickContextMutable context = TickContextMutable.uninitialized(); + + TileDataStack stack = getTDSOrNull(); + if (stack == null || stack.isEmpty()) return false; + + for (int layer = 0; layer < stack.size(); ++layer) { + context.rebuild().withServer(getServer()).withBlock(getBlockInWorld()).withFace(getFace()).withLayer(layer); + action.accept(context); + } + + return true; + } + + default TSTickContext getComplementary() { + return TickContextMutable.copyWorld(this) + .withBlock(getBlockInWorld().add_(getFace().getVector())) + .withFace(getFace().getCounter()) + .build(); + } + + default R evalComplementary(Function action) { + Objects.requireNonNull(action, "action"); + return action.apply(getComplementary()); + } + + default void forComplementary(Consumer action) { + Objects.requireNonNull(action, "action"); + evalComplementary((Function) ctxt -> { + action.accept(ctxt); + return null; + }); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java index 86e62d3..c44ec7e 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java @@ -10,7 +10,7 @@ public class TileLogic extends Namespaced { } public boolean canOccupyFace(TileTickContext context) { - return canOccupyFace(context.getCurrentFace()); + return canOccupyFace(context.getFace()); } public boolean canOccupyFace(BlockFace face) { diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicStack.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicStack.java new file mode 100644 index 0000000..ea5789b --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicStack.java @@ -0,0 +1,13 @@ +package ru.windcorp.progressia.server.world.tile; + +import ru.windcorp.progressia.common.world.tile.GenericTileStack; +import ru.windcorp.progressia.common.world.tile.TileDataStack; +import ru.windcorp.progressia.server.world.ChunkLogic; + +public abstract class TileLogicStack extends GenericTileStack { + + // TODO add @Deprecated or smth similar to all modification methods + + public abstract TileDataStack getData(); + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TileTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TileTickContext.java index 5d368bb..02a5164 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tile/TileTickContext.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TileTickContext.java @@ -1,147 +1,55 @@ package ru.windcorp.progressia.server.world.tile; -import java.util.List; - -import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.world.ChunkData; -import ru.windcorp.progressia.common.world.Coordinates; -import ru.windcorp.progressia.common.world.block.BlockData; -import ru.windcorp.progressia.common.world.block.BlockFace; import ru.windcorp.progressia.common.world.tile.TileData; -import ru.windcorp.progressia.server.world.ChunkLogic; -import ru.windcorp.progressia.server.world.TickAndUpdateUtil; -import ru.windcorp.progressia.server.world.TickContext; -import ru.windcorp.progressia.server.world.block.BlockLogic; -import ru.windcorp.progressia.server.world.block.BlockTickContext; +import ru.windcorp.progressia.common.world.tile.TileDataStack; +import ru.windcorp.progressia.common.world.tile.TileReference; -public interface TileTickContext extends TickContext { +public interface TileTickContext extends TSTickContext { /* * Specifications */ - /** - * Returns the current world coordinates. - * @return the world coordinates of the tile being ticked - */ - Vec3i getCurrentBlockInWorld(); - - /** - * Returns the counter world coordinates. - * @return the world coordinates of the tile being ticked - */ - Vec3i getCounterBlockInWorld(); - - /** - * Returns the current block face. - * @return the block face that the tile being ticked occupies - */ - BlockFace getCurrentFace(); - /** * Returns the current layer. * @return the layer that the tile being ticked occupies in the tile stack */ - int getCurrentLayer(); - - default BlockFace getCounterFace() { - return getCurrentFace().getCounter(); - } + int getLayer(); /* - * Tile-related + * Getters */ default TileLogic getTile() { - return getTiles().get(getCurrentLayer()); + TileLogicStack stack = getTLSOrNull(); + if (stack == null) return null; + return stack.get(getLayer()); } default TileData getTileData() { - return getTileDataList().get(getCurrentLayer()); + TileDataStack stack = getTDSOrNull(); + if (stack == null) return null; + return stack.get(getLayer()); } - default List getTiles() { - return getCurrentChunk().getTiles( - Coordinates.convertInWorldToInChunk(getCurrentBlockInWorld(), null), - getCurrentFace() - ); + default TileReference getReference() { + return getTDS().getReference(getLayer()); } - default List getTilesOrNull() { - return getCurrentChunk().getTilesOrNull( - Coordinates.convertInWorldToInChunk(getCurrentBlockInWorld(), null), - getCurrentFace() - ); - } - - default List getTileDataList() { - return getCurrentChunkData().getTiles( - Coordinates.convertInWorldToInChunk(getCurrentBlockInWorld(), null), - getCurrentFace() - ); - } - - default List getTileDataListOrNull() { - return getCurrentChunkData().getTilesOrNull( - Coordinates.convertInWorldToInChunk(getCurrentBlockInWorld(), null), - getCurrentFace() - ); + default int getTag() { + return getTDS().getTagByIndex(getLayer()); } /* - * Current block/chunk + * Contexts */ - default ChunkLogic getCurrentChunk() { - return getWorld().getChunkByBlock(getCurrentBlockInWorld()); - } - - default ChunkData getCurrentChunkData() { - return getWorldData().getChunkByBlock(getCurrentBlockInWorld()); - } - - default BlockLogic getCurrentBlock() { - return getWorld().getBlock(getCurrentBlockInWorld()); - } - - default BlockData getCurrentBlockData() { - return getWorldData().getBlock(getCurrentBlockInWorld()); - } - - default BlockTickContext getCurrentBlockContext() { - return TickAndUpdateUtil.getBlockTickContext(getServer(), getCurrentBlockInWorld()); - } - - /* - * Counter block/chunk - */ - - default ChunkLogic getCounterChunk() { - return getWorld().getChunkByBlock(getCounterBlockInWorld()); - } - - default ChunkData getCounterChunkData() { - return getWorldData().getChunkByBlock(getCounterBlockInWorld()); - } - - default BlockLogic getCounterBlock() { - return getWorld().getBlock(getCounterBlockInWorld()); - } - - default BlockData getCounterBlockData() { - return getWorldData().getBlock(getCounterBlockInWorld()); - } - - default BlockTickContext getCounterBlockContext() { - return TickAndUpdateUtil.getBlockTickContext(getServer(), getCounterBlockInWorld()); - } - /* * Convenience methods - changes */ default void removeThisTile() { - getAccessor().removeTile(getCurrentBlockInWorld(), getCurrentFace(), getTileData()); + getAccessor().removeTile(getBlockInWorld(), getFace(), getTag()); } } diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 357d8ab..6d2b13e 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -7,6 +7,9 @@ import java.util.function.Consumer; import org.lwjgl.glfw.GLFW; +import glm.Glm; +import glm.vec._2.Vec2; +import glm.vec._3.Vec3; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.ClientState; import ru.windcorp.progressia.client.audio.SoundEffect; @@ -22,6 +25,7 @@ import ru.windcorp.progressia.common.collision.AABB; import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.comms.controls.*; import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory; +import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.block.*; import ru.windcorp.progressia.common.world.entity.*; @@ -79,15 +83,15 @@ public class TestContent { register(new TileData("Test:Stones")); register(new TileRenderSimple("Test:Stones", getTileTexture("stones"))); - register(new EdgeTileLogic("Test:Stones")); + register(new HangingTileLogic("Test:Stones")); register(new TileData("Test:YellowFlowers")); register(new TileRenderSimple("Test:YellowFlowers", getTileTexture("yellow_flowers"))); - register(new EdgeTileLogic("Test:YellowFlowers")); + register(new HangingTileLogic("Test:YellowFlowers")); register(new TileData("Test:Sand")); register(new TileRenderSimple("Test:Sand", getTileTexture("sand"))); - register(new EdgeTileLogic("Test:Sand")); + register(new HangingTileLogic("Test:Sand")); } private static void registerEntities() { @@ -236,4 +240,91 @@ public class TestContent { server.getWorldAccessor().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Stone")); } + public static void generateChunk(ChunkData chunk) { + final int bpc = ChunkData.BLOCKS_PER_CHUNK; + + BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt"); + BlockData stone = BlockDataRegistry.getInstance().get("Test:Stone"); + BlockData air = BlockDataRegistry.getInstance().get("Test:Air"); + + TileData grass = TileDataRegistry.getInstance().get("Test:Grass"); + TileData stones = TileDataRegistry.getInstance().get("Test:Stones"); + TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers"); + TileData sand = TileDataRegistry.getInstance().get("Test:Sand"); + + Vec3i aPoint = new Vec3i(5, 0, bpc + bpc/2).sub(chunk.getPosition()); + Vec3i pos = new Vec3i(); + + for (int x = 0; x < bpc; ++x) { + for (int y = 0; y < bpc; ++y) { + for (int z = 0; z < bpc; ++z) { + + pos.set(x, y, z); + float f = aPoint.sub(pos, pos).length(); + pos.set(x, y, z); + + if (f > 17) { + chunk.setBlock(pos, stone, false); + } else if (f > 14) { + chunk.setBlock(pos, dirt, false); + } else { + chunk.setBlock(pos, air, false); + } + + } + } + } + + for (int x = 0; x < bpc; ++x) { + for (int y = 0; y < bpc; ++y) { + pos.set(x, y, 0); + + for (pos.z = bpc - 1; pos.z >= 0 && chunk.getBlock(pos) == air; --pos.z); + + chunk.getTiles(pos, BlockFace.TOP).add(grass); + for (BlockFace face : BlockFace.getFaces()) { + if (face.getVector().z != 0) continue; + pos.add(face.getVector()); + + if (!ChunkData.isInBounds(pos) || (chunk.getBlock(pos) == air)) { + pos.sub(face.getVector()); + chunk.getTiles(pos, face).add(grass); + } else { + pos.sub(face.getVector()); + } + } + + int hash = x*x * 19 ^ y*y * 41 ^ pos.z*pos.z * 147; + if (hash % 5 == 0) { + chunk.getTiles(pos, BlockFace.TOP).addFarthest(sand); + } + + hash = x*x * 13 ^ y*y * 37 ^ pos.z*pos.z * 129; + if (hash % 5 == 0) { + chunk.getTiles(pos, BlockFace.TOP).addFarthest(stones); + } + + hash = x*x * 17 ^ y*y * 39 ^ pos.z*pos.z * 131; + if (hash % 9 == 0) { + chunk.getTiles(pos, BlockFace.TOP).addFarthest(flowers); + } + } + } + + if (Glm.equals(chunk.getPosition(), Vectors.ZERO_3i)) { + EntityData player = EntityDataRegistry.getInstance().create("Test:Player"); + player.setEntityId(0x42); + player.setPosition(new Vec3(-6, -6, 20)); + player.setDirection(new Vec2( + (float) Math.toRadians(40), (float) Math.toRadians(45) + )); + chunk.getEntities().add(player); + + EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie"); + statie.setEntityId(0xDEADBEEF); + statie.setPosition(new Vec3(0, 15, 16)); + chunk.getEntities().add(statie); + } + } + } diff --git a/src/main/java/ru/windcorp/progressia/test/TestTileLogicGrass.java b/src/main/java/ru/windcorp/progressia/test/TestTileLogicGrass.java index b3212bc..3ad70a8 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestTileLogicGrass.java +++ b/src/main/java/ru/windcorp/progressia/test/TestTileLogicGrass.java @@ -1,22 +1,29 @@ package ru.windcorp.progressia.test; -import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.block.BlockFace; -import ru.windcorp.progressia.server.Server; -import ru.windcorp.progressia.server.world.TickAndUpdateUtil; import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.block.BlockTickContext; import ru.windcorp.progressia.server.world.ticking.TickingPolicy; -import ru.windcorp.progressia.server.world.tile.EdgeTileLogic; +import ru.windcorp.progressia.server.world.tile.HangingTileLogic; import ru.windcorp.progressia.server.world.tile.TickableTile; import ru.windcorp.progressia.server.world.tile.TileTickContext; -public class TestTileLogicGrass extends EdgeTileLogic implements TickableTile { +public class TestTileLogicGrass extends HangingTileLogic implements TickableTile { public TestTileLogicGrass(String id) { super(id); } + @Override + public boolean canOccupyFace(TileTickContext context) { + return context.getFace() != BlockFace.BOTTOM && super.canOccupyFace(context); + } + + @Override + public boolean canOccupyFace(BlockFace face) { + return face != BlockFace.BOTTOM; + } + @Override public TickingPolicy getTickingPolicy(TileTickContext context) { return TickingPolicy.RANDOM; @@ -35,27 +42,16 @@ public class TestTileLogicGrass extends EdgeTileLogic implements TickableTile { } private boolean isLocationSuitable(TileTickContext context) { - return - isSuitableHost(context.getCurrentBlockContext(), context.getCurrentFace()) != - isSuitableHost(context.getCounterBlockContext(), context.getCounterFace()); - } - - private boolean isSuitableHost(BlockTickContext bctxt, BlockFace face) { - if (face == BlockFace.BOTTOM) return false; - - BlockLogic block = bctxt.getBlock(); - if (block == null) return false; - if (!block.isSolid(bctxt, face)) return false; - return isBlockAboveTransparent(bctxt.getServer(), bctxt.getBlockInWorld()); + return canOccupyFace(context) && isBlockAboveTransparent(context); } - private boolean isBlockAboveTransparent(Server server, Vec3i blockInWorld) { - BlockTickContext bctxt = TickAndUpdateUtil.getBlockTickContext(server, blockInWorld.add_(BlockFace.TOP.getVector())); - - BlockLogic block = bctxt.getBlock(); - if (block == null) return true; - - return block.isTransparent(bctxt); + private boolean isBlockAboveTransparent(BlockTickContext context) { + return context.evalNeighbor(BlockFace.TOP, bctxt -> { + BlockLogic block = bctxt.getBlock(); + if (block == null) return true; + + return block.isTransparent(bctxt); + }); } }