1 /** 2 Copyright: Copyright (c) 2015-2018 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 EntityIdManager eidMan; 51 52 override void registerResourceManagers(void delegate(IResourceManager) registerHandler) 53 { 54 componentRegistry = new EntityComponentRegistry(); 55 eman.eidMan = &eidMan; 56 componentRegistry.eman = &eman; 57 registerHandler(componentRegistry); 58 } 59 } 60 61 immutable string componentMapKey = "voxelman.entity.componentmap"; 62 63 final class EntityPluginClient : IPlugin 64 { 65 mixin IdAndSemverFrom!"voxelman.entity.plugininfo"; 66 mixin EntityPluginCommon; 67 68 private EventDispatcherPlugin evDispatcher; 69 private NetClientPlugin connection; 70 private ClientWorld clientWorld; 71 72 override void init(IPluginManager pluginman) 73 { 74 evDispatcher = pluginman.getPlugin!EventDispatcherPlugin; 75 evDispatcher.subscribeToEvent(&onUpdateEvent); 76 clientWorld = pluginman.getPlugin!ClientWorld; 77 connection = pluginman.getPlugin!NetClientPlugin; 78 connection.registerPacket!ComponentSyncStartPacket(&handleComponentSyncStartPacket); 79 connection.registerPacket!ComponentSyncPacket(&handleComponentSyncPacket); 80 connection.registerPacket!ComponentSyncEndPacket(&handleComponentSyncEndPacket); 81 } 82 83 private void onUpdateEvent(ref UpdateEvent event) 84 { 85 evDispatcher.postEvent(ProcessComponentsEvent(event.deltaTime)); 86 } 87 88 private void handleComponentSyncStartPacket(ubyte[] packetData) 89 { 90 eman.removeSerializedComponents(IoStorageType.network); 91 } 92 93 private void handleComponentSyncPacket(ubyte[] packetData) 94 { 95 auto packet = unpackPacketNoDup!ComponentSyncPacket(packetData); 96 97 NetworkLoader netLoader; 98 netLoader.stringMap = &clientWorld.serverStrings; 99 netLoader.parseSavedData(packet.data); 100 enum bool clearComponents = false; 101 eman.load(netLoader, clearComponents); 102 netLoader.ioKeyToData.clear(); 103 } 104 105 private void handleComponentSyncEndPacket(ubyte[] packetData) 106 {} 107 } 108 109 final class EntityPluginServer : IPlugin 110 { 111 mixin IdAndSemverFrom!"voxelman.entity.plugininfo"; 112 mixin EntityPluginCommon; 113 114 EventDispatcherPlugin evDispatcher; 115 NetServerPlugin connection; 116 EntityObserverManager entityObserverManager; 117 StringMap* stringMap; 118 119 override void registerResources(IResourceManagerRegistry resmanRegistry) 120 { 121 auto ioman = resmanRegistry.getResourceManager!IoManager; 122 ioman.registerWorldLoadSaveHandlers(&load, &save); 123 stringMap = ioman.getStringMap(); 124 entityObserverManager.netSaver.stringMap = stringMap; 125 entityObserverManager.eman = &eman; 126 } 127 128 override void init(IPluginManager pluginman) 129 { 130 evDispatcher = pluginman.getPlugin!EventDispatcherPlugin; 131 evDispatcher.subscribeToEvent(&onUpdateEvent); 132 evDispatcher.subscribeToEvent(&onPostUpdateEvent); 133 connection = pluginman.getPlugin!NetServerPlugin; 134 connection.registerPacket!ComponentSyncStartPacket(); 135 connection.registerPacket!ComponentSyncPacket(); 136 connection.registerPacket!ComponentSyncEndPacket(); 137 138 entityObserverManager.connection = connection; 139 auto world = pluginman.getPlugin!ServerWorld; 140 entityObserverManager.chunkObserverManager = world.chunkObserverManager; 141 } 142 143 override void postInit() 144 { 145 // force stringMap sync 146 foreach(ref ioKey; eman.getIoKeys) 147 { 148 stringMap.get(ioKey); 149 } 150 } 151 152 private void onUpdateEvent(ref UpdateEvent event) 153 { 154 evDispatcher.postEvent(ProcessComponentsEvent(event.deltaTime)); 155 } 156 157 private void onPostUpdateEvent(ref PostUpdateEvent) 158 { 159 entityObserverManager.sendEntitiesToObservers(); 160 } 161 162 private void load(ref PluginDataLoader loader) 163 { 164 eman.eidMan.load(loader); 165 eman.load(loader); 166 } 167 168 private void save(ref PluginDataSaver saver) 169 { 170 eman.eidMan.save(saver); 171 eman.save(saver); 172 } 173 } 174 175 struct NetworkSaver 176 { 177 StringMap* stringMap; 178 package Buffer!ubyte buffer; 179 package size_t prevDataLength; 180 181 IoStorageType storageType() { return IoStorageType.network; } 182 183 Buffer!ubyte* beginWrite() { 184 prevDataLength = buffer.data.length; 185 return &buffer; 186 } 187 188 void endWrite(ref IoKey key) { 189 uint entrySize = cast(uint)(buffer.data.length - prevDataLength); 190 // dont write empty entries, since loader will return empty array for non-existing entries 191 if (entrySize == 0) return; 192 buffer.put(*cast(ubyte[4]*)&entrySize); 193 uint int_key = stringMap.get(key); 194 buffer.put(*cast(ubyte[4]*)&int_key); 195 } 196 197 void reset() { buffer.clear(); } 198 199 ubyte[] data() { return buffer.data; } 200 } 201 202 struct NetworkLoader 203 { 204 StringMap* stringMap; 205 ubyte[][uint] ioKeyToData; 206 207 IoStorageType storageType() { return IoStorageType.network; } 208 209 ubyte[] readEntryRaw(ref IoKey key) { 210 uint intKey = stringMap.get(key); 211 auto data = ioKeyToData.get(intKey, null); 212 return data; 213 } 214 215 void parseSavedData(ubyte[] data) { 216 while(!data.empty) 217 { 218 ubyte[4] _key = data[$-4..$]; 219 uint key = *cast(uint*)&_key; 220 uint entrySize = *cast(uint*)(data[$-4-4..$-4].ptr); 221 ubyte[] entry = data[$-4-4-entrySize..$-4-4]; 222 ioKeyToData[key] = entry; 223 data = data[0..$-4-4-entrySize]; 224 } 225 } 226 } 227 228 // test full save/load cycle 229 unittest 230 { 231 //import std.stdio; 232 static struct Test_vec2 { float x, y; } 233 static struct Test_vec3 { float x, y, z; } 234 235 static struct Test_ClientDimPos { 236 Test_vec3 pos = Test_vec3(0,0,0); 237 Test_vec2 heading = Test_vec2(0,0); 238 } 239 240 @Component("avatar.Test_AvatarPosition", Replication.toClient) 241 static struct Test_AvatarPosition { 242 Test_ClientDimPos dimPos; 243 ushort dimension; 244 } 245 246 @Component("avatar.Test_Wagon", Replication.toClient) 247 static struct Test_Wagon { 248 Test_vec3 pos = Test_vec3(0,0,0); 249 ushort dimension; 250 } 251 252 // reg components 253 EntityManager eman; 254 eman.registerComponent!Test_AvatarPosition; 255 eman.registerComponent!Test_Wagon; 256 257 // set components 258 auto component1 = Test_AvatarPosition(Test_ClientDimPos(Test_vec3(1,2,3),Test_vec2(4,5)), 6); 259 eman.set(1, component1); 260 auto component2 = Test_Wagon(Test_vec3(1,2,3), 6); 261 eman.set(1, component2); 262 263 // prepare NetworkSaver 264 StringMap stringMap; 265 NetworkSaver netSaver; 266 netSaver.stringMap = &stringMap; 267 268 // serialize 269 import voxelman.container.hash.set; 270 HashSet!EntityId entities; 271 entities.put(1); 272 eman.savePartial(netSaver, entities); 273 274 // prepare NetworkLoader 275 NetworkLoader netLoader; 276 netLoader.stringMap = &stringMap; 277 278 // begin sync 279 eman.removeSerializedComponents(IoStorageType.network); 280 assert(eman.get!Test_AvatarPosition(1) is null); 281 282 netLoader.parseSavedData(netSaver.data); 283 enum bool clearComponents = false; 284 eman.load(netLoader, clearComponents); 285 286 // clear temp buffers 287 netLoader.ioKeyToData.clear(); 288 netSaver.reset(); 289 290 // test 291 assert(*eman.get!Test_AvatarPosition(1) == component1); 292 assert(*eman.get!Test_Wagon(1) == component2); 293 }