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 /************************************************************************************************************************ 101 Return pointer to user side system object 102 */ 103 export void* ptr() nothrow @nogc 104 { 105 return m_system_pointer; 106 } 107 108 package: 109 110 ///destory system. Dispose all data 111 void destroy() nothrow @nogc 112 { 113 import bubel.ecs.std : Mallocator; 114 115 destroySystemData(); 116 117 if (m_name) 118 { 119 Mallocator.dispose(m_name); 120 m_name = null; 121 } 122 } 123 124 ///destroy all system data but keeps name which is used for case of system re-registration 125 void destroySystemData() nothrow @nogc 126 { 127 import bubel.ecs.std : Mallocator; 128 disable(); 129 if (m_destroy) 130 { 131 (cast(void function(void*) nothrow @nogc) m_destroy)(m_system_pointer); 132 m_destroy = null; 133 } 134 135 if (m_components) 136 { 137 Mallocator.dispose(m_components); 138 m_components = null; 139 } 140 if (m_excluded_components) 141 { 142 Mallocator.dispose(m_excluded_components); 143 m_excluded_components = null; 144 } 145 if (m_optional_components) 146 { 147 Mallocator.dispose(m_optional_components); 148 m_optional_components = null; 149 } 150 if (jobs) 151 { 152 Mallocator.dispose(jobs); 153 jobs = null; 154 } 155 if (m_read_only_components) 156 { 157 Mallocator.dispose(m_read_only_components); 158 m_read_only_components = null; 159 } 160 if (m_writable_components) 161 { 162 Mallocator.dispose(m_writable_components); 163 m_writable_components = null; 164 } 165 if (m_readonly_dependencies) 166 { 167 Mallocator.dispose(m_readonly_dependencies); 168 m_readonly_dependencies = null; 169 } 170 if (m_writable_dependencies) 171 { 172 Mallocator.dispose(m_writable_dependencies); 173 m_writable_dependencies = null; 174 } 175 if (m_event_callers) 176 { 177 Mallocator.dispose(m_event_callers); 178 m_event_callers = null; 179 } 180 181 if (m_system_pointer) 182 { 183 Mallocator.dispose(m_system_pointer); 184 m_system_pointer = null; 185 } 186 } 187 188 struct EventCaller 189 { 190 ushort id; 191 void* callback; 192 } 193 194 ///should system be executed in current update? 195 bool m_execute = true; 196 ///system id 197 ushort m_id; 198 ///is system empty? Empty systems don't update entities, and is called once per update 199 bool m_empty = false; 200 201 ///should system update and catch events? 202 bool m_enabled = false; 203 ///system priority 204 int m_priority; 205 ///pointer to system implementation 206 void* m_system_pointer; 207 ///system pass index 208 int m_pass; 209 210 ///system name 211 char[] m_name; 212 213 ///required components 214 ushort[] m_components; 215 ///excluded components 216 ushort[] m_excluded_components; 217 ///optional components 218 ushort[] m_optional_components; 219 220 EntityManager.Job[] jobs; 221 222 //System*[] m_dependencies; 223 ushort[] m_read_only_components; 224 ushort[] m_writable_components; 225 226 ushort[] m_readonly_dependencies; 227 ushort[] m_writable_dependencies; 228 229 EntityManager.SystemCaller* m_any_system_caller; 230 231 EventCaller[] m_event_callers; 232 233 //void function(ref EntityManager.CallData data) m_update; 234 void* m_update; ///workaroud for DMD bug with upper line 235 void delegate() m_update_delegate; 236 237 //void function(void* system_pointer) m_enable; 238 //void function(void* system_pointer) m_disable; 239 240 //void function(void* system_pointer) m_create; 241 //void function(void* system_pointer) m_destroy; 242 243 //void function(void* system_pointer) m_begin; 244 //void function(void* system_pointer) m_end; 245 246 void* m_enable; 247 void* m_disable; 248 249 void* m_create; 250 void* m_destroy; 251 252 void* m_begin; 253 void* m_end; 254 255 void* m_add_entity; 256 void* m_remove_entity; 257 void* m_change_entity; 258 259 void* m_filter_entity; 260 261 //void function(ref EntityManager.CallData data) m_initialize; 262 //void function(ref EntityManager.CallData data) m_deinitilize; 263 void* m_initialize; 264 void* m_deinitilize; 265 }