1 /**
2 Copyright: Copyright (c) 2016-2017 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 voxelman.log;
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 
17 import voxelman.world.block;
18 import voxelman.core.config;
19 import voxelman.core.events;
20 import voxelman.core.packets;
21 import voxelman.world.storage;
22 
23 import voxelman.block.plugin;
24 import voxelman.edit.plugin;
25 import voxelman.dbg.plugin;
26 import voxelman.graphics.plugin;
27 import voxelman.net.plugin;
28 import voxelman.world.clientworld;
29 import voxelman.world.serverworld;
30 import voxelman.worldinteraction.plugin;
31 
32 import voxelman.blockentity.blockentityman;
33 import voxelman.world.blockentity;
34 
35 final class BlockEntityClient : IPlugin {
36 	mixin IdAndSemverFrom!"voxelman.blockentity.plugininfo";
37 	mixin BlockEntityCommon;
38 
39 	private ClientWorld clientWorld;
40 	private WorldInteractionPlugin worldInteraction;
41 	private BlockPluginClient blockPlugin;
42 	private GraphicsPlugin graphics;
43 	private NetClientPlugin connection;
44 
45 	bool blockEntityDebug = false;
46 
47 	override void init(IPluginManager pluginman)
48 	{
49 		clientWorld = pluginman.getPlugin!ClientWorld;
50 		worldInteraction = pluginman.getPlugin!WorldInteractionPlugin;
51 		blockPlugin = pluginman.getPlugin!BlockPluginClient;
52 		graphics = pluginman.getPlugin!GraphicsPlugin;
53 
54 		connection = pluginman.getPlugin!NetClientPlugin;
55 
56 		auto debugClient = pluginman.getPlugin!DebugClient;
57 		debugClient.registerDebugGuiHandler(&showBlockInfo, INFO_ORDER - 2, "BlockInfo");
58 
59 		auto entityTool = new class ITool
60 		{
61 			this() { name = "test.blockentity.block_entity"; }
62 
63 			bool placing;
64 			WorldBox selection;
65 			BlockWorldPos startingPos;
66 
67 			override void onUpdate() {
68 				auto cursor = worldInteraction.sideBlockPos;
69 				selection = worldBoxFromCorners(startingPos.xyz,
70 					cursor.xyz, cast(DimensionId)cursor.w);
71 				drawSelection();
72 			}
73 
74 			// remove
75 			override void onMainActionRelease() {
76 				if (placing) return;
77 				auto block = worldInteraction.pickBlock();
78 				if (isBlockEntity(block.id)) {
79 					connection.send(RemoveBlockEntityPacket(worldInteraction.blockPos.vector));
80 				}
81 			}
82 
83 			// place
84 			override void onSecondaryActionPress() {
85 				placing = true;
86 				startingPos = worldInteraction.sideBlockPos;
87 			}
88 			override void onSecondaryActionRelease() {
89 				if (placing) {
90 					ulong sizeData = sizeToEntityData(selection.size);
91 					ulong payload = payloadFromIdAndEntityData(
92 						blockEntityMan.getId("multi"), sizeData);
93 					connection.send(PlaceBlockEntityPacket(selection, payload));
94 					placing = false;
95 				}
96 			}
97 
98 			void drawSelection() {
99 				if (placing) {
100 					graphics.debugBatch.putCube(vec3(selection.position) - cursorOffset,
101 						vec3(selection.size) + cursorOffset, Colors.blue, false);
102 				} else {
103 					if (!worldInteraction.cameraInSolidBlock)
104 					{
105 						worldInteraction.drawCursor(worldInteraction.sideBlockPos, Colors.blue);
106 					}
107 				}
108 			}
109 		};
110 
111 		auto editPlugin = pluginman.getPlugin!EditPlugin;
112 		editPlugin.registerTool(entityTool);
113 	}
114 
115 	void showBlockInfo()
116 	{
117 		auto block = worldInteraction.pickBlock();
118 		auto bwp = worldInteraction.blockPos;
119 		auto cwp = ChunkWorldPos(bwp);
120 
121 		if (isBlockEntity(block.id))
122 		{
123 			ushort blockIndex = blockEntityIndexFromBlockId(block.id);
124 			BlockEntityData entity = clientWorld.entityAccess.getBlockEntity(cwp, blockIndex);
125 			with(BlockEntityType) final switch(entity.type)
126 			{
127 				case localBlockEntity:
128 					BlockEntityInfo eInfo = blockEntityInfos[entity.id];
129 					auto entityBwp = BlockWorldPos(cwp, blockIndex);
130 					WorldBox eVol = eInfo.boxHandler(entityBwp, entity);
131 
132 					igTextf("Entity(main): id %s %s ind %s %s",
133 						entity.id, eInfo.name, blockIndex, eVol);
134 
135 					igCheckbox("Debug entity", &blockEntityDebug);
136 					if (blockEntityDebug && eInfo.debugHandler)
137 					{
138 						auto context = BlockEntityDebugContext(entityBwp, entity, graphics);
139 						eInfo.debugHandler(context);
140 					}
141 
142 					putCube(graphics.debugBatch, eVol, Colors.red, false);
143 					break;
144 				case foreignBlockEntity:
145 					auto mainPtr = entity.mainChunkPointer;
146 
147 					auto mainCwp = ChunkWorldPos(ivec3(cwp.xyz) - mainPtr.mainChunkOffset, cwp.w);
148 					BlockEntityData mainEntity = clientWorld.entityAccess.getBlockEntity(mainCwp, mainPtr.blockIndex);
149 					auto mainBwp = BlockWorldPos(mainCwp, mainPtr.blockIndex);
150 
151 					BlockEntityInfo eInfo = blockEntityInfos[mainPtr.entityId];
152 					WorldBox eVol = eInfo.boxHandler(mainBwp, mainEntity);
153 
154 					igTextf("Entity(other): ind %s mid %s mind %s moff %s",
155 						blockIndex, mainPtr.entityId,
156 						mainPtr.blockIndex, mainPtr.mainChunkOffset);
157 					igTextf(" %s %s", eInfo.name, eVol);
158 
159 					putCube(graphics.debugBatch, eVol, Colors.red, false);
160 					break;
161 				//case componentId:
162 				//	igTextf("Entity: @%s: entity id %s", blockIndex, entity.payload); break;
163 			}
164 		}
165 		else
166 		{
167 			auto binfo = blockPlugin.getBlocks()[block.id];
168 			igTextf("Block: %s:%s %s", block.id, block.metadata, binfo.name);
169 		}
170 	}
171 }
172 
173 final class BlockEntityServer : IPlugin {
174 	mixin IdAndSemverFrom!"voxelman.blockentity.plugininfo";
175 	mixin BlockEntityCommon;
176 	auto dbKey = IoKey("voxelman.blockentity.plugin");
177 
178 	override void registerResources(IResourceManagerRegistry resmanRegistry) {
179 		auto ioman = resmanRegistry.getResourceManager!IoManager;
180 		ioman.registerWorldLoadSaveHandlers(&read, &write);
181 	}
182 
183 	void read(ref PluginDataLoader loader) {
184 		loader.readMapping(dbKey, blockEntityMan.blockEntityMapping);
185 	}
186 
187 	void write(ref PluginDataSaver saver) {
188 		saver.writeMapping(dbKey, blockEntityMan.blockEntityMapping);
189 	}
190 }
191 
192 mixin template BlockEntityCommon()
193 {
194 	override void registerResourceManagers(void delegate(IResourceManager) reg) {
195 		blockEntityMan = new BlockEntityManager;
196 		blockEntityMan.regBlockEntity("unknown") // 0
197 			.boxHandler(&nullBoxHandler);
198 		blockEntityMan.regBlockEntity("multi")
199 			.boxHandler(&multichunkBoxHandler)
200 			.meshHandler(&multichunkMeshHandler)
201 			.blockShapeHandler(&multichunkBlockShapeHandler);
202 			//.debugHandler(&multichunkDebugHandler);
203 		reg(blockEntityMan);
204 	}
205 	BlockEntityManager blockEntityMan;
206 
207 	BlockEntityInfoTable blockEntityInfos() {
208 		return BlockEntityInfoTable(cast(immutable)blockEntityMan.blockEntityMapping.infoArray);
209 	}
210 }
211 
212 void multichunkMeshHandler(BlockEntityMeshingData meshingData)
213 {
214 	static ubvec3 mainColor = ubvec3(60,0,0);
215 	static ubvec3 otherColor = ubvec3(0,0,60);
216 
217 	ubvec3 col;
218 	if (meshingData.data.type == BlockEntityType.localBlockEntity)
219 		col = mainColor;
220 	else
221 		col = otherColor;
222 
223 	auto blockMeshingData = BlockMeshingData(
224 				&meshingData.output[Solidity.solid],
225 				meshingData.occlusionHandler,
226 				col,
227 				ubvec3(meshingData.chunkPos),
228 				meshingData.sides,
229 				meshingData.blockIndex);
230 	import voxelman.world.mesh.blockmeshers.full : makeColoredFullBlockMesh;
231 	makeColoredFullBlockMesh(blockMeshingData);
232 }
233 
234 WorldBox multichunkBoxHandler(BlockWorldPos bwp, BlockEntityData data)
235 {
236 	ulong sizeData = data.entityData;
237 	ivec3 size = entityDataToSize(sizeData);
238 	return WorldBox(bwp.xyz, size, cast(ushort)bwp.w);
239 }
240 
241 void multichunkDebugHandler(ref BlockEntityDebugContext context)
242 {
243 	ulong sizeData = context.data.entityData;
244 	ivec3 size = entityDataToSize(sizeData);
245 	//auto vol = WorldBox(bwp.xyz, size, cast(ushort)bwp.w);
246 }
247 
248 BlockShape multichunkBlockShapeHandler(ivec3, ivec3, BlockEntityData) {
249 	return fullShape;
250 }