1 /** 2 Copyright: Copyright (c) 2016 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module voxelman.blockentity.blockentityaccess; 7 8 import std.experimental.logger; 9 import std.string; 10 import voxelman.math; 11 import voxelman.geometry.box; 12 import voxelman.core.config; 13 import voxelman.block.utils; 14 import voxelman.world.storage.chunk; 15 import voxelman.world.storage.chunkmanager; 16 import voxelman.world.storage.coordinates; 17 import voxelman.world.storage.worldbox; 18 import voxelman.world.storage.worldaccess; 19 import voxelman.blockentity.plugin; 20 21 import voxelman.blockentity.blockentitymap; 22 import voxelman.blockentity.blockentitydata; 23 24 25 ushort boxEntityIndex(Box blockBox) { 26 return BlockChunkIndex(blockBox.position).index; 27 } 28 29 ulong payloadFromIdAndEntityData(ushort id, ulong entityData) { 30 ulong payload = cast(ulong)id << 46 | entityData & ENTITY_DATA_MASK; 31 return payload; 32 } 33 34 // get chunk local piece of world space box 35 Box chunkLocalBlockBox(ChunkWorldPos cwp, Box blockBox) { 36 Box chunkBlockBox = chunkToBlockBox(cwp); 37 auto intersection = boxIntersection(chunkBlockBox, blockBox); 38 assert(!intersection.empty); 39 auto chunkLocalBox = intersection; 40 chunkLocalBox.position -= chunkBlockBox.position; 41 return chunkLocalBox; 42 } 43 44 void placeEntity(WorldBox blockBox, ulong payload, 45 WorldAccess worldAccess, BlockEntityAccess entityAccess) 46 { 47 auto mainCwp = ChunkWorldPos(BlockWorldPos(blockBox.position, blockBox.dimention)); 48 Box mainChunkBox = chunkLocalBlockBox(mainCwp, blockBox); 49 ushort mainBlockIndex = boxEntityIndex(mainChunkBox); 50 auto mainData = BlockEntityData( 51 BlockEntityType.localBlockEntity, payload); 52 53 Box affectedChunks = blockBoxToChunkBox(blockBox); 54 ushort dimention = blockBox.dimention; 55 foreach(chunkPos; affectedChunks.positions) { 56 auto cwp = ChunkWorldPos(chunkPos, dimention); 57 Box chunkLocalBox = chunkLocalBlockBox(cwp, blockBox); 58 59 ushort blockIndex = boxEntityIndex(chunkLocalBox); 60 BlockId blockId = blockIdFromBlockIndex(blockIndex); 61 62 if (cwp == mainCwp) 63 { 64 entityAccess.setBlockEntity(cwp, mainBlockIndex, mainData); 65 } 66 else 67 { 68 ivec3 moff = cwp.xyz - mainCwp.xyz; 69 ubyte[3] mainOffset = [cast(ubyte)moff.x, 70 cast(ubyte)moff.y, cast(ubyte)moff.z]; 71 auto data = BlockEntityData(BlockEntityType.foreignBlockEntity, 72 mainData.id, mainOffset, mainBlockIndex); 73 entityAccess.setBlockEntity(cwp, blockIndex, data); 74 } 75 worldAccess.fillChunkBox(cwp, chunkLocalBox, blockId); 76 } 77 } 78 79 void placeChunkEntity(WorldBox blockBox, ulong payload, 80 WorldAccess worldAccess, BlockEntityAccess entityAccess) 81 { 82 auto corner = BlockWorldPos(blockBox.position, blockBox.dimention); 83 auto cwp = ChunkWorldPos(corner); 84 85 // limit entity to a single chunk 86 Box chunkLocalBox = chunkLocalBlockBox(cwp, blockBox); 87 88 ushort blockIndex = boxEntityIndex(chunkLocalBox); 89 BlockId blockId = blockIdFromBlockIndex(blockIndex); 90 worldAccess.fillChunkBox(cwp, chunkLocalBox, blockId); 91 auto beData = BlockEntityData(BlockEntityType.localBlockEntity, payload); 92 bool placed = entityAccess.setBlockEntity(cwp, blockIndex, beData); 93 } 94 95 WorldBox getBlockEntityBox(ChunkWorldPos cwp, ushort blockIndex, 96 BlockEntityInfoTable blockEntityInfos, BlockEntityAccess entityAccess) 97 { 98 BlockEntityData entity = entityAccess.getBlockEntity(cwp, blockIndex); 99 100 with(BlockEntityType) final switch(entity.type) 101 { 102 case localBlockEntity: 103 BlockEntityInfo eInfo = blockEntityInfos[entity.id]; 104 auto entityBwp = BlockWorldPos(cwp, blockIndex); 105 WorldBox eVol = eInfo.boxHandler(entityBwp, entity); 106 return eVol; 107 case foreignBlockEntity: 108 auto mainPtr = entity.mainChunkPointer; 109 auto mainCwp = ChunkWorldPos(ivec3(cwp.xyz) - mainPtr.mainChunkOffset, cwp.w); 110 BlockEntityData mainEntity = entityAccess.getBlockEntity(mainCwp, mainPtr.blockIndex); 111 auto mainBwp = BlockWorldPos(mainCwp, mainPtr.blockIndex); 112 113 BlockEntityInfo eInfo = blockEntityInfos[mainPtr.entityId]; 114 WorldBox eVol = eInfo.boxHandler(mainBwp, mainEntity); 115 return eVol; 116 } 117 } 118 119 /// Returns changed box 120 WorldBox removeEntity(BlockWorldPos bwp, BlockEntityInfoTable beInfos, 121 WorldAccess worldAccess, BlockEntityAccess entityAccess, 122 BlockId fillerBlock) 123 { 124 BlockId blockId = worldAccess.getBlock(bwp); 125 if (!isBlockEntity(blockId)) 126 return WorldBox(); 127 128 auto mainCwp = ChunkWorldPos(bwp); 129 ushort mainBlockIndex = blockIndexFromBlockId(blockId); 130 WorldBox blockBox = getBlockEntityBox(mainCwp, mainBlockIndex, beInfos, entityAccess); 131 132 Box affectedChunks = blockBoxToChunkBox(blockBox); 133 ushort dimention = blockBox.dimention; 134 foreach(chunkPos; affectedChunks.positions) { 135 auto cwp = ChunkWorldPos(chunkPos, dimention); 136 Box chunkLocalBox = chunkLocalBlockBox(cwp, blockBox); 137 138 ushort blockIndex = boxEntityIndex(chunkLocalBox); 139 140 entityAccess.removeEntity(cwp, blockIndex); 141 worldAccess.fillChunkBox(cwp, chunkLocalBox, fillerBlock); 142 } 143 144 return blockBox; 145 } 146 147 final class BlockEntityAccess 148 { 149 private ChunkManager chunkManager; 150 151 this(ChunkManager chunkManager) { 152 this.chunkManager = chunkManager; 153 } 154 155 bool setBlockEntity(ChunkWorldPos cwp, ushort blockIndex, BlockEntityData beData) 156 { 157 assert((blockIndex & BLOCK_ENTITY_FLAG) == 0); 158 if (!chunkManager.isChunkLoaded(cwp)) return false; 159 160 WriteBuffer* writeBuffer = chunkManager.getOrCreateWriteBuffer(cwp, 161 ENTITY_LAYER, WriteBufferPolicy.copySnapshotArray); 162 assert(writeBuffer); 163 164 BlockEntityMap map = getHashMapFromLayer(writeBuffer.layer); 165 map[blockIndex] = beData.storage; 166 setLayerMap(writeBuffer.layer, map); 167 return true; 168 } 169 170 BlockEntityData getBlockEntity(ChunkWorldPos cwp, ushort blockIndex) 171 { 172 assert((blockIndex & BLOCK_ENTITY_FLAG) == 0); 173 auto entities = chunkManager.getChunkSnapshot(cwp, ENTITY_LAYER, Yes.Uncompress); 174 if (entities.isNull) return BlockEntityData.init; 175 if (entities.type == StorageType.uniform) return BlockEntityData.init; 176 177 BlockEntityMap map = getHashMapFromLayer(entities); 178 179 ulong* entity = blockIndex in map; 180 if (entity is null) return BlockEntityData.init; 181 182 return BlockEntityData(*entity); 183 } 184 185 bool removeEntity(ChunkWorldPos cwp, ushort blockIndex) 186 { 187 assert((blockIndex & BLOCK_ENTITY_FLAG) == 0); 188 if (!chunkManager.isChunkLoaded(cwp)) return false; 189 190 WriteBuffer* writeBuffer = chunkManager.getOrCreateWriteBuffer(cwp, 191 ENTITY_LAYER, WriteBufferPolicy.copySnapshotArray); 192 assert(writeBuffer); 193 194 BlockEntityMap map = getHashMapFromLayer(writeBuffer.layer); 195 196 map.remove(blockIndex); 197 setLayerMap(writeBuffer.layer, map); 198 return true; 199 } 200 } 201 202 void setLayerMap(Layer)(ref Layer layer, BlockEntityMap map) { 203 ubyte[] arr = map.getTable(); 204 layer.dataPtr = arr.ptr; 205 layer.dataLength = cast(LayerDataLenType)arr.length; 206 layer.metadata = cast(ushort)map.length; 207 } 208 209 BlockEntityMap getHashMapFromLayer(Layer)(const ref Layer layer) { 210 if (layer.type == StorageType.uniform) 211 return BlockEntityMap(); 212 return BlockEntityMap(layer.getArray!ubyte, layer.metadata); 213 }