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 }