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 railroad.plugin; 7 8 import datadriven; 9 import pluginlib; 10 import voxelman.core.config; 11 import voxelman.core.events; 12 import voxelman.core.packets; 13 import voxelman.log; 14 15 import voxelman.blockentity.blockentityman; 16 import voxelman.command.plugin; 17 import voxelman.blockentity.plugin; 18 import voxelman.edit.plugin; 19 import voxelman.entity.plugin; 20 import voxelman.eventdispatcher.plugin; 21 import voxelman.graphics.plugin; 22 import voxelman.net.plugin; 23 import voxelman.world.clientworld; 24 import voxelman.world.serverworld; 25 import voxelman.world.storage; 26 import voxelman.worldinteraction.plugin; 27 28 import voxelman.world.block; 29 import voxelman.math; 30 import voxelman.geometry; 31 32 import voxelman.world.blockentity; 33 34 import railroad.rail.mesh; 35 import railroad.rail.utils; 36 import railroad.rail.railtool; 37 import railroad.rail.railgraph; 38 import railroad.rail.packets; 39 40 import railroad.wagon.wagontool; 41 import railroad.wagon.packets; 42 import railroad.wagon.wagon; 43 44 45 final class RailroadPluginClient : IPlugin 46 { 47 mixin IdAndSemverFrom!"railroad.plugininfo"; 48 mixin RailroadPluginCommon; 49 50 ClientWorld clientWorld; 51 NetClientPlugin connection; 52 WorldInteractionPlugin worldInteraction; 53 GraphicsPlugin graphics; 54 55 Batch batch; 56 EntityManager* eman; 57 58 override void registerResources(IResourceManagerRegistry resmanRegistry) { 59 retreiveBlockEntityManager(resmanRegistry); 60 auto components = resmanRegistry.getResourceManager!EntityComponentRegistry; 61 eman = components.eman; 62 eman.registerComponent!WagonClientComponent(); 63 } 64 65 override void preInit() { 66 import voxelman.globalconfig; 67 import voxelman.model.obj; 68 import voxelman.model.ply; 69 import voxelman.model.mesh; 70 import voxelman.model.utils; 71 import voxelman.world.mesh.chunkmesh; 72 try{ 73 railMeshes[0] = readPlyFile!RailVertexT(BUILD_TO_ROOT_PATH~"res/model/rail1.ply"); 74 railMeshes[1] = readPlyFile!RailVertexT(BUILD_TO_ROOT_PATH~"res/model/rail2.ply"); 75 railMeshes[2] = readPlyFile!RailVertexT(BUILD_TO_ROOT_PATH~"res/model/rail3.ply"); 76 } 77 catch(Exception e){ 78 warningf("Error reading model, %s", e); 79 } 80 } 81 82 override void init(IPluginManager pluginman) 83 { 84 worldInteraction = pluginman.getPlugin!WorldInteractionPlugin; 85 clientWorld = pluginman.getPlugin!ClientWorld; 86 graphics = pluginman.getPlugin!GraphicsPlugin; 87 88 connection = pluginman.getPlugin!NetClientPlugin; 89 connection.registerPacket!PlaceRailPacket; 90 connection.registerPacket!EditRailLinePacket; 91 connection.registerPacket!CreateWagonPacket; 92 93 auto railTool = new RailTool(clientWorld, blockEntityManager, 94 connection, worldInteraction); 95 96 auto wagonTool = new WagonTool(clientWorld, blockEntityManager, 97 connection, worldInteraction, graphics); 98 99 auto editPlugin = pluginman.getPlugin!EditPlugin; 100 editPlugin.registerTool(railTool); 101 editPlugin.registerTool(wagonTool); 102 103 auto evDispatcher = pluginman.getPlugin!EventDispatcherPlugin; 104 evDispatcher.subscribeToEvent(&drawEntities); 105 } 106 107 void drawEntities(ref RenderSolid3dEvent event) 108 { 109 batch.reset(); 110 auto query = eman.query!WagonClientComponent; 111 foreach (id, wagonClient; query) 112 { 113 if (wagonClient.dimension == clientWorld.currentDimension) 114 { 115 batch.putCube(wagonClient.dimPos - vec3(0.5,0.5,0.5), vec3(1,1,1), Colors.black, true); 116 } 117 } 118 graphics.draw(batch); 119 } 120 } 121 122 final class RailroadPluginServer : IPlugin 123 { 124 mixin IdAndSemverFrom!"railroad.plugininfo"; 125 mixin RailroadPluginCommon; 126 127 WagonLogicServer wagonLogic; 128 129 NetServerPlugin connection; 130 ServerWorld serverWorld; 131 BlockEntityServer blockEntityPlugin; 132 RailGraph railGraph; 133 134 override void registerResources(IResourceManagerRegistry resmanRegistry) 135 { 136 wagonLogic.registerResources(resmanRegistry); 137 auto ioman = resmanRegistry.getResourceManager!IoManager; 138 ioman.registerWorldLoadSaveHandlers(&railGraph.read, &railGraph.write); 139 retreiveBlockEntityManager(resmanRegistry); 140 } 141 142 override void init(IPluginManager pluginman) 143 { 144 connection = pluginman.getPlugin!NetServerPlugin; 145 connection.registerPacket!PlaceRailPacket(&handlePlaceRailPacket); 146 connection.registerPacket!EditRailLinePacket(&handleEditRailLinePacket); 147 connection.registerPacket!CreateWagonPacket(&wagonLogic.handleCreateWagonPacket); 148 149 serverWorld = pluginman.getPlugin!ServerWorld; 150 blockEntityPlugin = pluginman.getPlugin!BlockEntityServer; 151 152 wagonLogic.entityPlugin = pluginman.getPlugin!EntityPluginServer; 153 wagonLogic.railGraph = &railGraph; 154 155 auto evDispatcher = pluginman.getPlugin!EventDispatcherPlugin; 156 evDispatcher.subscribeToEvent(&wagonLogic.process); 157 158 auto command = pluginman.getPlugin!CommandPluginServer; 159 command.registerCommand(CommandInfo("remove_wagons", &handleRemoveWagons, null, "Removes all wagons from the world")); 160 } 161 162 private void handleRemoveWagons(CommandParams params) 163 { 164 wagonLogic.removeWagons; 165 } 166 167 void handlePlaceRailPacket(ubyte[] packetData, SessionId sessionId) 168 { 169 auto packet = unpackPacket!PlaceRailPacket(packetData); 170 RailPos railPos = packet.pos; 171 RailData railData = RailData(packet.data); 172 editRail(railPos, railData); 173 } 174 175 void handleEditRailLinePacket(ubyte[] packetData, SessionId sessionId) 176 { 177 auto packet = unpackPacket!EditRailLinePacket(packetData); 178 final switch(packet.orientation) { 179 case RailOrientation.x: 180 RailData railData = RailData(RailSegment.xpos); 181 foreach(dx; 0..packet.length) 182 { 183 RailPos railPos = packet.from; 184 railPos.x += dx; 185 editRail(railPos, railData, packet.editOp); 186 } 187 break; 188 case RailOrientation.z: 189 RailData railData = RailData(RailSegment.zneg); 190 foreach(dz; 0..packet.length) 191 { 192 RailPos railPos = packet.from; 193 railPos.z += dz; 194 editRail(railPos, railData, packet.editOp); 195 } 196 break; 197 case RailOrientation.xzSameSign: 198 bool topSide = packet.diagonalRailSide == DiagonalRailSide.zneg; 199 RailSegment[2] sides = [RailSegment.xposZpos, RailSegment.xnegZneg]; 200 ivec2[2] increments = [ivec2(1, 0), ivec2(0, -1)]; 201 placeDiagonalRail(topSide, packet.from, sides, increments, packet.length, packet.editOp); 202 break; 203 case RailOrientation.xzOppSign: 204 bool topSide = packet.diagonalRailSide == DiagonalRailSide.zneg; 205 RailSegment[2] sides = [RailSegment.xnegZpos, RailSegment.xposZneg]; 206 ivec2[2] increments = [ivec2(0, 1), ivec2(1, 0)]; 207 placeDiagonalRail(topSide, packet.from, sides, increments, packet.length, packet.editOp); 208 break; 209 } 210 } 211 212 private void placeDiagonalRail(bool topSide, RailPos railPos, RailSegment[2] sides, ivec2[2] increments, size_t length, RailEditOp editOp) 213 { 214 foreach(i; 0..length) 215 { 216 RailData railData = RailData(sides[cast(size_t)topSide]); 217 editRail(railPos, railData, editOp); 218 219 auto inc = increments[cast(size_t)topSide]; 220 railPos.x += inc.x; 221 railPos.z += inc.y; 222 topSide = !topSide; 223 } 224 } 225 226 void editRail(const RailPos railPos, const RailData railData, RailEditOp editOp = RailEditOp.add) 227 { 228 ChunkWorldPos cwp = railPos.chunkPos(); 229 ushort railEntityId = blockEntityManager.getId("rail"); 230 231 RailData railOnGround = getRailAt(railPos, railEntityId, 232 serverWorld.worldAccess, serverWorld.entityAccess); 233 234 RailData edited = railOnGround; 235 edited.editRail(railData, editOp); 236 237 // Adding existing rail segment / removing non-existing 238 if (railOnGround == edited) return; 239 240 railGraph.onRailEdit(railPos, railData, editOp); 241 242 if (!railOnGround.empty) 243 { 244 BlockWorldPos delPos = railPos.deletePos; 245 WorldBox changedBox = removeEntity(delPos, blockEntityPlugin.blockEntityInfos, 246 serverWorld.worldAccess, serverWorld.entityAccess, BlockId(1)); 247 connection.sendTo(serverWorld.chunkObserverManager.getChunkObservers(cwp), 248 RemoveBlockEntityPacket(delPos.vector)); 249 } 250 251 if (edited.empty) return; 252 253 WorldBox blockBox = edited.boundingBox(railPos); 254 ulong payload = payloadFromIdAndEntityData(railEntityId, edited.data); 255 256 placeEntity(blockBox, payload, serverWorld.worldAccess, serverWorld.entityAccess); 257 258 connection.sendTo(serverWorld.chunkObserverManager.getChunkObservers(cwp), 259 PlaceBlockEntityPacket(blockBox, payload)); 260 } 261 } 262 263 mixin template RailroadPluginCommon() 264 { 265 BlockEntityManager blockEntityManager; 266 void retreiveBlockEntityManager(IResourceManagerRegistry resmanRegistry) 267 { 268 blockEntityManager = resmanRegistry.getResourceManager!BlockEntityManager; 269 blockEntityManager.regBlockEntity("rail") 270 .boxHandler(&railBoxHandler) 271 .meshHandler(&makeRailMesh) 272 .color([128, 128, 128]) 273 .blockShapeHandler(&railBlockShapeHandler) 274 .sideSolidity(&railSideSolidity) 275 .debugHandler(&railDebugHandler); 276 } 277 } 278 279 WorldBox railBoxHandler(BlockWorldPos bwp, BlockEntityData data) 280 { 281 return RailData(data).boundingBox(bwp); 282 } 283 284 Solidity railSideSolidity(CubeSide side, ivec3 chunkPos, ivec3 entityPos, BlockEntityData data) 285 { 286 if (side == CubeSide.yneg) 287 { 288 return RailData(data).bottomSolidity(calcBlockTilePos(chunkPos)); 289 } 290 return Solidity.transparent; 291 } 292 293 BlockShape railBlockShapeHandler(ivec3 chunkPos, ivec3 entityPos, BlockEntityData data) 294 { 295 auto railData = RailData(data); 296 if (railData.bottomSolidity(calcBlockTilePos(chunkPos))) 297 { 298 if (railData.isSlope) 299 return railSlopeShapes[railData.data - SLOPE_RAIL_BIT]; 300 else 301 return railBlockShape; 302 } 303 else 304 return emptyShape; 305 } 306 307 const ShapeSideMask[6] railShapeSides = [ 308 ShapeSideMask.empty, 309 ShapeSideMask.empty, 310 ShapeSideMask.empty, 311 ShapeSideMask.empty, 312 ShapeSideMask.empty, 313 ShapeSideMask.full]; // bottom is full 314 315 const BlockShape railBlockShape = BlockShape(railShapeSides, 0b_0000_1111, true, true); 316 const BlockShape[4] railSlopeShapes = [ 317 BlockShape([ // zneg 318 ShapeSideMask.full, ShapeSideMask.empty, ShapeSideMask.empty, 319 ShapeSideMask.empty, ShapeSideMask.empty, ShapeSideMask.full], 320 0b_0011_1111, true, true), 321 BlockShape([ // xneg 322 ShapeSideMask.empty, ShapeSideMask.empty, ShapeSideMask.empty, 323 ShapeSideMask.full, ShapeSideMask.empty, ShapeSideMask.full], 324 0b_0101_1111, true, true), 325 BlockShape([ // zpos 326 ShapeSideMask.empty, ShapeSideMask.full, ShapeSideMask.empty, 327 ShapeSideMask.empty, ShapeSideMask.empty, ShapeSideMask.full], 328 0b_1100_1111, true, true), 329 BlockShape([ // xpos 330 ShapeSideMask.empty, ShapeSideMask.empty, ShapeSideMask.full, 331 ShapeSideMask.empty, ShapeSideMask.empty, ShapeSideMask.full], 332 0b_1010_1111, true, true)];