mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Public state reset function. (#3539)
This commit is contained in:
@@ -265,6 +265,8 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
_processor.LastFullStateRequested = null;
|
||||
_timing.LastProcessedTick = curState.ToSequence;
|
||||
DebugTools.Assert(curState.FromSequence == GameTick.Zero);
|
||||
PartialStateReset(curState, true, true);
|
||||
}
|
||||
else
|
||||
_timing.LastProcessedTick += 1;
|
||||
@@ -632,9 +634,6 @@ namespace Robust.Client.GameStates
|
||||
var toCreate = new Dictionary<EntityUid, EntityState>();
|
||||
var enteringPvs = 0;
|
||||
|
||||
if (curState.FromSequence == GameTick.Zero)
|
||||
FullReset(curState, metas, xforms, xformSys);
|
||||
|
||||
var curSpan = curState.EntityStates.Span;
|
||||
foreach (var es in curSpan)
|
||||
{
|
||||
@@ -776,39 +775,71 @@ namespace Robust.Client.GameStates
|
||||
return (toCreate.Keys, detached);
|
||||
}
|
||||
|
||||
private void FullReset(GameState curState, EntityQuery<MetaDataComponent> metas, EntityQuery<TransformComponent> xforms, SharedTransformSystem xformSys)
|
||||
/// <inheritdoc />
|
||||
public void PartialStateReset(GameState state, bool resetAllEnts, bool deleteClientSideEnts)
|
||||
{
|
||||
// This is somewhat of a hack to do a proper reset for exception tolerance. I need to do this properly at
|
||||
// some point. This is basically a hotfix to hide the side effects of #3413. Basically, this gets run if the
|
||||
// client encounters an error while applying game states and requests a full server state.
|
||||
using var _ = _timing.StartStateApplicationArea();
|
||||
|
||||
_sawmill.Info("Performing full entity reset.");
|
||||
if (state.FromSequence != GameTick.Zero)
|
||||
{
|
||||
_sawmill.Error("Attempted to reset to a state with incomplete data");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO properly reset maps
|
||||
// TODO properly reset player-states (e.g., attached enttiy and current eye).
|
||||
_sawmill.Info($"Resetting all entity states to tick {state.ToSequence}.");
|
||||
|
||||
// Linq bad, but this should be rare (when first connecting, and when encountering PVS errors, which ideally would just never happen).
|
||||
var ents = curState.EntityStates.Value?.Select(x => x.Uid)?.ToHashSet() ?? new HashSet<EntityUid>();
|
||||
foreach (var ent in _entities.GetEntities().ToArray())
|
||||
// Construct hashset for set.Contains() checks.
|
||||
var entityStates = state.EntityStates.Span;
|
||||
var stateEnts = new HashSet<EntityUid>(entityStates.Length);
|
||||
foreach (var entState in entityStates)
|
||||
{
|
||||
stateEnts.Add(entState.Uid);
|
||||
}
|
||||
|
||||
var metas = _entities.GetEntityQuery<MetaDataComponent>();
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
var xformSys = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
|
||||
|
||||
var currentEnts = _entities.GetEntities();
|
||||
var toDelete = new List<EntityUid>(Math.Max(64, currentEnts.Count() - stateEnts.Count()));
|
||||
foreach (var ent in currentEnts)
|
||||
{
|
||||
if (ent.IsClientSide())
|
||||
continue;
|
||||
|
||||
if (ents.Contains(ent) && metas.TryGetComponent(ent, out var meta))
|
||||
{
|
||||
meta.LastStateApplied = GameTick.Zero;
|
||||
if (deleteClientSideEnts)
|
||||
toDelete.Add(ent);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stateEnts.Contains(ent) && metas.TryGetComponent(ent, out var meta))
|
||||
{
|
||||
if (resetAllEnts || meta.LastStateApplied > state.ToSequence)
|
||||
meta.LastStateApplied = GameTick.Zero; // TODO track last-state-applied for individual components? Is it even worth it?
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!xforms.TryGetComponent(ent, out var xform))
|
||||
continue;
|
||||
|
||||
// this entity is going to get deleted, but maybe some if its children won't be, so lets detach them to null.
|
||||
xformSys.DetachParentToNull(xform, xforms, metas);
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
xformSys.DetachParentToNull(xforms.GetComponent(child.Value), xforms, metas, xform);
|
||||
|
||||
if (!deleteClientSideEnts && child.Value.IsClientSide())
|
||||
{
|
||||
// Even though we aren't meant to be deleting client-side entities here, this client-side entity is getting detached to null space, so we will delete it anyways).
|
||||
_sawmill.Warning($"Deleting client-side entity ({_entities.ToPrettyString(child.Value)}) as it was parented to a server side entity that is getting deleted ({_entities.ToPrettyString(ent)})");
|
||||
toDelete.Add(child.Value);
|
||||
}
|
||||
}
|
||||
toDelete.Add(ent);
|
||||
}
|
||||
|
||||
foreach (var ent in toDelete)
|
||||
{
|
||||
_entities.DeleteEntity(ent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,5 +96,17 @@ namespace Robust.Client.GameStates
|
||||
uint SystemMessageDispatched<T>(T message) where T : EntityEventArgs;
|
||||
|
||||
void UpdateFullRep(GameState state);
|
||||
|
||||
/// <summary>
|
||||
/// This will perform some setup in order to reset the game to an earlier state by deleting entities and
|
||||
/// marking ensuring component states will get applied properly. <see cref="ApplyGameState(GameState,
|
||||
/// GameState?)"/> Still needs to be called separately after this is run.
|
||||
/// </summary>
|
||||
/// <param name="state">The state to reset to.</param>
|
||||
/// <param name="resetAllEnts">Whether to apply component states to all entities, or only those that have been
|
||||
/// modified some time after the state's to-sequence</param>
|
||||
/// <param name="deleteClientSideEnts">Whether to delete all client-side entities (which are never part of the
|
||||
/// networked game state).</param>
|
||||
void PartialStateReset(GameState state, bool resetAllEnts, bool deleteClientSideEnts);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user