1 /*
2                                     __
3                                    / _|
4   __ _ _   _ _ __ ___  _ __ __ _  | |_ ___  ___ ___
5  / _` | | | | '__/ _ \| '__/ _` | |  _/ _ \/ __/ __|
6 | (_| | |_| | | | (_) | | | (_| | | || (_) \__ \__ \
7  \__,_|\__,_|_|  \___/|_|  \__,_| |_| \___/|___/___/
8 
9 Copyright (C) 2019-2020 Aurora Free Open Source Software.
10 Copyright (C) 2019 João Lourenço <joao@aurorafoss.org>
11 
12 This file is part of the Aurora Free Open Source Software. This
13 organization promote free and open source software that you can
14 redistribute and/or modify under the terms of the GNU Lesser General
15 Public License Version 3 as published by the Free Software Foundation or
16 (at your option) any later version approved by the Aurora Free Open Source
17 Software Organization. The license is available in the package root path
18 as 'LICENSE' file. Please review the following information to ensure the
19 GNU Lesser General Public License version 3 requirements will be met:
20 https://www.gnu.org/licenses/lgpl.html .
21 
22 Alternatively, this file may be used under the terms of the GNU General
23 Public License version 3 or later as published by the Free Software
24 Foundation. Please review the following information to ensure the GNU
25 General Public License requirements will be met:
26 https://www.gnu.org/licenses/gpl-3.0.html.
27 
28 NOTE: All products, services or anything associated to trademarks and
29 service marks used or referenced on this file are the property of their
30 respective companies/owners or its subsidiaries. Other names and brands
31 may be claimed as the property of others.
32 
33 For more info about intellectual property visit: aurorafoss.org or
34 directly send an email to: contact (at) aurorafoss.org .
35 */
36 
37 module aurorafw.entity.systemmanager;
38 
39 import aurorafw.entity.system;
40 import aurorafw.entity.entitymanager;
41 
42 version (unittest) import aurorafw.unit.assertion;
43 
44 import std.traits : fullyQualifiedName;
45 import std.exception;
46 
47 final class SystemHandlingException : Exception
48 {
49 	mixin basicExceptionCtors;
50 }
51 
52 class SystemManager
53 {
54 	// unittesting only
55 	@safe pure
56 	private this()
57 	{
58 	}
59 
60 	@safe pure
61 	public this(EntityManager entity)
62 	{
63 		_entity = entity;
64 	}
65 
66 	/**
67 	 * Create a new system
68 	 *
69 	 * Examples:
70 	 * --------------------
71 	 * world.system.create(new FooSystem());
72 	 * --------------------
73 	 *
74 	 * SeeAlso: create(S : System)()
75 	 */
76 	public void create(S : System)(S system)
77 	{
78 		enum id = fullyQualifiedName!S;
79 
80 		if (id in this.systems)
81 			throw new SystemHandlingException(
82 					"Cannot add system. " ~ __traits(identifier, S) ~ " is already created!"
83 			);
84 
85 		system.manager = this;
86 		this.systems[id] = system;
87 	}
88 
89 	/**
90 	 * Create a new System
91 	 *
92 	 * Examples:
93 	 * --------------------
94 	 * world.system.create!FooSystem;
95 	 * --------------------
96 	 */
97 	public void create(S : System)()
98 	{
99 		create(new S());
100 	}
101 
102 	/**
103 	 * Get a system
104 	 *
105 	 * Returns: the type you're passing
106 	 *
107 	 * Examples:
108 	 * --------------------
109 	 * world.system.get!FooSystem
110 	 * --------------------
111 	 */
112 	@safe pure
113 	public S get(S : System)()
114 	{
115 		enum id = fullyQualifiedName!S;
116 		System* p;
117 		p = id in this.systems;
118 
119 		return p !is null ? cast(S)(*p) : null;
120 	}
121 
122 	/**
123 	 * Remove
124 	 *
125 	 * Deletes a system from the world's scope
126 	 *
127 	 * Examples:
128 	 * --------------------
129 	 * world.system.remove!FooSystem
130 	 * --------------------
131 	 */
132 	@safe
133 	public void remove(S : System)()
134 	{
135 		enum id = fullyQualifiedName!S;
136 
137 		if (id in this.systems)
138 			this.systems.remove(id);
139 
140 		else
141 			throw new SystemHandlingException(
142 					"Cannot remove system. " ~ __traits(identifier, S) ~ " was already removed or it wasn't created!"
143 			);
144 	}
145 
146 	/**
147 	 * Clear
148 	 *
149 	 * Deletes every system in the world's scope
150 	 *
151 	 * Examples:
152 	 * --------------------
153 	 * world.system.clear();
154 	 * --------------------
155 	 */
156 	@safe pure
157 	public void clear()
158 	{
159 		foreach (key, value; this.systems)
160 			this.systems.remove(key);
161 	}
162 
163 	/**
164 	 * Update
165 	 *
166 	 * Updates every component with the Automatic Update Policy
167 	 *
168 	 * Examples:
169 	 * --------------------
170 	 * world.system.update();
171 	 * --------------------
172 	 */
173 	public void update()
174 	{
175 		foreach (System s; this.systems)
176 			if (s.updatePolicy == s.UpdatePolicy.Automatic)
177 				s.update();
178 	}
179 
180 	/**
181 	 * Update
182 	 *
183 	 * Updates any system passed
184 	 * It's useful when you only want to update systems manualy
185 	 *
186 	 * Examples:
187 	 * --------------------
188 	 * world.system.update!FooSystem;
189 	 * --------------------
190 	 */
191 	public void update(S : System)()
192 	{
193 		this.systems[fullyQualifiedName!S].update();
194 	}
195 
196 	/**
197 	 * Entity
198 	 *
199 	 * Returns the entity manager
200 	 *
201 	 * Examples:
202 	 * --------------------
203 	 * auto entity = entity;
204 	 * --------------------
205 	 */
206 	@safe pure
207 	public EntityManager entity()
208 	{
209 		return _entity;
210 	}
211 
212 	@property
213 
214 	private EntityManager _entity;
215 	private System[string] systems;
216 }
217 
218 version (unittest)
219 {
220 	private class unittest_FooSystem : System
221 	{
222 		@safe
223 		override public void update()
224 		{
225 			this.updatePolicy = UpdatePolicy.Manual;
226 		}
227 	}
228 
229 	private class unittest_BarSystem : System
230 	{
231 		@safe pure
232 		public this()
233 		{
234 			super(UpdatePolicy.Manual);
235 		}
236 
237 		@safe
238 		override public void update()
239 		{
240 			this.updatePolicy = UpdatePolicy.Automatic;
241 		}
242 	}
243 }
244 
245 ///
246 @safe pure
247 @("System Manager: System creation")
248 unittest
249 {
250 	SystemManager system = new SystemManager();
251 
252 	system.create(new unittest_FooSystem());
253 
254 	import std.traits : ReturnType;
255 
256 	assertTrue(is(ReturnType!(system.get!unittest_FooSystem) == unittest_FooSystem));
257 
258 	assertThrown!SystemHandlingException(system.create(new unittest_FooSystem())); // System created twice
259 }
260 
261 ///
262 @safe pure
263 @("System Manager: Accessing system properties")
264 unittest
265 {
266 	SystemManager system = new SystemManager();
267 
268 	system.create(new unittest_FooSystem());
269 
270 	import std.traits : ReturnType;
271 
272 	assertTrue((system.get!(unittest_FooSystem)).manager is system);
273 	assertTrue(is(ReturnType!(system.get!unittest_FooSystem) == unittest_FooSystem));
274 	assertTrue(system.get!(unittest_BarSystem) is null); // System wasn't created
275 }
276 
277 ///
278 @("System Manager: System update")
279 unittest
280 {
281 	SystemManager system = new SystemManager();
282 
283 	unittest_FooSystem fooSys = new unittest_FooSystem();
284 	unittest_BarSystem barSys = new unittest_BarSystem();
285 	system.create(fooSys);
286 	system.create(barSys);
287 
288 	assertEquals(fooSys.updatePolicy, fooSys.UpdatePolicy.Automatic);
289 	assertEquals(barSys.updatePolicy, barSys.UpdatePolicy.Manual);
290 
291 	system.update(); // Only updates systems with Automatic Update Policy
292 
293 	assertEquals(fooSys.updatePolicy, fooSys.UpdatePolicy.Manual); // Got updated
294 	assertEquals(barSys.updatePolicy, barSys.UpdatePolicy.Manual); // Didn't get updated
295 
296 	system.update!unittest_BarSystem; // Systems can be updated manualy
297 
298 	assertEquals(barSys.updatePolicy, barSys.UpdatePolicy.Automatic); // Got updated
299 }
300 
301 ///
302 @safe pure
303 @("System Manager: System clear")
304 unittest
305 {
306 	SystemManager system = new SystemManager();
307 
308 	system.create!unittest_FooSystem;
309 	system.clear();
310 
311 	assertEquals(0, system.systems.length);
312 }