1 /**
2 Copyright: Copyright (c) 2016-2018 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 voxelman.text.textformatter;
15 
16 import voxelman.world.block;
17 import voxelman.core.config;
18 import voxelman.core.events;
19 import voxelman.core.packets;
20 import voxelman.world.storage;
21 
22 import voxelman.block.plugin;
23 import voxelman.edit.plugin;
24 import voxelman.dbg.plugin;
25 import voxelman.graphics.plugin;
26 import voxelman.net.plugin;
27 import voxelman.world.clientworld;
28 import voxelman.world.serverworld;
29 import voxelman.worldinteraction.plugin;
30 
31 import voxelman.edit.tools.itool;
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 					worldInteraction.drawCursor(worldInteraction.sideBlockPos, Colors.blue);
104 				}
105 			}
106 		};
107 
108 		auto editPlugin = pluginman.getPlugin!EditPlugin;
109 		editPlugin.registerTool(entityTool);
110 	}
111 
112 	void showBlockInfo()
113 	{
114 		auto block = worldInteraction.pickBlock();
115 		auto bwp = worldInteraction.blockPos;
116 		auto cwp = ChunkWorldPos(bwp);
117 
118 		if (isBlockEntity(block.id))
119 		{
120 			ushort blockIndex = blockEntityIndexFromBlockId(block.id);
121 			BlockEntityData entity = clientWorld.entityAccess.getBlockEntity(cwp, blockIndex);
122 			with(BlockEntityType) final switch(entity.type)
123 			{
124 				case localBlockEntity:
125 					BlockEntityInfo eInfo = blockEntityInfos[entity.id];
126 					auto entityBwp = BlockWorldPos(cwp, blockIndex);
127 					WorldBox eVol = eInfo.boxHandler(entityBwp, entity);
128 
129 					//igTextf("Entity(main): id %s %s ind %s %s",
130 					//	entity.id, eInfo.name, blockIndex, eVol);
131 
132 					//igCheckbox("Debug entity", &blockEntityDebug);
133 					if (blockEntityDebug && eInfo.debugHandler)
134 					{
135 						auto context = BlockEntityDebugContext(entityBwp, entity, graphics);
136 						eInfo.debugHandler(context);
137 					}
138 
139 					putCube(graphics.debugBatch, eVol, Colors.red, false);
140 					break;
141 				case foreignBlockEntity:
142 					auto mainPtr = entity.mainChunkPointer;
143 
144 					auto mainCwp = ChunkWorldPos(ivec3(cwp.xyz) - mainPtr.mainChunkOffset, cwp.w);
145 					BlockEntityData mainEntity = clientWorld.entityAccess.getBlockEntity(mainCwp, mainPtr.blockIndex);
146 					auto mainBwp = BlockWorldPos(mainCwp, mainPtr.blockIndex);
147 
148 					BlockEntityInfo eInfo = blockEntityInfos[mainPtr.entityId];
149 					WorldBox eVol = eInfo.boxHandler(mainBwp, mainEntity);
150 
151 					//igTextf("Entity(other): ind %s mid %s mind %s moff %s",
152 					//	blockIndex, mainPtr.entityId,
153 					//	mainPtr.blockIndex, mainPtr.mainChunkOffset);
154 					//igTextf(" %s %s", eInfo.name, eVol);
155 
156 					putCube(graphics.debugBatch, eVol, Colors.red, false);
157 					break;
158 				//case componentId:
159 				//	igTextf("Entity: @%s: entity id %s", blockIndex, entity.payload); break;
160 			}
161 		}
162 		else
163 		{
164 			auto binfo = blockPlugin.getBlocks()[block.id];
165 			//igTextf("Block: %s:%s %s", block.id, block.metadata, binfo.name);
166 			//igTextf(" @ %s %s %s", bwp, cwp, BlockChunkPos(bwp));
167 		}
168 	}
169 }
170 
171 final class BlockEntityServer : IPlugin {
172 	mixin IdAndSemverFrom!"voxelman.blockentity.plugininfo";
173 	mixin BlockEntityCommon;
174 	auto dbKey = IoKey("voxelman.blockentity.plugin");
175 
176 	override void registerResources(IResourceManagerRegistry resmanRegistry) {
177 		auto ioman = resmanRegistry.getResourceManager!IoManager;
178 		ioman.registerWorldLoadSaveHandlers(&read, &write);
179 	}
180 
181 	void read(ref PluginDataLoader loader) {
182 		loader.readMapping(dbKey, blockEntityMan.blockEntityMapping);
183 	}
184 
185 	void write(ref PluginDataSaver saver) {
186 		saver.writeMapping(dbKey, blockEntityMan.blockEntityMapping);
187 	}
188 }
189 
190 mixin template BlockEntityCommon()
191 {
192 	override void registerResourceManagers(void delegate(IResourceManager) reg) {
193 		blockEntityMan = new BlockEntityManager;
194 		blockEntityMan.regBlockEntity("unknown") // 0
195 			.boxHandler(&nullBoxHandler);
196 		blockEntityMan.regBlockEntity("multi")
197 			.boxHandler(&multichunkBoxHandler)
198 			.meshHandler(&multichunkMeshHandler)
199 			.blockShapeHandler(&multichunkBlockShapeHandler);
200 			//.debugHandler(&multichunkDebugHandler);
201 		reg(blockEntityMan);
202 	}
203 	BlockEntityManager blockEntityMan;
204 
205 	BlockEntityInfoTable blockEntityInfos() {
206 		return BlockEntityInfoTable(cast(immutable)blockEntityMan.blockEntityMapping.infoArray);
207 	}
208 }
209 
210 void multichunkMeshHandler(BlockEntityMeshingData meshingData)
211 {
212 	static ubvec3 mainColor = ubvec3(60,0,0);
213 	static ubvec3 otherColor = ubvec3(0,0,60);
214 
215 	ubvec3 col;
216 	if (meshingData.data.type == BlockEntityType.localBlockEntity)
217 		col = mainColor;
218 	else
219 		col = otherColor;
220 
221 	auto blockMeshingData = BlockMeshingData(
222 				&meshingData.output[Solidity.solid],
223 				meshingData.occlusionHandler,
224 				col,
225 				[0,0],
226 				ubvec3(meshingData.chunkPos),
227 				meshingData.sides,
228 				meshingData.blockIndex);
229 	import voxelman.world.mesh.blockmeshers.full : makeColoredFullBlockMesh;
230 	makeColoredFullBlockMesh(blockMeshingData);
231 }
232 
233 WorldBox multichunkBoxHandler(BlockWorldPos bwp, BlockEntityData data)
234 {
235 	ulong sizeData = data.entityData;
236 	ivec3 size = entityDataToSize(sizeData);
237 	return WorldBox(bwp.xyz, size, cast(ushort)bwp.w);
238 }
239 
240 void multichunkDebugHandler(ref BlockEntityDebugContext context)
241 {
242 	ulong sizeData = context.data.entityData;
243 	ivec3 size = entityDataToSize(sizeData);
244 	//auto vol = WorldBox(bwp.xyz, size, cast(ushort)bwp.w);
245 }
246 
247 BlockShape multichunkBlockShapeHandler(ivec3, ivec3, BlockEntityData) {
248 	return fullShape;
249 }