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 7 module voxelman.entity.plugin; 8 9 import std.experimental.logger; 10 import std.array : empty; 11 12 import cbor; 13 import pluginlib; 14 import datadriven.api; 15 16 import voxelman.eventdispatcher.plugin; 17 import voxelman.net.plugin; 18 import voxelman.core.events; 19 import voxelman.world.plugin; 20 21 shared static this() 22 { 23 pluginRegistry.regClientPlugin(new EntityPluginClient); 24 pluginRegistry.regServerPlugin(new EntityPluginServer); 25 } 26 27 struct EntityManager 28 { 29 private EntityId lastEntityId; 30 31 EntityId nextEntityId() 32 { 33 return ++lastEntityId; 34 } 35 } 36 37 struct ComponentInfo 38 { 39 string name; 40 ComponentUnpacker unpacker; 41 TypeInfo componentType; 42 size_t id; 43 } 44 45 struct ComponentSyncPacket 46 { 47 size_t componentId; 48 ubyte[] componentData; 49 } 50 51 struct ProcessComponentsEvent { 52 float deltaTime; 53 } 54 struct SyncComponentsEvent {} 55 56 final class EntityPluginClient : IPlugin 57 { 58 mixin EntityPluginCommon; 59 mixin EntityPluginClientImpl; 60 } 61 62 final class EntityPluginServer : IPlugin 63 { 64 mixin EntityPluginCommon; 65 mixin EntityPluginServerImpl; 66 } 67 68 alias ComponentUnpacker = void delegate(ubyte[] componentData); 69 70 mixin template EntityPluginCommon() 71 { 72 mixin IdAndSemverFrom!(voxelman.entity.plugininfo); 73 74 EventDispatcherPlugin evDispatcher; 75 EntityManager entityManager; 76 77 ComponentInfo*[] componentArray; 78 ComponentInfo*[TypeInfo] componentMap; 79 80 override void init(IPluginManager pluginman) 81 { 82 evDispatcher = pluginman.getPlugin!EventDispatcherPlugin; 83 evDispatcher.subscribeToEvent(&onUpdateEvent); 84 evDispatcher.subscribeToEvent(&onPostUpdateEvent); 85 connection = pluginman.getPlugin!(typeof(connection)); 86 connection.registerPacket!ComponentSyncPacket(&handleComponentSyncPacket); 87 } 88 89 void registerComponent(C)(ComponentUnpacker unpacker = null, string componentName = C.stringof) 90 { 91 size_t newId = componentArray.length; 92 ComponentInfo* cinfo = new ComponentInfo(componentName, unpacker, typeid(C), newId); 93 componentArray ~= cinfo; 94 assert(typeid(C) !in componentMap); 95 componentMap[typeid(C)] = cinfo; 96 } 97 98 void onUpdateEvent(ref UpdateEvent event) 99 { 100 evDispatcher.postEvent(ProcessComponentsEvent(event.deltaTime)); 101 } 102 103 void onPostUpdateEvent(ref PostUpdateEvent event) 104 { 105 evDispatcher.postEvent(SyncComponentsEvent()); 106 } 107 } 108 109 mixin template EntityPluginClientImpl() 110 { 111 NetClientPlugin connection; 112 113 void unpackComponents(Storage)(ref Storage storage, ubyte[] data) 114 { 115 storage.removeAll(); 116 while(!data.empty) 117 { 118 storage.add( 119 decodeCborSingle!size_t(data), 120 decodeCborSingle!(componentType!Storage, Yes.Flatten)(data)); 121 } 122 } 123 124 void handleComponentSyncPacket(ubyte[] packetData, ClientId clientId) 125 { 126 auto componentId = decodeCborSingle!size_t(packetData); 127 128 if (componentId >= componentArray.length) 129 return; // out of range 130 131 auto unpacker = componentArray[componentId].unpacker; 132 if (unpacker is null) 133 return; // unpacker is not set 134 135 unpacker(packetData); 136 } 137 } 138 139 mixin template EntityPluginServerImpl() 140 { 141 NetServerPlugin connection; 142 immutable string eidKey = "voxelman.entity.lastEntityId"; 143 144 override void registerResources(IResourceManagerRegistry resmanRegistry) 145 { 146 auto ioman = resmanRegistry.getResourceManager!IoManager; 147 ioman.registerWorldLoadSaveHandlers(&read, &write); 148 } 149 150 void sendComponents(Storage)(Storage storage) 151 { 152 auto componentId = componentMap[typeid(componentType!Storage)].id; 153 auto packetData = createComponentPacket(componentId, storage); 154 if (packetData.length > 0) 155 connection.sendToAll(packetData); 156 } 157 158 ubyte[] createComponentPacket(Storage)(size_t componentId, Storage storage) 159 { 160 ubyte[] bufferTemp = connection.buffer; 161 size_t size; 162 163 size = encodeCbor(bufferTemp[], connection.packetId!ComponentSyncPacket); 164 size += encodeCbor(bufferTemp[size..$], componentId); 165 166 foreach(pair; storage.byKeyValue()) 167 { 168 size += encodeCbor(bufferTemp[size..$], pair.key); 169 size += encodeCbor!(Yes.Flatten)(bufferTemp[size..$], pair.value); 170 } 171 172 return bufferTemp[0..size]; 173 } 174 175 void handleComponentSyncPacket(ubyte[] packetData, ClientId clientId) 176 { 177 } 178 179 import cbor; 180 void read(ref PluginDataLoader loader) 181 { 182 ubyte[] data = loader.readEntry(eidKey); 183 if (data.length) 184 decodeCbor(data, entityManager.lastEntityId); 185 } 186 187 void write(ref PluginDataSaver saver) 188 { 189 auto sink = saver.tempBuffer; 190 size_t size = encodeCbor(sink[], entityManager.lastEntityId); 191 saver.writeEntry(eidKey, size); 192 } 193 }