Session specific state support (#3249)

This commit is contained in:
Leon Friedrich
2022-09-14 21:29:46 +12:00
committed by GitHub
parent fa5fdcb5a5
commit d87d6f7f8c
8 changed files with 113 additions and 18 deletions

View File

@@ -567,7 +567,7 @@ namespace Robust.Client.GameStates
foreach (var (netId, component) in _entityManager.GetNetComponents(createdEntity))
{
if (component.NetSyncEnabled)
compData.Add(netId, _entityManager.GetComponentState(bus, component));
compData.Add(netId, _entityManager.GetComponentState(bus, component, _players.LocalPlayer?.Session));
}
}

View File

@@ -8,6 +8,76 @@ public sealed class ServerMetaDataSystem : MetaDataSystem
{
[Dependency] private readonly PVSSystem _pvsSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MetaDataComponent, PlayerAttachedEvent>(OnActorPlayerAttach);
EntityManager.ComponentAdded += OnComponentAdded;
EntityManager.ComponentRemoved += OnComponentRemoved;
}
public override void Shutdown()
{
base.Shutdown();
EntityManager.ComponentAdded -= OnComponentAdded;
EntityManager.ComponentRemoved -= OnComponentRemoved;
}
/// <summary>
/// If a session-specific component gets added, make sure the meta-data flag is set.
/// </summary>
private void OnComponentAdded(AddedComponentEventArgs obj)
{
var comp = obj.BaseArgs.Component;
if (comp.NetSyncEnabled && (comp.SessionSpecific || comp.SendOnlyToOwner))
MetaData(obj.BaseArgs.Owner).Flags |= MetaDataFlags.SessionSpecific;
}
/// <summary>
/// If a session-specific component gets removed, this will update the meta-data flag.
/// </summary>
private void OnComponentRemoved(RemovedComponentEventArgs obj)
{
var removed = obj.BaseArgs.Component;
if (!removed.NetSyncEnabled || (!removed.SessionSpecific && !removed.SendOnlyToOwner))
return;
var meta = MetaData(obj.BaseArgs.Owner);
if (meta.EntityLifeStage >= EntityLifeStage.Terminating)
return;
foreach (var (_, comp) in EntityManager.GetNetComponents(obj.BaseArgs.Owner))
{
if (comp.LifeStage >= ComponentLifeStage.Removing)
continue;
if (comp.NetSyncEnabled && (comp.SessionSpecific || comp.SendOnlyToOwner))
return; // keep the flag
}
// remove the flag
meta.Flags &= ~MetaDataFlags.SessionSpecific;
}
/// <summary>
/// If a new player gets attached to an entity, this will ensure that the player receives session-restricted
/// component states by dirtying any restricted components.
/// </summary>
private void OnActorPlayerAttach(EntityUid uid, MetaDataComponent meta, PlayerAttachedEvent args)
{
if ((meta.Flags & MetaDataFlags.SessionSpecific) == 0)
return;
foreach (var (_, comp) in EntityManager.GetNetComponents(uid))
{
if (comp.SessionSpecific || comp.SendOnlyToOwner)
Dirty(comp);
}
}
public override void SetVisibilityMask(EntityUid uid, int value, MetaDataComponent? meta = null)
{
if (!Resolve(uid, ref meta) || meta.VisibilityMask == value)

View File

@@ -1051,9 +1051,6 @@ internal sealed partial class PVSSystem : EntitySystem
var bus = EntityManager.EventBus;
var changed = new List<ComponentChange>();
// Whether this entity has any component states that should only be sent to specific sessions.
var entitySpecific = (meta.Flags & MetaDataFlags.EntitySpecific) == MetaDataFlags.EntitySpecific;
foreach (var (netId, component) in EntityManager.GetNetComponents(entityUid))
{
if (!component.NetSyncEnabled)
@@ -1083,10 +1080,10 @@ internal sealed partial class PVSSystem : EntitySystem
if (component.SendOnlyToOwner && player.AttachedEntity != component.Owner)
continue;
if (entitySpecific && !EntityManager.CanGetComponentState(bus, component, player))
if (component.SessionSpecific && !EntityManager.CanGetComponentState(bus, component, player))
continue;
var state = changedState ? EntityManager.GetComponentState(bus, component) : null;
var state = changedState ? EntityManager.GetComponentState(bus, component, component.SessionSpecific ? player : null) : null;
changed.Add(ComponentChange.Added(netId, state, component.LastModifiedTick));
}
@@ -1105,7 +1102,6 @@ internal sealed partial class PVSSystem : EntitySystem
{
var bus = EntityManager.EventBus;
var changed = new List<ComponentChange>();
var entitySpecific = (meta.Flags & MetaDataFlags.EntitySpecific) == MetaDataFlags.EntitySpecific;
foreach (var (netId, component) in EntityManager.GetNetComponents(entityUid))
{
@@ -1115,10 +1111,10 @@ internal sealed partial class PVSSystem : EntitySystem
if (component.SendOnlyToOwner && player.AttachedEntity != component.Owner)
continue;
if (entitySpecific && !EntityManager.CanGetComponentState(bus, component, player))
if (component.SessionSpecific && !EntityManager.CanGetComponentState(bus, component, player))
continue;
changed.Add(ComponentChange.Added(netId, EntityManager.GetComponentState(bus, component), component.LastModifiedTick));
changed.Add(ComponentChange.Added(netId, EntityManager.GetComponentState(bus, component, component.SessionSpecific ? player : null), component.LastModifiedTick));
}
foreach (var netId in _serverEntManager.GetDeletedComponents(entityUid, GameTick.Zero))

View File

@@ -23,7 +23,16 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
[DataField("netsync")]
public bool NetSyncEnabled { get; set; } = true;
[ViewVariables(VVAccess.ReadWrite)]
public bool _netSync { get; set; } = true;
internal bool Networked = true;
public bool NetSyncEnabled
{
get => Networked && _netSync;
set => _netSync = value;
}
/// <inheritdoc />
[ViewVariables]
@@ -35,11 +44,17 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// If true, and if this is a networked component, then component data will only be sent to players if their
/// controlled entity is the owner of this component. This is a faster alternative to <see
/// cref="MetaDataFlags.EntitySpecific"/>.
/// controlled entity is the owner of this component. This is less performance intensive than <see cref="SessionSpecific"/>.
/// </summary>
public virtual bool SendOnlyToOwner => false;
/// <summary>
/// If true, and if this is a networked component, then this component will cause <see
/// cref="ComponentGetStateAttemptEvent"/> events to be raised to check whether a given player should
/// receive this component's state.
/// </summary>
public virtual bool SessionSpecific => false;
/// <summary>
/// Increases the life stage from <see cref="ComponentLifeStage.PreAdd" /> to <see cref="ComponentLifeStage.Added" />,
/// after raising a <see cref="ComponentAdd"/> event.

View File

@@ -205,10 +205,9 @@ namespace Robust.Shared.GameObjects
None = 0,
/// <summary>
/// Whether the entity has states specific to particular players. This will cause many state-attempt events to
/// be raised, and is generally somewhat expensive.
/// Whether the entity has any component that has state information specific to particular players.
/// </summary>
EntitySpecific = 1 << 0,
SessionSpecific = 1 << 0,
/// <summary>
/// Whether the entity is currently inside of a container.

View File

@@ -320,6 +320,10 @@ namespace Robust.Shared.GameObjects
// mark the component as dirty for networking
Dirty(component);
}
else
{
component.Networked = false;
}
var eventArgs = new AddedComponentEventArgs(new ComponentEventArgs(component, uid), reg.Idx);
ComponentAdded?.Invoke(eventArgs);
@@ -1167,10 +1171,10 @@ namespace Robust.Shared.GameObjects
}
/// <inheritdoc />
public ComponentState GetComponentState(IEventBus eventBus, IComponent component)
public ComponentState GetComponentState(IEventBus eventBus, IComponent component, ICommonSession? session)
{
DebugTools.Assert(component.NetSyncEnabled, $"Attempting to get component state for an un-synced component: {component.GetType()}");
var getState = new ComponentGetState();
var getState = new ComponentGetState(session);
eventBus.RaiseComponentEvent(component, ref getState);
return getState.State ?? component.GetComponentState();

View File

@@ -365,7 +365,7 @@ namespace Robust.Shared.GameObjects
/// <param name="component">Component to generate the state for.</param>
/// <returns>The component state of the component.</returns>
///
ComponentState GetComponentState(IEventBus eventBus, IComponent component);
ComponentState GetComponentState(IEventBus eventBus, IComponent component, ICommonSession? player);
/// <summary>
/// Checks if a certain player should get a component state.

View File

@@ -26,6 +26,17 @@ namespace Robust.Shared.GameStates
/// 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.
/// </summary>
public readonly ICommonSession? Player;
public ComponentGetState(ICommonSession? player)
{
Player = player;
State = null;
}
}
[ByRefEvent, ComponentEvent]