1 /************************************************************************************************************************
2 System module.
3 
4 Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
5 License: BSD 3-clause, see LICENSE file in project root folder.
6 */
7 module bubel.ecs.system;
8 
9 import bubel.ecs.entity;
10 import bubel.ecs.manager;
11 
12 /************************************************************************************************************************
13 System contain data required to proper glue EntityManager with Systems.
14 System callbacks:
15 $(LIST
16     * void onUpdate(EntitesData);
17     * void onEnable() - called inside system.enable() function
18     * void onDisable() - called inside system.disable() function
19     * bool onBegin() - called inside manager.begin()
20     * void onEnd() - called inside manager.end()
21     * void onCreate() - called after registration inside registerSystem function
22     * void onDestroy() - called during re-registration and inside manager destructor
23     * void onAddEntity(EntitesData) - called for every entity which are assigned to system (by adding new entity or changing its components)
24     * void onRemoveEntity(EntitiesData) - called for every entity removed from system update process
25     * void onChangeEntity(EntitiesData) - called for every entity which components are changed but it was previously assigned to system
26     * void handleEvent(Entity*, Event) - called for every event supported by system
27 )
28 */
29 struct System
30 {
31 
32     /************************************************************************************************************************
33     Check if system is enabled.
34     */
35     export bool enabled() nothrow @nogc
36     {
37         return m_enabled;
38     }
39 
40     /************************************************************************************************************************
41     Enable system. If actually it is enabled function do nothing.
42     */
43     export void enable() nothrow @nogc
44     {
45         if (!m_enabled && m_enable)
46             (cast(void function(void*) nothrow @nogc) m_enable)(m_system_pointer);
47         m_enabled = true;
48     }
49 
50     /************************************************************************************************************************
51     Disable system. If actually it is disabled function do nothing.
52     */
53     export void disable() nothrow @nogc
54     {
55         if (m_enabled && m_disable)
56             (cast(void function(void*) nothrow @nogc) m_disable)(m_system_pointer);
57         m_enabled = false;
58     }
59 
60     /************************************************************************************************************************
61     Get system priority.
62     */
63     export int priority() nothrow @nogc
64     {
65         return m_priority;
66     }
67 
68     /************************************************************************************************************************
69     Get if system will be executed during current frame. Should be checked after manager.begin(). Its value is setted as result of manager.onBegin() callback.
70     */
71     export bool willExecute() nothrow @nogc
72     {
73         return m_execute;
74     }
75 
76     /************************************************************************************************************************
77     Get system id.
78     */
79     export ushort id() nothrow @nogc
80     {
81         return m_id;
82     }
83 
84     /************************************************************************************************************************
85     Get system name.
86     */
87     export const(char)[] name() nothrow @nogc
88     {
89         return cast(const(char)[]) m_name;
90     }
91 
92     /************************************************************************************************************************
93     Return false if system was unregistered, true otherwise.
94     */
95     export bool isAlive() nothrow @nogc
96     {
97         return m_system_pointer != null;
98     }
99 
100 package:
101 
102     void destroy()
103     {
104         import bubel.ecs.std : Mallocator;
105         disable();
106         if (m_destroy)
107             (cast(void function(void*)) m_destroy)(m_system_pointer);
108 
109         if (m_name)
110             Mallocator.dispose(m_name);
111         if (m_components)
112             Mallocator.dispose(m_components);
113         if (m_excluded_components)
114             Mallocator.dispose(m_excluded_components);
115         if (m_optional_components)
116             Mallocator.dispose(m_optional_components);
117         if (jobs)
118             Mallocator.dispose(jobs);
119         if (m_read_only_components)
120             Mallocator.dispose(m_read_only_components);
121         if (m_writable_components)
122             Mallocator.dispose(m_writable_components);
123         if (m_readonly_dependencies)
124             Mallocator.dispose(m_readonly_dependencies);
125         if (m_writable_dependencies)
126             Mallocator.dispose(m_writable_dependencies);
127         if (m_event_callers)
128             Mallocator.dispose(m_event_callers);
129 
130         if (m_system_pointer)
131             Mallocator.dispose(m_system_pointer);
132     }
133 
134     struct EventCaller
135     {
136         ushort id;
137         void* callback;
138     }
139 
140     ///should system be executed in current update?
141     bool m_execute = true;
142     ///system id
143     ushort m_id;
144     ///is system empty? Empty systems don't update entities, and is called once per update
145     bool m_empty = false;
146 
147     ///should system update and catch events?
148     bool m_enabled = false;
149     ///system priority
150     int m_priority;
151     ///pointer to system implementation
152     void* m_system_pointer;
153     ///system pass index
154     int m_pass;
155 
156     ///system name
157     char[] m_name;
158 
159     ///required components
160     ushort[] m_components;
161     ///excluded components
162     ushort[] m_excluded_components;
163     ///optional components
164     ushort[] m_optional_components;
165 
166     EntityManager.Job[] jobs;
167 
168     //System*[] m_dependencies;
169     ushort[] m_read_only_components;
170     ushort[] m_writable_components;
171 
172     ushort[] m_readonly_dependencies;
173     ushort[] m_writable_dependencies;
174 
175     EntityManager.SystemCaller* m_any_system_caller;
176 
177     EventCaller[] m_event_callers;
178 
179     //void function(ref EntityManager.CallData data) m_update;
180     void* m_update; ///workaroud for DMD bug with upper line
181     void delegate() m_update_delegate;
182 
183     //void function(void* system_pointer) m_enable;
184     //void function(void* system_pointer) m_disable;
185 
186     //void function(void* system_pointer) m_create;
187     //void function(void* system_pointer) m_destroy;
188 
189     //void function(void* system_pointer) m_begin;
190     //void function(void* system_pointer) m_end;
191 
192     void* m_enable;
193     void* m_disable;
194 
195     void* m_create;
196     void* m_destroy;
197 
198     void* m_begin;
199     void* m_end;
200 
201     void* m_add_entity;
202     void* m_remove_entity;
203     void* m_change_entity;
204 
205     void* m_filter_entity;
206 
207     //void function(ref EntityManager.CallData data) m_initialize;
208     //void function(ref EntityManager.CallData data) m_deinitilize;
209     void* m_initialize;
210     void* m_deinitilize;
211 }