1 /** 2 Copyright: Copyright (c) 2015-2017 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 module voxelman.entity.plugin; 8 9 import voxelman.log; 10 import std.array : empty; 11 12 import cbor; 13 import pluginlib; 14 import datadriven; 15 import voxelman.container.buffer; 16 import voxelman.core.events; 17 18 import voxelman.eventdispatcher.plugin; 19 import voxelman.net.plugin; 20 import voxelman.world.clientworld; 21 import voxelman.world.serverworld; 22 import voxelman.world.storage : IoManager, StringMap, IoKey, PluginDataLoader, PluginDataSaver, IoStorageType; 23 24 import voxelman.entity.entityobservermanager; 25 26 27 struct ProcessComponentsEvent { 28 float deltaTime; 29 } 30 31 struct ComponentSyncPacket 32 { 33 ubyte[] data; 34 } 35 36 struct ComponentSyncStartPacket {} 37 struct ComponentSyncEndPacket {} 38 39 /// Use EntityComponentRegistry to receive EntityManager pointer. 40 final class EntityComponentRegistry : IResourceManager 41 { 42 EntityManager* eman; 43 override string id() @property { return "voxelman.entity.componentregistry"; } 44 } 45 46 mixin template EntityPluginCommon() 47 { 48 private EntityComponentRegistry componentRegistry; 49 private EntityManager eman; 50 private EntityObserverManager entityObserverManager; 51 private EntityIdManager eidMan; 52 53 override void registerResourceManagers(void delegate(IResourceManager) registerHandler) 54 { 55 componentRegistry = new EntityComponentRegistry(); 56 eman.eidMan = &eidMan; 57 componentRegistry.eman = &eman; 58 registerHandler(componentRegistry); 59 } 60 } 61 62 immutable string componentMapKey = "voxelman.entity.componentmap"; 63 64 final class EntityPluginClient : IPlugin 65 { 66 mixin IdAndSemverFrom!"voxelman.entity.plugininfo"; 67 mixin EntityPluginCommon; 68 69 private EventDispatcherPlugin evDispatcher; 70 private NetClientPlugin connection; 71 private ClientWorld clientWorld; 72 73 override void init(IPluginManager pluginman) 74 { 75 evDispatcher = pluginman.getPlugin!EventDispatcherPlugin; 76 evDispatcher.subscribeToEvent(&onUpdateEvent); 77 clientWorld = pluginman.getPlugin!ClientWorld; 78 connection = pluginman.getPlugin!NetClientPlugin; 79 connection.registerPacket!ComponentSyncStartPacket(&handleComponentSyncStartPacket); 80 connection.registerPacket!ComponentSyncPacket(&handleComponentSyncPacket); 81 connection.registerPacket!ComponentSyncEndPacket(&handleComponentSyncEndPacket); 82 } 83 84 private void onUpdateEvent(ref UpdateEvent event) 85 { 86 evDispatcher.postEvent(ProcessComponentsEvent(event.deltaTime)); 87 } 88 89 private void handleComponentSyncStartPacket(ubyte[] packetData) 90 { 91 eman.removeSerializedComponents(IoStorageType.network); 92 } 93 94 private void handleComponentSyncPacket(ubyte[] packetData) 95 { 96 auto packet = unpackPacketNoDup!ComponentSyncPacket(packetData); 97 98 NetworkLoader netLoader; 99 netLoader.stringMap = &clientWorld.serverStrings; 100 101 ubyte[] data = packet.data; 102 while(!data.empty) 103 { 104 ubyte[4] _key = data[$-4..$]; 105 uint key = *cast(uint*)&_key; 106 uint entrySize = *cast(uint*)(data[$-4-4..$-4].ptr); 107 ubyte[] entry = data[$-4-4-entrySize..$-4-4]; 108 netLoader.ioKeyToData[key] = entry; 109 data = data[0..$-4-4-entrySize]; 110 } 111 112 enum bool clearComponents = false; 113 eman.load(netLoader, clearComponents); 114 netLoader.ioKeyToData.clear(); 115 } 116 117 private void handleComponentSyncEndPacket(ubyte[] packetData) 118 { 119 120 } 121 } 122 123 final class EntityPluginServer : IPlugin 124 { 125 mixin IdAndSemverFrom!"voxelman.entity.plugininfo"; 126 mixin EntityPluginCommon; 127 128 EventDispatcherPlugin evDispatcher; 129 NetServerPlugin connection; 130 EntityObserverManager entityObserverManager; 131 132 override void registerResources(IResourceManagerRegistry resmanRegistry) 133 { 134 auto ioman = resmanRegistry.getResourceManager!IoManager; 135 ioman.registerWorldLoadSaveHandlers(&load, &save); 136 entityObserverManager.netSaver.stringMap = ioman.getStringMap(); 137 entityObserverManager.eman = &eman; 138 } 139 140 override void init(IPluginManager pluginman) 141 { 142 evDispatcher = pluginman.getPlugin!EventDispatcherPlugin; 143 evDispatcher.subscribeToEvent(&onUpdateEvent); 144 evDispatcher.subscribeToEvent(&onPostUpdateEvent); 145 connection = pluginman.getPlugin!NetServerPlugin; 146 connection.registerPacket!ComponentSyncStartPacket(); 147 connection.registerPacket!ComponentSyncPacket(); 148 connection.registerPacket!ComponentSyncEndPacket(); 149 150 entityObserverManager.connection = connection; 151 auto world = pluginman.getPlugin!ServerWorld; 152 entityObserverManager.chunkObserverManager = world.chunkObserverManager; 153 } 154 155 private void onUpdateEvent(ref UpdateEvent event) 156 { 157 evDispatcher.postEvent(ProcessComponentsEvent(event.deltaTime)); 158 } 159 160 private void onPostUpdateEvent(ref PostUpdateEvent) 161 { 162 entityObserverManager.sendEntitiesToObservers(); 163 } 164 165 private void load(ref PluginDataLoader loader) 166 { 167 eman.eidMan.load(loader); 168 eman.load(loader); 169 } 170 171 private void save(ref PluginDataSaver saver) 172 { 173 eman.eidMan.save(saver); 174 eman.save(saver); 175 } 176 } 177 178 struct NetworkSaver 179 { 180 StringMap* stringMap; 181 package Buffer!ubyte buffer; 182 package size_t prevDataLength; 183 184 IoStorageType storageType() { return IoStorageType.network; } 185 186 Buffer!ubyte* beginWrite() { 187 prevDataLength = buffer.data.length; 188 return &buffer; 189 } 190 191 void endWrite(ref IoKey key) { 192 uint entrySize = cast(uint)(buffer.data.length - prevDataLength); 193 // dont write empty entries, since loader will return empty array for non-existing entries 194 if (entrySize == 0) return; 195 buffer.put(*cast(ubyte[4]*)&entrySize); 196 uint int_key = stringMap.get(key); 197 buffer.put(*cast(ubyte[4]*)&int_key); 198 } 199 200 void reset() { buffer.clear(); } 201 202 ubyte[] data() { return buffer.data; } 203 } 204 205 struct NetworkLoader 206 { 207 StringMap* stringMap; 208 ubyte[][uint] ioKeyToData; 209 210 IoStorageType storageType() { return IoStorageType.network; } 211 212 ubyte[] readEntryRaw(ref IoKey key) { 213 uint intKey = stringMap.get(key); 214 auto data = ioKeyToData.get(intKey, null); 215 return data; 216 } 217 }