1 /** 2 Copyright: Copyright (c) 2015-2016 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 std.experimental.logger; 9 import std.string; 10 import voxelman.geometry.box; 11 import voxelman.core.config; 12 import voxelman.block.utils; 13 import voxelman.world.storage.chunk; 14 import voxelman.world.storage.chunkmanager; 15 import voxelman.world.storage.coordinates; 16 import voxelman.world.storage.worldbox; 17 18 final class WorldAccess 19 { 20 private ChunkManager chunkManager; 21 BlockInfoTable blockInfos; 22 BlockChange[][ChunkWorldPos] blockChanges; 23 24 this(ChunkManager chunkManager) { 25 this.chunkManager = chunkManager; 26 } 27 28 // TODO move out change management 29 import std.range : isInputRange, array; 30 void onBlockChange(ChunkWorldPos cwp, BlockChange change) 31 { 32 blockChanges[cwp] = blockChanges.get(cwp, null) ~ change; 33 } 34 35 void applyBlockChanges(ChunkWorldPos cwp, BlockChange[] changes) 36 { 37 WriteBuffer* writeBuffer = chunkManager.getOrCreateWriteBuffer(cwp, 38 FIRST_LAYER, WriteBufferPolicy.copySnapshotArray); 39 if (writeBuffer is null) return; 40 applyChanges(writeBuffer, changes); 41 writeBuffer.layer.metadata = calcChunkFullMetadata(writeBuffer.layer, blockInfos); 42 } 43 44 bool setBlock(BlockWorldPos bwp, BlockId blockId) { 45 auto blockIndex = BlockChunkIndex(bwp); 46 auto cwp = ChunkWorldPos(bwp); 47 WriteBuffer* writeBuffer = chunkManager.getOrCreateWriteBuffer(cwp, 48 FIRST_LAYER, WriteBufferPolicy.copySnapshotArray); 49 if (writeBuffer is null) return false; 50 51 BlockId[] blocks = writeBuffer.layer.getArray!BlockId; 52 blocks[blockIndex] = blockId; 53 onBlockChange(cwp, BlockChange(blockIndex.index, blockId)); 54 updateWriteBufferMetadata(writeBuffer); 55 return true; 56 } 57 58 bool fillBox(WorldBox blockFillBox, BlockId blockId) { 59 WorldBox affectedChunks = blockBoxToChunkBox(blockFillBox); 60 ushort dimention = blockFillBox.dimention; 61 62 foreach(chunkPos; affectedChunks.positions) { 63 Box chunkBlockBox = chunkToBlockBox(chunkPos); 64 auto intersection = boxIntersection(chunkBlockBox, blockFillBox); 65 assert(!intersection.empty); 66 67 auto cwp = ChunkWorldPos(chunkPos, dimention); 68 auto chunkLocalBox = intersection; 69 chunkLocalBox.position -= chunkBlockBox.position; 70 71 fillChunkBox(cwp, chunkLocalBox, blockId); 72 } 73 return true; 74 } 75 76 // blockBox is in chunk-local coordinates 77 bool fillChunkBox(ChunkWorldPos cwp, Box blockBox, BlockId blockId) { 78 auto old = chunkManager.getChunkSnapshot(cwp, FIRST_LAYER); 79 if (old.isNull) return false; 80 81 if (old.type == StorageType.uniform) 82 { 83 if (old.getUniform!BlockId() == blockId) { 84 return false; 85 } 86 } 87 88 WriteBuffer* writeBuffer; 89 90 // uniform fill 91 if (blockBox.size == CHUNK_SIZE_VECTOR) 92 { 93 writeBuffer = chunkManager.getOrCreateWriteBuffer(cwp, FIRST_LAYER); 94 if (writeBuffer is null) return false; 95 writeBuffer.makeUniform!BlockId(blockId); 96 } 97 else 98 { 99 writeBuffer = chunkManager.getOrCreateWriteBuffer(cwp, 100 FIRST_LAYER, WriteBufferPolicy.copySnapshotArray); 101 BlockId[] blocks = writeBuffer.layer.getArray!BlockId; 102 assert(blocks.length == CHUNK_SIZE_CUBE, format("blocks %s", blocks.length)); 103 setSubArray(blocks, blockBox, blockId); 104 } 105 updateWriteBufferMetadata(writeBuffer); 106 107 return true; 108 } 109 110 private void updateWriteBufferMetadata(WriteBuffer* writeBuffer) { 111 writeBuffer.layer.metadata = calcChunkFullMetadata(writeBuffer.layer, blockInfos); 112 } 113 114 BlockId getBlock(BlockWorldPos bwp) { 115 auto blockIndex = BlockChunkIndex(bwp); 116 auto chunkPos = ChunkWorldPos(bwp); 117 auto snap = chunkManager.getChunkSnapshot(chunkPos, FIRST_LAYER, Yes.Uncompress); 118 if (!snap.isNull) { 119 return snap.getBlockId(blockIndex); 120 } 121 return 0; 122 } 123 124 bool isFree(BlockWorldPos bwp) { 125 auto blockId = getBlock(bwp); 126 return blockId == 1; // air 127 } 128 }