diff --git a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs index 9b354babb9..1213d652e0 100644 --- a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs +++ b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs @@ -40,7 +40,7 @@ public sealed class PrototypeSaveTest [Test] public async Task UninitializedSaveTest() { - // Apparently SpawnTest fails to clean up properly. Due to the similarities, I'll assume this also fails. + // Apparently SpawnTest fails to clean up properly. Due to the similarities, I'll assume this also fails. await using var pairTracker = await PoolManager.GetServerClient(); var server = pairTracker.Pair.Server; @@ -190,14 +190,14 @@ public sealed class PrototypeSaveTest await pairTracker.CleanReturnAsync(); } - private sealed class TestEntityUidContext : ISerializationContext, + public sealed class TestEntityUidContext : ISerializationContext, ITypeSerializer { public SerializationManager.SerializerProvider SerializerProvider { get; } public bool WritingReadingPrototypes { get; set; } public string WritingComponent = string.Empty; - public EntityPrototype Prototype = default!; + public EntityPrototype? Prototype; public TestEntityUidContext() { @@ -215,7 +215,7 @@ public sealed class PrototypeSaveTest IDependencyCollection dependencies, bool alwaysWrite = false, ISerializationContext? context = null) { - if (WritingComponent != "Transform" && !Prototype.NoSpawn) + if (WritingComponent != "Transform" && (Prototype?.NoSpawn == false)) { // Maybe this will be necessary in the future, but at the moment it just indicates that there is some // issue, like a non-nullable entityUid data-field. If a component MUST have an entity uid to work with, diff --git a/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs new file mode 100644 index 0000000000..de1d9e1b90 --- /dev/null +++ b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs @@ -0,0 +1,52 @@ +using Robust.Shared.Prototypes; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.Tests.PrototypeTests; + +public sealed class PrototypeTests +{ + /// + /// This test writes all known prototypes as yaml files, then validates that the result is valid yaml. + /// Can help prevent instances where prototypes have bad C# default values. + /// + [Test] + public async Task TestAllPrototypesAreSerializable() + { + await using var pairTracker = await PoolManager.GetServerClient(); + var context = new PrototypeSaveTest.TestEntityUidContext(); + Assert.Multiple(() => + { + Validate(pairTracker.Pair.Server, "server", context); + + // TODO fix client serialization + //Validate(pairTracker.Pair.Client, "client", context); + }); + await pairTracker.CleanReturnAsync(); + } + + public void Validate(RobustIntegrationTest.IntegrationInstance instance, string instanceId, + PrototypeSaveTest.TestEntityUidContext ctx) + { + var protoMan = instance.ResolveDependency(); + var errors = protoMan.ValidateAllPrototypesSerializable(ctx); + + if (errors.Count == 0) + return; + + Assert.Multiple(() => + { + foreach (var (kind, ids) in errors) + { + foreach (var (id, nodes) in ids) + { + var msg = $"Error when validating {instanceId} prototype ({kind.Name}, {id}). Errors: \n"; + foreach (var errorNode in nodes) + { + msg += $" - {errorNode.ErrorReason}\n"; + } + Assert.Fail(msg); + } + } + }); + } +} diff --git a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeComponent.cs b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeComponent.cs index 8c3979f287..db55284b1f 100644 --- a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeComponent.cs +++ b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeComponent.cs @@ -3,8 +3,6 @@ using Robust.Shared.Audio; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; -// TODO: ECS. - namespace Content.Server.Arcade.SpaceVillain; [RegisterComponent] @@ -93,16 +91,7 @@ public sealed class SpaceVillainArcadeComponent : SharedSpaceVillainArcadeCompon /// [ViewVariables(VVAccess.ReadWrite)] [DataField("possibleRewards", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List PossibleRewards = new() - { - "ToyMouse", "ToyAi", "ToyNuke", "ToyAssistant", "ToyGriffin", "ToyHonk", "ToyIan", - "ToyMarauder", "ToyMauler", "ToyGygax", "ToyOdysseus", "ToyOwlman", "ToyDeathRipley", - "ToyPhazon", "ToyFireRipley", "ToyReticence", "ToyRipley", "ToySeraph", "ToyDurand", "ToySkeleton", - "FoamCrossbow", "RevolverCapGun", "PlushieHampter", "PlushieLizard", "PlushieAtmosian", "PlushieSpaceLizard", - "PlushieNuke", "PlushieCarp", "PlushieRatvar", "PlushieNar", "PlushieSnake", "Basketball", "Football", - "PlushieRouny", "PlushieBee", "PlushieSlime", "BalloonCorgi", "ToySword", "CrayonBox", "BoxDonkSoftBox", "BoxCartridgeCap", - "HarmonicaInstrument", "OcarinaInstrument", "RecorderInstrument", "GunpetInstrument", "BirdToyInstrument", "PlushieXeno", "BeachBall" - }; + public List PossibleRewards = new(); /// /// The minimum number of prizes the arcade machine can have. diff --git a/Content.Server/Holiday/Celebrate/DefaultHolidayCelebrate.cs b/Content.Server/Holiday/Celebrate/DefaultHolidayCelebrate.cs deleted file mode 100644 index 6c807e9544..0000000000 --- a/Content.Server/Holiday/Celebrate/DefaultHolidayCelebrate.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Content.Server.Holiday.Interfaces; - -namespace Content.Server.Holiday.Celebrate -{ - public sealed class DefaultHolidayCelebrate : IHolidayCelebrate - { - public void Celebrate(HolidayPrototype holiday) - { - // Nada. - } - } -} diff --git a/Content.Server/Holiday/Greet/DefaultHolidayGreet.cs b/Content.Server/Holiday/Greet/DefaultHolidayGreet.cs index fef7d462b8..f4bf2fbb20 100644 --- a/Content.Server/Holiday/Greet/DefaultHolidayGreet.cs +++ b/Content.Server/Holiday/Greet/DefaultHolidayGreet.cs @@ -2,6 +2,7 @@ using Content.Server.Holiday.Interfaces; namespace Content.Server.Holiday.Greet { + [DataDefinition] public sealed class DefaultHolidayGreet : IHolidayGreet { public string Greet(HolidayPrototype holiday) diff --git a/Content.Server/Holiday/HolidayPrototype.cs b/Content.Server/Holiday/HolidayPrototype.cs index 85640a80bc..6a3d9770c7 100644 --- a/Content.Server/Holiday/HolidayPrototype.cs +++ b/Content.Server/Holiday/HolidayPrototype.cs @@ -1,4 +1,3 @@ -using Content.Server.Holiday.Celebrate; using Content.Server.Holiday.Greet; using Content.Server.Holiday.Interfaces; using Content.Server.Holiday.ShouldCelebrate; @@ -40,7 +39,7 @@ namespace Content.Server.Holiday private readonly IHolidayGreet _greet = new DefaultHolidayGreet(); [DataField("celebrate")] - private readonly IHolidayCelebrate _celebrate = new DefaultHolidayCelebrate(); + private readonly IHolidayCelebrate? _celebrate = null; public bool ShouldCelebrate(DateTime date) { @@ -57,7 +56,7 @@ namespace Content.Server.Holiday /// public void Celebrate() { - _celebrate.Celebrate(this); + _celebrate?.Celebrate(this); } } } diff --git a/Content.Server/Holiday/ShouldCelebrate/DefaultHolidayShouldCelebrate.cs b/Content.Server/Holiday/ShouldCelebrate/DefaultHolidayShouldCelebrate.cs index ade68c5b20..342189df66 100644 --- a/Content.Server/Holiday/ShouldCelebrate/DefaultHolidayShouldCelebrate.cs +++ b/Content.Server/Holiday/ShouldCelebrate/DefaultHolidayShouldCelebrate.cs @@ -2,7 +2,7 @@ using Content.Server.Holiday.Interfaces; namespace Content.Server.Holiday.ShouldCelebrate { - [Virtual] + [Virtual, DataDefinition] public class DefaultHolidayShouldCelebrate : IHolidayShouldCelebrate { public virtual bool ShouldCelebrate(DateTime date, HolidayPrototype holiday) diff --git a/Content.Server/Nutrition/EntitySystems/FlavorProfileSystem.cs b/Content.Server/Nutrition/EntitySystems/FlavorProfileSystem.cs index c1e71c0f3e..9e20484c31 100644 --- a/Content.Server/Nutrition/EntitySystems/FlavorProfileSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FlavorProfileSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Nutrition.Components; using Content.Shared.CCVar; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; +using Content.Shared.Nutrition; using Microsoft.VisualBasic; using Robust.Shared.Configuration; using Robust.Shared.Prototypes; @@ -102,9 +103,8 @@ public sealed class FlavorProfileSystem : EntitySystem continue; } - var flavor = proto.Flavor; - - flavors.Add(flavor); + if (proto.Flavor != null) + flavors.Add(proto.Flavor); } return flavors; diff --git a/Content.Shared/Alert/AlertOrderPrototype.cs b/Content.Shared/Alert/AlertOrderPrototype.cs index 27053c8c0d..5c75f1cb4c 100644 --- a/Content.Shared/Alert/AlertOrderPrototype.cs +++ b/Content.Shared/Alert/AlertOrderPrototype.cs @@ -1,4 +1,6 @@ -using Robust.Shared.Prototypes; +using System.Linq; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; namespace Content.Shared.Alert { @@ -14,22 +16,25 @@ namespace Content.Shared.Alert public string ID { get; } = default!; [DataField("order")] - private List<(string type, string alert)> Order + private (string type, string alert)[] Order { + // why would paul do this to me. get { - var res = new List<(string, string)>(_typeToIdx.Count + _categoryToIdx.Count); + var res = new (string, string)[_typeToIdx.Count + _categoryToIdx.Count]; foreach (var (type, id) in _typeToIdx) { - res.Insert(id, ("alertType", type.ToString())); + res[id] = ("alertType", type.ToString()); } foreach (var (category, id) in _categoryToIdx) { - res.Insert(id, ("category", category.ToString())); + res[id] = ("category", category.ToString()); } + DebugTools.Assert(res.All(x => x != default)); + return res; } set diff --git a/Content.Shared/Body/Prototypes/BodyPrototypeSerializer.cs b/Content.Shared/Body/Prototypes/BodyPrototypeSerializer.cs index 69503396ae..c149b84c63 100644 --- a/Content.Shared/Body/Prototypes/BodyPrototypeSerializer.cs +++ b/Content.Shared/Body/Prototypes/BodyPrototypeSerializer.cs @@ -19,6 +19,7 @@ public sealed class BodyPrototypeSerializer : ITypeReader(); var prototypes = dependencies.Resolve(); + var factory = dependencies.Resolve(); var connections = new List(); if (slot.TryGet("connections", out SequenceDataNode? connectionsNode)) @@ -57,7 +58,7 @@ public sealed class BodyPrototypeSerializer : ITypeReader()) + if (!organPrototype.HasComponent(factory)) { nodes.Add(new ErrorNode(value, $"Organ {organ.Value} does not have a body component")); } diff --git a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs index 3c15174362..abf8fa8275 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs @@ -6,6 +6,7 @@ using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reaction; using Content.Shared.Database; using Content.Shared.FixedPoint; +using Content.Shared.Nutrition; using Robust.Shared.Audio; using Robust.Shared.Map; using Robust.Shared.Prototypes; @@ -60,8 +61,8 @@ namespace Content.Shared.Chemistry.Reagent [DataField("recognizable")] public bool Recognizable = false; - [DataField("flavor")] - public string Flavor { get; } = default!; + [DataField("flavor", customTypeSerializer:typeof(PrototypeIdSerializer))] + public string? Flavor; /// /// There must be at least this much quantity in a solution to be tasted. diff --git a/Content.Server/Nutrition/Flavor.cs b/Content.Shared/Nutrition/Flavor.cs similarity index 91% rename from Content.Server/Nutrition/Flavor.cs rename to Content.Shared/Nutrition/Flavor.cs index 85dcdc2948..475b4e2e66 100644 --- a/Content.Server/Nutrition/Flavor.cs +++ b/Content.Shared/Nutrition/Flavor.cs @@ -1,6 +1,6 @@ using Robust.Shared.Prototypes; -namespace Content.Server.Nutrition; +namespace Content.Shared.Nutrition; [Prototype("flavor")] public sealed class FlavorPrototype : IPrototype diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml index b911c67a5b..681a84e2bf 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml @@ -261,7 +261,7 @@ - type: FlavorProfile flavors: - meaty - - acidic + - acid - type: Sprite layers: - state: tin @@ -277,7 +277,7 @@ - type: FlavorProfile flavors: - meaty - - acidic + - acid - type: Sprite layers: - state: plate-small diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/burger.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/burger.yml index c566df7a25..8b4ca3edc0 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/burger.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/burger.yml @@ -797,7 +797,7 @@ flavors: - bun - meaty - - acidic + - acid - type: Sprite state: x - type: SolutionContainerManager diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml index c56816dbdd..f66ed04207 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml @@ -489,7 +489,7 @@ - type: FlavorProfile flavors: - meaty - - acidic + - acid - type: Tag tags: - Raw @@ -1080,7 +1080,7 @@ - type: FlavorProfile flavors: - meaty - - acidic + - acid - type: Tag tags: - Raw diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/arcades.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/arcades.yml index 95ad1c02f9..e48268bdc5 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/arcades.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/arcades.yml @@ -35,6 +35,54 @@ components: - type: SpaceVillainArcade rewardAmount: 0 + possibleRewards: + - ToyMouse + - ToyAi + - ToyNuke + - ToyGriffin + - ToyHonk + - ToyIan + - ToyMarauder + - ToyMauler + - ToyGygax + - ToyOdysseus + - ToyOwlman + - ToyDeathRipley + - ToyPhazon + - ToyFireRipley + - ToyReticence + - ToyRipley + - ToySeraph + - ToyDurand + - ToySkeleton + - FoamCrossbow + - RevolverCapGun + - PlushieHampter + - PlushieLizard + - PlushieAtmosian + - PlushieSpaceLizard + - PlushieNuke + - PlushieCarp + - PlushieRatvar + - PlushieNar + - PlushieSnake + - Basketball + - Football + - PlushieRouny + - PlushieBee + - PlushieSlime + - BalloonCorgi + - ToySword + - CrayonBox + - BoxDonkSoftBox + - BoxCartridgeCap + - HarmonicaInstrument + - OcarinaInstrument + - RecorderInstrument + - GunpetInstrument + - BirdToyInstrument + - PlushieXeno + - BeachBall - type: WiresPanel - type: Wires LayoutId: Arcade diff --git a/Resources/Prototypes/Reagents/Consumable/Food/condiments.yml b/Resources/Prototypes/Reagents/Consumable/Food/condiments.yml index 3afa4c8b42..f5bde4ac21 100644 --- a/Resources/Prototypes/Reagents/Consumable/Food/condiments.yml +++ b/Resources/Prototypes/Reagents/Consumable/Food/condiments.yml @@ -73,7 +73,8 @@ group: Foods desc: reagent-desc-ketchunaise physicalDesc: reagent-physical-desc-saucey - flavor: piquant + #I love it when people just make up fake prototypes. + #flavor: piquant color: "#fba399" recognizable: true diff --git a/Resources/Prototypes/Reagents/toxins.yml b/Resources/Prototypes/Reagents/toxins.yml index edaf29d6df..035be6e5d9 100644 --- a/Resources/Prototypes/Reagents/toxins.yml +++ b/Resources/Prototypes/Reagents/toxins.yml @@ -100,7 +100,7 @@ group: Toxins desc: reagent-desc-polytrinic-acid physicalDesc: reagent-physical-desc-strong-smelling - flavor: acidic + flavor: acid color: "#a1000b" boilingPoint: 78.2 # This isn't a real chemical... meltingPoint: -19.4 @@ -143,7 +143,7 @@ group: Toxins desc: reagent-desc-fluorosulfuric-acid physicalDesc: reagent-physical-desc-strong-smelling - flavor: acidic + flavor: acid color: "#5050ff" boilingPoint: 165 meltingPoint: -87 @@ -179,7 +179,7 @@ group: Toxins desc: reagent-desc-sulfuric-acid physicalDesc: reagent-physical-desc-oily - flavor: acidic + flavor: acid color: "#BF8C00" recognizable: true boilingPoint: 337.0