Beispiel #1
0
 private void DoAdd(RuntimeEntity added)
 {
     if (_triggerModified != null)
     {
         added.ModificationNotifier.Listener += ModificationNotifier_Listener;
     }
 }
Beispiel #2
0
        /// <summary>
        /// Internal method to add an entity to the entity manager and register it with all
        /// associated systems. This executes the add immediately.
        /// </summary>
        /// <param name="toAdd">The entity to add.</param>
        private void InternalAddEntity(RuntimeEntity toAdd)
        {
            toAdd.GameEngine = this;

            // notify listeners that we both added and created the entity
            Log <GameEngine> .Info("Submitting internal EntityAddedEvent for " + toAdd);

            EventNotifier.Submit(EntityAddedEvent.Create(toAdd));
            EventNotifier.Submit(ShowEntityEvent.Create(toAdd));

            // add it to the entity index
            _entityIndex.AddEntity(toAdd);

            // register listeners
            toAdd.ModificationNotifier.Listener    += OnEntityModified;
            toAdd.DataStateChangeNotifier.Listener += OnEntityDataStateChanged;

            // notify ourselves of data state changes so that it the entity is pushed to systems
            toAdd.DataStateChangeNotifier.Notify();

            // ensure it contains metadata for our keys
            toAdd.Metadata.UnorderedListMetadata[_entityUnorderedListMetadataKey] = new UnorderedListMetadata();

            // add it our list of entities
            _entities.Add(toAdd, GetEntitiesListFromMetadata(toAdd));
        }
Beispiel #3
0
 /// <summary>
 /// Ensures that an Entity is not in the cache.
 /// </summary>
 /// <returns>True if the entity was previously in the cache and was removed, false if it
 /// was not in the cache and was therefore not removed.</returns>
 public bool Remove(RuntimeEntity entity)
 {
     if (CachedEntities.Remove(entity, GetMetadata(entity)))
     {
         return(true);
     }
     return(false);
 }
Beispiel #4
0
 public static RuntimeEntity CreateRuntimeEntity() {
     RuntimeEntity entity = new RuntimeEntity();
     entity.Initialize(new ContentEntitySerializationFormat() {
         Data = new List<ContentEntity.DataInstance>(),
         PrettyName = "",
         UniqueId = _idGenerator.Next()
     }, new EventNotifier());
     return entity;
 }
Beispiel #5
0
 /// <summary>
 /// Called when an entity that is contained within the cache has been modified.
 /// </summary>
 /// <remarks>
 /// This function is only called if we have a modification trigger to invoke.
 /// </remarks>
 private void ModificationNotifier_Listener(RuntimeEntity entity)
 {
     // Notice that we cannot check to see if the entity passes the modification filter here,
     // because this callback is just informing us that the entity has been modified; it does
     // not mean that the entity is done being modified.
     //
     // In other words, the entity may fail the modification filter check now, but at a later
     // point in time the check may succeed.
     _notifiedModifiedEntities.Add(entity);
 }
Beispiel #6
0
        public IEntity Instantiate()
        {
            int           id            = _gameEngine.EntityIdGenerator.Next();
            EventNotifier eventNotifier = _gameEngine.EventNotifier;

            RuntimeEntity entity = new RuntimeEntity(id, this, eventNotifier);

            _gameEngine.AddEntity(entity);

            return(entity);
        }
Beispiel #7
0
        public void Restore(RuntimeEntity entity)
        {
            // if we don't have an entity cache then we have nothing to restore
            if (_entityCache == null)
            {
                return;
            }

            if (_entityCache.UpdateCache(entity) == EntityCache.CacheChangeResult.Added)
            {
                DoAdd(entity);
            }
        }
Beispiel #8
0
            /// <summary>
            /// Returns the CachedEntities metadata for the given entity.
            /// </summary>
            private UnorderedListMetadata GetMetadata(RuntimeEntity entity)
            {
                // get our unordered list metadata or create it
                UnorderedListMetadata metadata;

                if (entity.Metadata.UnorderedListMetadata.TryGetValue(_metadataKey, out metadata) == false)
                {
                    metadata = new UnorderedListMetadata();
                    entity.Metadata.UnorderedListMetadata[_metadataKey] = metadata;
                }

                return(metadata);
            }
Beispiel #9
0
        private void DoRemove(RuntimeEntity removed)
        {
            // if we removed an entity from the cache, then we don't want to hear of any more
            // modification events
            if (_triggerModified != null)
            {
                removed.ModificationNotifier.Listener -= ModificationNotifier_Listener;

                // We have to remove the entity from our list of entities to dispatch, as during the
                // frame when OnRemoved is called OnModified (and also OnUpdate) will not be
                // invoked.
                _dispatchModified.Remove(removed);
            }
        }
        /// <summary>
        /// Returns an entity instance for the given entity UniqueId. If an instance for the given
        /// id already exists, then it is returned. Otherwise, either a RuntimeEntity or
        /// ContentEntity is created.
        /// </summary>
        /// <param name="entityId">The id of the entity to get an instance for.</param>
        /// <param name="context">The GameEngineContext, used to determine if we should create a
        /// ContentTemplate or RuntimeTemplate instance.</param>
        public IEntity GetEntityInstance(int entityId, GameEngineContext context) {
            if (CreatedEntities.ContainsKey(entityId)) {
                return CreatedEntities[entityId];
            }

            IEntity entity;
            if (context.GameEngine.IsEmpty) {
                entity = new ContentEntity();
            }
            else {
                entity = new RuntimeEntity();
            }

            CreatedEntities[entityId] = entity;
            return entity;
        }
Beispiel #11
0
            /// <summary>
            /// Updates the status of the entity inside of the cache; ie, if the entity is now
            /// passing the filter but was not before, then it will be added to the cache.
            /// </summary>
            /// <returns>The change in cache status for the entity</returns>
            public CacheChangeResult UpdateCache(RuntimeEntity entity)
            {
                UnorderedListMetadata metadata = GetMetadata(entity);

                bool passed   = _filter.Check(entity);
                bool contains = CachedEntities.Contains(entity, metadata);

                // The entity is not in the cache it now passes the filter, so add it to the cache
                if (contains == false && passed)
                {
                    CachedEntities.Add(entity, metadata);
                    return(CacheChangeResult.Added);
                }

                // The entity is in the cache but it no longer passes the filter, so remove it
                if (contains && passed == false)
                {
                    CachedEntities.Remove(entity, metadata);
                    return(CacheChangeResult.Removed);
                }

                // no change to the cache
                return(CacheChangeResult.NoChange);
            }
            /// <summary>
            /// Returns the CachedEntities metadata for the given entity.
            /// </summary>
            private UnorderedListMetadata GetMetadata(RuntimeEntity entity) {
                // get our unordered list metadata or create it
                UnorderedListMetadata metadata;
                if (entity.Metadata.UnorderedListMetadata.TryGetValue(_metadataKey, out metadata) == false) {
                    metadata = new UnorderedListMetadata();
                    entity.Metadata.UnorderedListMetadata[_metadataKey] = metadata;
                }

                return metadata;
            }
 /// <summary>
 /// Ensures that an Entity is not in the cache.
 /// </summary>
 /// <returns>True if the entity was previously in the cache and was removed, false if it
 /// was not in the cache and was therefore not removed.</returns>
 public bool Remove(RuntimeEntity entity) {
     if (CachedEntities.Remove(entity, GetMetadata(entity))) {
         return true;
     }
     return false;
 }
Beispiel #14
0
 /// <summary>
 /// Called when an entity has data state changes
 /// </summary>
 private void OnEntityDataStateChanged(RuntimeEntity sender) {
     _notifiedStateChangeEntities.Add(sender);
 }
        public void Restore(RuntimeEntity entity) {
            // if we don't have an entity cache then we have nothing to restore
            if (_entityCache == null) {
                return;
            }

            if (_entityCache.UpdateCache(entity) == EntityCache.CacheChangeResult.Added) {
                DoAdd(entity);
            }
        }
Beispiel #16
0
        public IEntity Instantiate() {
            int id = _gameEngine.EntityIdGenerator.Next();
            EventNotifier eventNotifier = _gameEngine.EventNotifier;

            RuntimeEntity entity = new RuntimeEntity(id, this, eventNotifier);

            _gameEngine.AddEntity(entity);

            return entity;
        }
Beispiel #17
0
 /// <summary>
 /// Called when an entity has data state changes
 /// </summary>
 private void OnEntityDataStateChanged(RuntimeEntity sender)
 {
     _notifiedStateChangeEntities.Add(sender);
 }
Beispiel #18
0
 /// <summary>
 /// Called when an Entity has been modified.
 /// </summary>
 private void OnEntityModified(RuntimeEntity sender)
 {
     _notifiedModifiedEntities.Add(sender);
 }
Beispiel #19
0
 /// <summary>
 /// Helper method that returns the _entities unordered list metadata.
 /// </summary>
 private UnorderedListMetadata GetEntitiesListFromMetadata(RuntimeEntity entity)
 {
     return(entity.Metadata.UnorderedListMetadata[_entityUnorderedListMetadataKey]);
 }
Beispiel #20
0
 /// <summary>
 /// Removes the given entity from the world.
 /// </summary>
 /// <param name="instance">The entity instance to remove</param>
 internal void RemoveEntity(RuntimeEntity instance)
 {
     _notifiedRemovedEntities.Add(instance);
     EventNotifier.Submit(HideEntityEvent.Create(instance));
 }
Beispiel #21
0
        private void UpdateEntitiesWithStateChanges()
        {
            // update our list of added and removed entities
            _addedEntities.Clear();
            _removedEntities.Clear();
            _notifiedAddingEntities.IterateAndClear(entity => _addedEntities.Add(entity));
            _notifiedRemovedEntities.IterateAndClear(entity => _removedEntities.Add(entity));

            ++UpdateNumber;

            // Add entities
            for (int i = 0; i < _addedEntities.Count; ++i)
            {
                RuntimeEntity toAdd = _addedEntities[i];

                InternalAddEntity(toAdd);

                // apply initialization changes
                toAdd.ApplyModifications();
            }
            // can't clear b/c it is shared

            // copy our state change entities notice that we do this after adding entities, because
            // adding entities triggers the data state change notifier
            _notifiedStateChangeEntities.IterateAndClear(entity => _stateChangeEntities.Add(entity));

            // Remove entities
            for (int i = 0; i < _removedEntities.Count; ++i)
            {
                RuntimeEntity toRemove = _removedEntities[i];

                // remove listeners
                toRemove.ModificationNotifier.Listener    -= OnEntityModified;
                toRemove.DataStateChangeNotifier.Listener -= OnEntityDataStateChanged;

                // remove all data from the entity and then push said changes out
                foreach (DataAccessor accessor in toRemove.SelectData())
                {
                    toRemove.RemoveData(accessor);
                }
                toRemove.DataStateChangeUpdate();

                // remove the entity from the list of entities
                _entities.Remove(toRemove, GetEntitiesListFromMetadata(toRemove));
                EventNotifier.Submit(DestroyedEntityEvent.Create(toRemove));

                // notify listeners we removed an event
                EventNotifier.Submit(EntityRemovedEvent.Create(toRemove));

                // remove it from the index
                _entityIndex.RemoveEntity(toRemove);
            }
            // can't clear b/c it is shared

            // Do a data state change on the given items.
            {
                int i = 0;
                while (i < _stateChangeEntities.Count)
                {
                    if (_stateChangeEntities[i].NeedsMoreDataStateChangeUpdates() == false)
                    {
                        // reset the notifier so it can be added to the _stateChangeEntities again
                        _stateChangeEntities[i].DataStateChangeNotifier.Reset();
                        _stateChangeEntities.RemoveAt(i);
                    }
                    else
                    {
                        _stateChangeEntities[i].DataStateChangeUpdate();
                        ++i;
                    }
                }
            }

            // apply the modifications to the modified entities this data is not shared, so we can
            // clear it
            _notifiedModifiedEntities.IterateAndClear(modified => {
                modified.ApplyModifications();
            });

            // update the global entity
            _globalEntity.ApplyModifications();
            _globalEntity.DataStateChangeUpdate();
        }
Beispiel #22
0
 /// <summary>
 /// Called when an Entity has been modified.
 /// </summary>
 private void OnEntityModified(RuntimeEntity sender) {
     _notifiedModifiedEntities.Add(sender);
 }
Beispiel #23
0
 /// <summary>
 /// Helper method that returns the _entities unordered list metadata.
 /// </summary>
 private UnorderedListMetadata GetEntitiesListFromMetadata(RuntimeEntity entity) {
     return entity.Metadata.UnorderedListMetadata[_entityUnorderedListMetadataKey];
 }
Beispiel #24
0
        /// <summary>
        /// Internal method to add an entity to the entity manager and register it with all
        /// associated systems. This executes the add immediately.
        /// </summary>
        /// <param name="toAdd">The entity to add.</param>
        private void InternalAddEntity(RuntimeEntity toAdd) {
            toAdd.GameEngine = this;

            // notify listeners that we both added and created the entity
            Log<GameEngine>.Info("Submitting internal EntityAddedEvent for " + toAdd);
            EventNotifier.Submit(EntityAddedEvent.Create(toAdd));
            EventNotifier.Submit(ShowEntityEvent.Create(toAdd));

            // add it to the entity index
            _entityIndex.AddEntity(toAdd);

            // register listeners
            toAdd.ModificationNotifier.Listener += OnEntityModified;
            toAdd.DataStateChangeNotifier.Listener += OnEntityDataStateChanged;

            // notify ourselves of data state changes so that it the entity is pushed to systems
            toAdd.DataStateChangeNotifier.Notify();

            // ensure it contains metadata for our keys
            toAdd.Metadata.UnorderedListMetadata[_entityUnorderedListMetadataKey] = new UnorderedListMetadata();

            // add it our list of entities
            _entities.Add(toAdd, GetEntitiesListFromMetadata(toAdd));
        }
Beispiel #25
0
        /// <summary>
        /// Runs bookkeeping on the system. All systems concurrently run this function. This
        /// function makes an *extremely* important guarantee that there will be no external API
        /// calls made that can modify the state of other systems that are currently executing.
        /// </summary>
        public void BookkeepingBeforeRunningSystems()
        {
            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();

            try {
                // if we don't have entity cache (for example, the system could just have a
                // GlobalInput trigger), then we don't need to do any bookkeeping
                if (_entityCache == null)
                {
                    return;
                }

                // copy our modified entities into our dispatch modified list we do this before
                // state changes so that we only have to remove from _dispatchModified and not
                // _notifiedModifiedEntities
                if (_triggerModified != null)
                {
                    _dispatchModified.Clear();
                    _notifiedModifiedEntities.IterateAndClear(entity => _dispatchModified.Add(entity));
                }

                // process entities that were added to the system
                for (int i = 0; i < _shared.AddedEntities.Count; ++i)
                {
                    RuntimeEntity added = _shared.AddedEntities[i];
                    if (_entityCache.UpdateCache(added) == EntityCache.CacheChangeResult.Added)
                    {
                        DoAdd(added);
                        _dispatchAdded.Add(added);
                    }
                }
                PerformanceData.AddedTicks = stopwatch.ElapsedTicks;

                // process state changes
                for (int i = 0; i < _shared.StateChangedEntities.Count; ++i)   // immutable
                {
                    RuntimeEntity stateChanged = _shared.StateChangedEntities[i];
                    switch (_entityCache.UpdateCache(stateChanged))
                    {
                    case EntityCache.CacheChangeResult.Added:
                        DoAdd(stateChanged);
                        _dispatchAdded.Add(stateChanged);
                        break;

                    case EntityCache.CacheChangeResult.Removed:
                        DoRemove(stateChanged);
                        _dispatchRemoved.Add(stateChanged);
                        break;
                    }
                }
                PerformanceData.StateChangeTicks = stopwatch.ElapsedTicks - PerformanceData.AddedTicks;

                // process entities that were removed from the system
                for (int i = 0; i < _shared.RemovedEntities.Count; ++i)
                {
                    RuntimeEntity removed = _shared.RemovedEntities[i];
                    if (_entityCache.Remove(removed))
                    {
                        DoRemove(removed);
                        _dispatchRemoved.Add(removed);
                    }
                }
                PerformanceData.RemovedTicks = stopwatch.ElapsedTicks - PerformanceData.AddedTicks - PerformanceData.StateChangeTicks;
            }
            catch (Exception e) {
                _shared.Exceptions.Add(e);
            }
            finally {
                PerformanceData.BookkeepingTicks = stopwatch.ElapsedTicks;
            }
        }
        private void DoRemove(RuntimeEntity removed) {
            // if we removed an entity from the cache, then we don't want to hear of any more
            // modification events
            if (_triggerModified != null) {
                removed.ModificationNotifier.Listener -= ModificationNotifier_Listener;

                // We have to remove the entity from our list of entities to dispatch, as during the
                // frame when OnRemoved is called OnModified (and also OnUpdate) will not be
                // invoked.
                _dispatchModified.Remove(removed);
            }
        }
Beispiel #27
0
 /// <summary>
 /// Removes the given entity from the world.
 /// </summary>
 /// <param name="instance">The entity instance to remove</param>
 internal void RemoveEntity(RuntimeEntity instance) {
     _notifiedRemovedEntities.Add(instance);
     EventNotifier.Submit(HideEntityEvent.Create(instance));
 }
            /// <summary>
            /// Updates the status of the entity inside of the cache; ie, if the entity is now
            /// passing the filter but was not before, then it will be added to the cache.
            /// </summary>
            /// <returns>The change in cache status for the entity</returns>
            public CacheChangeResult UpdateCache(RuntimeEntity entity) {
                UnorderedListMetadata metadata = GetMetadata(entity);

                bool passed = _filter.Check(entity);
                bool contains = CachedEntities.Contains(entity, metadata);

                // The entity is not in the cache it now passes the filter, so add it to the cache
                if (contains == false && passed) {
                    CachedEntities.Add(entity, metadata);
                    return CacheChangeResult.Added;
                }

                // The entity is in the cache but it no longer passes the filter, so remove it
                if (contains && passed == false) {
                    CachedEntities.Remove(entity, metadata);
                    return CacheChangeResult.Removed;
                }

                // no change to the cache
                return CacheChangeResult.NoChange;
            }
 /// <summary>
 /// Called when an entity that is contained within the cache has been modified.
 /// </summary>
 /// <remarks>
 /// This function is only called if we have a modification trigger to invoke.
 /// </remarks>
 private void ModificationNotifier_Listener(RuntimeEntity entity) {
     // Notice that we cannot check to see if the entity passes the modification filter here,
     // because this callback is just informing us that the entity has been modified; it does
     // not mean that the entity is done being modified.
     //
     // In other words, the entity may fail the modification filter check now, but at a later
     // point in time the check may succeed.
     _notifiedModifiedEntities.Add(entity);
 }
Beispiel #30
0
        public GameEngine(string snapshotJson, string templateJson)
        {
            _templateJson = templateJson;

            // Create our own little island of references with its own set of templates
            GameSnapshotRestorer restorer = GameSnapshotRestorer.Restore(
                snapshotJson, templateJson, Maybe.Just(this));
            GameSnapshot snapshot = restorer.GameSnapshot;

            EntityIdGenerator = snapshot.EntityIdGenerator;

            _systems     = snapshot.Systems;
            _entityIndex = new EntityIndex();

            // TODO: ensure that when correctly restore UpdateNumber
            //UpdateNumber = updateNumber;

            _globalEntity = (RuntimeEntity)snapshot.GlobalEntity;

            EventNotifier.Submit(EntityAddedEvent.Create(_globalEntity));

            foreach (var entity in snapshot.AddedEntities)
            {
                AddEntity((RuntimeEntity)entity);
            }

            foreach (var entity in snapshot.ActiveEntities)
            {
                RuntimeEntity runtimeEntity = (RuntimeEntity)entity;

                // add the entity
                InternalAddEntity(runtimeEntity);

                // TODO: verify that if the modification notifier is already triggered, we can
                // ignore this
                //if (((ContentEntity)entity).HasModification) {
                //    runtimeEntity.ModificationNotifier.Notify();
                //}

                // done via InternalAddEntity
                //if (deserializedEntity.HasStateChange) {
                //    deserializedEntity.Entity.DataStateChangeNotifier.Notify();
                //}
            }

            foreach (var entity in snapshot.RemovedEntities)
            {
                RuntimeEntity runtimeEntity = (RuntimeEntity)entity;

                // add the entity
                InternalAddEntity(runtimeEntity);

                // TODO: verify that if the modification notifier is already triggered, we can
                // ignore this
                //if (((ContentEntity)entity).HasModification) {
                //    runtimeEntity.ModificationNotifier.Notify();
                //}

                // done via InternalAddEntity
                //if (deserializedEntity.HasStateChange) {
                //    deserializedEntity.Entity.DataStateChangeNotifier.Notify();
                //}

                RemoveEntity(runtimeEntity);
            }

            TemplateIndex templateIndex = new TemplateIndex(restorer.Templates.Templates);

            _executionGroups = new List <ExecutionGroup>();
            var executionGroups = SystemExecutionGroup.GetExecutionGroups(snapshot.Systems);

            foreach (var executionGroup in executionGroups)
            {
                List <MultithreadedSystem> multithreadedSystems = new List <MultithreadedSystem>();
                foreach (var system in executionGroup.Systems)
                {
                    MultithreadedSystem multithreaded = CreateMultithreadedSystem(system, templateIndex);
                    multithreadedSystems.Add(multithreaded);
                }
                _executionGroups.Add(new ExecutionGroup(multithreadedSystems));
            }

            _nextState = GameEngineNextState.SynchronizeState;
            SynchronizeState().Wait();

            // call of the engine loaded systems
            foreach (var group in _executionGroups)
            {
                foreach (var system in group.Systems)
                {
                    var onLoaded = system.System as Trigger.OnEngineLoaded;
                    if (onLoaded != null)
                    {
                        onLoaded.OnEngineLoaded(EventNotifier);

                        // TODO: verify that OnEngineLoaded didn't change any state (via hashing)
                    }
                }
            }
        }
 private void DoAdd(RuntimeEntity added) {
     if (_triggerModified != null) {
         added.ModificationNotifier.Listener += ModificationNotifier_Listener;
     }
 }
Beispiel #32
0
        public GameEngine(string snapshotJson, string templateJson) {
            _templateJson = templateJson;

            // Create our own little island of references with its own set of templates
            GameSnapshotRestorer restorer = GameSnapshotRestorer.Restore(
                snapshotJson, templateJson, Maybe.Just(this));
            GameSnapshot snapshot = restorer.GameSnapshot;

            EntityIdGenerator = snapshot.EntityIdGenerator;

            _systems = snapshot.Systems;
            _entityIndex = new EntityIndex();

            // TODO: ensure that when correctly restore UpdateNumber
            //UpdateNumber = updateNumber;

            _globalEntity = (RuntimeEntity)snapshot.GlobalEntity;

            EventNotifier.Submit(EntityAddedEvent.Create(_globalEntity));

            foreach (var entity in snapshot.AddedEntities) {
                AddEntity((RuntimeEntity)entity);
            }

            foreach (var entity in snapshot.ActiveEntities) {
                RuntimeEntity runtimeEntity = (RuntimeEntity)entity;

                // add the entity
                InternalAddEntity(runtimeEntity);

                // TODO: verify that if the modification notifier is already triggered, we can
                // ignore this
                //if (((ContentEntity)entity).HasModification) {
                //    runtimeEntity.ModificationNotifier.Notify();
                //}

                // done via InternalAddEntity
                //if (deserializedEntity.HasStateChange) {
                //    deserializedEntity.Entity.DataStateChangeNotifier.Notify();
                //}
            }

            foreach (var entity in snapshot.RemovedEntities) {
                RuntimeEntity runtimeEntity = (RuntimeEntity)entity;

                // add the entity
                InternalAddEntity(runtimeEntity);

                // TODO: verify that if the modification notifier is already triggered, we can
                // ignore this
                //if (((ContentEntity)entity).HasModification) {
                //    runtimeEntity.ModificationNotifier.Notify();
                //}

                // done via InternalAddEntity
                //if (deserializedEntity.HasStateChange) {
                //    deserializedEntity.Entity.DataStateChangeNotifier.Notify();
                //}

                RemoveEntity(runtimeEntity);
            }

            TemplateIndex templateIndex = new TemplateIndex(restorer.Templates.Templates);

            _executionGroups = new List<ExecutionGroup>();
            var executionGroups = SystemExecutionGroup.GetExecutionGroups(snapshot.Systems);
            foreach (var executionGroup in executionGroups) {
                List<MultithreadedSystem> multithreadedSystems = new List<MultithreadedSystem>();
                foreach (var system in executionGroup.Systems) {
                    MultithreadedSystem multithreaded = CreateMultithreadedSystem(system, templateIndex);
                    multithreadedSystems.Add(multithreaded);
                }
                _executionGroups.Add(new ExecutionGroup(multithreadedSystems));
            }

            _nextState = GameEngineNextState.SynchronizeState;
            SynchronizeState().Wait();

            // call of the engine loaded systems
            foreach (var group in _executionGroups) {
                foreach (var system in group.Systems) {
                    var onLoaded = system.System as Trigger.OnEngineLoaded;
                    if (onLoaded != null) {
                        onLoaded.OnEngineLoaded(EventNotifier);

                        // TODO: verify that OnEngineLoaded didn't change any state (via hashing)
                    }
                }
            }
        }