Add prototype serialization tests. (#18458)
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
@@ -190,14 +190,14 @@ public sealed class PrototypeSaveTest
|
|||||||
await pairTracker.CleanReturnAsync();
|
await pairTracker.CleanReturnAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class TestEntityUidContext : ISerializationContext,
|
public sealed class TestEntityUidContext : ISerializationContext,
|
||||||
ITypeSerializer<EntityUid, ValueDataNode>
|
ITypeSerializer<EntityUid, ValueDataNode>
|
||||||
{
|
{
|
||||||
public SerializationManager.SerializerProvider SerializerProvider { get; }
|
public SerializationManager.SerializerProvider SerializerProvider { get; }
|
||||||
public bool WritingReadingPrototypes { get; set; }
|
public bool WritingReadingPrototypes { get; set; }
|
||||||
|
|
||||||
public string WritingComponent = string.Empty;
|
public string WritingComponent = string.Empty;
|
||||||
public EntityPrototype Prototype = default!;
|
public EntityPrototype? Prototype;
|
||||||
|
|
||||||
public TestEntityUidContext()
|
public TestEntityUidContext()
|
||||||
{
|
{
|
||||||
@@ -215,7 +215,7 @@ public sealed class PrototypeSaveTest
|
|||||||
IDependencyCollection dependencies, bool alwaysWrite = false,
|
IDependencyCollection dependencies, bool alwaysWrite = false,
|
||||||
ISerializationContext? context = null)
|
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
|
// 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,
|
// issue, like a non-nullable entityUid data-field. If a component MUST have an entity uid to work with,
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.UnitTesting;
|
||||||
|
|
||||||
|
namespace Content.IntegrationTests.Tests.PrototypeTests;
|
||||||
|
|
||||||
|
public sealed class PrototypeTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[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<IPrototypeManager>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,6 @@ using Robust.Shared.Audio;
|
|||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||||
|
|
||||||
// TODO: ECS.
|
|
||||||
|
|
||||||
namespace Content.Server.Arcade.SpaceVillain;
|
namespace Content.Server.Arcade.SpaceVillain;
|
||||||
|
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
@@ -93,16 +91,7 @@ public sealed class SpaceVillainArcadeComponent : SharedSpaceVillainArcadeCompon
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("possibleRewards", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>))]
|
[DataField("possibleRewards", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>))]
|
||||||
public List<string> PossibleRewards = new()
|
public List<string> 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"
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The minimum number of prizes the arcade machine can have.
|
/// The minimum number of prizes the arcade machine can have.
|
||||||
|
|||||||
@@ -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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ using Content.Server.Holiday.Interfaces;
|
|||||||
|
|
||||||
namespace Content.Server.Holiday.Greet
|
namespace Content.Server.Holiday.Greet
|
||||||
{
|
{
|
||||||
|
[DataDefinition]
|
||||||
public sealed class DefaultHolidayGreet : IHolidayGreet
|
public sealed class DefaultHolidayGreet : IHolidayGreet
|
||||||
{
|
{
|
||||||
public string Greet(HolidayPrototype holiday)
|
public string Greet(HolidayPrototype holiday)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using Content.Server.Holiday.Celebrate;
|
|
||||||
using Content.Server.Holiday.Greet;
|
using Content.Server.Holiday.Greet;
|
||||||
using Content.Server.Holiday.Interfaces;
|
using Content.Server.Holiday.Interfaces;
|
||||||
using Content.Server.Holiday.ShouldCelebrate;
|
using Content.Server.Holiday.ShouldCelebrate;
|
||||||
@@ -40,7 +39,7 @@ namespace Content.Server.Holiday
|
|||||||
private readonly IHolidayGreet _greet = new DefaultHolidayGreet();
|
private readonly IHolidayGreet _greet = new DefaultHolidayGreet();
|
||||||
|
|
||||||
[DataField("celebrate")]
|
[DataField("celebrate")]
|
||||||
private readonly IHolidayCelebrate _celebrate = new DefaultHolidayCelebrate();
|
private readonly IHolidayCelebrate? _celebrate = null;
|
||||||
|
|
||||||
public bool ShouldCelebrate(DateTime date)
|
public bool ShouldCelebrate(DateTime date)
|
||||||
{
|
{
|
||||||
@@ -57,7 +56,7 @@ namespace Content.Server.Holiday
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Celebrate()
|
public void Celebrate()
|
||||||
{
|
{
|
||||||
_celebrate.Celebrate(this);
|
_celebrate?.Celebrate(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using Content.Server.Holiday.Interfaces;
|
|||||||
|
|
||||||
namespace Content.Server.Holiday.ShouldCelebrate
|
namespace Content.Server.Holiday.ShouldCelebrate
|
||||||
{
|
{
|
||||||
[Virtual]
|
[Virtual, DataDefinition]
|
||||||
public class DefaultHolidayShouldCelebrate : IHolidayShouldCelebrate
|
public class DefaultHolidayShouldCelebrate : IHolidayShouldCelebrate
|
||||||
{
|
{
|
||||||
public virtual bool ShouldCelebrate(DateTime date, HolidayPrototype holiday)
|
public virtual bool ShouldCelebrate(DateTime date, HolidayPrototype holiday)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Content.Server.Nutrition.Components;
|
|||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
|
using Content.Shared.Nutrition;
|
||||||
using Microsoft.VisualBasic;
|
using Microsoft.VisualBasic;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
@@ -102,9 +103,8 @@ public sealed class FlavorProfileSystem : EntitySystem
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var flavor = proto.Flavor;
|
if (proto.Flavor != null)
|
||||||
|
flavors.Add(proto.Flavor);
|
||||||
flavors.Add(flavor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return flavors;
|
return flavors;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Robust.Shared.Prototypes;
|
using System.Linq;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Alert
|
namespace Content.Shared.Alert
|
||||||
{
|
{
|
||||||
@@ -14,22 +16,25 @@ namespace Content.Shared.Alert
|
|||||||
public string ID { get; } = default!;
|
public string ID { get; } = default!;
|
||||||
|
|
||||||
[DataField("order")]
|
[DataField("order")]
|
||||||
private List<(string type, string alert)> Order
|
private (string type, string alert)[] Order
|
||||||
{
|
{
|
||||||
|
// why would paul do this to me.
|
||||||
get
|
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)
|
foreach (var (type, id) in _typeToIdx)
|
||||||
{
|
{
|
||||||
res.Insert(id, ("alertType", type.ToString()));
|
res[id] = ("alertType", type.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var (category, id) in _categoryToIdx)
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public sealed class BodyPrototypeSerializer : ITypeReader<BodyPrototype, Mapping
|
|||||||
{
|
{
|
||||||
var nodes = new List<ValidationNode>();
|
var nodes = new List<ValidationNode>();
|
||||||
var prototypes = dependencies.Resolve<IPrototypeManager>();
|
var prototypes = dependencies.Resolve<IPrototypeManager>();
|
||||||
|
var factory = dependencies.Resolve<IComponentFactory>();
|
||||||
|
|
||||||
var connections = new List<string>();
|
var connections = new List<string>();
|
||||||
if (slot.TryGet("connections", out SequenceDataNode? connectionsNode))
|
if (slot.TryGet("connections", out SequenceDataNode? connectionsNode))
|
||||||
@@ -57,7 +58,7 @@ public sealed class BodyPrototypeSerializer : ITypeReader<BodyPrototype, Mapping
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!organPrototype.HasComponent<OrganComponent>())
|
if (!organPrototype.HasComponent<OrganComponent>(factory))
|
||||||
{
|
{
|
||||||
nodes.Add(new ErrorNode(value, $"Organ {organ.Value} does not have a body component"));
|
nodes.Add(new ErrorNode(value, $"Organ {organ.Value} does not have a body component"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Content.Shared.Chemistry.Components;
|
|||||||
using Content.Shared.Chemistry.Reaction;
|
using Content.Shared.Chemistry.Reaction;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
|
using Content.Shared.Nutrition;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
@@ -60,8 +61,8 @@ namespace Content.Shared.Chemistry.Reagent
|
|||||||
[DataField("recognizable")]
|
[DataField("recognizable")]
|
||||||
public bool Recognizable = false;
|
public bool Recognizable = false;
|
||||||
|
|
||||||
[DataField("flavor")]
|
[DataField("flavor", customTypeSerializer:typeof(PrototypeIdSerializer<FlavorPrototype>))]
|
||||||
public string Flavor { get; } = default!;
|
public string? Flavor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// There must be at least this much quantity in a solution to be tasted.
|
/// There must be at least this much quantity in a solution to be tasted.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Nutrition;
|
namespace Content.Shared.Nutrition;
|
||||||
|
|
||||||
[Prototype("flavor")]
|
[Prototype("flavor")]
|
||||||
public sealed class FlavorPrototype : IPrototype
|
public sealed class FlavorPrototype : IPrototype
|
||||||
@@ -261,7 +261,7 @@
|
|||||||
- type: FlavorProfile
|
- type: FlavorProfile
|
||||||
flavors:
|
flavors:
|
||||||
- meaty
|
- meaty
|
||||||
- acidic
|
- acid
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- state: tin
|
- state: tin
|
||||||
@@ -277,7 +277,7 @@
|
|||||||
- type: FlavorProfile
|
- type: FlavorProfile
|
||||||
flavors:
|
flavors:
|
||||||
- meaty
|
- meaty
|
||||||
- acidic
|
- acid
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- state: plate-small
|
- state: plate-small
|
||||||
|
|||||||
@@ -797,7 +797,7 @@
|
|||||||
flavors:
|
flavors:
|
||||||
- bun
|
- bun
|
||||||
- meaty
|
- meaty
|
||||||
- acidic
|
- acid
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
state: x
|
state: x
|
||||||
- type: SolutionContainerManager
|
- type: SolutionContainerManager
|
||||||
|
|||||||
@@ -489,7 +489,7 @@
|
|||||||
- type: FlavorProfile
|
- type: FlavorProfile
|
||||||
flavors:
|
flavors:
|
||||||
- meaty
|
- meaty
|
||||||
- acidic
|
- acid
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- Raw
|
- Raw
|
||||||
@@ -1080,7 +1080,7 @@
|
|||||||
- type: FlavorProfile
|
- type: FlavorProfile
|
||||||
flavors:
|
flavors:
|
||||||
- meaty
|
- meaty
|
||||||
- acidic
|
- acid
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- Raw
|
- Raw
|
||||||
|
|||||||
@@ -35,6 +35,54 @@
|
|||||||
components:
|
components:
|
||||||
- type: SpaceVillainArcade
|
- type: SpaceVillainArcade
|
||||||
rewardAmount: 0
|
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: WiresPanel
|
||||||
- type: Wires
|
- type: Wires
|
||||||
LayoutId: Arcade
|
LayoutId: Arcade
|
||||||
|
|||||||
@@ -73,7 +73,8 @@
|
|||||||
group: Foods
|
group: Foods
|
||||||
desc: reagent-desc-ketchunaise
|
desc: reagent-desc-ketchunaise
|
||||||
physicalDesc: reagent-physical-desc-saucey
|
physicalDesc: reagent-physical-desc-saucey
|
||||||
flavor: piquant
|
#I love it when people just make up fake prototypes.
|
||||||
|
#flavor: piquant
|
||||||
color: "#fba399"
|
color: "#fba399"
|
||||||
recognizable: true
|
recognizable: true
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@
|
|||||||
group: Toxins
|
group: Toxins
|
||||||
desc: reagent-desc-polytrinic-acid
|
desc: reagent-desc-polytrinic-acid
|
||||||
physicalDesc: reagent-physical-desc-strong-smelling
|
physicalDesc: reagent-physical-desc-strong-smelling
|
||||||
flavor: acidic
|
flavor: acid
|
||||||
color: "#a1000b"
|
color: "#a1000b"
|
||||||
boilingPoint: 78.2 # This isn't a real chemical...
|
boilingPoint: 78.2 # This isn't a real chemical...
|
||||||
meltingPoint: -19.4
|
meltingPoint: -19.4
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
group: Toxins
|
group: Toxins
|
||||||
desc: reagent-desc-fluorosulfuric-acid
|
desc: reagent-desc-fluorosulfuric-acid
|
||||||
physicalDesc: reagent-physical-desc-strong-smelling
|
physicalDesc: reagent-physical-desc-strong-smelling
|
||||||
flavor: acidic
|
flavor: acid
|
||||||
color: "#5050ff"
|
color: "#5050ff"
|
||||||
boilingPoint: 165
|
boilingPoint: 165
|
||||||
meltingPoint: -87
|
meltingPoint: -87
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
group: Toxins
|
group: Toxins
|
||||||
desc: reagent-desc-sulfuric-acid
|
desc: reagent-desc-sulfuric-acid
|
||||||
physicalDesc: reagent-physical-desc-oily
|
physicalDesc: reagent-physical-desc-oily
|
||||||
flavor: acidic
|
flavor: acid
|
||||||
color: "#BF8C00"
|
color: "#BF8C00"
|
||||||
recognizable: true
|
recognizable: true
|
||||||
boilingPoint: 337.0
|
boilingPoint: 337.0
|
||||||
|
|||||||
Reference in New Issue
Block a user