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 }