From b89615342ecfadf3e1c9cfd4d1dce2e8023438ff Mon Sep 17 00:00:00 2001 From: moneyl <8206401+Moneyl@users.noreply.github.com> Date: Sat, 23 Nov 2019 15:55:31 -0500 Subject: [PATCH] Fix exception in ReagentPrototype caused by IMetabolizable (#451) * Fix exception in ReagentPrototype Due to client trying to access concrete implementations of IMetabolizable that are in Content.Server. This fix checks to see if the prototype is being loaded by the client through reflection. I don't want to move the prototype completely into Content.Server since it's useful for the client to view descriptions and values of reagents without needing to send that data from the server. * Make fix slightly more explicit Now it will only load the metabolizable when in Robust.Server, instead of it being anything that's not Robust.Client. * Add IModuleManager and ModuleManager Provide simple way to check if shared code is being run by the server or the client * Change ModuleManager implementations to not require assembly name comparison Now just has ClientModuleManager registered to client, and ServerModuleManager registered to server. * Change IModuleManager functions to properties * Fix failing tests. This was failing because the tests weren't initializing IoC. Simply using RobustUnitTest wasn't enough because that doesn't initialize content either. I did some cleaning up so now content IoC is registered via ContentUnitTest. --- Content.Client/ClientContentIoC.cs | 30 +++++++++++++++++++ Content.Client/EntryPoint.cs | 12 +------- Content.Client/Utility/ClientModuleManager.cs | 15 ++++++++++ Content.Server/AssemblyInfo.cs | 3 ++ Content.Server/EntryPoint.cs | 13 ++------ Content.Server/ServerContentIoC.cs | 29 ++++++++++++++++++ Content.Server/Utility/ServerModuleManager.cs | 15 ++++++++++ Content.Shared/Chemistry/ReagentPrototype.cs | 21 ++++++++++--- Content.Shared/Interfaces/IModuleManager.cs | 25 ++++++++++++++++ Content.Tests/ContentUnitTest.cs | 23 ++++++++++++++ .../Chemistry/ReagentPrototype_Tests.cs | 4 +-- 11 files changed, 162 insertions(+), 28 deletions(-) create mode 100644 Content.Client/ClientContentIoC.cs create mode 100644 Content.Client/Utility/ClientModuleManager.cs create mode 100644 Content.Server/AssemblyInfo.cs create mode 100644 Content.Server/ServerContentIoC.cs create mode 100644 Content.Server/Utility/ServerModuleManager.cs create mode 100644 Content.Shared/Interfaces/IModuleManager.cs create mode 100644 Content.Tests/ContentUnitTest.cs diff --git a/Content.Client/ClientContentIoC.cs b/Content.Client/ClientContentIoC.cs new file mode 100644 index 0000000000..d0c6a87ccb --- /dev/null +++ b/Content.Client/ClientContentIoC.cs @@ -0,0 +1,30 @@ +using Content.Client.Chat; +using Content.Client.GameTicking; +using Content.Client.Interfaces; +using Content.Client.Interfaces.Chat; +using Content.Client.Interfaces.Parallax; +using Content.Client.Parallax; +using Content.Client.Sandbox; +using Content.Client.UserInterface; +using Content.Client.Utility; +using Content.Shared.Interfaces; +using Robust.Shared.IoC; + +namespace Content.Client +{ + internal static class ClientContentIoC + { + public static void Register() + { + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + } + } +} diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 4ecc6e6a73..c7693e641e 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -1,7 +1,5 @@ using System; -using Content.Client.Chat; using Content.Client.GameObjects.Components.Actor; -using Content.Client.GameTicking; using Content.Client.Input; using Content.Client.Interfaces; using Content.Client.Interfaces.Chat; @@ -15,7 +13,6 @@ using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.GameObjects.Components.Markers; using Content.Shared.GameObjects.Components.Research; using Content.Shared.GameObjects.Components.VendingMachines; -using Content.Shared.Interfaces; using Robust.Client.Interfaces; using Robust.Client.Interfaces.Graphics.Overlays; using Robust.Client.Interfaces.Input; @@ -143,14 +140,7 @@ namespace Content.Client prototypes.RegisterIgnore("material"); prototypes.RegisterIgnore("reaction"); //Chemical reactions only needed by server. Reactions checks are server-side. - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); + ClientContentIoC.Register(); if (TestingCallbacks != null) { diff --git a/Content.Client/Utility/ClientModuleManager.cs b/Content.Client/Utility/ClientModuleManager.cs new file mode 100644 index 0000000000..75d51debe6 --- /dev/null +++ b/Content.Client/Utility/ClientModuleManager.cs @@ -0,0 +1,15 @@ +using Content.Shared.Interfaces; + +namespace Content.Client.Utility +{ + /// + /// Client implementation of IModuleManager. + /// Provides simple way for shared code to check if it's being run by + /// the client of the server. + /// + public class ClientModuleManager : IModuleManager + { + bool IModuleManager.IsClientModule => true; + bool IModuleManager.IsServerModule => false; + } +} diff --git a/Content.Server/AssemblyInfo.cs b/Content.Server/AssemblyInfo.cs new file mode 100644 index 0000000000..54b2cd50ac --- /dev/null +++ b/Content.Server/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Content.Tests")] diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs index adb7442ad3..3b1cbc7395 100644 --- a/Content.Server/EntryPoint.cs +++ b/Content.Server/EntryPoint.cs @@ -1,11 +1,8 @@ using Content.Server.Cargo; -using Content.Server.Chat; -using Content.Server.GameTicking; using Content.Server.Interfaces; using Content.Server.Interfaces.Chat; using Content.Server.Interfaces.GameTicking; using Content.Server.Sandbox; -using Content.Shared.Interfaces; using Robust.Server.Interfaces.Player; using Robust.Shared.ContentPack; using Robust.Shared.Interfaces.GameObjects; @@ -45,14 +42,8 @@ namespace Content.Server factory.RegisterIgnore(ignoreName); } - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); - IoCManager.Register(); + ServerContentIoC.Register(); + if (TestingCallbacks != null) { var cast = (ServerModuleTestingCallbacks) TestingCallbacks; diff --git a/Content.Server/ServerContentIoC.cs b/Content.Server/ServerContentIoC.cs new file mode 100644 index 0000000000..352ef2d564 --- /dev/null +++ b/Content.Server/ServerContentIoC.cs @@ -0,0 +1,29 @@ +using Content.Server.Cargo; +using Content.Server.Chat; +using Content.Server.GameTicking; +using Content.Server.Interfaces; +using Content.Server.Interfaces.Chat; +using Content.Server.Interfaces.GameTicking; +using Content.Server.Sandbox; +using Content.Server.Utility; +using Content.Shared.Interfaces; +using Robust.Shared.IoC; + +namespace Content.Server +{ + internal static class ServerContentIoC + { + public static void Register() + { + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + } + } +} diff --git a/Content.Server/Utility/ServerModuleManager.cs b/Content.Server/Utility/ServerModuleManager.cs new file mode 100644 index 0000000000..0058cbefb4 --- /dev/null +++ b/Content.Server/Utility/ServerModuleManager.cs @@ -0,0 +1,15 @@ +using Content.Shared.Interfaces; + +namespace Content.Server.Utility +{ + /// + /// Server implementation of IModuleManager. + /// Provides simple way for shared code to check if it's being run by + /// the client of the server. + /// + public class ServerModuleManager : IModuleManager + { + bool IModuleManager.IsClientModule => false; + bool IModuleManager.IsServerModule => true; + } +} diff --git a/Content.Shared/Chemistry/ReagentPrototype.cs b/Content.Shared/Chemistry/ReagentPrototype.cs index e4cbfab8f1..65b4756047 100644 --- a/Content.Shared/Chemistry/ReagentPrototype.cs +++ b/Content.Shared/Chemistry/ReagentPrototype.cs @@ -1,10 +1,10 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; +using Content.Shared.Interfaces; using Content.Shared.Interfaces.Chemistry; +using Robust.Shared.IoC; using Robust.Shared.Maths; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; -using Robust.Shared.Utility; using YamlDotNet.RepresentationModel; namespace Content.Shared.Chemistry @@ -12,6 +12,10 @@ namespace Content.Shared.Chemistry [Prototype("reagent")] public class ReagentPrototype : IPrototype, IIndexedPrototype { +#pragma warning disable 649 + [Dependency] private readonly IModuleManager _moduleManager; +#pragma warning restore 649 + private string _id; private string _name; private string _description; @@ -25,6 +29,11 @@ namespace Content.Shared.Chemistry //List of metabolism effects this reagent has, should really only be used server-side. public List Metabolism => _metabolism; + public ReagentPrototype() + { + IoCManager.InjectDependencies(this); + } + public void LoadFrom(YamlMappingNode mapping) { var serializer = YamlObjectSerializer.NewReader(mapping); @@ -33,7 +42,11 @@ namespace Content.Shared.Chemistry serializer.DataField(ref _name, "name", string.Empty); serializer.DataField(ref _description, "desc", string.Empty); serializer.DataField(ref _substanceColor, "color", Color.White); - serializer.DataField(ref _metabolism, "metabolism", new List{new DefaultMetabolizable()}); + + if (_moduleManager.IsServerModule) + serializer.DataField(ref _metabolism, "metabolism", new List {new DefaultMetabolizable()}); + else + _metabolism = new List { new DefaultMetabolizable() }; } } } diff --git a/Content.Shared/Interfaces/IModuleManager.cs b/Content.Shared/Interfaces/IModuleManager.cs new file mode 100644 index 0000000000..f6e4ecc222 --- /dev/null +++ b/Content.Shared/Interfaces/IModuleManager.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Content.Shared.Interfaces +{ + /// + /// Provides a simple way to check whether calling code is being run by + /// Robust.Client, or Robust.Server. Useful for code in Content.Shared + /// that wants different behavior depending on if client or server is using it. + /// + public interface IModuleManager + { + /// + /// Returns true if the code is being run by the client, returns false otherwise. + /// + bool IsClientModule { get; } + /// + /// Returns true if the code is being run by the server, returns false otherwise. + /// + bool IsServerModule { get; } + } +} diff --git a/Content.Tests/ContentUnitTest.cs b/Content.Tests/ContentUnitTest.cs new file mode 100644 index 0000000000..fe6980a312 --- /dev/null +++ b/Content.Tests/ContentUnitTest.cs @@ -0,0 +1,23 @@ +using Content.Client; +using Content.Server; +using Robust.UnitTesting; + +namespace Content.Tests +{ + public class ContentUnitTest : RobustUnitTest + { + protected override void OverrideIoC() + { + base.OverrideIoC(); + + if (Project == UnitTestProject.Server) + { + ServerContentIoC.Register(); + } + else if (Project == UnitTestProject.Client) + { + ClientContentIoC.Register(); + } + } + } +} diff --git a/Content.Tests/Shared/Chemistry/ReagentPrototype_Tests.cs b/Content.Tests/Shared/Chemistry/ReagentPrototype_Tests.cs index d78a67178c..b2f1133eff 100644 --- a/Content.Tests/Shared/Chemistry/ReagentPrototype_Tests.cs +++ b/Content.Tests/Shared/Chemistry/ReagentPrototype_Tests.cs @@ -7,8 +7,8 @@ using YamlDotNet.RepresentationModel; namespace Content.Tests.Shared.Chemistry { - [TestFixture, Parallelizable, TestOf(typeof(ReagentPrototype))] - public class ReagentPrototype_Tests + [TestFixture, TestOf(typeof(ReagentPrototype))] + public class ReagentPrototype_Tests : ContentUnitTest { [Test] public void DeserializeReagentPrototype()