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.plugin;
7 
8 import std.experimental.logger;
9 
10 import pluginlib;
11 import voxelman.container.buffer;
12 import voxelman.math;
13 
14 import derelict.imgui.imgui;
15 import voxelman.utils.textformatter;
16 import voxelman.utils.mapping;
17 
18 import voxelman.block.utils;
19 import voxelman.core.config;
20 import voxelman.core.events;
21 import voxelman.core.packets;
22 import voxelman.core.chunkmesh;
23 import voxelman.world.storage.coordinates;
24 import voxelman.world.storage.worldbox;
25 
26 import voxelman.block.plugin;
27 import voxelman.edit.plugin;
28 import voxelman.eventdispatcher.plugin;
29 import voxelman.graphics.plugin;
30 import voxelman.net.plugin;
31 import voxelman.world.clientworld;
32 import voxelman.worldinteraction.plugin;
33 
34 public import voxelman.blockentity.blockentityaccess;
35 public import voxelman.blockentity.blockentitydata;
36 public import voxelman.blockentity.blockentitymap;
37 public import voxelman.blockentity.utils;
38 
39 final class BlockEntityClient : IPlugin {
40 	mixin IdAndSemverFrom!(voxelman.blockentity.plugininfo);
41 	mixin BlockEntityCommon;
42 
43 	private ClientWorld clientWorld;
44 	private WorldInteractionPlugin worldInteraction;
45 	private BlockPluginClient blockPlugin;
46 	private GraphicsPlugin graphics;
47 	private NetClientPlugin connection;
48 
49 	override void init(IPluginManager pluginman)
50 	{
51 		clientWorld = pluginman.getPlugin!ClientWorld;
52 		worldInteraction = pluginman.getPlugin!WorldInteractionPlugin;
53 		blockPlugin = pluginman.getPlugin!BlockPluginClient;
54 		graphics = pluginman.getPlugin!GraphicsPlugin;
55 
56 		EventDispatcherPlugin evDispatcher = pluginman.getPlugin!EventDispatcherPlugin;
57 		evDispatcher.subscribeToEvent(&onUpdateEvent);
58 
59 		connection = pluginman.getPlugin!NetClientPlugin;
60 
61 		auto removeEntityTool = new class ITool
62 		{
63 			this() { name = "test.blockentity.block_entity"; }
64 
65 			bool placing;
66 			WorldBox selection;
67 			BlockWorldPos startingPos;
68 
69 			override void onUpdate() {
70 				auto cursor = worldInteraction.sideBlockPos;
71 				selection = worldBoxFromCorners(startingPos.xyz,
72 					cursor.xyz, cast(DimentionId)cursor.w);
73 				drawSelection();
74 			}
75 
76 			// remove
77 			override void onMainActionRelease() {
78 				if (placing) return;
79 				auto blockId = worldInteraction.pickBlock();
80 				if (isBlockEntity(blockId)) {
81 					connection.send(RemoveBlockEntityPacket(worldInteraction.blockPos.vector.arrayof));
82 				}
83 			}
84 
85 			// place
86 			override void onSecondaryActionPress() {
87 				placing = true;
88 				startingPos = worldInteraction.sideBlockPos;
89 			}
90 			override void onSecondaryActionRelease() {
91 				if (placing) {
92 					ulong sizeData = sizeToEntityData(selection.size);
93 					ulong payload = payloadFromIdAndEntityData(
94 						blockEntityMan.getId("multi"), sizeData);
95 					connection.send(PlaceBlockEntityPacket(selection, payload));
96 					placing = false;
97 				}
98 			}
99 
100 			void drawSelection() {
101 				if (placing) {
102 					graphics.debugBatch.putCube(vec3(selection.position) - cursorOffset,
103 						vec3(selection.size) + cursorOffset, Colors.blue, false);
104 				} else {
105 					if (!worldInteraction.cameraInSolidBlock)
106 					{
107 						worldInteraction.drawCursor(worldInteraction.sideBlockPos, Colors.blue);
108 					}
109 				}
110 			}
111 		};
112 
113 		auto editPlugin = pluginman.getPlugin!EditPlugin;
114 		editPlugin.registerTool(removeEntityTool);
115 	}
116 
117 	void onUpdateEvent(ref UpdateEvent event)
118 	{
119 		auto blockId = worldInteraction.pickBlock();
120 		auto cwp = ChunkWorldPos(worldInteraction.blockPos);
121 		igBegin("Debug");
122 		if (isBlockEntity(blockId))
123 		{
124 			ushort blockIndex = blockIndexFromBlockId(blockId);
125 			BlockEntityData entity = clientWorld.entityAccess.getBlockEntity(cwp, blockIndex);
126 			with(BlockEntityType) final switch(entity.type)
127 			{
128 				case localBlockEntity:
129 					BlockEntityInfo eInfo = blockEntityInfos[entity.id];
130 					auto entityBwp = BlockWorldPos(cwp, blockIndex);
131 					WorldBox eVol = eInfo.boxHandler(entityBwp, entity);
132 
133 					igTextf("Entity(main): ind %s: id %s %s %s",
134 						blockIndex, entity.id, eInfo.name, eVol);
135 					if (eInfo.debugHandler)
136 						eInfo.debugHandler(entityBwp, entity);
137 
138 					voxelman.world.storage.worldbox.putCube(graphics.debugBatch, eVol, Colors.red, false);
139 					break;
140 				case foreignBlockEntity:
141 					auto mainPtr = entity.mainChunkPointer;
142 
143 					auto mainCwp = ChunkWorldPos(ivec3(cwp.xyz) - mainPtr.mainChunkOffset, cwp.w);
144 					BlockEntityData mainEntity = clientWorld.entityAccess.getBlockEntity(mainCwp, mainPtr.blockIndex);
145 					auto mainBwp = BlockWorldPos(mainCwp, mainPtr.blockIndex);
146 
147 					BlockEntityInfo eInfo = blockEntityInfos[mainPtr.entityId];
148 					WorldBox eVol = eInfo.boxHandler(mainBwp, mainEntity);
149 
150 					igTextf("Entity(other): ind %s mid %s mind %s moff %s",
151 						blockIndex, mainPtr.entityId,
152 						mainPtr.blockIndex, mainPtr.mainChunkOffset);
153 					igTextf(" %s %s", eInfo.name, eVol);
154 
155 					voxelman.world.storage.worldbox.putCube(graphics.debugBatch, eVol, Colors.red, false);
156 					break;
157 				//case componentId:
158 				//	igTextf("Entity: @%s: entity id %s", blockIndex, entity.payload); break;
159 			}
160 		}
161 		else
162 		{
163 			igTextf("Block: %s %s", blockId, blockPlugin.getBlocks()[blockId].name);
164 		}
165 		igEnd();
166 	}
167 }
168 
169 final class BlockEntityServer : IPlugin {
170 	mixin IdAndSemverFrom!(voxelman.blockentity.plugininfo);
171 	mixin BlockEntityCommon;
172 }
173 
174 mixin template BlockEntityCommon()
175 {
176 	override void registerResourceManagers(void delegate(IResourceManager) reg) {
177 		blockEntityMan = new BlockEntityManager;
178 		blockEntityMan.regBlockEntity("unknown") // 0
179 			.boxHandler(&nullBoxHandler);
180 		blockEntityMan.regBlockEntity("multi")
181 			.boxHandler(&multichunkBoxHandler)
182 			.meshHandler(&multichunkMeshHandler);
183 			//.debugHandler(&multichunkDebugHandler);
184 		reg(blockEntityMan);
185 	}
186 	BlockEntityManager blockEntityMan;
187 
188 	BlockEntityInfoTable blockEntityInfos() {
189 		return BlockEntityInfoTable(cast(immutable)blockEntityMan.blockEntityMapping.infoArray);
190 	}
191 }
192 
193 void multichunkMeshHandler(
194 	Buffer!MeshVertex[] output,
195 	BlockEntityData data,
196 	ubyte[3] color,
197 	ubyte sides,
198 	//ivec3 worldPos,
199 	ivec3 chunkPos,
200 	ivec3 entityPos)
201 {
202 	static ubyte[3] mainColor = [60,0,0];
203 	static ubyte[3] otherColor = [0,0,60];
204 
205 	ubyte[3] col;
206 	if (data.type == BlockEntityType.localBlockEntity)
207 		col = mainColor;
208 	else
209 		col = otherColor;
210 
211 	makeColoredBlockMesh(output[Solidity.solid], col,
212 		cast(ubyte)chunkPos.x,
213 		cast(ubyte)chunkPos.y,
214 		cast(ubyte)chunkPos.z,
215 		sides);
216 }
217 
218 WorldBox multichunkBoxHandler(BlockWorldPos bwp, BlockEntityData data)
219 {
220 	ulong sizeData = data.entityData;
221 	ivec3 size = entityDataToSize(sizeData);
222 	return WorldBox(bwp.xyz, size, cast(ushort)bwp.w);
223 }
224 
225 void multichunkDebugHandler(BlockWorldPos bwp, BlockEntityData data)
226 {
227 	ulong sizeData = data.entityData;
228 	ivec3 size = entityDataToSize(sizeData);
229 	//auto vol = WorldBox(bwp.xyz, size, cast(ushort)bwp.w);
230 }
231 
232 struct BlockEntityInfoSetter
233 {
234 	private Mapping!(BlockEntityInfo)* mapping;
235 	private size_t blockId;
236 	private ref BlockEntityInfo info() {return (*mapping)[blockId]; }
237 
238 	ref BlockEntityInfoSetter color(ubyte[3] color ...) { info.color = color; return this; }
239 	ref BlockEntityInfoSetter colorHex(uint hex) { info.color = [(hex>>16)&0xFF,(hex>>8)&0xFF,hex&0xFF]; return this; }
240 	//ref BlockEntityInfoSetter isVisible(bool val) { info.isVisible = val; return this; }
241 	ref BlockEntityInfoSetter meshHandler(BlockEntityMeshhandler val) { info.meshHandler = val; return this; }
242 	ref BlockEntityInfoSetter sideSolidity(SolidityHandler val) { info.sideSolidity = val; return this; }
243 	ref BlockEntityInfoSetter boxHandler(EntityBoxHandler val) { info.boxHandler = val; return this; }
244 	ref BlockEntityInfoSetter debugHandler(EntityDebugHandler val) { info.debugHandler = val; return this; }
245 }
246 
247 final class BlockEntityManager : IResourceManager
248 {
249 private:
250 	Mapping!BlockEntityInfo blockEntityMapping;
251 
252 public:
253 	override string id() @property { return "voxelman.blockentity.blockentitymanager"; }
254 
255 	BlockEntityInfoSetter regBlockEntity(string name) {
256 		size_t id = blockEntityMapping.put(BlockEntityInfo(name));
257 		assert(id <= ushort.max);
258 		return BlockEntityInfoSetter(&blockEntityMapping, id);
259 	}
260 
261 	ushort getId(string name) {
262 		return cast(ushort)blockEntityMapping.id(name);
263 	}
264 }