1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <algorithm>
17 #include <atomic>
18 #include <cstdint>
19
20 #include <base/containers/array_view.h>
21 #include <base/containers/iterator.h>
22 #include <base/containers/unique_ptr.h>
23 #include <base/containers/unordered_map.h>
24 #include <base/containers/vector.h>
25 #include <base/namespace.h>
26 #include <base/util/uid.h>
27 #include <core/ecs/entity.h>
28 #include <core/ecs/intf_component_manager.h>
29 #include <core/ecs/intf_ecs.h>
30 #include <core/ecs/intf_system.h>
31 #include <core/log.h>
32 #include <core/namespace.h>
33 #include <core/perf/cpu_perf_scope.h>
34 #include <core/plugin/intf_plugin.h>
35 #include <core/plugin/intf_plugin_register.h>
36 #include <core/threading/intf_thread_pool.h>
37
38 #include "ecs/entity_manager.h"
39
40 CORE_BEGIN_NAMESPACE()
41 namespace {
42 using BASE_NS::array_view;
43 using BASE_NS::pair;
44 using BASE_NS::Uid;
45 using BASE_NS::unique_ptr;
46 using BASE_NS::unordered_map;
47 using BASE_NS::vector;
48
49 class Ecs final : public IEcs, IPluginRegister::ITypeInfoListener {
50 public:
51 Ecs(IClassFactory&, const IThreadPool::Ptr& threadPool);
52 ~Ecs() override;
53
54 Ecs(const Ecs&) = delete;
55 Ecs(const Ecs&&) = delete;
56 Ecs& operator=(const Ecs&) = delete;
57 Ecs& operator=(const Ecs&&) = delete;
58
59 IEntityManager& GetEntityManager() override;
60 const IEntityManager& GetEntityManager() const override;
61 void GetComponents(Entity entity, vector<IComponentManager*>& result) const override;
62 vector<ISystem*> GetSystems() const override;
63 ISystem* GetSystem(const Uid& uid) const override;
64 vector<IComponentManager*> GetComponentManagers() const override;
65 IComponentManager* GetComponentManager(const Uid& uid) const override;
66 Entity CloneEntity(Entity entity) override;
67 void ProcessEvents() override;
68
69 void Initialize() override;
70 bool Update(uint64_t time, uint64_t delta) override;
71 void Uninitialize() override;
72
73 IComponentManager* CreateComponentManager(const ComponentManagerTypeInfo& componentManagerTypeInfo) override;
74 ISystem* CreateSystem(const SystemTypeInfo& systemInfo) override;
75
76 void AddListener(EntityListener& listener) override;
77 void RemoveListener(EntityListener& listener) override;
78 void AddListener(ComponentListener& listener) override;
79 void RemoveListener(ComponentListener& listener) override;
80 void AddListener(IComponentManager& manager, ComponentListener& listener) override;
81 void RemoveListener(IComponentManager& manager, ComponentListener& listener) override;
82
83 void RequestRender() override;
84 void SetRenderMode(RenderMode renderMode) override;
85 RenderMode GetRenderMode() override;
86
87 bool NeedRender() const override;
88
89 IClassFactory& GetClassFactory() const override;
90
91 const IThreadPool::Ptr& GetThreadPool() const override;
92
93 float GetTimeScale() const override;
94 void SetTimeScale(float scale) override;
95
96 void Ref() noexcept override;
97 void Unref() noexcept override;
98
99 protected:
100 using SystemPtr = unique_ptr<ISystem, SystemTypeInfo::DestroySystemFn>;
101 using ManagerPtr = unique_ptr<IComponentManager, ComponentManagerTypeInfo::DestroyComponentManagerFn>;
102
103 // IPluginRegister::ITypeInfoListener
104 void OnTypeInfoEvent(EventType type, array_view<const ITypeInfo* const> typeInfos) override;
105
106 void ProcessComponentEvents(
107 IEcs::ComponentListener::EventType eventType, array_view<const Entity> removedEntities) const;
108
109 IThreadPool::Ptr threadPool_;
110
111 // for storing systems and component managers in creation order
112 vector<SystemPtr> systemOrder_;
113 vector<ManagerPtr> managerOrder_;
114 // for finding systems and component managers with UID
115 unordered_map<Uid, ISystem*> systems_;
116 unordered_map<Uid, IComponentManager*> managers_;
117
118 vector<EntityListener*> entityListeners_;
119 vector<ComponentListener*> componentListeners_;
120 unordered_map<IComponentManager*, vector<ComponentListener*>> componentManagerListeners_;
121
122 bool needRender_ { false };
123 bool renderRequested_ { false };
124 RenderMode renderMode_ { RENDER_ALWAYS };
125
126 IClassFactory& pluginRegistry_;
127 EntityManager entityManager_;
128
129 vector<pair<PluginToken, const IEcsPlugin*>> plugins_;
130 float timeScale_ { 1.f };
131 std::atomic<int32_t> refcnt_ { 0 };
132
133 bool processingEvents_ { false };
134 };
135
136 template<typename ListType, typename ValueType>
Find(ListType & list,const ValueType & value)137 auto Find(ListType& list, const ValueType& value)
138 {
139 return std::find(list.begin(), list.end(), value);
140 }
141
ProcessEntityListeners(const array_view<const pair<Entity,IEntityManager::EventType>> states,const array_view<IEcs::EntityListener * > entityListeners)142 void ProcessEntityListeners(const array_view<const pair<Entity, IEntityManager::EventType>> states,
143 const array_view<IEcs::EntityListener*> entityListeners)
144 {
145 // handle state changes (collect to groups of same kind of events)
146 vector<Entity> res;
147 res.reserve(states.size());
148 auto type = states[0U].second;
149 for (const auto& s : states) {
150 if (s.second != type) {
151 if (!res.empty()) {
152 // Let listeners know that entity state has changed.
153 for (auto* listener : entityListeners) {
154 if (listener) {
155 listener->OnEntityEvent(type, res);
156 }
157 }
158 // start collecting new events.
159 res.clear();
160 }
161 type = s.second;
162 }
163 // add to event list.
164 res.push_back(s.first);
165 }
166 if (!res.empty()) {
167 // Send the final events.
168 for (auto* listener : entityListeners) {
169 if (listener) {
170 listener->OnEntityEvent(type, res);
171 }
172 }
173 }
174 }
175
AddListener(EntityListener & listener)176 void Ecs::AddListener(EntityListener& listener)
177 {
178 if (Find(entityListeners_, &listener) != entityListeners_.end()) {
179 // already added.
180 return;
181 }
182 entityListeners_.push_back(&listener);
183 }
184
RemoveListener(EntityListener & listener)185 void Ecs::RemoveListener(EntityListener& listener)
186 {
187 if (auto it = Find(entityListeners_, &listener); it != entityListeners_.end()) {
188 // Setting the listener to null instead of removing. This allows removing listeners from a listener callback.
189 *it = nullptr;
190 return;
191 }
192 }
193
AddListener(ComponentListener & listener)194 void Ecs::AddListener(ComponentListener& listener)
195 {
196 if (Find(componentListeners_, &listener) != componentListeners_.end()) {
197 // already added.
198 return;
199 }
200 componentListeners_.push_back(&listener);
201 }
202
RemoveListener(ComponentListener & listener)203 void Ecs::RemoveListener(ComponentListener& listener)
204 {
205 if (auto it = Find(componentListeners_, &listener); it != componentListeners_.end()) {
206 *it = nullptr;
207 return;
208 }
209 }
210
AddListener(IComponentManager & manager,ComponentListener & listener)211 void Ecs::AddListener(IComponentManager& manager, ComponentListener& listener)
212 {
213 auto list = componentManagerListeners_.find(&manager);
214 if (list != componentManagerListeners_.end()) {
215 if (auto it = Find(list->second, &listener); it != list->second.end()) {
216 return;
217 }
218 list->second.push_back(&listener);
219 return;
220 }
221 componentManagerListeners_[&manager].push_back(&listener);
222 }
223
RemoveListener(IComponentManager & manager,ComponentListener & listener)224 void Ecs::RemoveListener(IComponentManager& manager, ComponentListener& listener)
225 {
226 auto list = componentManagerListeners_.find(&manager);
227 if (list == componentManagerListeners_.end()) {
228 return;
229 }
230 if (auto it = Find(list->second, &listener); it != list->second.end()) {
231 *it = nullptr;
232 return;
233 }
234 }
235
CreateComponentManager(const ComponentManagerTypeInfo & componentManagerTypeInfo)236 IComponentManager* Ecs::CreateComponentManager(const ComponentManagerTypeInfo& componentManagerTypeInfo)
237 {
238 IComponentManager* manager = nullptr;
239 if (componentManagerTypeInfo.createManager) {
240 manager = GetComponentManager(componentManagerTypeInfo.uid);
241 if (manager) {
242 CORE_LOG_W("Duplicate component manager creation, returning existing instance");
243 } else {
244 manager = componentManagerTypeInfo.createManager(*this);
245 if (manager) {
246 managers_.insert({ componentManagerTypeInfo.uid, manager });
247 managerOrder_.emplace_back(manager, componentManagerTypeInfo.destroyManager);
248 }
249 }
250 }
251 return manager;
252 }
253
CreateSystem(const SystemTypeInfo & systemInfo)254 ISystem* Ecs::CreateSystem(const SystemTypeInfo& systemInfo)
255 {
256 ISystem* system = nullptr;
257 if (systemInfo.createSystem) {
258 system = GetSystem(systemInfo.uid);
259 if (system) {
260 CORE_LOG_W("Duplicate system creation, returning existing instance");
261 } else {
262 system = systemInfo.createSystem(*this);
263 if (system) {
264 systems_.insert({ systemInfo.uid, system });
265 systemOrder_.emplace_back(system, systemInfo.destroySystem);
266 }
267 }
268 }
269 return system;
270 }
271
Ecs(IClassFactory & registry,const IThreadPool::Ptr & threadPool)272 Ecs::Ecs(IClassFactory& registry, const IThreadPool::Ptr& threadPool)
273 : threadPool_(threadPool), pluginRegistry_(registry)
274 {
275 for (auto info : CORE_NS::GetPluginRegister().GetTypeInfos(IEcsPlugin::UID)) {
276 if (auto ecsPlugin = static_cast<const IEcsPlugin*>(info); ecsPlugin && ecsPlugin->createPlugin) {
277 auto token = ecsPlugin->createPlugin(*this);
278 plugins_.push_back({ token, ecsPlugin });
279 }
280 }
281 GetPluginRegister().AddListener(*this);
282 }
283
~Ecs()284 Ecs::~Ecs()
285 {
286 GetPluginRegister().RemoveListener(*this);
287
288 Uninitialize();
289 managerOrder_.clear();
290 systemOrder_.clear();
291
292 for (auto& plugin : plugins_) {
293 if (plugin.second->destroyPlugin) {
294 plugin.second->destroyPlugin(plugin.first);
295 }
296 }
297 }
298
GetClassFactory() const299 IClassFactory& Ecs::GetClassFactory() const
300 {
301 return pluginRegistry_;
302 }
303
GetEntityManager()304 IEntityManager& Ecs::GetEntityManager()
305 {
306 return entityManager_;
307 }
308
GetEntityManager() const309 const IEntityManager& Ecs::GetEntityManager() const
310 {
311 return entityManager_;
312 }
313
GetComponents(Entity entity,vector<IComponentManager * > & result) const314 void Ecs::GetComponents(Entity entity, vector<IComponentManager*>& result) const
315 {
316 result.clear();
317 result.reserve(managers_.size());
318 for (auto& m : managerOrder_) {
319 if (m->HasComponent(entity)) {
320 result.push_back(m.get());
321 }
322 }
323 }
324
GetSystems() const325 vector<ISystem*> Ecs::GetSystems() const
326 {
327 vector<ISystem*> result;
328 result.reserve(systemOrder_.size());
329 for (auto& t : systemOrder_) {
330 result.push_back(t.get());
331 }
332 return result;
333 }
334
GetSystem(const Uid & uid) const335 ISystem* Ecs::GetSystem(const Uid& uid) const
336 {
337 if (auto pos = systems_.find(uid); pos != systems_.end()) {
338 return pos->second;
339 }
340 return nullptr;
341 }
342
GetComponentManagers() const343 vector<IComponentManager*> Ecs::GetComponentManagers() const
344 {
345 vector<IComponentManager*> result;
346 result.reserve(managerOrder_.size());
347 for (auto& t : managerOrder_) {
348 result.push_back(t.get());
349 }
350 return result;
351 }
352
GetComponentManager(const Uid & uid) const353 IComponentManager* Ecs::GetComponentManager(const Uid& uid) const
354 {
355 if (auto pos = managers_.find(uid); pos != managers_.end()) {
356 return pos->second;
357 }
358 return nullptr;
359 }
360
CloneEntity(const Entity entity)361 Entity Ecs::CloneEntity(const Entity entity)
362 {
363 if (!EntityUtil::IsValid(entity)) {
364 return {};
365 }
366
367 const Entity clonedEntity = entityManager_.Create();
368 if (entityManager_.IsAlive(entity)) {
369 for (auto& cm : managerOrder_) {
370 const auto id = cm->GetComponentId(entity);
371 auto data = cm->GetData(id);
372 if (data && id != IComponentManager::INVALID_COMPONENT_ID) {
373 cm->Create(clonedEntity);
374 cm->SetData(clonedEntity, *data);
375 }
376 }
377 }
378 return clonedEntity;
379 }
380
ProcessComponentEvents(ComponentListener::EventType eventType,const array_view<const Entity> removedEntities) const381 void Ecs::ProcessComponentEvents(
382 ComponentListener::EventType eventType, const array_view<const Entity> removedEntities) const
383 {
384 vector<Entity> (IComponentManager::*getter)();
385 switch (eventType) {
386 case ComponentListener::EventType::CREATED:
387 getter = &IComponentManager::GetAddedComponents;
388 break;
389 case ComponentListener::EventType::MODIFIED:
390 getter = &IComponentManager::GetUpdatedComponents;
391 break;
392 case ComponentListener::EventType::DESTROYED:
393 getter = &IComponentManager::GetRemovedComponents;
394 break;
395 case ComponentListener::EventType::MOVED:
396 getter = &IComponentManager::GetMovedComponents;
397 break;
398 default:
399 return;
400 }
401 for (const auto& m : managerOrder_) {
402 vector<Entity> affectedEntities = (*m.*getter)();
403 if (!removedEntities.empty()) {
404 affectedEntities.erase(
405 std::remove_if(affectedEntities.begin(), affectedEntities.end(),
406 [removedEntities](const Entity& entity) {
407 const auto pos = std::lower_bound(removedEntities.cbegin(), removedEntities.cend(), entity,
408 [](const Entity& entity, const Entity& removed) { return entity < removed; });
409 return ((pos != removedEntities.cend()) && entity >= *pos);
410 }),
411 affectedEntities.cend());
412 }
413 if (!affectedEntities.empty()) {
414 // global listeners
415 for (auto* listener : componentListeners_) {
416 if (listener) {
417 listener->OnComponentEvent(eventType, *m, affectedEntities);
418 }
419 }
420 // per manager listeners
421 if (auto it = componentManagerListeners_.find(m.get()); it != componentManagerListeners_.cend()) {
422 for (auto* listener : it->second) {
423 if (listener) {
424 listener->OnComponentEvent(eventType, *m, affectedEntities);
425 }
426 }
427 }
428 }
429 }
430 }
431
ProcessEvents()432 void Ecs::ProcessEvents()
433 {
434 if (processingEvents_) {
435 CORE_LOG_W("Calling ProcessEvents() from an event callback is not allowed");
436 return;
437 }
438 processingEvents_ = true;
439
440 vector<Entity> allRemovedEntities;
441 bool deadEntities = false;
442 do {
443 // Let entity manager check entity reference counts
444 entityManager_.UpdateDeadEntities();
445
446 // Send entity related events
447 if (const auto events = entityManager_.GetEvents(); !events.empty()) {
448 ProcessEntityListeners(events, entityListeners_);
449 }
450
451 // Remove components for removed entities.
452 const vector<Entity> removed = entityManager_.GetRemovedEntities();
453 deadEntities = !removed.empty();
454 if (deadEntities) {
455 allRemovedEntities.append(removed.cbegin(), removed.cend());
456 }
457 for (auto& m : managerOrder_) {
458 // Destroy all components related to these entities.
459 if (deadEntities) {
460 m->Destroy(removed);
461 }
462 m->Gc();
463 }
464 // Destroying components may release the last reference for some entity so we loop until there are no new
465 // deaths reported.
466 } while (deadEntities);
467
468 if (!allRemovedEntities.empty()) {
469 std::sort(allRemovedEntities.begin(), allRemovedEntities.end());
470 }
471
472 // Send component related events
473 ProcessComponentEvents(ComponentListener::EventType::CREATED, allRemovedEntities);
474 ProcessComponentEvents(ComponentListener::EventType::MOVED, allRemovedEntities);
475 ProcessComponentEvents(ComponentListener::EventType::MODIFIED, allRemovedEntities);
476 ProcessComponentEvents(ComponentListener::EventType::DESTROYED, {});
477
478 // Clean-up removed listeners.
479 entityListeners_.erase(
480 std::remove(entityListeners_.begin(), entityListeners_.end(), nullptr), entityListeners_.cend());
481 componentListeners_.erase(
482 std::remove(componentListeners_.begin(), componentListeners_.end(), nullptr), componentListeners_.cend());
483
484 for (auto it = componentManagerListeners_.begin(); it != componentManagerListeners_.cend();) {
485 auto& listeners = it->second;
486 listeners.erase(std::remove(listeners.begin(), listeners.end(), nullptr), listeners.cend());
487 if (listeners.empty()) {
488 it = componentManagerListeners_.erase(it);
489 } else {
490 ++it;
491 }
492 }
493
494 processingEvents_ = false;
495 }
496
Initialize()497 void Ecs::Initialize()
498 {
499 for (auto& s : systemOrder_) {
500 s->Initialize();
501 }
502 }
503
504 CORE_PROFILER_SYMBOL(escUpdate, "Update");
505
Update(uint64_t time,uint64_t delta)506 bool Ecs::Update(uint64_t time, uint64_t delta)
507 {
508 CORE_PROFILER_MARK_FRAME_START(escUpdate);
509 CORE_CPU_PERF_SCOPE("CORE", "Update", "Total_Cpu", CORE_PROFILER_DEFAULT_COLOR);
510 bool frameRenderingQueued = false;
511 if (GetRenderMode() == RENDER_ALWAYS || renderRequested_) {
512 frameRenderingQueued = true;
513 }
514
515 // Update all systems.
516 delta = static_cast<uint64_t>(static_cast<float>(delta) * timeScale_);
517 for (auto& s : systemOrder_) {
518 CORE_CPU_PERF_SCOPE("CORE", "SystemUpdate", s->GetName(), CORE_PROFILER_DEFAULT_COLOR);
519 if (s->Update(frameRenderingQueued, time, delta)) {
520 frameRenderingQueued = true;
521 }
522 }
523
524 // Clear modification flags from component managers.
525 for (auto& componentManager : managerOrder_) {
526 componentManager->ClearModifiedFlags();
527 }
528
529 renderRequested_ = false;
530 needRender_ = frameRenderingQueued;
531
532 CORE_PROFILER_MARK_FRAME_END(escUpdate);
533 return frameRenderingQueued;
534 }
535
Uninitialize()536 void Ecs::Uninitialize()
537 {
538 // Destroy all entities from scene.
539 entityManager_.DestroyAllEntities();
540
541 // Garbage-collect.
542 ProcessEvents();
543
544 // Uninitialize systems.
545 for (auto it = systemOrder_.rbegin(); it != systemOrder_.rend(); ++it) {
546 (*it)->Uninitialize();
547 }
548 }
549
RequestRender()550 void Ecs::RequestRender()
551 {
552 renderRequested_ = true;
553 }
554
SetRenderMode(RenderMode renderMode)555 void Ecs::SetRenderMode(RenderMode renderMode)
556 {
557 renderMode_ = renderMode;
558 }
559
GetRenderMode()560 IEcs::RenderMode Ecs::GetRenderMode()
561 {
562 return renderMode_;
563 }
564
NeedRender() const565 bool Ecs::NeedRender() const
566 {
567 return needRender_;
568 }
569
GetThreadPool() const570 const IThreadPool::Ptr& Ecs::GetThreadPool() const
571 {
572 return threadPool_;
573 }
574
GetTimeScale() const575 float Ecs::GetTimeScale() const
576 {
577 return timeScale_;
578 }
579
SetTimeScale(float scale)580 void Ecs::SetTimeScale(float scale)
581 {
582 timeScale_ = scale;
583 }
584
Ref()585 void Ecs::Ref() noexcept
586 {
587 refcnt_.fetch_add(1, std::memory_order_relaxed);
588 }
589
Unref()590 void Ecs::Unref() noexcept
591 {
592 if (std::atomic_fetch_sub_explicit(&refcnt_, 1, std::memory_order_release) == 1) {
593 std::atomic_thread_fence(std::memory_order_acquire);
594 delete this;
595 }
596 }
597
598 template<typename Container>
RemoveUid(Container & container,const Uid & uid)599 inline auto RemoveUid(Container& container, const Uid& uid)
600 {
601 container.erase(std::remove_if(container.begin(), container.end(),
602 [&uid](const auto& thing) { return thing->GetUid() == uid; }),
603 container.cend());
604 }
605
OnTypeInfoEvent(EventType type,array_view<const ITypeInfo * const> typeInfos)606 void Ecs::OnTypeInfoEvent(EventType type, array_view<const ITypeInfo* const> typeInfos)
607 {
608 if (type == EventType::ADDED) {
609 // not really interesed in these events. systems and component managers are added when SystemGraphLoader parses
610 // a configuration. we could store them in systems_ and managers_ and only define the order based on the graph.
611 } else if (type == EventType::REMOVED) {
612 for (const auto* info : typeInfos) {
613 if (info && info->typeUid == SystemTypeInfo::UID) {
614 const auto systemInfo = static_cast<const SystemTypeInfo*>(info);
615 // for systems Untinitialize should be called before destroying the instance
616 if (const auto pos = systems_.find(systemInfo->uid); pos != systems_.cend()) {
617 pos->second->Uninitialize();
618 systems_.erase(pos);
619 }
620 RemoveUid(systemOrder_, systemInfo->uid);
621 } else if (info && info->typeUid == ComponentManagerTypeInfo::UID) {
622 const auto managerInfo = static_cast<const ComponentManagerTypeInfo*>(info);
623 // BaseManager expects that the component list is empty when it's destroyed. might be also
624 // nice to notify all the listeners that the components are being destroyed.
625 if (const auto pos = managers_.find(managerInfo->uid); (pos != managers_.end()) && (pos->second)) {
626 auto* manager = pos->second;
627
628 // remove all the components.
629 const auto components = static_cast<IComponentManager::ComponentId>(manager->GetComponentCount());
630 for (IComponentManager::ComponentId i = 0; i < components; ++i) {
631 manager->Destroy(manager->GetEntity(i));
632 }
633
634 // check are there generic or specific component listeners to inform.
635 if (const auto listenerIt = componentManagerListeners_.find(manager);
636 !componentListeners_.empty() ||
637 ((listenerIt != componentManagerListeners_.end()) && !listenerIt->second.empty())) {
638 if (const vector<Entity> removed = manager->GetRemovedComponents(); !removed.empty()) {
639 const auto removedView = array_view<const Entity>(removed);
640 for (auto* lister : componentListeners_) {
641 if (lister) {
642 lister->OnComponentEvent(
643 ComponentListener::EventType::DESTROYED, *manager, removedView);
644 }
645 }
646 if (listenerIt != componentManagerListeners_.end()) {
647 for (auto* lister : listenerIt->second) {
648 if (lister) {
649 lister->OnComponentEvent(
650 ComponentListener::EventType::DESTROYED, *manager, removedView);
651 }
652 }
653 // remove all the listeners for this manager. RemoveListener won't do anything. this
654 // isn't neccessary, but rather not leave invalid manager pointer even if it's just used
655 // as the key.
656 componentManagerListeners_.erase(listenerIt);
657 }
658 }
659 }
660 // garbage collection will remove dead entries from the list and BaseManager is happy.
661 manager->Gc();
662 managers_.erase(pos);
663 }
664 RemoveUid(managerOrder_, managerInfo->uid);
665 }
666 }
667 }
668 }
669 } // namespace
670
IEcsInstance(IClassFactory & registry,const IThreadPool::Ptr & threadPool)671 IEcs* IEcsInstance(IClassFactory& registry, const IThreadPool::Ptr& threadPool)
672 {
673 return new Ecs(registry, threadPool);
674 }
675 CORE_END_NAMESPACE()
676