mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Adds basic delta state support (#3492)
This commit is contained in:
@@ -192,7 +192,7 @@ namespace Robust.Client.GameStates
|
||||
AckGameState(message.State.ToSequence);
|
||||
}
|
||||
|
||||
public void UpdateFullRep(GameState state) => _processor.UpdateFullRep(state);
|
||||
public void UpdateFullRep(GameState state, bool cloneDelta = false) => _processor.UpdateFullRep(state, cloneDelta);
|
||||
|
||||
private void HandlePvsLeaveMessage(MsgStateLeavePvs message)
|
||||
{
|
||||
@@ -578,8 +578,12 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var (netId, component) in _entityManager.GetNetComponents(createdEntity))
|
||||
{
|
||||
if (component.NetSyncEnabled)
|
||||
compData.Add(netId, _entityManager.GetComponentState(bus, component, _players.LocalPlayer?.Session));
|
||||
if (!component.NetSyncEnabled)
|
||||
continue;
|
||||
|
||||
var state = _entityManager.GetComponentState(bus, component, _players.LocalPlayer?.Session, GameTick.Zero);
|
||||
DebugTools.Assert(state is not IComponentDeltaState delta || delta.FullState);
|
||||
compData.Add(netId, state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Client.Timing;
|
||||
@@ -152,7 +151,7 @@ namespace Robust.Client.GameStates
|
||||
return applyNextState;
|
||||
}
|
||||
|
||||
public void UpdateFullRep(GameState state)
|
||||
public void UpdateFullRep(GameState state, bool cloneDelta = false)
|
||||
{
|
||||
// Note: the most recently received server state currently doesn't include pvs-leave messages (detaching
|
||||
// transform to null-space). This is because a client should never predict an entity being moved back from
|
||||
@@ -177,11 +176,36 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
compData = new Dictionary<ushort, ComponentState>();
|
||||
_lastStateFullRep.Add(entityState.Uid, compData);
|
||||
|
||||
#if DEBUG
|
||||
foreach (var comp in entityState.ComponentChanges.Span)
|
||||
{
|
||||
DebugTools.Assert(comp.State is not IComponentDeltaState delta || delta.FullState);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
foreach (var change in entityState.ComponentChanges.Span)
|
||||
{
|
||||
compData[change.NetID] = change.State;
|
||||
var compState = change.State;
|
||||
|
||||
if (compState is IComponentDeltaState delta && !delta.FullState)
|
||||
{
|
||||
var old = compData[change.NetID];
|
||||
|
||||
if (cloneDelta)
|
||||
{
|
||||
compState = delta.CreateNewFullState(old);
|
||||
}
|
||||
else
|
||||
{
|
||||
delta.ApplyToFullState(old);
|
||||
compState = old;
|
||||
}
|
||||
DebugTools.Assert(compState is IComponentDeltaState newState && newState.FullState);
|
||||
}
|
||||
|
||||
compData[change.NetID] = compState;
|
||||
}
|
||||
|
||||
if (entityState.NetComponents == null)
|
||||
|
||||
@@ -95,7 +95,12 @@ namespace Robust.Client.GameStates
|
||||
|
||||
uint SystemMessageDispatched<T>(T message) where T : EntityEventArgs;
|
||||
|
||||
void UpdateFullRep(GameState state);
|
||||
/// <summary>
|
||||
/// Updates the cached game sates that are used to reset predicted entities.
|
||||
/// </summary>
|
||||
/// <param name="cloneDelta">If true, this will clone old states while applying delta states, rather than
|
||||
/// modifying them directly. Useful if they are still cached elsewhere (e.g., replays).</param>
|
||||
void UpdateFullRep(GameState state, bool cloneDelta = false);
|
||||
|
||||
/// <summary>
|
||||
/// This will perform some setup in order to reset the game to an earlier state by deleting entities and
|
||||
|
||||
@@ -1154,7 +1154,8 @@ internal sealed partial class PVSSystem : EntitySystem
|
||||
if (component.SessionSpecific && !EntityManager.CanGetComponentState(bus, component, player))
|
||||
continue;
|
||||
|
||||
var state = EntityManager.GetComponentState(bus, component, component.SessionSpecific ? player : null);
|
||||
var state = EntityManager.GetComponentState(bus, component, player, fromTick);
|
||||
DebugTools.Assert(fromTick > component.CreationTick || state is not IComponentDeltaState delta || delta.FullState);
|
||||
changed.Add(new ComponentChange(netId, state, component.LastModifiedTick));
|
||||
|
||||
if (sendCompList)
|
||||
@@ -1188,7 +1189,9 @@ internal sealed partial class PVSSystem : EntitySystem
|
||||
if (component.SessionSpecific && !EntityManager.CanGetComponentState(bus, component, player))
|
||||
continue;
|
||||
|
||||
changed.Add(new ComponentChange(netId, EntityManager.GetComponentState(bus, component, component.SessionSpecific ? player : null), component.LastModifiedTick));
|
||||
var state = EntityManager.GetComponentState(bus, component, player, GameTick.Zero);
|
||||
DebugTools.Assert(state is not IComponentDeltaState delta || delta.FullState);
|
||||
changed.Add(new ComponentChange(netId, state, component.LastModifiedTick));
|
||||
netComps.Add(netId);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
@@ -8,4 +7,26 @@ namespace Robust.Shared.GameObjects
|
||||
[Serializable, NetSerializable]
|
||||
[Virtual]
|
||||
public class ComponentState { }
|
||||
|
||||
/// <summary>
|
||||
/// Interface for components that support delta-states.
|
||||
/// </summary>
|
||||
public interface IComponentDeltaState
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether this state is a delta or full state.
|
||||
/// </summary>
|
||||
bool FullState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This function will apply the current delta state to the provided full state, modifying it in the process.
|
||||
/// </summary>
|
||||
public void ApplyToFullState(ComponentState fullState);
|
||||
|
||||
/// <summary>
|
||||
/// This function should take in a full state and return a new full state with the current delta applied,
|
||||
/// WITHOUT modifying the original input state.
|
||||
/// </summary>
|
||||
public ComponentState CreateNewFullState(ComponentState fullState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Log;
|
||||
using System.Diagnostics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Timing;
|
||||
#if EXCEPTION_TOLERANCE
|
||||
using Robust.Shared.Exceptions;
|
||||
#endif
|
||||
@@ -1270,10 +1271,10 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ComponentState GetComponentState(IEventBus eventBus, IComponent component, ICommonSession? session)
|
||||
public ComponentState GetComponentState(IEventBus eventBus, IComponent component, ICommonSession? session, GameTick fromTick)
|
||||
{
|
||||
DebugTools.Assert(component.NetSyncEnabled, $"Attempting to get component state for an un-synced component: {component.GetType()}");
|
||||
var getState = new ComponentGetState(session);
|
||||
var getState = new ComponentGetState(session, fromTick);
|
||||
eventBus.RaiseComponentEvent(component, ref getState);
|
||||
|
||||
return getState.State ?? component.GetComponentState();
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
@@ -371,9 +372,10 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
/// <param name="eventBus">A reference to the event bus instance.</param>
|
||||
/// <param name="component">Component to generate the state for.</param>
|
||||
/// <param name="player">The player that is going to receive this state. Null implies that this state is for a replay.</param>
|
||||
/// <param name="fromTick">The from tick, which indicates the range of data that must be included for delta-states.</param>
|
||||
/// <returns>The component state of the component.</returns>
|
||||
///
|
||||
ComponentState GetComponentState(IEventBus eventBus, IComponent component, ICommonSession? player);
|
||||
ComponentState GetComponentState(IEventBus eventBus, IComponent component, ICommonSession? player, GameTick fromTick);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a certain player should get a component state.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.GameStates
|
||||
{
|
||||
@@ -22,19 +23,28 @@ namespace Robust.Shared.GameStates
|
||||
[ByRefEvent, ComponentEvent]
|
||||
public struct ComponentGetState
|
||||
{
|
||||
public GameTick FromTick { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Output parameter. Set this to the component's state for the player.
|
||||
/// </summary>
|
||||
public ComponentState? State { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Input parameter. The player the state is being sent to.
|
||||
/// If true, this state is intended for replays or some other server spectator entity, not for specific
|
||||
/// clients.
|
||||
/// </summary>
|
||||
public bool ReplayState => Player == null;
|
||||
|
||||
/// <summary>
|
||||
/// The player the state is being sent to. Null implies the state is for a replay or some spectator entity.
|
||||
/// </summary>
|
||||
public readonly ICommonSession? Player;
|
||||
|
||||
public ComponentGetState(ICommonSession? player)
|
||||
public ComponentGetState(ICommonSession? player, GameTick fromTick)
|
||||
{
|
||||
Player = player;
|
||||
FromTick = fromTick;
|
||||
State = null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user