/// <inheritdoc /> public void DisposeComponents(EntityUid uid) { foreach (var kvTypeDict in _dictComponents) { if (kvTypeDict.Value.TryGetValue(uid, out var comp)) { #if EXCEPTION_TOLERANCE try { #endif comp.Running = false; #if EXCEPTION_TOLERANCE } catch (Exception e) { _runtimeLog.LogException(e, $"DisposeComponents comp.Running=false, owner={uid}, type={comp.GetType()}"); } #endif } } foreach (var kvTypeDict in _dictComponents) { // because we are iterating over references instead of instances, and a comp instance // can have multiple references, we filter out already deleted instances. if (kvTypeDict.Value.TryGetValue(uid, out var comp) && !comp.Deleted) { RemoveComponentDeferred(comp, true); } } }
// Parameter is used only on release. // ReSharper disable once UnusedParameter.Global public void Update(float frameTime, IRuntimeLog runtimeLog) { if (IsActive) { _timeCounter -= (int)(frameTime * 1000); if (_timeCounter <= 0) { #if EXCEPTION_TOLERANCE try #endif { OnFired(); } #if EXCEPTION_TOLERANCE catch (Exception e) { runtimeLog.LogException(e, "Timer Callback"); } #endif if (IsRepeating) { _timeCounter = Time; } else { IsActive = false; } } } }
/// <summary> /// Start running the loop. This function will block for as long as the loop is Running. /// Set Running to false to exit the loop and return from this function. /// </summary> public void Run() { if (_timing.TickRate <= 0) { throw new InvalidOperationException("TickRate must be greater than 0."); } Running = true; FrameEventArgs realFrameEvent; FrameEventArgs simFrameEvent; _timing.ResetRealTime(); while (Running) { // maximum number of ticks to queue before the loop slows down. var maxTime = TimeSpan.FromTicks(_timing.TickPeriod.Ticks * MaxQueuedTicks); var accumulator = _timing.RealTime - _lastTick; // If the game can't keep up, limit time. if (accumulator > maxTime) { // limit accumulator to max time. accumulator = maxTime; // pull lastTick up to the current realTime // This will slow down the simulation, but if we are behind from a // lag spike hopefully it will be able to catch up. _lastTick = _timing.RealTime - maxTime; // announce we are falling behind if ((_timing.RealTime - _lastKeepUp).TotalSeconds >= 15.0) { Logger.WarningS("eng", "MainLoop: Cannot keep up!"); _lastKeepUp = _timing.RealTime; } } _timing.StartFrame(); realFrameEvent = new FrameEventArgs((float)_timing.RealFrameTime.TotalSeconds); #if EXCEPTION_TOLERANCE try #endif { // process Net/KB/Mouse input Input?.Invoke(this, realFrameEvent); } #if EXCEPTION_TOLERANCE catch (Exception exp) { _runtimeLog.LogException(exp, "GameLoop Input"); } #endif _timing.InSimulation = true; var tickPeriod = CalcTickPeriod(); // run the simulation for every accumulated tick while (accumulator >= tickPeriod) { accumulator -= tickPeriod; _lastTick += tickPeriod; // only run the simulation if unpaused, but still use up the accumulated time if (_timing.Paused) { continue; } // update the simulation simFrameEvent = new FrameEventArgs((float)_timing.FrameTime.TotalSeconds); #if EXCEPTION_TOLERANCE var threw = false; try { #endif if (EnableMetrics) { using (_frameTimeHistogram.NewTimer()) { Tick?.Invoke(this, simFrameEvent); } } else { Tick?.Invoke(this, simFrameEvent); } #if EXCEPTION_TOLERANCE } catch (Exception exp) { threw = true; _runtimeLog.LogException(exp, "GameLoop Tick"); _tickExceptions += 1; if (_tickExceptions > MaxSoftLockExceptions && DetectSoftLock) { Logger.FatalS("eng", "MainLoop: 10 consecutive exceptions inside GameLoop Tick, shutting down!"); Running = false; } } if (!threw) { _tickExceptions = 0; } #endif _timing.CurTick = new GameTick(_timing.CurTick.Value + 1); tickPeriod = CalcTickPeriod(); if (SingleStep) { _timing.Paused = true; } } // if not paused, save how close to the next tick we are so interpolation works if (!_timing.Paused) { _timing.TickRemainder = accumulator; } _timing.InSimulation = false; // update out of the simulation #if EXCEPTION_TOLERANCE try #endif { Update?.Invoke(this, realFrameEvent); } #if EXCEPTION_TOLERANCE catch (Exception exp) { _runtimeLog.LogException(exp, "GameLoop Update"); } #endif // render the simulation #if EXCEPTION_TOLERANCE try #endif { Render?.Invoke(this, realFrameEvent); } #if EXCEPTION_TOLERANCE catch (Exception exp) { _runtimeLog.LogException(exp, "GameLoop Render"); } #endif // Set sleep to 1 if you want to be nice and give the rest of the timeslice up to the os scheduler. // Set sleep to 0 if you want to use 100% cpu, but still cooperate with the scheduler. // do not call sleep if you want to be 'that thread' and hog 100% cpu. if (SleepMode != SleepMode.None) { Thread.Sleep((int)SleepMode); } } }
private void HandleEntityState(IComponentManager compMan, IEntity entity, EntityState curState, EntityState nextState) { var compStateWork = new Dictionary <uint, (ComponentState curState, ComponentState nextState)>(); var entityUid = entity.Uid; if (curState?.ComponentChanges != null) { foreach (var compChange in curState.ComponentChanges) { if (compChange.Deleted) { if (compMan.TryGetComponent(entityUid, compChange.NetID, out var comp)) { compMan.RemoveComponent(entityUid, comp); } } else { if (compMan.HasComponent(entityUid, compChange.NetID)) { continue; } var newComp = (Component)_compFactory.GetComponent(compChange.ComponentName); newComp.Owner = entity; compMan.AddComponent(entity, newComp, true); } } } if (curState?.ComponentStates != null) { foreach (var compState in curState.ComponentStates) { compStateWork[compState.NetID] = (compState, null); } } if (nextState?.ComponentStates != null) { foreach (var compState in nextState.ComponentStates) { if (compStateWork.TryGetValue(compState.NetID, out var state)) { compStateWork[compState.NetID] = (state.curState, compState); } else { compStateWork[compState.NetID] = (null, compState); } } } foreach (var kvStates in compStateWork) { if (!compMan.TryGetComponent(entityUid, kvStates.Key, out var component)) { var eUid = entityUid; var eExpectedNetUid = kvStates.Key; var eRegisteredNetUidName = _compFactory.GetRegistration(eExpectedNetUid).Name; DebugTools.Assert($"Component does not exist for state: entUid={eUid}, expectedNetId={eExpectedNetUid}, expectedName={eRegisteredNetUidName}"); continue; } try { component.HandleComponentState(kvStates.Value.curState, kvStates.Value.nextState); } catch (Exception e) { var wrapper = new ComponentStateApplyException( $"Failed to apply comp state: entity={component.Owner}, comp={component.Name}", e); #if EXCEPTION_TOLERANCE _runtimeLog.LogException(wrapper, "Component state apply"); #else throw wrapper; #endif } } }
private void HandleEntityState(IComponentManager compMan, IEntity entity, EntityState?curState, EntityState?nextState) { var compStateWork = new Dictionary <uint, (ComponentState?curState, ComponentState?nextState)>(); var entityUid = entity.Uid; if (curState?.ComponentChanges != null) { foreach (var compChange in curState.ComponentChanges) { if (compChange.Deleted) { if (compMan.TryGetComponent(entityUid, compChange.NetID, out var comp)) { compMan.RemoveComponent(entityUid, comp); } } else { if (compMan.HasComponent(entityUid, compChange.NetID)) { continue; } var newComp = (Component)_compFactory.GetComponent(compChange.ComponentName !); newComp.Owner = entity; compMan.AddComponent(entity, newComp, true); } } } if (curState?.ComponentStates != null) { foreach (var compState in curState.ComponentStates) { compStateWork[compState.NetID] = (compState, null); } } if (nextState?.ComponentStates != null) { foreach (var compState in nextState.ComponentStates) { if (compStateWork.TryGetValue(compState.NetID, out var state)) { compStateWork[compState.NetID] = (state.curState, compState); } else { compStateWork[compState.NetID] = (null, compState); } } } foreach (var(netId, (cur, next)) in compStateWork) { if (compMan.TryGetComponent(entityUid, netId, out var component)) { try { component.HandleComponentState(cur, next); } catch (Exception e) { var wrapper = new ComponentStateApplyException( $"Failed to apply comp state: entity={component.Owner}, comp={component.Name}", e); #if EXCEPTION_TOLERANCE _runtimeLog.LogException(wrapper, "Component state apply"); #else throw wrapper; #endif } } else { // The component can be null here due to interp. // Because the NEXT state will have a new component, but this one doesn't yet. // That's fine though. if (cur == null) { continue; } var eUid = entityUid; var eRegisteredNetUidName = _compFactory.GetRegistration(netId).Name; DebugTools.Assert( $"Component does not exist for state: entUid={eUid}, expectedNetId={netId}, expectedName={eRegisteredNetUidName}"); } } }
/// <summary> /// Start running the loop. This function will block for as long as the loop is Running. /// Set Running to false to exit the loop and return from this function. /// </summary> public void Run() { if (_timing.TickRate <= 0) { throw new InvalidOperationException("TickRate must be greater than 0."); } Running = true; // maximum number of ticks to queue before the loop slows down. var maxTime = TimeSpan.FromTicks(_timing.TickPeriod.Ticks * MaxQueuedTicks); var realFrameEvent = new MutableFrameEventArgs(0); var simFrameEvent = new MutableFrameEventArgs(0); _timing.ResetRealTime(); while (Running) { var accumulator = _timing.RealTime - _lastTick; // If the game can't keep up, limit time. if (accumulator > maxTime) { // limit accumulator to max time. accumulator = maxTime; // pull lastTick up to the current realTime // This will slow down the simulation, but if we are behind from a // lag spike hopefully it will be able to catch up. _lastTick = _timing.RealTime - maxTime; // announce we are falling behind if ((_timing.RealTime - _lastKeepUp).TotalSeconds >= 15.0) { Logger.Warning("[ENG] MainLoop: Cannot keep up!"); _lastKeepUp = _timing.RealTime; } } _timing.StartFrame(); realFrameEvent.SetDeltaSeconds((float)_timing.RealFrameTime.TotalSeconds); try { // process Net/KB/Mouse input Input?.Invoke(this, realFrameEvent); } catch (Exception exp) { _runtimeLog.LogException(exp, "GameLoop Input"); } _timing.InSimulation = true; // run the simulation for every accumulated tick while (accumulator >= _timing.TickPeriod) { accumulator -= _timing.TickPeriod; _lastTick += _timing.TickPeriod; // only run the simulation if unpaused, but still use up the accumulated time if (_timing.Paused) { continue; } // update the simulation simFrameEvent.SetDeltaSeconds((float)_timing.FrameTime.TotalSeconds); try { Tick?.Invoke(this, simFrameEvent); } catch (Exception exp) { _runtimeLog.LogException(exp, "GameLoop Tick"); } _timing.CurTick++; if (SingleStep) { _timing.Paused = true; } } // if not paused, save how close to the next tick we are so interpolation works if (!_timing.Paused) { _timing.TickRemainder = accumulator; } _timing.InSimulation = false; // update out of the simulation simFrameEvent.SetDeltaSeconds((float)_timing.FrameTime.TotalSeconds); try { Update?.Invoke(this, simFrameEvent); } catch (Exception exp) { _runtimeLog.LogException(exp, "GameLoop Update"); } // render the simulation try { Render?.Invoke(this, realFrameEvent); } catch (Exception exp) { _runtimeLog.LogException(exp, "GameLoop Render"); } // Set sleep to 1 if you want to be nice and give the rest of the timeslice up to the os scheduler. // Set sleep to 0 if you want to use 100% cpu, but still cooperate with the scheduler. // do not call sleep if you want to be 'that thread' and hog 100% cpu. if (SleepMode != SleepMode.None) { Thread.Sleep((int)SleepMode); } } }