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 }