1 /** 2 Copyright: Copyright (c) 2015-2016 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module voxelman.world.plugin; 7 8 import netlib; 9 import pluginlib; 10 11 import voxelman.core.config : BlockType; 12 import voxelman.core.events : PreUpdateEvent, PostUpdateEvent, GameStopEvent; 13 import voxelman.core.blockman; 14 15 import voxelman.config.configmanager : ConfigOption, ConfigManager; 16 import voxelman.eventdispatcher.plugin : EventDispatcherPlugin; 17 import voxelman.net.plugin : NetServerPlugin; 18 19 import voxelman.storage.chunk; 20 import voxelman.storage.chunkmanager; 21 import voxelman.storage.chunkobservermanager; 22 import voxelman.storage.chunkprovider; 23 import voxelman.storage.chunkstorage; 24 import voxelman.storage.coordinates; 25 import voxelman.storage.volume; 26 import voxelman.storage.world; 27 28 shared static this() 29 { 30 //pluginRegistry.regClientPlugin(new ClientWorld); 31 pluginRegistry.regServerPlugin(new ServerWorld); 32 } 33 34 final class WorldAccess { 35 private ChunkManager* chunkManager; 36 37 this(ChunkManager* chunkManager) { 38 this.chunkManager = chunkManager; 39 } 40 41 bool setBlock(BlockWorldPos bwp, BlockType blockId) { 42 auto blockIndex = BlockChunkIndex(bwp); 43 auto chunkPos = ChunkWorldPos(bwp); 44 BlockType[] blocks = chunkManager.getWriteBuffer(chunkPos); 45 if (blocks is null) 46 return false; 47 blocks[blockIndex] = blockId; 48 49 import std.range : only; 50 chunkManager.onBlockChanges(chunkPos, only(BlockChange(blockIndex.index, blockId))); 51 return true; 52 } 53 54 BlockType getBlock(BlockWorldPos bwp) { 55 auto blockIndex = BlockChunkIndex(bwp); 56 auto chunkPos = ChunkWorldPos(bwp); 57 auto snap = chunkManager.getChunkSnapshot(chunkPos); 58 if (!snap.isNull) { 59 return snap.blockData.getBlockType(blockIndex); 60 } 61 return 0; 62 } 63 64 bool isFree(BlockWorldPos bwp) { 65 return getBlock(bwp) < 2; // air or unknown 66 } 67 } 68 69 final class ServerWorld : IPlugin 70 { 71 private: 72 EventDispatcherPlugin evDispatcher; 73 NetServerPlugin connection; 74 75 ConfigOption saveDirOpt; 76 ConfigOption worldNameOpt; 77 ConfigOption numWorkersOpt; 78 79 public: 80 ChunkManager chunkManager; 81 ChunkProvider chunkProvider; 82 ChunkObserverManager chunkObserverManager; 83 84 World world; 85 WorldAccess worldAccess; 86 BlockMan blockMan; 87 88 mixin IdAndSemverFrom!(voxelman.world.plugininfo); 89 90 override void registerResourceManagers(void delegate(IResourceManager) registerHandler) {} 91 92 override void registerResources(IResourceManagerRegistry resmanRegistry) 93 { 94 ConfigManager config = resmanRegistry.getResourceManager!ConfigManager; 95 saveDirOpt = config.registerOption!string("save_dir", "../../saves"); 96 worldNameOpt = config.registerOption!string("world_name", "world"); 97 numWorkersOpt = config.registerOption!uint("num_workers", 4); 98 } 99 100 override void preInit() 101 { 102 chunkManager = new ChunkManager(); 103 worldAccess = new WorldAccess(&chunkManager); 104 chunkObserverManager = new ChunkObserverManager(); 105 106 // Component connections 107 chunkManager.loadChunkHandler = &chunkProvider.loadChunk; 108 chunkManager.saveChunkHandler = &chunkProvider.saveChunk; 109 110 chunkProvider.onChunkLoadedHandlers ~= &chunkManager.onSnapshotLoaded; 111 chunkProvider.onChunkSavedHandlers ~= &chunkManager.onSnapshotSaved; 112 113 chunkObserverManager.changeChunkNumObservers = &chunkManager.setExternalChunkUsers; 114 chunkObserverManager.chunkObserverAdded = &onChunkObserverAdded; 115 chunkObserverManager.loadQueueSpaceAvaliable = &chunkProvider.loadQueueSpaceAvaliable; 116 117 chunkManager.onChunkLoadedHandlers ~= &onChunkLoaded; 118 chunkManager.chunkChangesHandlers ~= &sendChanges; 119 120 blockMan.loadBlockTypes(); 121 auto worldDir = saveDirOpt.get!string ~ "/" ~ worldNameOpt.get!string; 122 chunkProvider.init(worldDir, numWorkersOpt.get!uint); 123 world.init(worldDir); 124 world.load(); 125 } 126 127 override void init(IPluginManager pluginman) 128 { 129 evDispatcher = pluginman.getPlugin!EventDispatcherPlugin; 130 evDispatcher.subscribeToEvent(&handlePreUpdateEvent); 131 evDispatcher.subscribeToEvent(&handlePostUpdateEvent); 132 evDispatcher.subscribeToEvent(&handleStopEvent); 133 134 import voxelman.core.packets : PlaceBlockPacket; 135 connection = pluginman.getPlugin!NetServerPlugin; 136 connection.registerPacketHandler!PlaceBlockPacket(&handlePlaceBlockPacket); 137 } 138 139 override void postInit() {} 140 141 void handlePreUpdateEvent(ref PreUpdateEvent event) 142 { 143 chunkProvider.update(); 144 chunkObserverManager.update(); 145 world.update(); 146 } 147 148 void handlePostUpdateEvent(ref PostUpdateEvent event) 149 { 150 chunkManager.commitSnapshots(world.currentTimestamp); 151 chunkManager.sendChanges(); 152 } 153 154 void handleStopEvent(ref GameStopEvent event) 155 { 156 chunkProvider.stop(); 157 world.save(); 158 } 159 160 void onChunkObserverAdded(ChunkWorldPos cwp, ClientId clientId) 161 { 162 import voxelman.core.packets : ChunkDataPacket; 163 auto snap = chunkManager.getChunkSnapshot(cwp); 164 if (!snap.isNull) { 165 connection.sendTo(clientId, ChunkDataPacket(cwp.vector, snap.blockData)); 166 } 167 } 168 169 void onChunkLoaded(ChunkWorldPos cwp, BlockDataSnapshot snap) 170 { 171 import voxelman.core.packets : ChunkDataPacket; 172 connection.sendTo( 173 chunkObserverManager.getChunkObservers(cwp), 174 ChunkDataPacket(cwp.vector, snap.blockData)); 175 } 176 177 void sendChanges(BlockChange[][ChunkWorldPos] changes) 178 { 179 import voxelman.core.packets : MultiblockChangePacket; 180 foreach(pair; changes.byKeyValue) 181 { 182 connection.sendTo( 183 chunkObserverManager.getChunkObservers(pair.key), 184 MultiblockChangePacket(pair.key.vector, pair.value)); 185 } 186 } 187 188 void handlePlaceBlockPacket(ubyte[] packetData, ClientId clientId) 189 { 190 import voxelman.core.packets : PlaceBlockPacket; 191 //if (serverPlugin.isLoggedIn(clientId)) 192 { 193 auto packet = unpackPacket!PlaceBlockPacket(packetData); 194 worldAccess.setBlock(BlockWorldPos(packet.blockPos), packet.blockType); 195 } 196 } 197 }