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 
20 shared static this()
21 {
22 	pluginRegistry.regClientPlugin(new EntityPluginClient);
23 	pluginRegistry.regServerPlugin(new EntityPluginServer);
24 }
25 
26 struct EntityManager
27 {
28 	private EntityId lastEntityId;
29 
30 	EntityId nextEntityId()
31 	{
32 		return ++lastEntityId;
33 	}
34 }
35 
36 struct ComponentInfo
37 {
38 	string name;
39 	ComponentUnpacker unpacker;
40 	TypeInfo componentType;
41 	size_t id;
42 }
43 
44 struct ComponentSyncPacket
45 {
46 	size_t componentId;
47 	ubyte[] componentData;
48 }
49 
50 struct ProcessComponentsEvent {
51 	float deltaTime;
52 	Profiler profiler;
53 	bool continuePropagation = true;
54 }
55 
56 struct SyncComponentsEvent {
57 	Profiler profiler;
58 	bool continuePropagation = true;
59 }
60 
61 final class EntityPluginClient : IPlugin
62 {
63 	mixin EntityPluginCommon;
64 	mixin EntityPluginClientImpl;
65 }
66 
67 final class EntityPluginServer : IPlugin
68 {
69 	mixin EntityPluginCommon;
70 	mixin EntityPluginServerImpl;
71 }
72 
73 alias ComponentUnpacker = void delegate(ubyte[] componentData);
74 
75 mixin template EntityPluginCommon()
76 {
77 	mixin IdAndSemverFrom!(voxelman.entity.plugininfo);
78 
79 	EventDispatcherPlugin evDispatcher;
80 	EntityManager entityManager;
81 
82 	ComponentInfo*[] componentArray;
83 	ComponentInfo*[TypeInfo] componentMap;
84 
85 	override void init(IPluginManager pluginman)
86 	{
87 		evDispatcher = pluginman.getPlugin!EventDispatcherPlugin;
88 		evDispatcher.subscribeToEvent(&onUpdateEvent);
89 		evDispatcher.subscribeToEvent(&onPostUpdateEvent);
90 		connection = pluginman.getPlugin!(typeof(connection));
91 		connection.registerPacket!ComponentSyncPacket(&handleComponentSyncPacket);
92 	}
93 
94 	void registerComponent(C)(ComponentUnpacker unpacker = null, string componentName = C.stringof)
95 	{
96 		size_t newId = componentArray.length;
97 		ComponentInfo* cinfo = new ComponentInfo(componentName, unpacker, typeid(C), newId);
98 		componentArray ~= cinfo;
99 		assert(typeid(C) !in componentMap);
100 		componentMap[typeid(C)] = cinfo;
101 	}
102 
103 	void onUpdateEvent(ref UpdateEvent event)
104 	{
105 		evDispatcher.postEvent(ProcessComponentsEvent(event.deltaTime));
106 	}
107 
108 	void onPostUpdateEvent(ref PostUpdateEvent event)
109 	{
110 		evDispatcher.postEvent(SyncComponentsEvent());
111 	}
112 }
113 
114 mixin template EntityPluginClientImpl()
115 {
116 	NetClientPlugin connection;
117 
118 	void unpackComponents(Storage)(ref Storage storage, ubyte[] data)
119 	{
120 		storage.removeAll();
121 		while(!data.empty)
122 		{
123 			storage.add(decodeCborSingle!size_t(data), decodeCborSingle!(componentType!Storage)(data));
124 		}
125 	}
126 
127 	void handleComponentSyncPacket(ubyte[] packetData, ClientId clientId)
128 	{
129 		auto componentId = decodeCborSingle!size_t(packetData);
130 
131 		if (componentId >= componentArray.length)
132 			return; // out of range
133 
134 		auto unpacker = componentArray[componentId].unpacker;
135 		if (unpacker is null)
136 			return; // unpacker is not set
137 
138 		unpacker(packetData);
139 	}
140 }
141 
142 mixin template EntityPluginServerImpl()
143 {
144 	NetServerPlugin connection;
145 
146 	void sendComponents(Storage)(Storage storage)
147 	{
148 		auto componentId = componentMap[typeid(componentType!Storage)].id;
149 		auto packetData = createComponentPacket(componentId, storage);
150 		if (packetData.length > 0)
151 			connection.sendToAll(packetData);
152 	}
153 
154 	ubyte[] createComponentPacket(Storage)(size_t componentId, Storage storage)
155 	{
156 		ubyte[] bufferTemp = connection.buffer;
157 		size_t size;
158 
159 		size = encodeCbor(bufferTemp[], connection.packetId!ComponentSyncPacket);
160 		size += encodeCbor(bufferTemp[size..$], componentId);
161 
162 		foreach(pair; storage.byKeyValue())
163 		{
164 			size += encodeCbor(bufferTemp[size..$], pair.key);
165 			size += encodeCbor(bufferTemp[size..$], pair.value);
166 		}
167 
168 		return bufferTemp[0..size];
169 	}
170 
171 	void handleComponentSyncPacket(ubyte[] packetData, ClientId clientId)
172 	{
173 	}
174 }