1 /** 2 Copyright: Copyright (c) 2015-2018 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module voxelman.world.storage.worldaccess; 7 8 import voxelman.log; 9 import std..string; 10 import voxelman.math.box; 11 import voxelman.core.config; 12 import voxelman.world.block; 13 import voxelman.world.storage; 14 15 final class WorldAccess 16 { 17 private ChunkManager chunkManager; 18 private ChunkEditor chunkEditor; 19 BlockInfoTable blockInfos; 20 BlockChange[][ChunkWorldPos] blockChanges; 21 22 this(ChunkManager chunkManager, ChunkEditor chunkEditor) { 23 this.chunkManager = chunkManager; 24 this.chunkEditor = chunkEditor; 25 } 26 27 // TODO move out change management 28 import std.range : isInputRange, array; 29 void onBlockChange(ChunkWorldPos cwp, BlockChange change) 30 { 31 blockChanges[cwp] = blockChanges.get(cwp, null) ~ change; 32 } 33 34 bool isChunkLoaded(ChunkWorldPos cwp) { 35 return chunkManager.isChunkLoaded(cwp); 36 } 37 38 void applyBlockChanges(ChunkWorldPos cwp, BlockChange[] changes) 39 { 40 WriteBuffer* writeBuffer = chunkEditor.getOrCreateWriteBuffer(cwp, 41 BLOCK_LAYER, WriteBufferPolicy.copySnapshotArray); 42 if (writeBuffer is null) return; 43 applyChanges(writeBuffer, changes); 44 writeBuffer.layer.metadata = calcChunkFullMetadata(writeBuffer.layer, blockInfos); 45 } 46 47 bool setBlock(BlockWorldPos bwp, BlockId blockId, BlockMetadata blockMeta = 0) { 48 auto blockIndex = BlockChunkIndex(bwp); 49 auto cwp = ChunkWorldPos(bwp); 50 WriteBuffer* writeBuffer = chunkEditor.getOrCreateWriteBuffer(cwp, 51 BLOCK_LAYER, WriteBufferPolicy.copySnapshotArray); 52 if (writeBuffer is null) return false; 53 54 BlockId[] blocks = writeBuffer.layer.getArray!BlockId; 55 blocks[blockIndex] = blockId; 56 onBlockChange(cwp, BlockChange(blockIndex.index, blockId, blockMeta)); 57 updateWriteBufferMetadata(writeBuffer); 58 return true; 59 } 60 61 bool fillBox(WorldBox blockFillBox, BlockId blockId, BlockMetadata blockMeta = 0) { 62 WorldBox affectedChunks = blockBoxToChunkBox(blockFillBox); 63 ushort dimension = blockFillBox.dimension; 64 65 foreach(chunkPos; affectedChunks.positions) { 66 Box chunkBlockBox = chunkToBlockBox(chunkPos); 67 auto intersection = boxIntersection(chunkBlockBox, blockFillBox); 68 assert(!intersection.empty); 69 70 auto cwp = ChunkWorldPos(chunkPos, dimension); 71 auto chunkLocalBox = intersection; 72 chunkLocalBox.position -= chunkBlockBox.position; 73 74 fillChunkBox(cwp, chunkLocalBox, blockId, blockMeta); 75 } 76 return true; 77 } 78 79 // blockBox is in chunk-local coordinates 80 bool fillChunkBox(ChunkWorldPos cwp, Box blockBox, BlockId blockId, BlockMetadata blockMeta = 0) { 81 auto oldBlocks = chunkManager.getChunkSnapshot(cwp, BLOCK_LAYER); 82 if (oldBlocks.isNull) return false; 83 84 if (oldBlocks.type == StorageType.uniform) 85 { 86 if (oldBlocks.getUniform!BlockId() == blockId) { 87 return false; 88 } 89 } 90 91 WriteBuffer* blockWB; 92 93 // uniform fill 94 if (blockBox.size == CHUNK_SIZE_VECTOR) 95 { 96 blockWB = chunkEditor.getOrCreateWriteBuffer(cwp, BLOCK_LAYER); 97 if (blockWB is null) return false; 98 blockWB.makeUniform!BlockId(blockId); 99 } 100 else 101 { 102 blockWB = chunkEditor.getOrCreateWriteBuffer(cwp, 103 BLOCK_LAYER, WriteBufferPolicy.copySnapshotArray); 104 BlockId[] blocks = blockWB.getArray!BlockId; 105 assert(blocks.length == CHUNK_SIZE_CUBE, format("blocks %s", blocks.length)); 106 setSubArray3d(blocks, CHUNK_SIZE_VECTOR, blockBox, blockId); 107 } 108 fillChunkBoxMetadata(cwp, blockBox, blockMeta); 109 updateWriteBufferMetadata(blockWB); 110 111 return true; 112 } 113 114 private void fillChunkBoxMetadata(ChunkWorldPos cwp, Box blockBox, BlockMetadata blockMeta) { 115 auto oldMetas = chunkManager.getChunkSnapshot(cwp, METADATA_LAYER); 116 117 if (oldMetas.type == StorageType.uniform) 118 { 119 if (oldMetas.getUniform!BlockMetadata == blockMeta) { 120 return; 121 } 122 } 123 124 if (blockBox.size == CHUNK_SIZE_VECTOR) 125 { 126 WriteBuffer* metadataWB = chunkEditor.getOrCreateWriteBuffer(cwp, METADATA_LAYER); 127 metadataWB.makeUniform!BlockMetadata(blockMeta); 128 if (blockMeta == 0) { 129 metadataWB.removeSnapshot = true; 130 } 131 } 132 else 133 { 134 WriteBuffer* metadataWB = chunkEditor.getOrCreateWriteBuffer(cwp, 135 METADATA_LAYER, WriteBufferPolicy.copySnapshotArray); 136 assert(metadataWB.layer.type == StorageType.fullArray); 137 BlockMetadata[] metas = metadataWB.getArray!BlockMetadata; 138 assert(metas.length == CHUNK_SIZE_CUBE, format("metas %s", metas.length)); 139 setSubArray3d(metas, CHUNK_SIZE_VECTOR, blockBox, blockMeta); 140 } 141 } 142 143 private void updateWriteBufferMetadata(WriteBuffer* writeBuffer) { 144 writeBuffer.layer.metadata = calcChunkFullMetadata(writeBuffer.layer, blockInfos); 145 } 146 147 BlockId getBlock(BlockWorldPos bwp) { 148 auto blockIndex = BlockChunkIndex(bwp); 149 auto chunkPos = ChunkWorldPos(bwp); 150 auto snap = chunkManager.getChunkSnapshot(chunkPos, BLOCK_LAYER, Yes.Uncompress); 151 if (!snap.isNull) { 152 return snap.getBlockId(blockIndex); 153 } 154 return 0; 155 } 156 157 BlockMetadata getBlockMeta(BlockWorldPos bwp) { 158 auto blockIndex = BlockChunkIndex(bwp); 159 auto chunkPos = ChunkWorldPos(bwp); 160 auto snap = chunkManager.getChunkSnapshot(chunkPos, METADATA_LAYER, Yes.Uncompress); 161 if (!snap.isNull) { 162 return snap.getLayerItemNoncompressed!BlockMetadata(blockIndex); 163 } 164 return 0; 165 } 166 167 BlockIdAndMeta getBlockIdAndMeta(BlockWorldPos bwp) { 168 BlockIdAndMeta result; 169 auto blockIndex = BlockChunkIndex(bwp); 170 auto chunkPos = ChunkWorldPos(bwp); 171 172 auto blocks = chunkManager.getChunkSnapshot(chunkPos, BLOCK_LAYER, Yes.Uncompress); 173 if (!blocks.isNull) { 174 result.id = blocks.getBlockId(blockIndex); 175 } 176 177 auto metas = chunkManager.getChunkSnapshot(chunkPos, METADATA_LAYER, Yes.Uncompress); 178 if (!metas.isNull) { 179 result.metadata = metas.getLayerItemNoncompressed!BlockMetadata(blockIndex); 180 } 181 182 return result; 183 } 184 185 bool isFree(BlockWorldPos bwp) { 186 auto blockId = getBlock(bwp); 187 return blockId == 1; // air 188 } 189 }