Files
RobustToolbox/Robust.Server/ViewVariables/ViewVariablesSession.cs
DrSmugleaf cb384b8242 Make VV work with structs in components (#6377)
* Make VV work with structs in components

* Fix missing imports
2026-01-19 20:41:24 +01:00

152 lines
5.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Robust.Server.ViewVariables.Traits;
using Robust.Shared.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Network;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Robust.Server.ViewVariables
{
internal sealed class ViewVariablesSession : IViewVariablesSession
{
private readonly List<ViewVariablesTrait> _traits = new();
private readonly ISawmill _logger;
public IServerViewVariablesInternal Host { get; }
public IRobustSerializer RobustSerializer { get; }
public IEntityManager EntityManager { get; }
public NetUserId PlayerUser { get; }
public object Object { get; }
public uint SessionId { get; }
public Type ObjectType { get; }
public Action<object>? ObjectChangeDelegate { get; }
/// <param name="playerUser">The session ID of the player who opened this session.</param>
/// <param name="o">The object we represent.</param>
/// <param name="sessionId">
/// The session ID for this session. This is what the server and client use to talk about this session.
/// </param>
/// <param name="host">The view variables host owning this session.</param>
public ViewVariablesSession(NetUserId playerUser, object o, Action<object>? objectChangeDelegate, uint sessionId, IServerViewVariablesInternal host,
IRobustSerializer robustSerializer, IEntityManager entMan, ISawmill logger)
{
PlayerUser = playerUser;
Object = o;
SessionId = sessionId;
ObjectType = o.GetType();
ObjectChangeDelegate = objectChangeDelegate;
Host = host;
RobustSerializer = robustSerializer;
EntityManager = entMan;
_logger = logger;
var traitIds = Host.TraitIdsFor(ObjectType);
if (traitIds.Contains(ViewVariablesTraits.Members))
{
var trait = new ViewVariablesTraitMembers(this, _logger);
_traits.Add(trait);
}
if (traitIds.Contains(ViewVariablesTraits.Enumerable))
{
var trait = new ViewVariablesTraitEnumerable(this);
_traits.Add(trait);
}
if (traitIds.Contains(ViewVariablesTraits.Entity))
{
var trait = new ViewVariablesTraitEntity(this);
_traits.Add(trait);
}
}
public ViewVariablesBlob? DataRequest(ViewVariablesRequest messageRequestMeta)
{
if (messageRequestMeta is ViewVariablesRequestMetadata)
{
return new ViewVariablesBlobMetadata
{
ObjectType = ObjectType.AssemblyQualifiedName,
ObjectTypePretty = TypeAbbreviation.Abbreviate(ObjectType),
Stringified = Object.ToString(),
Traits = new List<object>(Host.TraitIdsFor(ObjectType))
};
}
foreach (var trait in _traits)
{
var blob = trait.DataRequest(messageRequestMeta);
if (blob != null)
{
return blob;
}
}
return null;
}
public void Modify(object[] propertyIndex, object value)
{
foreach (var trait in _traits)
{
if (trait.TryModifyProperty(propertyIndex, value))
{
break;
}
}
// Auto-dirty component. Only works when modifying a field that is directly on a component,
// Does not work for nested objects.
if (Object is Component { NetSyncEnabled: true } comp)
EntityManager.Dirty(comp.Owner, comp);
}
public bool TryGetRelativeObject(object[] propertyIndex, out object? value)
{
// First property chain entry is for the trait, rest is value.
value = default;
foreach (var trait in _traits)
{
if (trait.TryGetRelativeObject(propertyIndex[0], out value))
{
goto found;
}
}
return false;
// Yes I just used goto.
// The fuck you gonna do about it?
found:
for (var i = 1; i < propertyIndex.Length; i++)
{
var selector = propertyIndex[i];
switch (selector)
{
case ViewVariablesSelectorKeyValuePair kvPair:
if (value == null ||
!value.GetType().IsGenericType ||
value.GetType().GetGenericTypeDefinition() != typeof(KeyValuePair<,>))
return false;
dynamic kv = value;
value = kvPair.Key ? kv.Key : kv.Value;
break;
case ViewVariablesTupleIndexSelector indexSelector
when value is ITuple tuple:
value = indexSelector.Index <= tuple.Length - 1 ? tuple[indexSelector.Index] : null;
break;
}
}
return true;
}
}
}