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 }