Compare commits

..

4 Commits

Author SHA1 Message Date
Silver
d49075a970 Update and clean up Prototype manager methods 2022-02-11 00:27:34 -07:00
Silver
a67acd453c Split out Interface and Attribute 2022-02-11 00:26:58 -07:00
ElectroJr
b540f04a7a Version: 0.8.52 2022-02-10 21:46:08 +13:00
Leon Friedrich
bcaa7001ad Hopefully fix container occlusion bug? (#2464) 2022-02-10 09:29:26 +01:00
16 changed files with 457 additions and 407 deletions

View File

@@ -1,4 +1,4 @@
<Project>
<!-- This file automatically reset by Tools/version.py -->
<PropertyGroup><Version>0.8.51</Version></PropertyGroup>
<PropertyGroup><Version>0.8.52</Version></PropertyGroup>
</Project>

View File

@@ -5,6 +5,8 @@ using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Serialization;
using static Robust.Shared.Containers.ContainerManagerComponent;
@@ -131,12 +133,51 @@ namespace Robust.Client.GameObjects
continue;
}
// If an entity is currently in the shadow realm, it means we probably left PVS and are now getting
// back into range. We do not want to directly insert this entity, as IF the container and entity
// transform states did not get sent simultaneously, the entity's transform will be modified by the
// insert operation. This means it will then be reset to the shadow realm, causing it to be ejected
// from the container. It would then subsequently be parented to the container without ever being
// re-inserted, leading to the client seeing what should be hidden entities attached to
// containers/players.
if (Transform(entity).MapID == MapId.Nullspace)
{
AddExpectedEntity(entity, container);
continue;
}
if (!container.ContainedEntities.Contains(entity))
container.Insert(entity);
}
}
}
protected override void HandleParentChanged(ref EntParentChangedMessage message)
{
base.HandleParentChanged(ref message);
// If an entity warped in from null-space (i.e., re-entered PVS) and got attached to a container, do the same checks as for newly initialized entities.
if (message.OldParent != null && message.OldParent.Value.IsValid())
return;
if (!ExpectedEntities.TryGetValue(message.Entity, out var container))
return;
if (Transform(message.Entity).ParentUid != container.Owner)
{
// This container is expecting an entity... but it got parented to some other entity???
// Ah well, the sever should send a new container state that updates expected entities so just ignore it for now.
return;
}
RemoveExpectedEntity(message.Entity);
if (container.Deleted)
return;
container.Insert(message.Entity);
}
private IContainer ContainerFactory(ContainerManagerComponent component, string containerType, string id)
{
var type = _serializer.FindSerializedType(typeof(IContainer), containerType);

View File

@@ -212,7 +212,7 @@ namespace Robust.Shared.Containers
#endregion
// Eject entities from their parent container if the parent change is done by the transform only.
private void HandleParentChanged(ref EntParentChangedMessage message)
protected virtual void HandleParentChanged(ref EntParentChangedMessage message)
{
var oldParentEntity = message.OldParent;

View File

@@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Robust.Shared.Serialization.Markdown.Validation;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Robust.Shared.Prototypes
{
/// <summary>
/// Handle storage and loading of YAML prototypes.
/// </summary>
public interface IPrototypeManager
{
void Initialize();
/// <summary>
/// Return an IEnumerable to iterate all prototypes of a certain type.
/// </summary>
/// <exception cref="KeyNotFoundException">
/// Thrown if the type of prototype is not registered.
/// </exception>
IEnumerable<T> EnumeratePrototypes<T>() where T : class, IPrototype;
/// <summary>
/// Return an IEnumerable to iterate all prototypes of a certain type.
/// </summary>
/// <exception cref="KeyNotFoundException">
/// Thrown if the type of prototype is not registered.
/// </exception>
IEnumerable<IPrototype> EnumeratePrototypes(Type type);
/// <summary>
/// Return an IEnumerable to iterate all prototypes of a certain variant.
/// </summary>
/// <exception cref="KeyNotFoundException">
/// Thrown if the variant of prototype is not registered.
/// </exception>
IEnumerable<IPrototype> EnumeratePrototypes(string variant);
/// <summary>
/// Index for a <see cref="IPrototype"/> by ID.
/// </summary>
/// <exception cref="KeyNotFoundException">
/// Thrown if the type of prototype is not registered.
/// </exception>
T Index<T>(string id) where T : class, IPrototype;
/// <summary>
/// Index for a <see cref="IPrototype"/> by ID.
/// </summary>
/// <exception cref="KeyNotFoundException">
/// Thrown if the ID does not exist or the type of prototype is not registered.
/// </exception>
IPrototype Index(Type type, string id);
/// <summary>
/// Returns whether a prototype of type <typeparamref name="T"/> with the specified <param name="id"/> exists.
/// </summary>
bool HasIndex<T>(string id) where T : class, IPrototype;
bool TryIndex<T>(string id, [NotNullWhen(true)] out T? prototype) where T : class, IPrototype;
bool TryIndex(Type type, string id, [NotNullWhen(true)] out IPrototype? prototype);
/// <summary>
/// Returns whether a prototype variant <param name="variant"/> exists.
/// </summary>
/// <param name="variant">Identifier for the prototype variant.</param>
/// <returns>Whether the prototype variant exists.</returns>
bool HasVariant(string variant);
/// <summary>
/// Returns the Type for a prototype variant.
/// </summary>
/// <param name="variant">Identifier for the prototype variant.</param>
/// <returns>The specified prototype Type.</returns>
/// <exception cref="KeyNotFoundException">
/// Thrown when the specified prototype variant isn't registered or doesn't exist.
/// </exception>
Type GetVariantType(string variant);
/// <summary>
/// Attempts to get the Type for a prototype variant.
/// </summary>
/// <param name="variant">Identifier for the prototype variant.</param>
/// <param name="prototype">The specified prototype Type, or null.</param>
/// <returns>Whether the prototype type was found and <see cref="prototype"/> isn't null.</returns>
bool TryGetVariantType(string variant, [NotNullWhen(true)] out Type? prototype);
/// <summary>
/// Attempts to get a prototype's variant.
/// </summary>
/// <param name="type"></param>
/// <param name="variant"></param>
/// <returns></returns>
bool TryGetVariantFrom(Type type, [NotNullWhen(true)] out string? variant);
/// <summary>
/// Attempts to get a prototype's variant.
/// </summary>
/// <param name="prototype">The prototype in question.</param>
/// <param name="variant">Identifier for the prototype variant, or null.</param>
/// <returns>Whether the prototype variant was successfully retrieved.</returns>
bool TryGetVariantFrom(IPrototype prototype, [NotNullWhen(true)] out string? variant);
/// <summary>
/// Attempts to get a prototype's variant.
/// </summary>
/// <param name="variant">Identifier for the prototype variant, or null.</param>
/// <typeparam name="T">The prototype in question.</typeparam>
/// <returns>Whether the prototype variant was successfully retrieved.</returns>
bool TryGetVariantFrom<T>([NotNullWhen(true)] out string? variant) where T : class, IPrototype;
/// <summary>
/// Load prototypes from files in a directory, recursively.
/// </summary>
List<IPrototype> LoadDirectory(ResourcePath path, bool overwrite = false);
Dictionary<string, HashSet<ErrorNode>> ValidateDirectory(ResourcePath path);
HashSet<IPrototype> LoadFromString(string str, bool overwrite = false, string actionMessage="");
void RemoveString(string prototypes);
/// <summary>
/// Clear out all prototypes and reset to a blank slate.
/// </summary>
void Clear();
/// <summary>
/// Syncs all inter-prototype data. Call this when operations adding new prototypes are done.
/// </summary>
void Resync();
/// <summary>
/// Registers a specific prototype name to be ignored.
/// </summary>
void RegisterIgnore(string name);
/// <summary>
/// Loads a single prototype class type into the manager.
/// </summary>
/// <param name="protoClass">A prototype class type that implements IPrototype. This type also
/// requires a <see cref="PrototypeAttribute"/> with a non-empty class string.</param>
void RegisterType(Type protoClass);
event Action<YamlStream, string>? LoadedData;
/// <summary>
/// Fired when prototype are reloaded. The event args contain the modified prototypes.
/// </summary>
/// <remarks>
/// This does NOT fire on initial prototype load.
/// </remarks>
event Action<PrototypesReloadedEventArgs> PrototypesReloaded;
}
}

View File

@@ -0,0 +1,32 @@
using System;
using JetBrains.Annotations;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.Shared.Prototypes
{
/// <summary>
/// Quick attribute to give the prototype its type string.
/// To prevent needing to instantiate it because interfaces can't declare statics.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
[BaseTypeRequired(typeof(IPrototype))]
[MeansImplicitUse]
[MeansDataDefinition]
public sealed class PrototypeAttribute : Attribute
{
private readonly string type;
public string Type => type;
public readonly int LoadPriority;
public readonly string LoadBefore;
public readonly string LoadAfter;
public PrototypeAttribute(string type, int loadPriority = 1, string loadBefore="" ,string loadAfter= "")
{
this.type = type;
LoadPriority = loadPriority;
LoadBefore = loadBefore;
LoadAfter = loadAfter;
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Threading;
using JetBrains.Annotations;
@@ -25,175 +26,8 @@ using YamlDotNet.RepresentationModel;
namespace Robust.Shared.Prototypes
{
/// <summary>
/// Handle storage and loading of YAML prototypes.
/// </summary>
public interface IPrototypeManager
{
void Initialize();
/// <summary>
/// Return an IEnumerable to iterate all prototypes of a certain type.
/// </summary>
/// <exception cref="KeyNotFoundException">
/// Thrown if the type of prototype is not registered.
/// </exception>
IEnumerable<T> EnumeratePrototypes<T>() where T : class, IPrototype;
/// <summary>
/// Return an IEnumerable to iterate all prototypes of a certain type.
/// </summary>
/// <exception cref="KeyNotFoundException">
/// Thrown if the type of prototype is not registered.
/// </exception>
IEnumerable<IPrototype> EnumeratePrototypes(Type type);
/// <summary>
/// Return an IEnumerable to iterate all prototypes of a certain variant.
/// </summary>
/// <exception cref="KeyNotFoundException">
/// Thrown if the variant of prototype is not registered.
/// </exception>
IEnumerable<IPrototype> EnumeratePrototypes(string variant);
/// <summary>
/// Index for a <see cref="IPrototype"/> by ID.
/// </summary>
/// <exception cref="KeyNotFoundException">
/// Thrown if the type of prototype is not registered.
/// </exception>
T Index<T>(string id) where T : class, IPrototype;
/// <summary>
/// Index for a <see cref="IPrototype"/> by ID.
/// </summary>
/// <exception cref="KeyNotFoundException">
/// Thrown if the ID does not exist or the type of prototype is not registered.
/// </exception>
IPrototype Index(Type type, string id);
/// <summary>
/// Returns whether a prototype of type <typeparamref name="T"/> with the specified <param name="id"/> exists.
/// </summary>
bool HasIndex<T>(string id) where T : class, IPrototype;
bool TryIndex<T>(string id, [NotNullWhen(true)] out T? prototype) where T : class, IPrototype;
bool TryIndex(Type type, string id, [NotNullWhen(true)] out IPrototype? prototype);
/// <summary>
/// Returns whether a prototype variant <param name="variant"/> exists.
/// </summary>
/// <param name="variant">Identifier for the prototype variant.</param>
/// <returns>Whether the prototype variant exists.</returns>
bool HasVariant(string variant);
/// <summary>
/// Returns the Type for a prototype variant.
/// </summary>
/// <param name="variant">Identifier for the prototype variant.</param>
/// <returns>The specified prototype Type.</returns>
/// <exception cref="KeyNotFoundException">
/// Thrown when the specified prototype variant isn't registered or doesn't exist.
/// </exception>
Type GetVariantType(string variant);
/// <summary>
/// Attempts to get the Type for a prototype variant.
/// </summary>
/// <param name="variant">Identifier for the prototype variant.</param>
/// <param name="prototype">The specified prototype Type, or null.</param>
/// <returns>Whether the prototype type was found and <see cref="prototype"/> isn't null.</returns>
bool TryGetVariantType(string variant, [NotNullWhen(true)] out Type? prototype);
/// <summary>
/// Attempts to get a prototype's variant.
/// </summary>
/// <param name="type"></param>
/// <param name="variant"></param>
/// <returns></returns>
bool TryGetVariantFrom(Type type, [NotNullWhen(true)] out string? variant);
/// <summary>
/// Attempts to get a prototype's variant.
/// </summary>
/// <param name="prototype">The prototype in question.</param>
/// <param name="variant">Identifier for the prototype variant, or null.</param>
/// <returns>Whether the prototype variant was successfully retrieved.</returns>
bool TryGetVariantFrom(IPrototype prototype, [NotNullWhen(true)] out string? variant);
/// <summary>
/// Attempts to get a prototype's variant.
/// </summary>
/// <param name="variant">Identifier for the prototype variant, or null.</param>
/// <typeparam name="T">The prototype in question.</typeparam>
/// <returns>Whether the prototype variant was successfully retrieved.</returns>
bool TryGetVariantFrom<T>([NotNullWhen(true)] out string? variant) where T : class, IPrototype;
/// <summary>
/// Load prototypes from files in a directory, recursively.
/// </summary>
List<IPrototype> LoadDirectory(ResourcePath path, bool overwrite = false);
Dictionary<string, HashSet<ErrorNode>> ValidateDirectory(ResourcePath path);
List<IPrototype> LoadFromStream(TextReader stream, bool overwrite = false);
List<IPrototype> LoadString(string str, bool overwrite = false);
void RemoveString(string prototypes);
/// <summary>
/// Clear out all prototypes and reset to a blank slate.
/// </summary>
void Clear();
/// <summary>
/// Syncs all inter-prototype data. Call this when operations adding new prototypes are done.
/// </summary>
void Resync();
/// <summary>
/// Registers a specific prototype name to be ignored.
/// </summary>
void RegisterIgnore(string name);
/// <summary>
/// Loads a single prototype class type into the manager.
/// </summary>
/// <param name="protoClass">A prototype class type that implements IPrototype. This type also
/// requires a <see cref="PrototypeAttribute"/> with a non-empty class string.</param>
void RegisterType(Type protoClass);
event Action<YamlStream, string>? LoadedData;
/// <summary>
/// Fired when prototype are reloaded. The event args contain the modified prototypes.
/// </summary>
/// <remarks>
/// This does NOT fire on initial prototype load.
/// </remarks>
event Action<PrototypesReloadedEventArgs> PrototypesReloaded;
}
/// <summary>
/// Quick attribute to give the prototype its type string.
/// To prevent needing to instantiate it because interfaces can't declare statics.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
[BaseTypeRequired(typeof(IPrototype))]
[MeansImplicitUse]
[MeansDataDefinition]
public sealed class PrototypeAttribute : Attribute
{
private readonly string type;
public string Type => type;
public readonly int LoadPriority = 1;
public PrototypeAttribute(string type, int loadPriority = 1)
{
this.type = type;
LoadPriority = loadPriority;
}
}
[Virtual]
public class PrototypeManager : IPrototypeManager
@@ -204,11 +38,12 @@ namespace Robust.Shared.Prototypes
[Dependency] protected readonly ITaskManager TaskManager = default!;
[Dependency] private readonly ISerializationManager _serializationManager = default!;
private readonly Dictionary<string, Type> _prototypeTypes = new();
private readonly Dictionary<string, Type> _types = new();
private readonly Dictionary<Type, int> _prototypePriorities = new();
private bool _initialized;
private bool _hasEverBeenReloaded;
private int mappingErrors;
#region IPrototypeManager members
@@ -216,8 +51,13 @@ namespace Robust.Shared.Prototypes
private readonly Dictionary<Type, Dictionary<string, DeserializationResult>> _prototypeResults = new();
private readonly Dictionary<Type, PrototypeInheritanceTree> _inheritanceTrees = new();
private readonly HashSet<string> _ignoredPrototypeTypes = new();
private readonly HashSet<Type> LoadBeforeList = new ();
private readonly HashSet<Type> LoadNormalList = new ();
private readonly HashSet<Type> LoadAfterList = new ();
private readonly HashSet<ErrorNode> ErrorNodes = new ();
private readonly HashSet<string> _ignoredPrototypeTypes = new();
public virtual void Initialize()
{
if (_initialized)
@@ -225,8 +65,9 @@ namespace Robust.Shared.Prototypes
throw new InvalidOperationException($"{nameof(PrototypeManager)} has already been initialized.");
}
mappingErrors = 0;
ReloadTypes();
_initialized = true;
ReloadPrototypeTypes();
}
public IEnumerable<T> EnumeratePrototypes<T>() where T : class, IPrototype
@@ -283,7 +124,8 @@ namespace Robust.Shared.Prototypes
public void Clear()
{
_prototypeTypes.Clear();
mappingErrors = 0;
_types.Clear();
_prototypes.Clear();
_prototypeResults.Clear();
_inheritanceTrees.Clear();
@@ -297,7 +139,7 @@ namespace Robust.Shared.Prototypes
protected void ReloadPrototypes(IEnumerable<ResourcePath> filePaths)
{
#if !FULL_RELEASE
var changed = filePaths.SelectMany(f => LoadFile(f.ToRootedPath(), true)).ToList();
var changed = filePaths.SelectMany(f => LoadFromFile(f.ToRootedPath(), true)).ToList();
ReloadPrototypes(changed);
#endif
}
@@ -374,6 +216,7 @@ namespace Robust.Shared.Prototypes
#endif
}
#region Inheritance Tree
public void Resync()
{
var trees = _inheritanceTrees.Keys.ToList();
@@ -442,174 +285,6 @@ namespace Robust.Shared.Prototypes
_prototypes[type][id] = (IPrototype) populatedRes.RawValue!;
}
/// <inheritdoc />
public List<IPrototype> LoadDirectory(ResourcePath path, bool overwrite = false)
{
var changedPrototypes = new List<IPrototype>();
_hasEverBeenReloaded = true;
var streams = Resources.ContentFindFiles(path).ToList().AsParallel()
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith("."));
foreach (var resourcePath in streams)
{
var filePrototypes = LoadFile(resourcePath, overwrite);
changedPrototypes.AddRange(filePrototypes);
}
return changedPrototypes;
}
public Dictionary<string, HashSet<ErrorNode>> ValidateDirectory(ResourcePath path)
{
var streams = Resources.ContentFindFiles(path).ToList().AsParallel()
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith("."));
var dict = new Dictionary<string, HashSet<ErrorNode>>();
foreach (var resourcePath in streams)
{
using var reader = ReadFile(resourcePath);
if (reader == null)
{
continue;
}
var yamlStream = new YamlStream();
yamlStream.Load(reader);
for (var i = 0; i < yamlStream.Documents.Count; i++)
{
var rootNode = (YamlSequenceNode) yamlStream.Documents[i].RootNode;
foreach (YamlMappingNode node in rootNode.Cast<YamlMappingNode>())
{
var type = node.GetNode("type").AsString();
if (!_prototypeTypes.ContainsKey(type))
{
if (_ignoredPrototypeTypes.Contains(type))
{
continue;
}
throw new PrototypeLoadException($"Unknown prototype type: '{type}'");
}
var mapping = node.ToDataNodeCast<MappingDataNode>();
mapping.Remove("type");
var errorNodes = _serializationManager.ValidateNode(_prototypeTypes[type], mapping).GetErrors()
.ToHashSet();
if (errorNodes.Count == 0) continue;
if (!dict.TryGetValue(resourcePath.ToString(), out var hashSet))
dict[resourcePath.ToString()] = new HashSet<ErrorNode>();
dict[resourcePath.ToString()].UnionWith(errorNodes);
}
}
}
return dict;
}
private StreamReader? ReadFile(ResourcePath file, bool @throw = true)
{
var retries = 0;
// This might be shit-code, but its pjb-responded-idk-when-asked shit-code.
while (true)
{
try
{
var reader = new StreamReader(Resources.ContentFileRead(file), EncodingHelpers.UTF8);
return reader;
}
catch (IOException e)
{
if (retries > 10)
{
if (@throw)
{
throw;
}
Logger.Error($"Error reloading prototypes in file {file}.", e);
return null;
}
retries++;
Thread.Sleep(10);
}
}
}
public HashSet<IPrototype> LoadFile(ResourcePath file, bool overwrite = false)
{
var changedPrototypes = new HashSet<IPrototype>();
try
{
using var reader = ReadFile(file, !overwrite);
if (reader == null)
{
return changedPrototypes;
}
var yamlStream = new YamlStream();
yamlStream.Load(reader);
LoadedData?.Invoke(yamlStream, file.ToString());
for (var i = 0; i < yamlStream.Documents.Count; i++)
{
try
{
var documentPrototypes = LoadFromDocument(yamlStream.Documents[i], overwrite);
changedPrototypes.UnionWith(documentPrototypes);
}
catch (Exception e)
{
Logger.ErrorS("eng", $"Exception whilst loading prototypes from {file}#{i}:\n{e}");
}
}
}
catch (YamlException e)
{
var sawmill = Logger.GetSawmill("eng");
sawmill.Error("YamlException whilst loading prototypes from {0}: {1}", file, e.Message);
}
return changedPrototypes;
}
public List<IPrototype> LoadFromStream(TextReader stream, bool overwrite = false)
{
var changedPrototypes = new List<IPrototype>();
_hasEverBeenReloaded = true;
var yaml = new YamlStream();
yaml.Load(stream);
for (var i = 0; i < yaml.Documents.Count; i++)
{
try
{
var documentPrototypes = LoadFromDocument(yaml.Documents[i], overwrite);
changedPrototypes.AddRange(documentPrototypes);
}
catch (Exception e)
{
throw new PrototypeLoadException($"Failed to load prototypes from document#{i}", e);
}
}
LoadedData?.Invoke(yaml, "anonymous prototypes YAML stream");
return changedPrototypes;
}
public List<IPrototype> LoadString(string str, bool overwrite = false)
{
return LoadFromStream(new StringReader(str), overwrite);
}
public void RemoveString(string prototypes)
{
var reader = new StringReader(prototypes);
@@ -623,7 +298,7 @@ namespace Robust.Shared.Prototypes
foreach (var node in root.Cast<YamlMappingNode>())
{
var typeString = node.GetNode("type").AsString();
var type = _prototypeTypes[typeString];
var type = _types[typeString];
var id = node.GetNode("id").AsString();
@@ -636,63 +311,204 @@ namespace Robust.Shared.Prototypes
}
}
}
#endregion
#endregion IPrototypeManager members
private void ReloadPrototypeTypes()
public Dictionary<string, HashSet<ErrorNode>> ValidateDirectory(ResourcePath path)
{
Clear();
foreach (var type in _reflectionManager.GetAllChildren<IPrototype>())
var streams = Resources.ContentFindFiles(path).ToList().AsParallel()
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith("."));
var dict = new Dictionary<string, HashSet<ErrorNode>>();
foreach (var resourcePath in streams)
{
RegisterType(type);
LoadFromFile(resourcePath, false, true);
if (!ErrorNodes.Any())
continue;
if (!dict.TryGetValue(resourcePath.ToString(), out var hashSet))
dict[resourcePath.ToString()] = new HashSet<ErrorNode>();
dict[resourcePath.ToString()].UnionWith(ErrorNodes);
}
return dict;
}
private HashSet<IPrototype> LoadFromDocument(YamlDocument document, bool overwrite = false)
#region Prototype Loading
/// <summary>
/// Loads Prototypes from a path.
/// </summary>
/// <param name="path">path to files to load as prototype.</param>
/// <param name="overwrite">Overwrite if prototype already is loaded and exists</param>
/// <returns>HashSet of Loaded Prototypes</returns>
public List<IPrototype> LoadDirectory(ResourcePath path, bool overwrite = false)
{
var changedPrototypes = new HashSet<IPrototype>();
var rootNode = (YamlSequenceNode) document.RootNode;
var changedPrototypes = new List<IPrototype>();
foreach (YamlMappingNode node in rootNode.Cast<YamlMappingNode>())
_hasEverBeenReloaded = true;
var streams = Resources.ContentFindFiles(path).ToList().AsParallel()
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith("."));
foreach (var resourcePath in streams)
{
var type = node.GetNode("type").AsString();
if (!_prototypeTypes.ContainsKey(type))
{
if (_ignoredPrototypeTypes.Contains(type))
{
continue;
}
throw new PrototypeLoadException($"Unknown prototype type: '{type}'");
}
var prototypeType = _prototypeTypes[type];
var res = _serializationManager.Read(prototypeType, node.ToDataNode(), skipHook: true);
var prototype = (IPrototype) res.RawValue!;
if (!overwrite && _prototypes[prototypeType].ContainsKey(prototype.ID))
{
throw new PrototypeLoadException($"Duplicate ID: '{prototype.ID}'");
}
_prototypeResults[prototypeType][prototype.ID] = res;
if (prototype is IInheritingPrototype inheritingPrototype)
{
_inheritanceTrees[prototypeType].AddId(prototype.ID, inheritingPrototype.Parent, true);
}
else
{
//we call it here since it wont get called when pushing inheritance
res.CallAfterDeserializationHook();
}
_prototypes[prototypeType][prototype.ID] = prototype;
changedPrototypes.Add(prototype);
var filePrototypes = LoadFromFile(resourcePath, overwrite);
changedPrototypes.AddRange(filePrototypes);
}
return changedPrototypes;
}
/// <summary>
/// Loads Prototypes from a file.
/// </summary>
/// <param name="file">file to load as prototype.</param>
/// <param name="overwrite">Overwrite if prototype already is loaded and exists</param>
/// /// <param name="validateMapping">toggle true to receive a count of total mapping errors</param>
/// <returns>HashSet of Loaded Prototypes</returns>
public HashSet<IPrototype> LoadFromFile(ResourcePath file, bool overwrite = false, bool validateMapping = false)
{
HashSet<IPrototype> LoadedPrototypes = new();
try
{
try
{
var reader = new StreamReader(Resources.ContentFileRead(file), EncodingHelpers.UTF8);
var yamlStream = new YamlStream();
yamlStream.Load(reader);
LoadedPrototypes = LoadFromDocument(yamlStream, overwrite, validateMapping, actionMessage: file.ToString());
}
catch (IOException e)
{
Logger.Error($"Error loading prototypes in file {file}.", e);
}
}
catch (YamlException e)
{
var sawmill = Logger.GetSawmill("eng");
sawmill.Error("Caught YamlException whilst loading prototypes from a File {0}: {1}", file.Filename, e.Message);
}
return LoadedPrototypes;
}
/// <summary>
/// Loads Prototypes from a string.
/// </summary>
/// <param name="str">Input string to load as prototype.</param>
/// <param name="overwrite">Overwrite if prototype already is loaded and exists</param>
/// <param name="actionMessage">String that will be included in the LoadedData Event</param>
/// <returns>HashSet of Loaded Prototypes</returns>
public HashSet<IPrototype> LoadFromString(string str, bool overwrite = false, string actionMessage = "")
{
var yamlStream = new YamlStream();
yamlStream.Load(new StringReader(str));
return LoadFromDocument(yamlStream, overwrite, actionMessage: actionMessage);
}
/// <summary>
/// Loads YAML Prototypes via a Yaml Stream
/// </summary>
/// <param name="yamlStream">YamlStream to process</param>
/// <param name="mappingErrors">a count of mapping errors. is 0 until you toggle validateMapping on.</param>
/// <param name="validateMapping">toggle true to receive a count of total mapping errors</param>
/// <param name="overwrite">Overwrite if prototype already is loaded and exists.</param>
/// <param name="actionMessage">String that will be included in the LoadedData Event</param>
/// <returns>HashSet of Loaded Prototypes</returns>
/// <exception cref="PrototypeLoadException">Thrown when Prototype failed to load</exception>
private HashSet<IPrototype> LoadFromDocument(YamlStream yamlStream, bool overwrite = false,
bool validateMapping = false, string actionMessage = "")
{
var loadedPrototypes = new HashSet<IPrototype>();
for (var i = 0; i < yamlStream.Documents.Count(); i++)
{
var prototypeDocument = yamlStream.Documents[i];
var rootNode = (YamlSequenceNode) prototypeDocument.RootNode;
foreach (YamlMappingNode node in rootNode.Cast<YamlMappingNode>())
{
var type = node.GetNode("type").AsString();
if (!_types.ContainsKey(type))
{
if (_ignoredPrototypeTypes.Contains(type))
{
continue;
}
throw new PrototypeLoadException($"Unknown prototype type: '{type}'");
}
var prototypeType = _types[type];
var res = _serializationManager.Read(prototypeType, node.ToDataNode(), skipHook: true);
var prototype = (IPrototype) res.RawValue!;
if (!overwrite && _prototypes[prototypeType].ContainsKey(prototype.ID))
{
throw new PrototypeLoadException($"Duplicate ID: '{prototype.ID}'");
}
_prototypeResults[prototypeType][prototype.ID] = res;
if (prototype is IInheritingPrototype inheritingPrototype)
{
_inheritanceTrees[prototypeType].AddId(prototype.ID, inheritingPrototype.Parent, true);
}
else
{
//we call it here since it wont get called when pushing inheritance
res.CallAfterDeserializationHook();
}
_prototypes[prototypeType][prototype.ID] = prototype;
loadedPrototypes.Add(prototype);
if (validateMapping)
{
var mapping = node.ToDataNodeCast<MappingDataNode>();
mapping.Remove("type");
var errorNodes = _serializationManager.ValidateNode(_types[type], mapping).GetErrors()
.ToHashSet();
mappingErrors = errorNodes.Count;
}
}
}
LoadedData?.Invoke(yamlStream, actionMessage);
return loadedPrototypes;
}
#endregion
#endregion IPrototypeManager members
private void ReloadTypes()
{
Clear();
foreach (var type in _reflectionManager.GetAllChildren<IPrototype>())
{
var prototypeAttributes = (PrototypeAttribute?) Attribute.GetCustomAttribute(type, typeof(PrototypeAttribute));
if ( prototypeAttributes!.LoadBefore != string.Empty)
LoadBeforeList.Add(type);
else if (prototypeAttributes!.LoadAfter != string.Empty)
LoadAfterList.Add(type);
else
LoadNormalList.Add(type);
RegisterType(type);
}
}
public bool HasIndex<T>(string id) where T : class, IPrototype
{
if (!_prototypes.TryGetValue(typeof(T), out var index))
@@ -723,19 +539,19 @@ namespace Robust.Shared.Prototypes
/// <inheritdoc />
public bool HasVariant(string variant)
{
return _prototypeTypes.ContainsKey(variant);
return _types.ContainsKey(variant);
}
/// <inheritdoc />
public Type GetVariantType(string variant)
{
return _prototypeTypes[variant];
return _types[variant];
}
/// <inheritdoc />
public bool TryGetVariantType(string variant, [NotNullWhen(true)] out Type? prototype)
{
return _prototypeTypes.TryGetValue(variant, out prototype);
return _types.TryGetValue(variant, out prototype);
}
/// <inheritdoc />
@@ -793,14 +609,14 @@ namespace Robust.Shared.Prototypes
"No " + nameof(PrototypeAttribute) + " to give it a type string.");
}
if (_prototypeTypes.ContainsKey(attribute.Type))
if (_types.ContainsKey(attribute.Type))
{
throw new InvalidImplementationException(type,
typeof(IPrototype),
$"Duplicate prototype type ID: {attribute.Type}. Current: {_prototypeTypes[attribute.Type]}");
$"Duplicate prototype type ID: {attribute.Type}. Current: {_types[attribute.Type]}");
}
_prototypeTypes[attribute.Type] = type;
_types[attribute.Type] = type;
_prototypePriorities[type] = attribute.LoadPriority;
if (typeof(IPrototype).IsAssignableFrom(type))

View File

@@ -17,7 +17,7 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
{
var sim = RobustServerSimulation
.NewSimulation()
.RegisterPrototypes(protoMan => protoMan.LoadString(Prototypes))
.RegisterPrototypes(protoMan => protoMan.LoadFromString(Prototypes))
.InitializeInstance();
// Adds the map with id 1, and spawns entity 1 as the map entity.

View File

@@ -72,7 +72,7 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
IoCManager.Resolve<ISerializationManager>().Initialize();
var manager = IoCManager.Resolve<IPrototypeManager>();
manager.RegisterType(typeof(EntityPrototype));
manager.LoadFromStream(new StringReader(PROTOTYPES));
manager.LoadFromString(PROTOTYPES);
manager.Resync();
// build the net dream

View File

@@ -51,7 +51,7 @@ namespace Robust.UnitTesting.Server.GameObjects
IoCManager.Resolve<ISerializationManager>().Initialize();
var manager = IoCManager.Resolve<IPrototypeManager>();
manager.RegisterType(typeof(EntityPrototype));
manager.LoadFromStream(new StringReader(PROTOTYPES));
manager.LoadFromString(PROTOTYPES);
manager.Resync();
//NOTE: The grids have not moved, so we can assert worldpos == localpos for the test

View File

@@ -22,7 +22,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
{
var sim = RobustServerSimulation
.NewSimulation()
.RegisterPrototypes(protoMan => protoMan.LoadString(PROTOTYPE))
.RegisterPrototypes(protoMan => protoMan.LoadFromString(PROTOTYPE))
.InitializeInstance();
var mapManager = sim.Resolve<IMapManager>();

View File

@@ -34,7 +34,7 @@ namespace Robust.UnitTesting.Shared.GameObjects.Systems
.NewSimulation()
.RegisterPrototypes(f=>
{
f.LoadString(Prototypes);
f.LoadFromString(Prototypes);
})
.InitializeInstance();

View File

@@ -43,7 +43,7 @@ namespace Robust.UnitTesting.Shared.Map
IoCManager.Resolve<ISerializationManager>().Initialize();
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
prototypeManager.RegisterType(typeof(EntityPrototype));
prototypeManager.LoadFromStream(new StringReader(PROTOTYPES));
prototypeManager.LoadFromString(PROTOTYPES);
prototypeManager.Resync();
var factory = IoCManager.Resolve<IComponentFactory>();

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -47,7 +48,7 @@ namespace Robust.UnitTesting.Shared.Prototypes
IoCManager.Resolve<ISerializationManager>().Initialize();
_prototypes = (PrototypeManager) IoCManager.Resolve<IPrototypeManager>();
_prototypes.RegisterType(typeof(EntityPrototype));
_prototypes.LoadString(InitialPrototypes);
_prototypes.LoadFromString(InitialPrototypes);
_prototypes.Resync();
_maps = IoCManager.Resolve<IMapManager>();
@@ -75,8 +76,8 @@ namespace Robust.UnitTesting.Shared.Prototypes
Assert.That(entityComponent.Value, Is.EqualTo(5));
Assert.False(IoCManager.Resolve<IEntityManager>().HasComponent<HotReloadTestTwoComponent>(entity));
var changedPrototypes = _prototypes.LoadString(ReloadedPrototypes, true);
_prototypes.ReloadPrototypes(changedPrototypes);
var changedPrototypes = _prototypes.LoadFromString(ReloadedPrototypes, true);
_prototypes.ReloadPrototypes(changedPrototypes.ToList());
Assert.True(reloaded);
reloaded = false;
@@ -87,8 +88,8 @@ namespace Robust.UnitTesting.Shared.Prototypes
// New components are added
Assert.True(IoCManager.Resolve<IEntityManager>().HasComponent<HotReloadTestTwoComponent>(entity));
changedPrototypes = _prototypes.LoadString(InitialPrototypes, true);
_prototypes.ReloadPrototypes(changedPrototypes);
changedPrototypes = _prototypes.LoadFromString(InitialPrototypes, true);
_prototypes.ReloadPrototypes(changedPrototypes.ToList());
Assert.True(reloaded);
reloaded = false;

View File

@@ -27,7 +27,7 @@ namespace Robust.UnitTesting.Shared.Prototypes
IoCManager.Resolve<ISerializationManager>().Initialize();
manager = IoCManager.Resolve<IPrototypeManager>();
manager.RegisterType(typeof(EntityPrototype));
manager.LoadString(DOCUMENT);
manager.LoadFromString(DOCUMENT);
manager.Resync();
}
@@ -106,7 +106,7 @@ namespace Robust.UnitTesting.Shared.Prototypes
[Test]
public void TestLoadString()
{
manager.LoadString(LoadStringDocument);
manager.LoadFromString(LoadStringDocument);
manager.Resync();
var prototype = manager.Index<EntityPrototype>(LoadStringTestDummyId);

View File

@@ -59,7 +59,7 @@ namespace Robust.UnitTesting.Shared.Serialization
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
prototypeManager.RegisterType(typeof(EntityPrototype));
prototypeManager.LoadString(Prototypes);
prototypeManager.LoadFromString(Prototypes);
prototypeManager.Resync();
var entityManager = IoCManager.Resolve<IEntityManager>();

View File

@@ -46,7 +46,7 @@ entitiesImmutableList:
var protoMan = IoCManager.Resolve<IPrototypeManager>();
protoMan.RegisterType(typeof(EntityPrototype));
protoMan.LoadString(Prototypes);
protoMan.LoadFromString(Prototypes);
protoMan.Resync();
}