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 }