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 }