]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Stop network serializing prototypes (#38602)
authorPieter-Jan Briers <pieterjan.briers+git@gmail.com>
Thu, 26 Jun 2025 23:27:23 +0000 (01:27 +0200)
committerGitHub <noreply@github.com>
Thu, 26 Jun 2025 23:27:23 +0000 (19:27 -0400)
* Stop network serializing prototypes

Send the damn proto ID instead.

* Fix sandbox violation

28 files changed:
Content.Client/Administration/AdminNameOverlay.cs
Content.Client/Administration/Systems/AdminSystem.Overlay.cs
Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabEntry.xaml.cs
Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs
Content.Server/Administration/Logs/AdminLogManager.cs
Content.Server/Administration/Systems/AdminSystem.cs
Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs
Content.Server/Speech/EntitySystems/MumbleAccentSystem.cs
Content.Server/Speech/EntitySystems/VocalSystem.cs
Content.Shared/Administration/PlayerInfo.cs
Content.Shared/Cargo/Prototypes/CargoBountyPrototype.cs
Content.Shared/Chat/Prototypes/EmoteSoundsPrototype.cs
Content.Shared/Damage/Prototypes/DamageContainerPrototype.cs
Content.Shared/Damage/Prototypes/DamageGroupPrototype.cs
Content.Shared/DeviceLinking/DevicePortPrototype.cs
Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs
Content.Shared/DeviceNetwork/DeviceFrequencyPrototype.cs
Content.Shared/DeviceNetwork/NetworkConfiguratorUserInterfaceState.cs
Content.Shared/Mind/RoleTypePrototype.cs
Content.Shared/Roles/AntagPrototype.cs
Content.Shared/Silicons/Laws/SiliconLawPrototype.cs
Content.Shared/Silicons/Laws/SiliconLawsetPrototype.cs
Content.Shared/Speech/Components/VocalComponent.cs
Content.Shared/Store/CurrencyPrototype.cs
Content.Shared/Store/ListingPrototype.cs
Content.Shared/Store/StoreCategoryPrototype.cs
Content.Shared/StoryGen/Prototypes/StoryTemplatePrototype.cs
Content.Shared/VendingMachines/VendingMachineInventoryPrototype.cs

index 0d424cbff047f0bac43bce692d28b417a70838d1..abeed657323b0ba2340a9a91eadf745205f58b48 100644 (file)
@@ -1,3 +1,4 @@
+using System.Collections.Frozen;
 using System.Linq;
 using System.Numerics;
 using Content.Client.Administration.Systems;
@@ -24,6 +25,7 @@ internal sealed class AdminNameOverlay : Overlay
     private readonly EntityLookupSystem _entityLookup;
     private readonly IUserInterfaceManager _userInterfaceManager;
     private readonly SharedRoleSystem _roles;
+    private readonly IPrototypeManager _prototypeManager;
     private readonly Font _font;
     private readonly Font _fontBold;
     private AdminOverlayAntagFormat _overlayFormat;
@@ -36,8 +38,9 @@ internal sealed class AdminNameOverlay : Overlay
     private float _overlayMergeDistance;
 
     //TODO make this adjustable via GUI?
-    private readonly ProtoId<RoleTypePrototype>[] _filter =
-        ["SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"];
+    private static readonly FrozenSet<ProtoId<RoleTypePrototype>> Filter =
+        new ProtoId<RoleTypePrototype>[] {"SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"}
+        .ToFrozenSet();
 
     private readonly string _antagLabelClassic = Loc.GetString("admin-overlay-antag-classic");
 
@@ -49,7 +52,8 @@ internal sealed class AdminNameOverlay : Overlay
         EntityLookupSystem entityLookup,
         IUserInterfaceManager userInterfaceManager,
         IConfigurationManager config,
-        SharedRoleSystem roles)
+        SharedRoleSystem roles,
+        IPrototypeManager prototypeManager)
     {
         _system = system;
         _entityManager = entityManager;
@@ -57,6 +61,7 @@ internal sealed class AdminNameOverlay : Overlay
         _entityLookup = entityLookup;
         _userInterfaceManager = userInterfaceManager;
         _roles = roles;
+        _prototypeManager = prototypeManager;
         ZIndex = 200;
         // Setting these to a specific ttf would break the antag symbols
         _font = resourceCache.NotoStack();
@@ -125,6 +130,14 @@ internal sealed class AdminNameOverlay : Overlay
         foreach (var info in sortable.OrderBy(s => s.Item4.Y).ToList())
         {
             var playerInfo = info.Item1;
+            var rolePrototype = playerInfo.RoleProto == null
+                ? null
+                : _prototypeManager.Index(playerInfo.RoleProto.Value);
+
+            var roleName = Loc.GetString(rolePrototype?.Name ?? RoleTypePrototype.FallbackName);
+            var roleColor = rolePrototype?.Color ?? RoleTypePrototype.FallbackColor;
+            var roleSymbol = rolePrototype?.Symbol ?? RoleTypePrototype.FallbackSymbol;
+
             var aabb = info.Item2;
             var entity = info.Item3;
             var screenCoordinatesCenter = info.Item4;
@@ -209,7 +222,7 @@ internal sealed class AdminNameOverlay : Overlay
             switch (_overlaySymbolStyle)
             {
                 case AdminOverlayAntagSymbolStyle.Specific:
-                    symbol = playerInfo.RoleProto.Symbol;
+                    symbol = roleSymbol;
                     break;
                 case AdminOverlayAntagSymbolStyle.Basic:
                     symbol = Loc.GetString("player-tab-antag-prefix");
@@ -225,17 +238,17 @@ internal sealed class AdminNameOverlay : Overlay
             switch (_overlayFormat)
             {
                 case AdminOverlayAntagFormat.Roletype:
-                    color = playerInfo.RoleProto.Color;
-                    symbol = _filter.Contains(playerInfo.RoleProto) ? symbol : string.Empty;
-                    text = _filter.Contains(playerInfo.RoleProto)
-                        ? Loc.GetString(playerInfo.RoleProto.Name).ToUpper()
+                    color = roleColor;
+                    symbol = IsFiltered(playerInfo.RoleProto) ? symbol : string.Empty;
+                    text = IsFiltered(playerInfo.RoleProto)
+                        ? roleName.ToUpper()
                         : string.Empty;
                     break;
                 case AdminOverlayAntagFormat.Subtype:
-                    color = playerInfo.RoleProto.Color;
-                    symbol = _filter.Contains(playerInfo.RoleProto) ? symbol : string.Empty;
-                    text = _filter.Contains(playerInfo.RoleProto)
-                        ? _roles.GetRoleSubtypeLabel(playerInfo.RoleProto.Name, playerInfo.Subtype).ToUpper()
+                    color = roleColor;
+                    symbol = IsFiltered(playerInfo.RoleProto) ? symbol : string.Empty;
+                    text = IsFiltered(playerInfo.RoleProto)
+                        ? _roles.GetRoleSubtypeLabel(roleName, playerInfo.Subtype).ToUpper()
                         : string.Empty;
                     break;
                 default:
@@ -258,4 +271,12 @@ internal sealed class AdminNameOverlay : Overlay
             drawnOverlays.Add((screenCoordinatesCenter, currentOffset));
         }
     }
+
+    private static bool IsFiltered(ProtoId<RoleTypePrototype>? roleProtoId)
+    {
+        if (roleProtoId == null)
+            return false;
+
+        return Filter.Contains(roleProtoId.Value);
+    }
 }
index a630df45213c4a6241d01f46dde032b6a071aea5..e000bdc0ba06719dd69e00d1a51e13ae91e1046b 100644 (file)
@@ -4,6 +4,7 @@ using Robust.Client.Graphics;
 using Robust.Client.ResourceManagement;
 using Robust.Client.UserInterface;
 using Robust.Shared.Configuration;
+using Robust.Shared.Prototypes;
 
 namespace Content.Client.Administration.Systems
 {
@@ -17,6 +18,7 @@ namespace Content.Client.Administration.Systems
         [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
         [Dependency] private readonly IConfigurationManager _configurationManager = default!;
         [Dependency] private readonly SharedRoleSystem _roles = default!;
+        [Dependency] private readonly IPrototypeManager _proto = default!;
 
         private AdminNameOverlay _adminNameOverlay = default!;
 
@@ -33,7 +35,8 @@ namespace Content.Client.Administration.Systems
                 _entityLookup,
                 _userInterfaceManager,
                 _configurationManager,
-                _roles);
+                _roles,
+                _proto);
             _adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
         }
 
index 41e3fca76121e8d1d4b70f9787cd0445f67f2197..6a2d939b4bbb6552f10039fd561cbf77fea2af3e 100644 (file)
@@ -1,9 +1,11 @@
 using Content.Shared.Administration;
+using Content.Shared.Mind;
 using Content.Shared.Roles;
 using Robust.Client.AutoGenerated;
 using Robust.Client.Graphics;
 using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
 
 namespace Content.Client.Administration.UI.Tabs.PlayerTab;
 
@@ -11,6 +13,7 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab;
 public sealed partial class PlayerTabEntry : PanelContainer
 {
     [Dependency] private readonly IEntityManager _entMan = default!;
+    [Dependency] private readonly IPrototypeManager _prototype = default!;
 
     public PlayerTabEntry(
         PlayerInfo player,
@@ -23,6 +26,8 @@ public sealed partial class PlayerTabEntry : PanelContainer
         RobustXamlLoader.Load(this);
         var roles = _entMan.System<SharedRoleSystem>();
 
+        var rolePrototype = player.RoleProto == null ? null : _prototype.Index(player.RoleProto.Value);
+
         UsernameLabel.Text = player.Username;
         if (!player.Connected)
             UsernameLabel.StyleClasses.Add("Disabled");
@@ -57,19 +62,19 @@ public sealed partial class PlayerTabEntry : PanelContainer
                 break;
             default:
             case AdminPlayerTabSymbolOption.Specific:
-                symbol = player.Antag ? player.RoleProto.Symbol : string.Empty;
+                symbol = player.Antag ? rolePrototype?.Symbol ?? RoleTypePrototype.FallbackSymbol : string.Empty;
                 break;
         }
 
         CharacterLabel.Text = Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", player.CharacterName));
 
         if (player.Antag && colorAntags)
-            CharacterLabel.FontColorOverride = player.RoleProto.Color;
+            CharacterLabel.FontColorOverride = rolePrototype?.Color ?? RoleTypePrototype.FallbackColor;
         if (player.IdentityName != player.CharacterName)
             CharacterLabel.Text += $" [{player.IdentityName}]";
 
-        var roletype = RoleTypeLabel.Text = Loc.GetString(player.RoleProto.Name);
-        var subtype = roles.GetRoleSubtypeLabel(player.RoleProto.Name, player.Subtype);
+        var roletype = RoleTypeLabel.Text = Loc.GetString(rolePrototype?.Name ?? RoleTypePrototype.FallbackName);
+        var subtype = roles.GetRoleSubtypeLabel(rolePrototype?.Name ?? RoleTypePrototype.FallbackName, player.Subtype);
         switch (roleSetting)
         {
             case AdminPlayerTabRoleTypeOption.RoleTypeSubtype:
@@ -92,7 +97,7 @@ public sealed partial class PlayerTabEntry : PanelContainer
         }
 
         if (colorRoles)
-            RoleTypeLabel.FontColorOverride = player.RoleProto.Color;
+            RoleTypeLabel.FontColorOverride = rolePrototype?.Color ?? RoleTypePrototype.FallbackColor;
         BackgroundColorPanel.PanelOverride = styleBoxFlat;
         OverallPlaytimeLabel.Text = player.PlaytimeString;
     }
index 8cdffd16af608e2e7e24bc6fcfbe30589b92692f..c627dc2ce8daaaf33dac63a3a907e3889d58cb78 100644 (file)
@@ -1,4 +1,5 @@
-using System.Numerics;
+using System.Linq;
+using System.Numerics;
 using Content.Client.UserInterface.Controls;
 using Content.Shared.DeviceLinking;
 using Content.Shared.DeviceNetwork;
@@ -14,6 +15,8 @@ namespace Content.Client.NetworkConfigurator;
 [GenerateTypedNameReferences]
 public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow
 {
+    private readonly IPrototypeManager _prototypeManager = null!;
+
     private const string PanelBgColor = "#202023";
 
     private readonly LinksRender _links;
@@ -33,6 +36,7 @@ public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow
     public NetworkConfiguratorLinkMenu()
     {
         RobustXamlLoader.Load(this);
+        IoCManager.InjectDependencies(this);
 
         var footerStyleBox = new StyleBoxFlat()
         {
@@ -61,7 +65,7 @@ public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow
         ButtonContainerRight.RemoveAllChildren();
 
         _sources.Clear();
-        _sources.AddRange(linkState.Sources);
+        _sources.AddRange(linkState.Sources.Select(s => _prototypeManager.Index(s)));
         _links.SourceButtons.Clear();
         var i = 0;
         foreach (var source in _sources)
@@ -73,7 +77,7 @@ public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow
         }
 
         _sinks.Clear();
-        _sinks.AddRange(linkState.Sinks);
+        _sinks.AddRange(linkState.Sinks.Select(s => _prototypeManager.Index(s)));
         _links.SinkButtons.Clear();
         i = 0;
         foreach (var sink in _sinks)
index 1d6ff13a5a586df655f6d2ce575f32e07d829b27..add51a777578537a6c044e3fadf524d4ee319885 100644 (file)
@@ -9,12 +9,14 @@ using Content.Shared.Administration.Logs;
 using Content.Shared.CCVar;
 using Content.Shared.Chat;
 using Content.Shared.Database;
+using Content.Shared.Mind;
 using Content.Shared.Players.PlayTimeTracking;
 using Prometheus;
 using Robust.Shared;
 using Robust.Shared.Configuration;
 using Robust.Shared.Network;
 using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Reflection;
 using Robust.Shared.Timing;
 
@@ -33,6 +35,7 @@ public sealed partial class AdminLogManager : SharedAdminLogManager, IAdminLogMa
     [Dependency] private readonly ISharedPlayerManager _player = default!;
     [Dependency] private readonly ISharedPlaytimeManager _playtime = default!;
     [Dependency] private readonly ISharedChatManager _chat = default!;
+    [Dependency] private readonly IPrototypeManager _proto = default!;
 
     public const string SawmillId = "admin.logs";
 
@@ -330,7 +333,8 @@ public sealed partial class AdminLogManager : SharedAdminLogManager, IAdminLogMa
                 var cachedInfo = adminSys.GetCachedPlayerInfo(new NetUserId(id));
                 if (cachedInfo != null && cachedInfo.Antag)
                 {
-                    var subtype = Loc.GetString(cachedInfo.Subtype ?? cachedInfo.RoleProto.Name);
+                    var proto = cachedInfo.RoleProto == null ? null : _proto.Index(cachedInfo.RoleProto.Value);
+                    var subtype = Loc.GetString(cachedInfo.Subtype ?? proto?.Name ?? RoleTypePrototype.FallbackName);
                     logMessage = Loc.GetString(
                         "admin-alert-antag-label",
                         ("message", logMessage),
index 0e5138ba9680d844012219379a00974dc3c0765f..95c6578a947b7ee336836872c20af7e939a9a647 100644 (file)
@@ -231,7 +231,7 @@ public sealed class AdminSystem : EntitySystem
         var antag = false;
 
         // Starting role, antagonist status and role type
-        RoleTypePrototype roleType = new();
+        RoleTypePrototype? roleType = null;
         var startingRole = string.Empty;
         LocId? subtype = null;
         if (_minds.TryGetMind(session, out var mindId, out var mindComp) && mindComp is not null)
@@ -244,7 +244,7 @@ public sealed class AdminSystem : EntitySystem
                 subtype = mindComp.Subtype;
             }
             else
-                Log.Error($"{ToPrettyString(mindId)} has invalid Role Type '{mindComp.RoleType}'. Displaying '{Loc.GetString(roleType.Name)}' instead");
+                Log.Error($"{ToPrettyString(mindId)} has invalid Role Type '{mindComp.RoleType}'. Displaying '{Loc.GetString(RoleTypePrototype.FallbackName)}' instead");
 
             antag = _role.MindIsAntagonist(mindId);
             startingRole = _jobs.MindTryGetJobName(mindId);
@@ -270,7 +270,7 @@ public sealed class AdminSystem : EntitySystem
             identityName,
             startingRole,
             antag,
-            roleType,
+            roleType?.ID,
             subtype,
             sortWeight,
             GetNetEntity(session?.AttachedEntity),
index 6bcbe30456193b6a09bf956b24b419f899db3575..e34929bd2e2489544bc59cf40a3f91fcac5dfa3a 100644 (file)
@@ -18,6 +18,7 @@ using Robust.Server.Audio;
 using Robust.Server.GameObjects;
 using Robust.Shared.Audio;
 using Robust.Shared.Map.Events;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Timing;
 using Robust.Shared.Utility;
 
@@ -496,14 +497,15 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
             return;
 
         var sources = _deviceLinkSystem.GetSourcePorts(sourceUid, sourceComponent);
-        var sinks = _deviceLinkSystem.GetSinkPorts(sinkUid, sinkComponent);
+        var sinks = _deviceLinkSystem.GetSinkPortIds((sinkUid, sinkComponent));
         var links = _deviceLinkSystem.GetLinks(sourceUid, sinkUid, sourceComponent);
         var defaults = _deviceLinkSystem.GetDefaults(sources);
+        var sourceIds = sources.Select(s => (ProtoId<SourcePortPrototype>)s.ID).ToArray();
 
         var sourceAddress = Resolve(sourceUid, ref sourceNetworkComponent, false) ? sourceNetworkComponent.Address : "";
         var sinkAddress = Resolve(sinkUid, ref sinkNetworkComponent, false) ? sinkNetworkComponent.Address : "";
 
-        var state = new DeviceLinkUserInterfaceState(sources, sinks, links, sourceAddress, sinkAddress, defaults);
+        var state = new DeviceLinkUserInterfaceState(sourceIds, sinks, links, sourceAddress, sinkAddress, defaults);
         _uiSystem.SetUiState(configuratorUid, NetworkConfiguratorUiKey.Link, state);
     }
 
index 6b1af5c227eb0449dc11dc95e910e03570f5c710..57469a25aae9dc0bed30b106c0b9b5bd3faf1001 100644 (file)
@@ -2,6 +2,7 @@ using Content.Server.Chat.Systems;
 using Content.Server.Speech.Components;
 using Content.Shared.Chat.Prototypes;
 using Content.Shared.Speech.Components;
+using Robust.Shared.Prototypes;
 
 namespace Content.Server.Speech.EntitySystems;
 
@@ -9,6 +10,7 @@ public sealed class MumbleAccentSystem : EntitySystem
 {
     [Dependency] private readonly ChatSystem _chat = default!;
     [Dependency] private readonly ReplacementAccentSystem _replacement = default!;
+    [Dependency] private readonly IPrototypeManager _prototype = default!;
 
     public override void Initialize()
     {
@@ -23,10 +25,14 @@ public sealed class MumbleAccentSystem : EntitySystem
         if (args.Handled || !args.Emote.Category.HasFlag(EmoteCategory.Vocal))
             return;
 
-        if (TryComp<VocalComponent>(ent.Owner, out var vocalComp))
+        if (TryComp<VocalComponent>(ent.Owner, out var vocalComp) && vocalComp.EmoteSounds is { } sounds)
         {
             // play a muffled version of the vocal emote
-            args.Handled = _chat.TryPlayEmoteSound(ent.Owner, vocalComp.EmoteSounds, args.Emote, ent.Comp.EmoteAudioParams);
+            args.Handled = _chat.TryPlayEmoteSound(
+                ent.Owner,
+                _prototype.Index(sounds),
+                args.Emote,
+                ent.Comp.EmoteAudioParams);
         }
     }
 
index 14fac0bab8f7ca9a3a75493591de36a4a6d2378c..fb882382886afc8fbb922fc4316f324689e15a1f 100644 (file)
@@ -64,8 +64,11 @@ public sealed class VocalSystem : EntitySystem
             return;
         }
 
+        if (component.EmoteSounds is not { } sounds)
+            return;
+
         // just play regular sound based on emote proto
-        args.Handled = _chat.TryPlayEmoteSound(uid, component.EmoteSounds, args.Emote);
+        args.Handled = _chat.TryPlayEmoteSound(uid, _proto.Index(sounds), args.Emote);
     }
 
     private void OnScreamAction(EntityUid uid, VocalComponent component, ScreamActionEvent args)
@@ -85,7 +88,10 @@ public sealed class VocalSystem : EntitySystem
             return true;
         }
 
-        return _chat.TryPlayEmoteSound(uid, component.EmoteSounds, component.ScreamId);
+        if (component.EmoteSounds is not { } sounds)
+            return false;
+
+        return _chat.TryPlayEmoteSound(uid, _proto.Index(sounds), component.ScreamId);
     }
 
     private void LoadSounds(EntityUid uid, VocalComponent component, Sex? sex = null)
@@ -97,6 +103,10 @@ public sealed class VocalSystem : EntitySystem
 
         if (!component.Sounds.TryGetValue(sex.Value, out var protoId))
             return;
-        _proto.TryIndex(protoId, out component.EmoteSounds);
+
+        if (!_proto.HasIndex(protoId))
+            return;
+
+        component.EmoteSounds = protoId;
     }
 }
index 516442534771fcdd7a9115731421bb2b25b01a9d..0f8dd814a5a948faeedfd85ca0fa96deb1ff96ed 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Shared.Mind;
 using Robust.Shared.Network;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Administration;
@@ -11,7 +12,7 @@ public sealed record PlayerInfo(
     string IdentityName,
     string StartingJob,
     bool Antag,
-    RoleTypePrototype RoleProto,
+    ProtoId<RoleTypePrototype>? RoleProto,
     LocId? Subtype,
     int SortWeight,
     NetEntity? NetEntity,
index 38ca2286eefbbe161a34a065a74df7ae29360294..495ad14a9d370bf49f5a6eca3fee993a475ad561 100644 (file)
@@ -10,7 +10,7 @@ namespace Content.Shared.Cargo.Prototypes;
 /// that must be sold together in a labeled container in order
 /// to receive a monetary reward.
 /// </summary>
-[Prototype, Serializable, NetSerializable]
+[Prototype]
 public sealed partial class CargoBountyPrototype : IPrototype
 {
     /// <inheritdoc/>
index 73a3ba0bbf806a7ccfb9e63d34a2a37e00aacebf..4364e13d843474fb56ccf65db0a03cdb8aa7f9c0 100644 (file)
@@ -9,7 +9,7 @@ namespace Content.Shared.Chat.Prototypes;
 ///     Sounds collection for each <see cref="EmotePrototype"/>.
 ///     Different entities may use different sounds collections.
 /// </summary>
-[Prototype, Serializable, NetSerializable]
+[Prototype]
 public sealed partial class EmoteSoundsPrototype : IPrototype
 {
     [IdDataField]
index 836978dd034194ffdb3f0280155450c509be449d..95de0fb29d02a242a3fb7de4929f2c36fa03bc6b 100644 (file)
@@ -13,7 +13,6 @@ namespace Content.Shared.Damage.Prototypes
     ///     cref="DamageableComponent"/> should support.
     /// </remarks>
     [Prototype]
-    [Serializable, NetSerializable]
     public sealed partial class DamageContainerPrototype : IPrototype
     {
         [ViewVariables]
index 6df2138cd475d21c7ff945adc5bac839980c85ea..7615724467224c0710982e2edac3a7c808b98231 100644 (file)
@@ -12,7 +12,6 @@ namespace Content.Shared.Damage.Prototypes
     ///     to change/get/set damage in a <see cref="DamageableComponent"/>.
     /// </remarks>
     [Prototype(2)]
-    [Serializable, NetSerializable]
     public sealed partial class DamageGroupPrototype : IPrototype
     {
         [IdDataField] public string ID { get; private set; } = default!;
index d01f4d6d2adac13b07e5a9d79b30ae31b60098a5..bf062ddce492119a47f01dff75a2ee5d8bdb4737 100644 (file)
@@ -7,7 +7,6 @@ namespace Content.Shared.DeviceLinking;
 /// <summary>
 ///     A prototype for a device port, for use with device linking.
 /// </summary>
-[Serializable, NetSerializable]
 public abstract class DevicePortPrototype
 {
     [IdDataField]
@@ -28,13 +27,11 @@ public abstract class DevicePortPrototype
 }
 
 [Prototype]
-[Serializable, NetSerializable]
 public sealed partial class SinkPortPrototype : DevicePortPrototype, IPrototype
 {
 }
 
 [Prototype]
-[Serializable, NetSerializable]
 public sealed partial class SourcePortPrototype : DevicePortPrototype, IPrototype
 {
     /// <summary>
index e51087fdf6b734ea5b93541d8054ab04d6d39666..5d51bbc3e83b4bdbad02dee64fbf075466f1a031 100644 (file)
@@ -1,3 +1,4 @@
+using System.Linq;
 using Content.Shared.Administration.Logs;
 using Content.Shared.Database;
 using Content.Shared.DeviceLinking.Events;
@@ -142,6 +143,11 @@ public abstract class SharedDeviceLinkSystem : EntitySystem
         }
     }
 
+    public ProtoId<SourcePortPrototype>[] GetSourcePortIds(Entity<DeviceLinkSourceComponent> source)
+    {
+        return source.Comp.Ports.ToArray();
+    }
+
     /// <summary>
     /// Retrieves the available ports from a source
     /// </summary>
@@ -160,6 +166,11 @@ public abstract class SharedDeviceLinkSystem : EntitySystem
         return sourcePorts;
     }
 
+    public ProtoId<SinkPortPrototype>[] GetSinkPortIds(Entity<DeviceLinkSinkComponent> source)
+    {
+        return source.Comp.Ports.ToArray();
+    }
+
     /// <summary>
     /// Retrieves the available ports from a sink
     /// </summary>
index 19ccb5e2fe754edeca92e92c683f0e99e91071c5..ac5b6eec1895ce46e9034d6948e976f320d8fa3f 100644 (file)
@@ -7,7 +7,6 @@ namespace Content.Shared.DeviceNetwork;
 ///     A named device network frequency. Useful for ensuring entity prototypes can communicate with each other.
 /// </summary>
 [Prototype]
-[Serializable, NetSerializable]
 public sealed partial class DeviceFrequencyPrototype : IPrototype
 {
     [IdDataField]
index 13000d00c9a2d487183c420db7537c7a678d9183..4c021244872c770283317d8bf5133e5e81e22766 100644 (file)
@@ -29,16 +29,16 @@ public sealed class DeviceListUserInterfaceState : BoundUserInterfaceState
 [Serializable, NetSerializable]
 public sealed class DeviceLinkUserInterfaceState : BoundUserInterfaceState
 {
-    public readonly List<SourcePortPrototype> Sources;
-    public readonly List<SinkPortPrototype> Sinks;
+    public readonly ProtoId<SourcePortPrototype>[] Sources;
+    public readonly ProtoId<SinkPortPrototype>[] Sinks;
     public readonly HashSet<(ProtoId<SourcePortPrototype> source, ProtoId<SinkPortPrototype> sink)> Links;
     public readonly List<(string source, string sink)>? Defaults;
     public readonly string SourceAddress;
     public readonly string SinkAddress;
 
     public DeviceLinkUserInterfaceState(
-        List<SourcePortPrototype> sources,
-        List<SinkPortPrototype> sinks,
+        ProtoId<SourcePortPrototype>[] sources,
+        ProtoId<SinkPortPrototype>[] sinks,
         HashSet<(ProtoId<SourcePortPrototype> source, ProtoId<SinkPortPrototype> sink)> links,
         string sourceAddress,
         string sinkAddress,
index 03f88ba75132110a971280283c356dcfb7ceb610..690bd894a06de1e659aa2e3f5c1e657297ea6c02 100644 (file)
@@ -5,27 +5,31 @@ namespace Content.Shared.Mind;
 /// <summary>
 ///     The core properties of Role Types
 /// </summary>
-[Prototype, Serializable]
+[Prototype]
 public sealed partial class RoleTypePrototype : IPrototype
 {
     [IdDataField]
     public string ID { get; private set; } = default!;
 
+    public static readonly LocId FallbackName = "role-type-crew-aligned-name";
+    public const string FallbackSymbol = "";
+    public static readonly Color FallbackColor = Color.FromHex("#eeeeee");
+
     /// <summary>
     ///     The role's name as displayed on the UI.
     /// </summary>
     [DataField]
-    public LocId Name = "role-type-crew-aligned-name";
+    public LocId Name = FallbackName;
 
     /// <summary>
     ///     The role's displayed color.
     /// </summary>
     [DataField]
-    public Color Color = Color.FromHex("#eeeeee");
+    public Color Color = FallbackColor;
 
     /// <summary>
     ///     A symbol used to represent the role type.
     /// </summary>
     [DataField]
-    public string Symbol = string.Empty;
+    public string Symbol = FallbackSymbol;
 }
index 3f5b2a1bd60181f45367f35568e7b219eef61677..ff2712600a294d03acce0b68c3c0e7b3e99c0ec0 100644 (file)
@@ -8,7 +8,6 @@ namespace Content.Shared.Roles;
 ///     Describes information for a single antag.
 /// </summary>
 [Prototype]
-[Serializable, NetSerializable]
 public sealed partial class AntagPrototype : IPrototype
 {
     [ViewVariables]
index 434f80e16e6f7ab7f519e7483015ccf65712da63..d2bdf7d02b7e5a5e6c063dac2c27fca5d51aa13e 100644 (file)
@@ -78,7 +78,6 @@ public partial class SiliconLaw : IComparable<SiliconLaw>, IEquatable<SiliconLaw
 /// This is a prototype for a law governing the behavior of silicons.
 /// </summary>
 [Prototype]
-[Serializable, NetSerializable]
 public sealed partial class SiliconLawPrototype : SiliconLaw, IPrototype
 {
     /// <inheritdoc/>
index 49c618ef8cd7e4203272338fd72e771481758692..a3861504e972ca426f38b6190bfe2dbd4a59fbc2 100644 (file)
@@ -61,7 +61,7 @@ public sealed partial class SiliconLawset
 /// This is a prototype for a <see cref="SiliconLawPrototype"/> list.
 /// Cannot be used directly since it is a list of prototype ids rather than List<Siliconlaw>.
 /// </summary>
-[Prototype, Serializable, NetSerializable]
+[Prototype]
 public sealed partial class SiliconLawsetPrototype : IPrototype
 {
     /// <inheritdoc/>
index a520da4354abcea3d931d306026df668324b7e4d..0f62a39d453ce8ede0fc160485f302378ceb050d 100644 (file)
@@ -49,5 +49,5 @@ public sealed partial class VocalComponent : Component
     /// </summary>
     [ViewVariables]
     [AutoNetworkedField]
-    public EmoteSoundsPrototype? EmoteSounds = null;
+    public ProtoId<EmoteSoundsPrototype>? EmoteSounds = null;
 }
index 03cc59d2df0b9bfe064be5864ca08371ac62fb44..fdd113d3a2867d4becdd742d420edcf873a2f5f3 100644 (file)
@@ -11,7 +11,7 @@ namespace Content.Shared.Store;
 ///     This is separate to the cargo ordering system.
 /// </summary>
 [Prototype]
-[DataDefinition, Serializable, NetSerializable]
+[DataDefinition]
 public sealed partial class CurrencyPrototype : IPrototype
 {
     [ViewVariables]
index b68c5ffa0dc857c5472e2eb6041a2836d104ee12..4d01f91c44cd1afd02436a9f8841548cd76cd2a0 100644 (file)
@@ -242,7 +242,6 @@ public partial class ListingData : IEquatable<ListingData>
 ///     Defines a set item listing that is available in a store
 /// </summary>
 [Prototype]
-[Serializable, NetSerializable]
 [DataDefinition]
 public sealed partial class ListingPrototype : ListingData, IPrototype
 {
@@ -423,7 +422,7 @@ public sealed partial class ListingDataWithCostModifiers : ListingData
 ///     how <see cref="StoreDiscountComponent"/> will be filled by respective system.
 /// </summary>
 [Prototype]
-[DataDefinition, Serializable, NetSerializable]
+[DataDefinition]
 public sealed partial class DiscountCategoryPrototype : IPrototype
 {
     [ViewVariables]
index 5713ecd98abb459bc6ffd270811ccff3ad8da003..fcf7ec34966e61350fe0232535f1684412851f20 100644 (file)
@@ -7,7 +7,6 @@ namespace Content.Shared.Store;
 ///     Used to define different categories for a store.
 /// </summary>
 [Prototype]
-[Serializable, NetSerializable, DataDefinition]
 public sealed partial class StoreCategoryPrototype : IPrototype
 {
     private string _name = string.Empty;
index 38dab902e24456210a698c76fa2a39d5998fb68b..564a3f16d4e44b340665c5063781240c536c50d9 100644 (file)
@@ -6,7 +6,7 @@ namespace Content.Shared.StoryGen;
 /// <summary>
 /// Prototype for a story template that can be filled in with words chosen from <see cref="DatasetPrototype"/>s.
 /// </summary>
-[Serializable, Prototype]
+[Prototype]
 public sealed partial class StoryTemplatePrototype : IPrototype
 {
     /// <summary>
index b39e29aeadaf512716efcf1291a83fff03d12ad8..be4816b319d9f637a1a32a85f2b1518bcadd8816 100644 (file)
@@ -4,7 +4,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
 
 namespace Content.Shared.VendingMachines
 {
-    [Serializable, NetSerializable, Prototype]
+    [Prototype]
     public sealed partial class VendingMachineInventoryPrototype : IPrototype
     {
         [ViewVariables]