mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Add and update a lot of documentation (#6337)
* Serialization docs Co-authored-by: Moony <moonheart08@users.noreply.github.com> * ECS docs Co-authored-by: Moony <moonheart08@users.noreply.github.com> * scattered docs Co-authored-by: Moony <moonheart08@users.noreply.github.com> * Fixes --------- Co-authored-by: Moony <moonheart08@users.noreply.github.com> Co-authored-by: PJB3005 <pieterjan.briers+git@gmail.com>
This commit is contained in:
@@ -9,38 +9,44 @@ namespace Robust.Shared.Configuration
|
||||
public enum CVar : short
|
||||
{
|
||||
/// <summary>
|
||||
/// No special flags.
|
||||
/// No special flags.
|
||||
/// </summary>
|
||||
NONE = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Debug vars that are considered 'cheating' to change.
|
||||
/// Debug vars that are considered 'cheating' to change.
|
||||
/// </summary>
|
||||
CHEAT = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Only the server can change this variable.
|
||||
/// Only the server can change this variable.
|
||||
/// </summary>
|
||||
SERVER = 2,
|
||||
|
||||
/// <summary>
|
||||
/// This can only be changed when not connected to a server.
|
||||
/// This can only be changed when not connected to a server.
|
||||
/// </summary>
|
||||
NOT_CONNECTED = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Changing this var syncs between clients and server.
|
||||
/// Changing this var syncs between clients and server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should only ever be used on shared CVars.
|
||||
/// </remarks>
|
||||
REPLICATED = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Non-default values are saved to the configuration file.
|
||||
/// Non-default values are saved to the configuration file.
|
||||
/// </summary>
|
||||
ARCHIVE = 16,
|
||||
|
||||
/// <summary>
|
||||
/// Changing this var on the server notifies all clients, does nothing client-side.
|
||||
/// Changing this var on the server notifies all clients, does nothing client-side.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should only ever be used on shared CVars.
|
||||
/// </remarks>
|
||||
NOTIFY = 32,
|
||||
|
||||
/// <summary>
|
||||
@@ -60,15 +66,15 @@ namespace Robust.Shared.Configuration
|
||||
CLIENTONLY = 128,
|
||||
|
||||
/// <summary>
|
||||
/// CVar contains sensitive data that should not be accidentally leaked.
|
||||
/// CVar contains sensitive data that should not be accidentally leaked.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This currently hides the content of the cvar in the "cvar" command completions.
|
||||
/// This currently hides the content of the cvar in the "cvar" command completions.
|
||||
/// </remarks>
|
||||
CONFIDENTIAL = 256,
|
||||
|
||||
/// <summary>
|
||||
/// Only the client can change this variable.
|
||||
/// Only the client can change this variable.
|
||||
/// </summary>
|
||||
CLIENT = 512,
|
||||
}
|
||||
|
||||
@@ -3,11 +3,29 @@ using JetBrains.Annotations;
|
||||
|
||||
namespace Robust.Shared.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract base class for <see cref="CVarDef{T}"/>. You shouldn't inherit this yourself and may be looking for
|
||||
/// <see cref="M:Robust.Shared.Configuration.CVarDef.Create``1(System.String,``0,Robust.Shared.Configuration.CVar,System.String)"/>
|
||||
/// </summary>
|
||||
public abstract class CVarDef
|
||||
{
|
||||
/// <summary>
|
||||
/// The default value of this CVar when no override is specified by configuration or the user.
|
||||
/// </summary>
|
||||
public object DefaultValue { get; }
|
||||
/// <summary>
|
||||
/// Flags for this CVar.
|
||||
/// </summary>
|
||||
public CVar Flags { get; }
|
||||
/// <summary>
|
||||
/// The name of this CVar. This needs to contain only printable characters.
|
||||
/// Periods '.' are reserved. Everything before the last period is a nested table identifier,
|
||||
/// everything after is the CVar name in the TOML document.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
/// <summary>
|
||||
/// The description of this CVar.
|
||||
/// </summary>
|
||||
public string? Desc { get; }
|
||||
|
||||
private protected CVarDef(string name, object defaultValue, CVar flags, string? desc)
|
||||
@@ -18,6 +36,14 @@ namespace Robust.Shared.Configuration
|
||||
Desc = desc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CVar definition, for use in <see cref="CVarDefsAttribute"/>-annotated classes.
|
||||
/// </summary>
|
||||
/// <param name="name">See <see cref="Name"/>.</param>
|
||||
/// <param name="defaultValue">See <see cref="DefaultValue"/>.</param>
|
||||
/// <param name="flag">See <see cref="Flags"/>.</param>
|
||||
/// <param name="desc">See <see cref="Desc"/>.</param>
|
||||
/// <typeparam name="T">The type of the CVar, which can be any of: bool, int, long, float, string, any enum, and ushort.</typeparam>
|
||||
public static CVarDef<T> Create<T>(
|
||||
string name,
|
||||
T defaultValue,
|
||||
@@ -28,6 +54,12 @@ namespace Robust.Shared.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains information defining a CVar for <see cref="IConfigurationManager"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the CVar, which can be any of: bool, int, long, float, string, any enum, and ushort.</typeparam>
|
||||
/// <seealso cref="CVarDefsAttribute"/>
|
||||
/// <seealso cref="M:Robust.Shared.Configuration.IConfigurationManager.RegisterCVar``1(System.String,``0,Robust.Shared.Configuration.CVar,System.Action{``0})"/>
|
||||
public sealed class CVarDef<T> : CVarDef where T : notnull
|
||||
{
|
||||
public new T DefaultValue { get; }
|
||||
@@ -39,6 +71,28 @@ namespace Robust.Shared.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a static class as containing CVar definitions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// There is no limit on the number of CVarDefs classes you can have, and all CVars will ultimately share the
|
||||
/// same namespace regardless of which class they're in.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// CVar definitions can be in any assembly, but should never be marked <see cref="CVar.REPLICATED"/> or
|
||||
/// <see cref="CVar.NOTIFY"/> if not in a shared assembly.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public static class MyCVars
|
||||
/// {
|
||||
/// public static readonly CVarDef<bool> MyEnabled =
|
||||
/// CVarDef.Create("mycvars.enabled", true, CVar.SERVER, "Enables the thing.");
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
[MeansImplicitUse]
|
||||
public sealed class CVarDefsAttribute : Attribute
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Toolshed;
|
||||
|
||||
namespace Robust.Shared.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic interface to handle console commands. Any class implementing this will be
|
||||
/// registered with the console system through reflection.
|
||||
/// Basic interface to handle console commands. Any class implementing this will be
|
||||
/// registered with the console system through reflection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For server commands, it is much preferred to use <see cref="ToolshedCommand"/>.
|
||||
/// </remarks>
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public interface IConsoleCommand
|
||||
{
|
||||
|
||||
@@ -3,9 +3,16 @@ using System.Threading.Tasks;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Toolshed;
|
||||
|
||||
namespace Robust.Shared.Console;
|
||||
|
||||
/// <summary>
|
||||
/// A variant on <see cref="IConsoleCommand"/> that has some built-in default localization strings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For server commands, it is much preferred to use <see cref="ToolshedCommand"/>.
|
||||
/// </remarks>
|
||||
public abstract class LocalizedCommands : IConsoleCommand
|
||||
{
|
||||
[Dependency] protected readonly ILocalizationManager LocalizationManager = default!;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -7,7 +8,24 @@ using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <inheritdoc cref="IComponent"/>
|
||||
/// <summary>
|
||||
/// The base class for all ECS components. A component is a piece of data, ideally without methods, that is
|
||||
/// attached to or will be attached to some <see cref="EntityUid"/>. Entities do not have any data without
|
||||
/// components to specify it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Components must be registered, usually with <see cref="RegisterComponentAttribute"/>, for the ECS to
|
||||
/// recognize them.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Components implicitly have <see cref="DataDefinitionAttribute"/>, and are always valid for YAML ser/de.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="NetworkedComponentAttribute"/>
|
||||
/// <seealso cref="DataFieldAttribute"/>
|
||||
/// <seealso cref="ComponentState"/>
|
||||
/// <seealso cref="UnsavedComponentAttribute"/>
|
||||
[Reflect(false)]
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract partial class Component : IComponent
|
||||
|
||||
@@ -6,14 +6,16 @@ namespace Robust.Shared.GameObjects;
|
||||
/// <summary>
|
||||
/// Marks a component as being automatically registered by <see cref="IComponentFactory.DoAutoRegistrations" />
|
||||
/// </summary>
|
||||
/// <seealso cref="Component"/>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
[BaseTypeRequired(typeof(IComponent))]
|
||||
[MeansImplicitUse]
|
||||
public sealed class RegisterComponentAttribute : Attribute;
|
||||
|
||||
/// <summary>
|
||||
/// Defines Name that this component is represented with in prototypes.
|
||||
/// Defines Name that this component is represented with in prototypes.
|
||||
/// </summary>
|
||||
/// <seealso cref="Component"/>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class ComponentProtoNameAttribute(string prototypeName) : Attribute
|
||||
{
|
||||
@@ -21,8 +23,9 @@ public sealed class ComponentProtoNameAttribute(string prototypeName) : Attribut
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a component as not being saved when saving maps/grids.
|
||||
/// Marks a component as not being saved when saving maps/grids. This is useful for purely runtime information.
|
||||
/// </summary>
|
||||
/// <seealso cref="ComponentRegistration.Unsaved"/>
|
||||
/// <seealso cref="Component"/>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class UnsavedComponentAttribute : Attribute;
|
||||
|
||||
@@ -582,6 +582,10 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception fired whenever a component not recognized by the engine is encountered.
|
||||
/// This is usually caused by forgetting <see cref="RegisterComponentAttribute"/>.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class UnknownComponentException : Exception
|
||||
{
|
||||
@@ -596,10 +600,17 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception fired if you try to register a new component after all registrations have been locked.
|
||||
/// This is, typically, after network IDs have been assigned.
|
||||
/// </summary>
|
||||
public sealed class ComponentRegistrationLockException : Exception
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception fired when a component's name is entirely invalid. All component type names must end with Component.
|
||||
/// </summary>
|
||||
public sealed class InvalidComponentNameException : Exception
|
||||
{
|
||||
public InvalidComponentNameException(string message) : base(message)
|
||||
|
||||
@@ -1,18 +1,37 @@
|
||||
using System;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
/// <summary>
|
||||
/// An abstract base class for a component's network state. For simple cases, you can automatically generate this using
|
||||
/// <see cref="AutoGenerateComponentStateAttribute"/> and <see cref="AutoNetworkedFieldAttribute"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If your component's state is particularly complex, or you otherwise want manual control, you can implement this
|
||||
/// directly and register necessary event handlers for <see cref="ComponentHandleState"/> and
|
||||
/// <see cref="ComponentGetState"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// How state is actually applied for a component, and what it looks like, is user defined. For an example, look at
|
||||
/// <see cref="OccluderComponent.OccluderComponentState"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[RequiresSerializable]
|
||||
[Serializable, NetSerializable]
|
||||
[Virtual]
|
||||
public abstract class ComponentState : IComponentState;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the state of a component for networking purposes.
|
||||
/// Represents the state of a component for networking purposes.
|
||||
/// </summary>
|
||||
public interface IComponentState;
|
||||
|
||||
/// <summary>
|
||||
/// Internal for RT, you probably want <see cref="IComponentDeltaState{TState}"/>.
|
||||
/// </summary>
|
||||
public interface IComponentDeltaState : IComponentState
|
||||
{
|
||||
public void ApplyToFullState(IComponentState fullState);
|
||||
@@ -21,20 +40,20 @@ public interface IComponentDeltaState : IComponentState
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for component states that only contain partial state data. The actual delta state class should be a
|
||||
/// separate class from the full component states.
|
||||
/// Interface for component states that only contain partial state data. The actual delta state class should be a
|
||||
/// separate class from the full component states.
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">The full-state class associated with this partial state</typeparam>
|
||||
public interface IComponentDeltaState<TState> : IComponentDeltaState where TState: IComponentState
|
||||
{
|
||||
/// <summary>
|
||||
/// This function will apply the current delta state to the provided full state, modifying it in the process.
|
||||
/// This function will apply the current delta state to the provided full state, modifying it in the process.
|
||||
/// </summary>
|
||||
public void ApplyToFullState(TState fullState);
|
||||
|
||||
/// <summary>
|
||||
/// This function should take in a full state and return a new full state with the current delta applied, WITHOUT
|
||||
/// modifying the original input state.
|
||||
/// This function should take in a full state and return a new full state with the current delta applied,
|
||||
/// WITHOUT modifying the original input state.
|
||||
/// </summary>
|
||||
public TState CreateNewFullState(TState fullState);
|
||||
|
||||
|
||||
@@ -17,8 +17,11 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the position and orientation of the entity.
|
||||
/// Stores the relative and global position and orientation of the entity.<br/>
|
||||
/// This also tracks the overall transform hierarchy, which allows entities to be children of other entities
|
||||
/// and move when their parent moves cheaply.
|
||||
/// </summary>
|
||||
/// <seealso cref="SharedTransformSystem"/>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class TransformComponent : Component, IComponentDebug
|
||||
{
|
||||
|
||||
14
Robust.Shared/GameObjects/Docs.xml
Normal file
14
Robust.Shared/GameObjects/Docs.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<entries>
|
||||
<entry name="EntityQueryResolve">
|
||||
<summary>
|
||||
Attempts to look up <typeparamref name="TComp1"/> on the given entity, writing it into the given space
|
||||
if it finds it and the space was not already empty.
|
||||
</summary>
|
||||
<remarks>
|
||||
This is preferable to
|
||||
<see cref="M:Robust.Shared.GameObjects.EntityQuery`1.TryComp(Robust.Shared.GameObjects.EntityUid,`0@)"/> if it
|
||||
is erroneous for the component to not be present, but you don't want to crash the game.<br/>
|
||||
This is also preferable if you may have already looked up the component, saving on lookup time.
|
||||
</remarks>
|
||||
</entry>
|
||||
</entries>
|
||||
@@ -5,6 +5,16 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="EntityUid"/> with an associated component (or components) looked up in advance.
|
||||
/// This is used by APIs to strongly type them over a required component, and can easily be obtained for an
|
||||
/// EntityUid using <see cref="M:Robust.Shared.GameObjects.EntityQuery`1.Get(Robust.Shared.GameObjects.EntityUid)"/>,
|
||||
/// <see cref="M:Robust.Shared.GameObjects.EntityQuery`1.TryComp(Robust.Shared.GameObjects.EntityUid,`0@)"/>,
|
||||
/// and other <see cref="EntityQuery{TComp1}"/> methods.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This type exists for up to eight (i.e. <c>Entity<T1, T2, T3, T4, T5, T6, T7, T8></c>) parameters.
|
||||
/// </remarks>
|
||||
[NotYamlSerializable]
|
||||
public record struct Entity<T> : IFluentEntityUid, IAsType<EntityUid>
|
||||
where T : IComponent?
|
||||
@@ -53,6 +63,7 @@ public record struct Entity<T> : IFluentEntityUid, IAsType<EntityUid>
|
||||
public readonly EntityUid AsType() => Owner;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Entity{T}"/>
|
||||
[NotYamlSerializable]
|
||||
public record struct Entity<T1, T2> : IFluentEntityUid, IAsType<EntityUid>
|
||||
where T1 : IComponent? where T2 : IComponent?
|
||||
@@ -124,6 +135,7 @@ public record struct Entity<T1, T2> : IFluentEntityUid, IAsType<EntityUid>
|
||||
public readonly EntityUid AsType() => Owner;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Entity{T}"/>
|
||||
[NotYamlSerializable]
|
||||
public record struct Entity<T1, T2, T3> : IFluentEntityUid, IAsType<EntityUid>
|
||||
where T1 : IComponent? where T2 : IComponent? where T3 : IComponent?
|
||||
@@ -231,6 +243,7 @@ public record struct Entity<T1, T2, T3> : IFluentEntityUid, IAsType<EntityUid>
|
||||
public readonly EntityUid AsType() => Owner;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Entity{T}"/>
|
||||
[NotYamlSerializable]
|
||||
public record struct Entity<T1, T2, T3, T4> : IFluentEntityUid, IAsType<EntityUid>
|
||||
where T1 : IComponent? where T2 : IComponent? where T3 : IComponent? where T4 : IComponent?
|
||||
@@ -362,6 +375,7 @@ public record struct Entity<T1, T2, T3, T4> : IFluentEntityUid, IAsType<EntityUi
|
||||
public readonly EntityUid AsType() => Owner;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Entity{T}"/>
|
||||
[NotYamlSerializable]
|
||||
public record struct Entity<T1, T2, T3, T4, T5> : IFluentEntityUid, IAsType<EntityUid>
|
||||
where T1 : IComponent? where T2 : IComponent? where T3 : IComponent? where T4 : IComponent? where T5 : IComponent?
|
||||
@@ -517,6 +531,7 @@ public record struct Entity<T1, T2, T3, T4, T5> : IFluentEntityUid, IAsType<Enti
|
||||
public readonly EntityUid AsType() => Owner;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Entity{T}"/>
|
||||
[NotYamlSerializable]
|
||||
public record struct Entity<T1, T2, T3, T4, T5, T6> : IFluentEntityUid, IAsType<EntityUid>
|
||||
where T1 : IComponent? where T2 : IComponent? where T3 : IComponent? where T4 : IComponent? where T5 : IComponent? where T6 : IComponent?
|
||||
@@ -696,6 +711,7 @@ public record struct Entity<T1, T2, T3, T4, T5, T6> : IFluentEntityUid, IAsType<
|
||||
public readonly EntityUid AsType() => Owner;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Entity{T}"/>
|
||||
[NotYamlSerializable]
|
||||
public record struct Entity<T1, T2, T3, T4, T5, T6, T7> : IFluentEntityUid, IAsType<EntityUid>
|
||||
where T1 : IComponent? where T2 : IComponent? where T3 : IComponent? where T4 : IComponent? where T5 : IComponent? where T6 : IComponent? where T7 : IComponent?
|
||||
@@ -899,6 +915,7 @@ public record struct Entity<T1, T2, T3, T4, T5, T6, T7> : IFluentEntityUid, IAsT
|
||||
public readonly EntityUid AsType() => Owner;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Entity{T}"/>
|
||||
[NotYamlSerializable]
|
||||
public record struct Entity<T1, T2, T3, T4, T5, T6, T7, T8> : IFluentEntityUid, IAsType<EntityUid>
|
||||
where T1 : IComponent? where T2 : IComponent? where T3 : IComponent? where T4 : IComponent? where T5 : IComponent? where T6 : IComponent? where T7 : IComponent? where T8 : IComponent?
|
||||
|
||||
@@ -67,7 +67,8 @@ namespace Robust.Shared.GameObjects
|
||||
/// <remarks>
|
||||
/// This has a very specific purpose, and has massive potential to be abused.
|
||||
/// DO NOT USE THIS IN CONTENT UNLESS YOU KNOW WHAT YOU'RE DOING, the only reason it's not internal
|
||||
/// is because of the component network source generator.
|
||||
/// is because of the component network source generator.<br/>
|
||||
/// This may be removed, modified, or pulled back internal at ANY TIME.
|
||||
/// </remarks>
|
||||
public void RaiseComponentEvent<TEvent, TComponent>(EntityUid uid, TComponent component, TEvent args)
|
||||
where TEvent : notnull
|
||||
|
||||
@@ -21,7 +21,6 @@ using Robust.Shared.Exceptions;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class EntityManager
|
||||
{
|
||||
[IoC.Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
@@ -1756,6 +1755,38 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An index of all entities with a given component, avoiding looking up the component's storage every time.
|
||||
/// Using these saves on dictionary lookups, making your code slightly more efficient, and ties in nicely with
|
||||
/// <see cref="Entity{T}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TComp1">Any component type.</typeparam>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public sealed class MySystem : EntitySystem
|
||||
/// {
|
||||
/// private EntityQuery<TransformComponent> _transforms = default!;
|
||||
/// <br/>
|
||||
/// public override void Initialize()
|
||||
/// {
|
||||
/// _transforms = GetEntityQuery<TransformComponent>();
|
||||
/// }
|
||||
/// <br/>
|
||||
/// public void DoThings(EntityUid myEnt)
|
||||
/// {
|
||||
/// var ent = _transforms.Get(myEnt);
|
||||
/// // ...
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <remarks>
|
||||
/// Queries hold references to <see cref="IEntityManager"/> internals, and are always up to date with the world.
|
||||
/// They can not however perform mutation, if you need to add or remove components you must use
|
||||
/// <see cref="EntitySystem"/> or <see cref="IEntityManager"/> methods.
|
||||
/// </remarks>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.EntitySystem.GetEntityQuery``1">EntitySystem.GetEntityQuery()</seealso>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.EntityManager.GetEntityQuery``1">EntityManager.GetEntityQuery()</seealso>
|
||||
public readonly struct EntityQuery<TComp1> where TComp1 : IComponent
|
||||
{
|
||||
private readonly Dictionary<EntityUid, IComponent> _traitDict;
|
||||
@@ -1767,6 +1798,18 @@ namespace Robust.Shared.GameObjects
|
||||
_sawmill = sawmill;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <typeparamref name="TComp1"/> for an entity, throwing if it can't find it.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity to do a lookup for.</param>
|
||||
/// <returns>The located component.</returns>
|
||||
/// <exception cref="KeyNotFoundException">Thrown if the entity does not have a component of type <typeparamref name="TComp1"/>.</exception>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.IEntityManager.GetComponent``1(Robust.Shared.GameObjects.EntityUid)">
|
||||
/// IEntityManager.GetComponent<T>(EntityUid)
|
||||
/// </seealso>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.EntitySystem.Comp``1(Robust.Shared.GameObjects.EntityUid)">
|
||||
/// EntitySystem.Comp<T>(EntityUid)
|
||||
/// </seealso>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public TComp1 GetComponent(EntityUid uid)
|
||||
@@ -1777,6 +1820,7 @@ namespace Robust.Shared.GameObjects
|
||||
throw new KeyNotFoundException($"Entity {uid} does not have a component of type {typeof(TComp1)}");
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GetComponent"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
public Entity<TComp1> Get(EntityUid uid)
|
||||
{
|
||||
@@ -1786,6 +1830,22 @@ namespace Robust.Shared.GameObjects
|
||||
throw new KeyNotFoundException($"Entity {uid} does not have a component of type {typeof(TComp1)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <typeparamref name="TComp1"/> for an entity, if it's present.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If it is strictly errorenous for a component to not be present, you may want to use
|
||||
/// <see cref="Resolve(Robust.Shared.GameObjects.EntityUid,ref TComp1?,bool)"/> instead.
|
||||
/// </remarks>
|
||||
/// <param name="uid">The entity to do a lookup for.</param>
|
||||
/// <param name="component">The located component, if any.</param>
|
||||
/// <returns>Whether the component was found.</returns>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.IEntityManager.TryGetComponent``1(Robust.Shared.GameObjects.EntityUid,``0@)">
|
||||
/// IEntityManager.TryGetComponent<T>(EntityUid, out T?)
|
||||
/// </seealso>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.EntitySystem.TryComp``1(Robust.Shared.GameObjects.EntityUid,``0@)">
|
||||
/// EntitySystem.TryComp<T>(EntityUid, out T?)
|
||||
/// </seealso>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public bool TryGetComponent([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out TComp1? component)
|
||||
@@ -1799,6 +1859,7 @@ namespace Robust.Shared.GameObjects
|
||||
return TryGetComponent(uid.Value, out component);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="TryGetComponent(Robust.Shared.GameObjects.EntityUid?,out TComp1?)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public bool TryGetComponent(EntityUid uid, [NotNullWhen(true)] out TComp1? component)
|
||||
@@ -1813,24 +1874,40 @@ namespace Robust.Shared.GameObjects
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="TryGetComponent(Robust.Shared.GameObjects.EntityUid?,out TComp1?)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public bool TryComp(EntityUid uid, [NotNullWhen(true)] out TComp1? component)
|
||||
=> TryGetComponent(uid, out component);
|
||||
|
||||
/// <inheritdoc cref="TryGetComponent(Robust.Shared.GameObjects.EntityUid?,out TComp1?)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public bool TryComp([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out TComp1? component)
|
||||
=> TryGetComponent(uid, out component);
|
||||
|
||||
/// <summary>
|
||||
/// Tests if the given entity has <typeparamref name="TComp1"/>.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity to do a lookup for.</param>
|
||||
/// <returns>Whether the component exists for that entity.</returns>
|
||||
/// <remarks>If you immediately need to then look up that component, it's more efficient to use <see cref="TryComp(Robust.Shared.GameObjects.EntityUid,out TComp1?)"/>.</remarks>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.IEntityManager.HasComponent``1(Robust.Shared.GameObjects.EntityUid)">
|
||||
/// IEntityManager.HasComponent<T>(EntityUid)
|
||||
/// </seealso>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.EntitySystem.HasComp``1(Robust.Shared.GameObjects.EntityUid)">
|
||||
/// EntitySystem.HasComp<T>(EntityUid)
|
||||
/// </seealso>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public bool HasComp(EntityUid uid) => HasComponent(uid);
|
||||
|
||||
/// <inheritdoc cref="HasComp(Robust.Shared.GameObjects.EntityUid)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public bool HasComp([NotNullWhen(true)] EntityUid? uid) => HasComponent(uid);
|
||||
|
||||
/// <inheritdoc cref="HasComp(Robust.Shared.GameObjects.EntityUid)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public bool HasComponent(EntityUid uid)
|
||||
@@ -1838,6 +1915,7 @@ namespace Robust.Shared.GameObjects
|
||||
return _traitDict.TryGetValue(uid, out var comp) && !comp.Deleted;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HasComp(Robust.Shared.GameObjects.EntityUid)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public bool HasComponent([NotNullWhen(true)] EntityUid? uid)
|
||||
@@ -1845,6 +1923,14 @@ namespace Robust.Shared.GameObjects
|
||||
return uid != null && HasComponent(uid.Value);
|
||||
}
|
||||
|
||||
/// <include file='Docs.xml' path='entries/entry[@name="EntityQueryResolve"]/*'/>
|
||||
/// <param name="uid">The entity to do a lookup for.</param>
|
||||
/// <param name="component">The space to write the component into if found.</param>
|
||||
/// <param name="logMissing">Whether to log if the component is missing, for diagnostics.</param>
|
||||
/// <returns>Whether the component was found.</returns>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.EntitySystem.Resolve``1(Robust.Shared.GameObjects.EntityUid,``0@,System.Boolean)">
|
||||
/// EntitySystem.Resolve<T>(EntityUid, out T?)
|
||||
/// </seealso>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Resolve(EntityUid uid, [NotNullWhen(true)] ref TComp1? component, bool logMissing = true)
|
||||
{
|
||||
@@ -1868,12 +1954,24 @@ namespace Robust.Shared.GameObjects
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <include file='Docs.xml' path='entries/entry[@name="EntityQueryResolve"]/*'/>
|
||||
/// <param name="entity">The space to write the component into if found.</param>
|
||||
/// <param name="logMissing">Whether to log if the component is missing, for diagnostics.</param>
|
||||
/// <returns>Whether the component was found.</returns>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.EntitySystem.Resolve``1(Robust.Shared.GameObjects.EntityUid,``0@,System.Boolean)">
|
||||
/// EntitySystem.Resolve<T>(EntityUid, out T?)
|
||||
/// </seealso>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Resolve(ref Entity<TComp1?> entity, bool logMissing = true)
|
||||
{
|
||||
return Resolve(entity.Owner, ref entity.Comp, logMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <typeparamref name="TComp1"/> for an entity if it's present, or null if it's not.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity to do the lookup on.</param>
|
||||
/// <returns>The component, if it exists.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public TComp1? CompOrNull(EntityUid uid)
|
||||
@@ -1884,6 +1982,7 @@ namespace Robust.Shared.GameObjects
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GetComponent"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public TComp1 Comp(EntityUid uid)
|
||||
@@ -2113,8 +2212,35 @@ namespace Robust.Shared.GameObjects
|
||||
#region Query
|
||||
|
||||
/// <summary>
|
||||
/// Returns all matching unpaused components.
|
||||
/// Iterates all entities that have the given components, including the components themselves, but only if
|
||||
/// the entity they're on is not <see cref="EntitySystem.Paused">Paused</see>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Internally, checks for what components exist are done in the order of the generics, so the least frequently
|
||||
/// occurring component should always be the first argument, and so on.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This type exists for <b>up to</b> four components, TComp1 through TComp4, as generic arguments.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // within a system.
|
||||
/// var enumerator = EntityQueryEnumerator<TransformComponent>();
|
||||
/// <br/>
|
||||
/// while (enumerator.MoveNext(out var ent, out var xform))
|
||||
/// {
|
||||
/// // ... do work with the entity we just found.
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.EntitySystem.EntityQueryEnumerator``1">
|
||||
/// EntitySystem.EntityQueryEnumerator<TComp1, ...>()
|
||||
/// </seealso>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.IEntityManager.EntityQueryEnumerator``1">
|
||||
/// IEntityManager.EntityQueryEnumerator<TComp1, ...>()
|
||||
/// </seealso>
|
||||
public struct EntityQueryEnumerator<TComp1> : IDisposable
|
||||
where TComp1 : IComponent
|
||||
{
|
||||
@@ -2129,6 +2255,12 @@ namespace Robust.Shared.GameObjects
|
||||
_metaQuery = metaQuery;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides the next entity and component in the enumerator, if there are still more to iterate through.
|
||||
/// </summary>
|
||||
/// <param name="uid">The found entity, if any.</param>
|
||||
/// <param name="comp1">A component on the found entity.</param>
|
||||
/// <returns>Whether the enumerator was empty (and as such no entity nor component were returned)</returns>
|
||||
public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1)
|
||||
{
|
||||
while (true)
|
||||
@@ -2170,9 +2302,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all matching unpaused components.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="EntityQueryEnumerator{TComp1}"/>
|
||||
public struct EntityQueryEnumerator<TComp1, TComp2> : IDisposable
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
@@ -2191,6 +2321,8 @@ namespace Robust.Shared.GameObjects
|
||||
_metaQuery = metaQuery;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="M:Robust.Shared.GameObjects.EntityQueryEnumerator`1.MoveNext(Robust.Shared.GameObjects.EntityUid@,`0@)"/>
|
||||
/// <param name="comp2">A component on the found entity.</param>
|
||||
public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2)
|
||||
{
|
||||
while (true)
|
||||
@@ -2239,9 +2371,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all matching unpaused components.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="EntityQueryEnumerator{TComp1}"/>
|
||||
public struct EntityQueryEnumerator<TComp1, TComp2, TComp3> : IDisposable
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
@@ -2264,6 +2394,9 @@ namespace Robust.Shared.GameObjects
|
||||
_metaQuery = metaQuery;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="M:Robust.Shared.GameObjects.EntityQueryEnumerator`1.MoveNext(Robust.Shared.GameObjects.EntityUid@,`0@)"/>
|
||||
/// <param name="comp2">A component on the found entity.</param>
|
||||
/// <param name="comp3">A component on the found entity.</param>
|
||||
public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2, [NotNullWhen(true)] out TComp3? comp3)
|
||||
{
|
||||
while (true)
|
||||
@@ -2322,9 +2455,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all matching unpaused components.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="EntityQueryEnumerator{TComp1}"/>
|
||||
public struct EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4> : IDisposable
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
@@ -2351,6 +2482,10 @@ namespace Robust.Shared.GameObjects
|
||||
_metaQuery = metaQuery;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="M:Robust.Shared.GameObjects.EntityQueryEnumerator`1.MoveNext(Robust.Shared.GameObjects.EntityUid@,`0@)"/>
|
||||
/// <param name="comp2">A component on the found entity.</param>
|
||||
/// <param name="comp3">A component on the found entity.</param>
|
||||
/// <param name="comp4">A component on the found entity.</param>
|
||||
public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2, [NotNullWhen(true)] out TComp3? comp3, [NotNullWhen(true)] out TComp4? comp4)
|
||||
{
|
||||
while (true)
|
||||
@@ -2422,8 +2557,35 @@ namespace Robust.Shared.GameObjects
|
||||
#region All query
|
||||
|
||||
/// <summary>
|
||||
/// Returns all matching components, paused or not.
|
||||
/// Iterates all entities that have the given components, including the components themselves, regardless
|
||||
/// of if the entity is <see cref="EntitySystem.Paused">Paused</see>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Internally, checks for what components exist are done in the order of the generics, so the least frequently
|
||||
/// occurring component should always be the first argument, and so on.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This type exists for <b>up to</b> four components, TComp1 through TComp4, as generic arguments.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // within a system.
|
||||
/// var enumerator = AllEntityQueryEnumerator<TransformComponent>();
|
||||
/// <br/>
|
||||
/// while (enumerator.MoveNext(out var ent, out var xform))
|
||||
/// {
|
||||
/// // ... do work with the entity we just found.
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.EntitySystem.AllEntityQuery``1">
|
||||
/// EntitySystem.AllEntityQuery<TComp1, ...>()
|
||||
/// </seealso>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.IEntityManager.AllEntityQueryEnumerator``1">
|
||||
/// IEntityManager.AllEntityQueryEnumerator<TComp1, ...>()
|
||||
/// </seealso>
|
||||
public struct AllEntityQueryEnumerator<TComp1> : IDisposable
|
||||
where TComp1 : IComponent
|
||||
{
|
||||
@@ -2435,6 +2597,7 @@ namespace Robust.Shared.GameObjects
|
||||
_traitDict = traitDict.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="M:Robust.Shared.GameObjects.EntityQueryEnumerator`1.MoveNext(Robust.Shared.GameObjects.EntityUid@,`0@)"/>
|
||||
public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1)
|
||||
{
|
||||
while (true)
|
||||
@@ -2471,9 +2634,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all matching components, paused or not.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="AllEntityQueryEnumerator{TComp1}"/>
|
||||
public struct AllEntityQueryEnumerator<TComp1, TComp2> : IDisposable
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
@@ -2489,6 +2650,7 @@ namespace Robust.Shared.GameObjects
|
||||
_traitDict2 = traitDict2;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="M:Robust.Shared.GameObjects.EntityQueryEnumerator`2.MoveNext(Robust.Shared.GameObjects.EntityUid@,`0@,`1@)"/>
|
||||
public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2)
|
||||
{
|
||||
while (true)
|
||||
@@ -2532,9 +2694,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all matching components, paused or not.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="AllEntityQueryEnumerator{TComp1}"/>
|
||||
public struct AllEntityQueryEnumerator<TComp1, TComp2, TComp3> : IDisposable
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
@@ -2554,6 +2714,7 @@ namespace Robust.Shared.GameObjects
|
||||
_traitDict3 = traitDict3;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="M:Robust.Shared.GameObjects.EntityQueryEnumerator`3.MoveNext(Robust.Shared.GameObjects.EntityUid@,`0@,`1@,`2@)"/>
|
||||
public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2, [NotNullWhen(true)] out TComp3? comp3)
|
||||
{
|
||||
while (true)
|
||||
@@ -2607,9 +2768,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all matching components, paused or not.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="AllEntityQueryEnumerator{TComp1}"/>
|
||||
public struct AllEntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4> : IDisposable
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
@@ -2633,6 +2792,7 @@ namespace Robust.Shared.GameObjects
|
||||
_traitDict4 = traitDict4;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="M:Robust.Shared.GameObjects.EntityQueryEnumerator`4.MoveNext(Robust.Shared.GameObjects.EntityUid@,`0@,`1@,`2@,`3@)"/>
|
||||
public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2, [NotNullWhen(true)] out TComp3? comp3, [NotNullWhen(true)] out TComp4? comp4)
|
||||
{
|
||||
while (true)
|
||||
|
||||
@@ -31,8 +31,14 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
protected IComponentFactory Factory => EntityManager.ComponentFactory;
|
||||
|
||||
/// <summary>
|
||||
/// A logger sawmill for logging debug/informational messages into.
|
||||
/// </summary>
|
||||
public ISawmill Log { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The name for the preprovided log sawmill <see cref="Log"/>.
|
||||
/// </summary>
|
||||
protected virtual string SawmillName
|
||||
{
|
||||
get
|
||||
@@ -59,9 +65,19 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of systems this system MUST update after. This determines the overall update order for systems.
|
||||
/// </summary>
|
||||
protected internal List<Type> UpdatesAfter { get; } = new();
|
||||
/// <summary>
|
||||
/// A list of systems this system MUST update before. This determines the overall update order for systems.
|
||||
/// </summary>
|
||||
protected internal List<Type> UpdatesBefore { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Whether this system will also tick outside of predicted ticks on the client.
|
||||
/// </summary>
|
||||
/// <seealso cref="Update"/>
|
||||
public bool UpdatesOutsidePrediction { get; protected internal set; }
|
||||
|
||||
IEnumerable<Type> IEntitySystem.UpdatesAfter => UpdatesAfter;
|
||||
@@ -97,36 +113,69 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
#region Event Proxy
|
||||
|
||||
/// <summary>
|
||||
/// Raise an event on the event bus, broadcasted locally to all listeners by value.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to send, consuming it.</param>
|
||||
/// <typeparam name="T">The type of the message to send.</typeparam>
|
||||
protected void RaiseLocalEvent<T>(T message) where T : notnull
|
||||
{
|
||||
EntityManager.EventBusInternal.RaiseEvent(EventSource.Local, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise an event on the event bus, broadcasted locally to all listeners by ref.
|
||||
/// </summary>
|
||||
/// <param name="message">The location of a message, to be sent by reference and modified in place.</param>
|
||||
/// <typeparam name="T">The type of the message to send.</typeparam>
|
||||
protected void RaiseLocalEvent<T>(ref T message) where T : notnull
|
||||
{
|
||||
EntityManager.EventBusInternal.RaiseEvent(EventSource.Local, ref message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise an event of unknown type on the event bus, broadcasted locally to all listeners of its underlying
|
||||
/// type.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to send.</param>
|
||||
protected void RaiseLocalEvent(object message)
|
||||
{
|
||||
EntityManager.EventBusInternal.RaiseEvent(EventSource.Local, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue an event to broadcast locally at the end of the tick.
|
||||
/// </summary>
|
||||
/// <param name="message">The entity event to raise.</param>
|
||||
protected void QueueLocalEvent(EntityEventArgs message)
|
||||
{
|
||||
EntityManager.EventBusInternal.QueueEvent(EventSource.Local, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a networked event to be broadcast to all clients at the end of the tick.
|
||||
/// </summary>
|
||||
/// <param name="message">The entity event to raise.</param>
|
||||
protected void RaiseNetworkEvent(EntityEventArgs message)
|
||||
{
|
||||
EntityManager.EntityNetManager?.SendSystemNetworkMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a networked event to be sent to a specific connection at the end of the tick.
|
||||
/// </summary>
|
||||
/// <param name="message">The entity event to raise.</param>
|
||||
/// <param name="channel">The session to send the event to.</param>
|
||||
protected void RaiseNetworkEvent(EntityEventArgs message, INetChannel channel)
|
||||
{
|
||||
EntityManager.EntityNetManager?.SendSystemNetworkMessage(message, channel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a networked event to be sent to a specific session at the end of the tick.
|
||||
/// </summary>
|
||||
/// <param name="message">The entity event to raise.</param>
|
||||
/// <param name="session">The session to send the event to.</param>
|
||||
protected void RaiseNetworkEvent(EntityEventArgs message, ICommonSession session)
|
||||
{
|
||||
EntityManager.EntityNetManager?.SendSystemNetworkMessage(message, session.Channel);
|
||||
@@ -135,7 +184,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Raises a networked event with some filter.
|
||||
/// </summary>
|
||||
/// <param name="message">The event to send</param>
|
||||
/// <param name="message">The entity event to raise.</param>
|
||||
/// <param name="filter">The filter that specifies recipients</param>
|
||||
/// <param name="recordReplay">Optional bool specifying whether or not to save this event to replays.</param>
|
||||
protected void RaiseNetworkEvent(EntityEventArgs message, Filter filter, bool recordReplay = true)
|
||||
@@ -149,34 +198,68 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise a networked event for a given recipient entity, if there's a client attached.
|
||||
/// </summary>
|
||||
/// <param name="message">The entity event to raise.</param>
|
||||
/// <param name="recipient">The entity to look up a session to send to on.</param>
|
||||
protected void RaiseNetworkEvent(EntityEventArgs message, EntityUid recipient)
|
||||
{
|
||||
if (_playerMan.TryGetSessionByEntity(recipient, out var session))
|
||||
EntityManager.EntityNetManager?.SendSystemNetworkMessage(message, session.Channel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise a local event, optionally broadcasted, on a specific entity by value.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity to raise the event on.</param>
|
||||
/// <param name="args">The event to raise, sent by value.</param>
|
||||
/// <param name="broadcast">Whether to broadcast the event alongside raising it directed.</param>
|
||||
/// <typeparam name="TEvent">The type of the event to raise.</typeparam>
|
||||
protected void RaiseLocalEvent<TEvent>(EntityUid uid, TEvent args, bool broadcast = false)
|
||||
where TEvent : notnull
|
||||
{
|
||||
EntityManager.EventBusInternal.RaiseLocalEvent(uid, args, broadcast);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise a local event, optionally broadcasted, on a specific entity by value.
|
||||
/// This raise the event for <paramref name="args"/>' underlying concrete type.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity to raise the event on.</param>
|
||||
/// <param name="args">The event to raise, sent by value.</param>
|
||||
/// <param name="broadcast">Whether to broadcast the event alongside raising it directed.</param>
|
||||
protected void RaiseLocalEvent(EntityUid uid, object args, bool broadcast = false)
|
||||
{
|
||||
EntityManager.EventBusInternal.RaiseLocalEvent(uid, args, broadcast);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise a local event, optionally broadcasted, on a specific entity by ref.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity to raise the event on.</param>
|
||||
/// <param name="args">The event to raise, sent by ref.</param>
|
||||
/// <param name="broadcast">Whether to broadcast the event alongside raising it directed.</param>
|
||||
/// <typeparam name="TEvent">The type of the event to raise.</typeparam>
|
||||
protected void RaiseLocalEvent<TEvent>(EntityUid uid, ref TEvent args, bool broadcast = false)
|
||||
where TEvent : notnull
|
||||
{
|
||||
EntityManager.EventBusInternal.RaiseLocalEvent(uid, ref args, broadcast);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise a local event, optionally broadcasted, on a specific entity by ref.
|
||||
/// This raise the event for <paramref name="args"/>' underlying concrete type.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity to raise the event on.</param>
|
||||
/// <param name="args">The event to raise, sent by ref.</param>
|
||||
/// <param name="broadcast">Whether to broadcast the event alongside raising it directed.</param>
|
||||
protected void RaiseLocalEvent(EntityUid uid, ref object args, bool broadcast = false)
|
||||
{
|
||||
EntityManager.EventBusInternal.RaiseLocalEvent(uid, ref args, broadcast);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="M:Robust.Shared.GameObjects.EntityEventBus.RaiseComponentEvent``2(Robust.Shared.GameObjects.EntityUid,``1,``0@)"/>
|
||||
protected void RaiseComponentEvent<TEvent, TComp>(EntityUid uid, TComp comp, ref TEvent args)
|
||||
where TEvent : notnull
|
||||
where TComp : IComponent
|
||||
@@ -184,6 +267,7 @@ namespace Robust.Shared.GameObjects
|
||||
EntityManager.EventBusInternal.RaiseComponentEvent(uid, comp, ref args);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="M:Robust.Shared.GameObjects.EntityEventBus.RaiseComponentEvent``2(Robust.Shared.GameObjects.EntityUid,``1,``0@)"/>
|
||||
public void RaiseComponentEvent<TEvent>(EntityUid uid, IComponent component, ref TEvent args)
|
||||
where TEvent : notnull
|
||||
{
|
||||
|
||||
@@ -9,9 +9,27 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// This type contains a network identification number of an entity.
|
||||
/// This can be used by the EntityManager to access an entity
|
||||
/// This type contains the unique identifier for a given entity.
|
||||
/// This can be used with and obtained from <see cref="IEntityManager"/> to manipulate, query, and otherwise
|
||||
/// work with entities and their components.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// An entity is a unique identifier (this type) and a collection of assorted <see cref="Component"/>s that are
|
||||
/// attached to it. Components provide data to describe the entity, and entities+components are operated on by
|
||||
/// <see cref="EntitySystem"/>s.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// EntityUids are not guaranteed to be unique across individual instances of the game, or individual instances
|
||||
/// of <see cref="IEntityManager"/>. For network identification, see <see cref="NetEntity"/>, and for global
|
||||
/// uniqueness across time you'll need to make something yourself.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Sharing EntityUids between <see cref="IEntityManager"/>s, or otherwise summoning IDs from thin air, is
|
||||
/// effectively undefined behavior and most likely will refer to some random other entity that may or may not
|
||||
/// still exist.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[CopyByRef]
|
||||
public readonly struct EntityUid : IEquatable<EntityUid>, IComparable<EntityUid>, ISpanFormattable
|
||||
{
|
||||
@@ -28,7 +46,7 @@ namespace Robust.Shared.GameObjects
|
||||
public static readonly EntityUid FirstUid = new(1);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of this structure, with the given network ID.
|
||||
/// Creates an instance of this structure, with the given unique id.
|
||||
/// </summary>
|
||||
public EntityUid(int id)
|
||||
{
|
||||
@@ -58,8 +76,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the ID value is valid. Does not check if it identifies
|
||||
/// a valid Entity.
|
||||
/// Checks if the ID value is valid at all, but does not check if it identifies a currently living entity.
|
||||
/// </summary>
|
||||
[Pure]
|
||||
public bool IsValid()
|
||||
|
||||
@@ -5,10 +5,10 @@ using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <remarks>
|
||||
/// Base component for the ECS system.
|
||||
/// Instances are dynamically instantiated by a <c>ComponentFactory</c>, and will have their IoC Dependencies resolved.
|
||||
/// </remarks>
|
||||
/// <summary>
|
||||
/// The base interface for an ECS component. You probably want <see cref="Component"/>.<br/>
|
||||
/// </summary>
|
||||
/// <include file='../Serialization/Manager/Attributes/Docs.xml' path='entries/entry[@name="ImpliesDataDefinition"]/*'/>
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public partial interface IComponent
|
||||
{
|
||||
@@ -50,18 +50,21 @@ namespace Robust.Shared.GameObjects
|
||||
EntityUid Owner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Component has been (or is currently being) initialized.
|
||||
/// Whether the component has been or is being initialized.
|
||||
/// </summary>
|
||||
/// <seealso cref="ComponentInit"/>
|
||||
bool Initialized { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This is true when the component is active.
|
||||
/// </summary>
|
||||
/// <seealso cref="ComponentStartup"/>
|
||||
bool Running { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the component has been removed from its owner, AKA deleted.
|
||||
/// </summary>
|
||||
/// <seealso cref="ComponentRemove"/>
|
||||
bool Deleted { get; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// An ages old interface to add extra debug data to the "entfo" server-side command.
|
||||
/// </summary>
|
||||
public partial interface IComponentDebug : IComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a debug string to print to the console or otherwise display from debug tools.
|
||||
/// This can contain newlines.
|
||||
/// </summary>
|
||||
string GetDebugString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Raised directed on an entity when the map is initialized.
|
||||
/// </summary>
|
||||
[ComponentEvent(Exclusive = false)]
|
||||
public sealed class MapInitEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
}
|
||||
9
Robust.Shared/GameObjects/MapInitEvent.cs
Normal file
9
Robust.Shared/GameObjects/MapInitEvent.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed on an entity when the map is initialized.
|
||||
/// </summary>
|
||||
[ComponentEvent(Exclusive = false)]
|
||||
public sealed class MapInitEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
@@ -14,6 +14,9 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages all the grids and maps in the ECS, providing methods to create and modify them.
|
||||
/// </summary>
|
||||
public abstract partial class SharedMapSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ITileDefinitionManager _tileMan = default!;
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Robust.Shared.Map
|
||||
public delegate bool GridCallback<TState>(EntityUid uid, MapGridComponent grid, ref TState state);
|
||||
|
||||
/// <summary>
|
||||
/// This manages all of the grids in the world.
|
||||
/// This manages all the grids and maps in the world. Largely superseded by <see cref="SharedMapSystem"/>.
|
||||
/// </summary>
|
||||
public interface IMapManager
|
||||
{
|
||||
|
||||
@@ -13,6 +13,9 @@ namespace Robust.Shared.Map
|
||||
/// <summary>
|
||||
/// The numeric tile ID used to refer to this tile inside the map datastructure.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The engine does not automatically generate these IDs, games are responsible for assigning them.
|
||||
/// </remarks>
|
||||
ushort TileId { get; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.Map
|
||||
{
|
||||
/// <summary>
|
||||
/// Uniquely identifies a map.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All maps, aside from <see cref="Nullspace"/>, are also entities. When writing generic code it's usually
|
||||
/// preferable to use <see cref="EntityUid"/> or <see cref="Entity{T}"/> instead.
|
||||
/// </remarks>
|
||||
/// <seealso cref="IMapManager"/>
|
||||
/// <seealso cref="SharedMapSystem"/>
|
||||
[Serializable, NetSerializable]
|
||||
public readonly struct MapId : IEquatable<MapId>
|
||||
{
|
||||
/// <summary>
|
||||
/// The equivalent of <c>null</c> for maps. There is no map entity assigned to this and anything here is
|
||||
/// a root (has no parent) for the <see cref="TransformComponent">transform hierarchy</see>.<br/>
|
||||
/// All map entities live in nullspace and function as roots, for example.
|
||||
/// </summary>
|
||||
public static readonly MapId Nullspace = new(0);
|
||||
|
||||
internal readonly int Value;
|
||||
|
||||
@@ -23,12 +23,12 @@ public readonly struct Tile : IEquatable<Tile>, ISpanFormattable
|
||||
public readonly byte Flags;
|
||||
|
||||
/// <summary>
|
||||
/// Variant of this tile to render.
|
||||
/// Variant of this tile to render.
|
||||
/// </summary>
|
||||
public readonly byte Variant;
|
||||
|
||||
/// <summary>
|
||||
/// Rotation and mirroring of this tile to render. 0-3 is normal, 4-7 is mirrored.
|
||||
/// Rotation and mirroring of this tile to render. 0-3 is normal, 4-7 is mirrored.
|
||||
/// </summary>
|
||||
public readonly byte RotationMirroring;
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
namespace Robust.Shared.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// The possible kinds of login a user can engage in.
|
||||
/// </summary>
|
||||
public enum LoginType : byte
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -3,6 +3,23 @@ using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// A unique identifier for a given user's account.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If connected to auth and auth is mandatory, this is guaranteed to be
|
||||
/// <b>globally unique</b> within that auth service, duplicate players will not exist.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Similarly, the engine assumes that if the user is <see cref="LoginType.GuestAssigned">a known guest with an
|
||||
/// assigned ID</see>, their ID is also globally unique.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This is independent of username, and in the current auth implementation users can freely change username.
|
||||
/// Think of this as a way to refer to a given account regardless of what it's named.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Serializable, NetSerializable]
|
||||
public struct NetUserId : IEquatable<NetUserId>, ISelfSerialize
|
||||
{
|
||||
|
||||
@@ -3,9 +3,16 @@ using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.Player;
|
||||
|
||||
/// <summary>
|
||||
/// This component is added to entities that are currently controlled by a player and is removed when the player is detached.
|
||||
/// </summary>
|
||||
/// <seealso cref="M:Robust.Shared.Player.ISharedPlayerManager.SetAttachedEntity(Robust.Shared.Player.ICommonSession,System.Nullable{Robust.Shared.GameObjects.EntityUid},Robust.Shared.Player.ICommonSession@,System.Boolean)"/>
|
||||
[RegisterComponent, UnsavedComponent]
|
||||
public sealed partial class ActorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The player session currently attached to the entity.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public ICommonSession PlayerSession { get; internal set; } = default!;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ public sealed class ActorSystem : EntitySystem
|
||||
_playerManager.SetAttachedEntity(component.PlayerSession, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the session on a given entity, if one exists.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool TryGetSession(EntityUid? uid, out ICommonSession? session)
|
||||
{
|
||||
@@ -35,6 +38,9 @@ public sealed class ActorSystem : EntitySystem
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the session on a given entity, if one exists.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[Pure]
|
||||
public ICommonSession? GetSession(EntityUid? uid)
|
||||
|
||||
@@ -8,69 +8,85 @@ using Robust.Shared.Network;
|
||||
namespace Robust.Shared.Player;
|
||||
|
||||
/// <summary>
|
||||
/// Common info between client and server sessions.
|
||||
/// Common info between client and server sessions.
|
||||
/// </summary>
|
||||
/// <seealso cref="ISharedPlayerManager"/>
|
||||
public interface ICommonSession
|
||||
{
|
||||
/// <summary>
|
||||
/// Status of the session.
|
||||
/// Status of the session, dictating the connection state.
|
||||
/// </summary>
|
||||
SessionStatus Status { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Entity UID that this session is represented by in the world, if any.
|
||||
/// Entity UID that this session is represented by in the world, if any. Any attached entity will have
|
||||
/// <see cref="ActorComponent"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorSystem"/>
|
||||
EntityUid? AttachedEntity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The UID of this session.
|
||||
/// The unique user ID of this session.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this user's <see cref="AuthType"/> is <see cref="LoginType.LoggedIn"/> or
|
||||
/// <see cref="LoginType.GuestAssigned"/> (<see cref="LoginTypeExt.HasStaticUserId"/>),
|
||||
/// their user id is globally unique as described on
|
||||
/// <see cref="NetUserId"/>.
|
||||
/// </remarks>
|
||||
NetUserId UserId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Current name of this player.
|
||||
/// Current name of this player.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Current connection latency of this session. If <see cref="Channel"/> is not null this simply returns
|
||||
/// <see cref="INetChannel.Ping"/>. This is not currently usable by client-side code that wants to try access ping
|
||||
/// information of other players.
|
||||
/// Current connection latency of this session. If <see cref="Channel"/> is not null this simply returns
|
||||
/// <see cref="INetChannel.Ping"/>. This is not currently usable by client-side code that wants to try access
|
||||
/// ping information of other players.
|
||||
/// </summary>
|
||||
short Ping { get; }
|
||||
// TODO PlayerManager ping networking.
|
||||
|
||||
/// <summary>
|
||||
/// The current network channel for this session.
|
||||
/// The current network channel for this session, if one exists.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// On the Server every player has a network channel,
|
||||
/// on the Client only the LocalPlayer has a network channel, and that channel points to the server.
|
||||
/// On the server every player has a network channel,
|
||||
/// and on the client only the LocalPlayer has a network channel, and that channel points to the server.
|
||||
/// Sessions without channels will have null here.
|
||||
/// </remarks>
|
||||
INetChannel Channel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How this session logged in, which dictates the uniqueness of <see cref="UserId"/>.
|
||||
/// </summary>
|
||||
LoginType AuthType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// List of "eyes" to use for PVS range checks.
|
||||
/// List of "eyes" to use for PVS range checks.
|
||||
/// </summary>
|
||||
HashSet<EntityUid> ViewSubscriptions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The last time this session connected.
|
||||
/// </summary>
|
||||
DateTime ConnectedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Session state, for sending player lists to clients.
|
||||
/// Session state, for sending player lists to clients.
|
||||
/// </summary>
|
||||
SessionState State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Class for storing arbitrary session-specific data that is not lost upon reconnect.
|
||||
/// Class for storing arbitrary session-specific data that is not lost upon reconnect.
|
||||
/// </summary>
|
||||
SessionData Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, this indicates that this is a client-side session, and should be ignored when applying a server's
|
||||
/// game state.
|
||||
/// If true, this indicates that this is a client-side session, and should be ignored when applying a server's
|
||||
/// game state.
|
||||
/// </summary>
|
||||
bool ClientSide { get; set; }
|
||||
}
|
||||
|
||||
@@ -13,18 +13,24 @@ namespace Robust.Shared.Player;
|
||||
public interface ISharedPlayerManager
|
||||
{
|
||||
/// <summary>
|
||||
/// list of connected sessions.
|
||||
/// List of all connected sessions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You should not modify the contents of this list.
|
||||
/// </remarks>
|
||||
ICommonSession[] Sessions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sessions with a remote endpoint. On the server, this is equivalent to <see cref="Sessions"/>. On the client,
|
||||
/// this will only ever contain <see cref="LocalSession"/>
|
||||
/// Sessions with a remote endpoint. On the server, this is equivalent to <see cref="Sessions"/>. On the client,
|
||||
/// this will only ever contain <see cref="LocalSession"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You should not modify the contents of this list.
|
||||
/// </remarks>
|
||||
ICommonSession[] NetworkedSessions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary mapping connected users to their sessions.
|
||||
/// Dictionary mapping connected users to their sessions.
|
||||
/// </summary>
|
||||
IReadOnlyDictionary<NetUserId, ICommonSession> SessionsDict { get; }
|
||||
|
||||
@@ -39,7 +45,7 @@ public interface ISharedPlayerManager
|
||||
int MaxPlayers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the manager.
|
||||
/// Initializes the manager.
|
||||
/// </summary>
|
||||
/// <param name="maxPlayers">Maximum number of players that can connect to this server at one time. Does nothing
|
||||
/// on the client.</param>
|
||||
@@ -49,79 +55,123 @@ public interface ISharedPlayerManager
|
||||
void Shutdown();
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that some session's networked data has changed. This will cause an updated player list to be sent to
|
||||
/// all players.
|
||||
/// Indicates that some session's networked data has changed. This will cause an updated player list to be sent
|
||||
/// to all players.
|
||||
/// </summary>
|
||||
void Dirty();
|
||||
|
||||
/// <summary>
|
||||
/// The session of the local player. This will be null on the server.
|
||||
/// The session of the local player. This will be null on the server.
|
||||
/// </summary>
|
||||
[ViewVariables] ICommonSession? LocalSession { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The user Id of the local player. This will be null on the server.
|
||||
/// The user id of the local player. This will be null on the server.
|
||||
/// </summary>
|
||||
[ViewVariables] NetUserId? LocalUser { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity currently controlled by the local player. This will be null on the server.
|
||||
/// The entity currently controlled by the local player. This will be null on the server.
|
||||
/// </summary>
|
||||
[ViewVariables] EntityUid? LocalEntity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This gets invoked when a session's <see cref="ICommonSession.Status"/> changes.
|
||||
/// This gets invoked when a session's <see cref="ICommonSession.Status"/> changes.
|
||||
/// </summary>
|
||||
event EventHandler<SessionStatusEventArgs>? PlayerStatusChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to resolve a username into a <see cref="NetUserId"/>.
|
||||
/// Attempts to resolve a username into a <see cref="NetUserId"/>.
|
||||
/// </summary>
|
||||
bool TryGetUserId(string userName, out NetUserId userId);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get the session that is currently attached to a given entity.
|
||||
/// Attempts to get the session that is currently attached to a given entity.
|
||||
/// </summary>
|
||||
bool TryGetSessionByEntity(EntityUid uid, [NotNullWhen(true)] out ICommonSession? session);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get the session with the given <see cref="NetUserId"/>.
|
||||
/// Attempts to get the session with the given <see cref="NetUserId"/>.
|
||||
/// </summary>
|
||||
bool TryGetSessionById([NotNullWhen(true)] NetUserId? user, [NotNullWhen(true)] out ICommonSession? session);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get the session with the given <see cref="ICommonSession.Name"/>.
|
||||
/// Attempts to get the session with the given <see cref="ICommonSession.Name"/>.
|
||||
/// </summary>
|
||||
bool TryGetSessionByUsername(string username, [NotNullWhen(true)] out ICommonSession? session);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get the session that corresponds to the given channel.
|
||||
/// Attempts to get the session that corresponds to the given channel.
|
||||
/// </summary>
|
||||
bool TryGetSessionByChannel(INetChannel channel, [NotNullWhen(true)] out ICommonSession? session);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the session that corresponds to the given channel, throwing if it doesn't exist.
|
||||
/// </summary>
|
||||
/// <exception cref="KeyNotFoundException">Thrown if no such session exists.</exception>
|
||||
ICommonSession GetSessionByChannel(INetChannel channel) => GetSessionById(channel.UserId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the session that corresponds to the given user id, throwing if it doesn't exist.
|
||||
/// </summary>
|
||||
/// <exception cref="KeyNotFoundException">Thrown if no such session exists.</exception>
|
||||
ICommonSession GetSessionById(NetUserId user);
|
||||
|
||||
/// <summary>
|
||||
/// Check if the given user id has an active session.
|
||||
/// Check if the given user id has an active session.
|
||||
/// </summary>
|
||||
bool ValidSessionId(NetUserId user) => TryGetSessionById(user, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Alternate method to get <see cref="ICommonSession.Data"/>
|
||||
/// </summary>
|
||||
SessionData GetPlayerData(NetUserId userId);
|
||||
/// <summary>
|
||||
/// Grabs a session's <see cref="ICommonSession.Data"/> if it can be found.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user ID to get data for.</param>
|
||||
/// <param name="data">The session data if found.</param>
|
||||
/// <returns>Success or failure.</returns>
|
||||
bool TryGetPlayerData(NetUserId userId, [NotNullWhen(true)] out SessionData? data);
|
||||
/// <summary>
|
||||
/// Grabs a session's <see cref="ICommonSession.Data"/> if it can be found.
|
||||
/// </summary>
|
||||
/// <param name="userName">The username to get data for.</param>
|
||||
/// <param name="data">The session data if found.</param>
|
||||
/// <returns>Success or failure.</returns>
|
||||
bool TryGetPlayerDataByUsername(string userName, [NotNullWhen(true)] out SessionData? data);
|
||||
/// <summary>
|
||||
/// Checks if a given user has any <see cref="ICommonSession.Data"/>.
|
||||
/// </summary>
|
||||
bool HasPlayerData(NetUserId userId);
|
||||
|
||||
/// <summary>
|
||||
/// Returns all <see cref="ICommonSession.Data">session data</see>.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IEnumerable<SessionData> GetAllPlayerData();
|
||||
void GetPlayerStates(GameTick fromTick, List<SessionState> states);
|
||||
void UpdateState(ICommonSession commonSession);
|
||||
|
||||
/// <inheritdoc cref="RemoveSession(Robust.Shared.Player.ICommonSession,bool)"/>
|
||||
void RemoveSession(ICommonSession session, bool removeData = false);
|
||||
/// <summary>
|
||||
/// Completely destroys a session, optionally also removing its data.
|
||||
/// </summary>
|
||||
void RemoveSession(NetUserId user, bool removeData = false);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a session from a network channel.
|
||||
/// </summary>
|
||||
ICommonSession CreateAndAddSession(INetChannel channel);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new session, without a network channel attached.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This should be used carefully, games tend to expect a network channel to be present unless they're client
|
||||
/// side. This is for example used to create a session for singleplayer clients.
|
||||
/// </remarks>
|
||||
ICommonSession CreateAndAddSession(NetUserId user, string name);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -7,9 +7,14 @@ 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.
|
||||
/// Defines the unique type id ("kind") and load priority for a prototype, and registers it so the game knows about
|
||||
/// the type in question during init and can deserialize it.
|
||||
/// <br/>
|
||||
/// </summary>
|
||||
/// <include file='../Serialization/Manager/Attributes/Docs.xml' path='entries/entry[@name="ImpliesDataDefinition"]/*'/>
|
||||
/// <seealso cref="PrototypeRecordAttribute"/>
|
||||
/// <seealso cref="IdDataFieldAttribute"/>
|
||||
/// <seealso cref=""/>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
#if !ROBUST_ANALYZERS_TEST
|
||||
[BaseTypeRequired(typeof(IPrototype))]
|
||||
@@ -20,11 +25,18 @@ namespace Robust.Shared.Prototypes;
|
||||
public class PrototypeAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Override for the name of this kind of prototype. If not specified, this is automatically inferred via <see cref="PrototypeManager.CalculatePrototypeName"/>
|
||||
/// Override for the name of this kind of prototype.
|
||||
/// If not specified, this is automatically inferred via <see cref="PrototypeManager.CalculatePrototypeName"/>
|
||||
/// </summary>
|
||||
public string? Type { get; internal set; }
|
||||
public readonly int LoadPriority = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the load order for prototype kinds. Higher priorities are loaded earlier.
|
||||
/// </summary>
|
||||
public readonly int LoadPriority;
|
||||
|
||||
/// <param name="type">See <see cref="Type"/>.</param>
|
||||
/// <param name="loadPriority">See <see cref="LoadPriority"/>.</param>
|
||||
public PrototypeAttribute(string? type = null, int loadPriority = 1)
|
||||
{
|
||||
Type = type;
|
||||
@@ -38,6 +50,13 @@ public class PrototypeAttribute : Attribute
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the unique type id ("kind") and load priority for a prototype, and registers it so the game knows about
|
||||
/// the type in question during init and can deserialize it.
|
||||
/// <br/>
|
||||
/// </summary>
|
||||
/// <include file='../Serialization/Manager/Attributes/Docs.xml' path='entries/entry[@name="ImpliesDataRecord"]/*'/>
|
||||
/// <seealso cref="PrototypeAttribute"/>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
#if !ROBUST_ANALYZERS_TEST
|
||||
[BaseTypeRequired(typeof(IPrototype))]
|
||||
@@ -47,6 +66,8 @@ public class PrototypeAttribute : Attribute
|
||||
#endif
|
||||
public sealed class PrototypeRecordAttribute : PrototypeAttribute
|
||||
{
|
||||
/// <param name="type">See <see cref="PrototypeAttribute.Type"/>.</param>
|
||||
/// <param name="loadPriority">See <see cref="PrototypeAttribute.LoadPriority"/>.</param>
|
||||
public PrototypeRecordAttribute(string type, int loadPriority = 1) : base(type, loadPriority)
|
||||
{
|
||||
}
|
||||
|
||||
18
Robust.Shared/Prototypes/Docs.xml
Normal file
18
Robust.Shared/Prototypes/Docs.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<entries>
|
||||
<entry name="IPrototype">
|
||||
<remarks>
|
||||
<para>
|
||||
It is strongly discouraged to ever construct inheritors of this interface manually, if you need a prototype that
|
||||
is also runtime-constructable data, consider moving all the data into its own class and using
|
||||
<see cref="T:Robust.Shared.Serialization.Manager.Attributes.IncludeDataFieldAttribute"/> to flatten it into
|
||||
the prototype class.
|
||||
</para>
|
||||
<para>
|
||||
Prototypes should <b>never</b> be mutated at runtime, and should be treated as a read only source of truth. Keep
|
||||
in mind that for example reading a container like a list from the prototype does not make a copy, and you must do
|
||||
so yourself if you intend to mutate.
|
||||
</para>
|
||||
</remarks>
|
||||
</entry>
|
||||
</entries>
|
||||
@@ -7,17 +7,20 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Shared.Prototypes
|
||||
{
|
||||
/// <summary>
|
||||
/// An IPrototype is a prototype that can be loaded from the global YAML prototypes.
|
||||
/// IPrototype, when combined with <see cref="PrototypeAttribute"/>, defines a type that the game can load from
|
||||
/// the global YAML prototypes folder during init or runtime. It's a way of defining data for the game to read
|
||||
/// and act on.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To use this, the prototype must be accessible through IoC with <see cref="IoCTargetAttribute"/>
|
||||
/// and it must have a <see cref="PrototypeAttribute"/> to give it a type string.
|
||||
/// </remarks>
|
||||
/// <include file='Docs.xml' path='entries/entry[@name="IPrototype"]/*'/>
|
||||
/// <seealso cref="IPrototypeManager"/>
|
||||
/// <seealso cref="PrototypeAttribute"/>
|
||||
/// <seealso cref="IInheritingPrototype"/>
|
||||
public interface IPrototype
|
||||
{
|
||||
/// <summary>
|
||||
/// An ID for this prototype instance.
|
||||
/// If this is a duplicate, an error will be thrown.
|
||||
/// A unique ID for this prototype instance.
|
||||
/// This will never be a duplicate, and the game will error during loading if there are multiple prototypes
|
||||
/// with the same unique ID.
|
||||
/// </summary>
|
||||
#if !ROBUST_ANALYZERS_TEST
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
@@ -25,13 +28,40 @@ namespace Robust.Shared.Prototypes
|
||||
string ID { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An extension of <see cref="IPrototype"/> that allows for a prototype to have parents that it inherits data
|
||||
/// from. This, alongside <see cref="AlwaysPushInheritanceAttribute"/> and
|
||||
/// <see cref="NeverPushInheritanceAttribute"/>, allow data-based multiple inheritance.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// An example of this in practice is <see cref="EntityPrototype"/>.
|
||||
/// </example>
|
||||
/// <include file='Docs.xml' path='entries/entry[@name="IPrototype"]/*'/>
|
||||
/// <seealso cref="IPrototypeManager"/>
|
||||
/// <seealso cref="PrototypeAttribute"/>
|
||||
/// <seealso cref="IPrototype"/>
|
||||
public interface IInheritingPrototype
|
||||
{
|
||||
/// <summary>
|
||||
/// The collection of parents for this prototype. Parents' data is applied to the child in order of
|
||||
/// specification in the array.
|
||||
/// </summary>
|
||||
string[]? Parents { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this prototype is "abstract". This behaves ike an abstract class, abstract prototypes are never
|
||||
/// indexable and do not show up when enumerating prototypes, as they're just a source of data to inherit
|
||||
/// from.
|
||||
/// </summary>
|
||||
bool Abstract { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a field as a prototype's unique identifier. This field must always be a <c>string?</c>.
|
||||
/// <br/>
|
||||
/// This field is always required.
|
||||
/// </summary>
|
||||
/// <seealso cref="IPrototype"/>
|
||||
public sealed class IdDataFieldAttribute : DataFieldAttribute
|
||||
{
|
||||
public const string Name = "id";
|
||||
@@ -41,6 +71,13 @@ namespace Robust.Shared.Prototypes
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a field as the parent/parents field for this prototype, as required by
|
||||
/// <see cref="IInheritingPrototype"/>. This must either be a <c>string?</c>, or <c>string[]?</c>.
|
||||
/// <br/>
|
||||
/// This field is never required.
|
||||
/// </summary>
|
||||
/// <seealso cref="IInheritingPrototype"/>
|
||||
public sealed class ParentDataFieldAttribute : DataFieldAttribute
|
||||
{
|
||||
public const string Name = "parent";
|
||||
@@ -50,6 +87,13 @@ namespace Robust.Shared.Prototypes
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a field as the abstract field for this prototype, as required by
|
||||
/// <see cref="IInheritingPrototype"/>. This must be a <c>bool</c>.
|
||||
/// <br/>
|
||||
/// This field is never required.
|
||||
/// </summary>
|
||||
/// <seealso cref="IInheritingPrototype"/>
|
||||
public sealed class AbstractDataFieldAttribute : DataFieldAttribute
|
||||
{
|
||||
public const string Name = "abstract";
|
||||
|
||||
@@ -16,13 +16,18 @@ using Robust.Shared.Utility;
|
||||
namespace Robust.Shared.Prototypes;
|
||||
|
||||
/// <summary>
|
||||
/// Handle storage and loading of YAML prototypes.
|
||||
/// Handle storage and loading of YAML prototypes. These are defined in code using <see cref="PrototypeAttribute"/>
|
||||
/// and <see cref="PrototypeRecordAttribute"/> on classes that implement <see cref="IPrototype"/> or
|
||||
/// <see cref="IInheritingPrototype"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Terminology:
|
||||
/// "Kinds" are the types of prototypes there are, like <see cref="EntityPrototype"/>.
|
||||
/// "Prototypes" are simply filled-in prototypes from YAML.
|
||||
/// Terminology:<br/>
|
||||
/// "Kinds" are the types of prototypes there are, like <see cref="EntityPrototype"/>.<br/>
|
||||
/// "Prototypes" are simply filled-in prototypes from YAML.<br/>
|
||||
/// </remarks>
|
||||
/// <seealso cref="IPrototype"/>
|
||||
/// <seealso cref="IInheritingPrototype"/>
|
||||
/// <seealso cref="PrototypeAttribute"/>
|
||||
public interface IPrototypeManager
|
||||
{
|
||||
void Initialize();
|
||||
|
||||
@@ -10,9 +10,9 @@ namespace Robust.Shared.Sandboxing
|
||||
/// Effectively equivalent to <see cref="Activator.CreateInstance(Type)"/> but safe for content use.
|
||||
/// </summary>
|
||||
/// <exception cref="SandboxArgumentException">
|
||||
/// Thrown if <paramref name="type"/> is not defined in content.
|
||||
/// Thrown if <paramref name="type"/> is not defined in content.
|
||||
/// </exception>
|
||||
/// <seealso cref="IDynamicTypeFactory.CreateInstance(System.Type)"/>
|
||||
/// <seealso cref="IDynamicTypeFactory.CreateInstance(System.Type, bool, bool)"/>
|
||||
object CreateInstance(Type type);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows a type with an argument-free constructor (i.e. <c>new()</c>) to provide its own serializer and
|
||||
/// deserializer in place from a string, without deferring to an <see cref="ITypeSerializer{TType,TNode}"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is much more limited than a full serializer, and only allows working with a scalar, but may be
|
||||
/// convenient.
|
||||
/// </remarks>
|
||||
public interface ISelfSerialize
|
||||
{
|
||||
/// <summary>
|
||||
/// Deserialize the type from a given string value, after having already constructed one with <c>new()</c>.
|
||||
/// </summary>
|
||||
/// <param name="value">The scalar to deserialize from.</param>
|
||||
void Deserialize(string value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the type to a yaml scalar (i.e. string).
|
||||
/// </summary>
|
||||
/// <returns>The serialized representation of the data.</returns>
|
||||
string Serialize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
namespace Robust.Shared.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a method that gets executed after deserialization is complete and a method that gets executed before serialization
|
||||
/// Provides a method that gets executed after deserialization is complete.
|
||||
/// </summary>
|
||||
[RequiresExplicitImplementation]
|
||||
public interface ISerializationHooks
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets executed after deserialization is complete
|
||||
/// Gets executed after deserialization is complete
|
||||
/// </summary>
|
||||
void AfterDeserialization() {}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,33 @@ using System;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes
|
||||
{
|
||||
// TODO Serialization: find a way to constrain this to DataFields only & make exclusive w/ NeverPush
|
||||
/// <summary>
|
||||
/// Adds the parent DataDefinition field to this field.
|
||||
/// When inheriting a field from a parent, always <b>merge</b> the two fields when such behavior exists.
|
||||
/// This is unlike the normal behavior where the child's field will always <b>overwrite</b> the parent's.
|
||||
/// <br/>
|
||||
/// Merging is done at a YAML level by merging mappings and sequences recursively.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// - id: Parent
|
||||
/// myField: [Foo, Bar]
|
||||
/// <br/>
|
||||
/// - id: Child
|
||||
/// parents: [Parent]
|
||||
/// myField: [Baz, Qux]
|
||||
/// </code>
|
||||
/// Which, when deserialized and assuming myField is marked with AlwaysPushInheritance, will result in data that
|
||||
/// looks like this:
|
||||
/// <code>
|
||||
/// - id: Child
|
||||
/// myField: [Foo, Bar, Baz, Qux]
|
||||
/// </code>
|
||||
/// compared to the default behavior:
|
||||
/// <code>
|
||||
/// - id: Child
|
||||
/// myField: [Baz, Qux]
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class AlwaysPushInheritanceAttribute : Attribute
|
||||
{
|
||||
|
||||
@@ -7,49 +7,49 @@ namespace Robust.Shared.Serialization.Manager.Attributes;
|
||||
/// <see cref="ISerializationManager.CopyTo"/> and <see cref="ISerializationManager.CreateCopy"/>.
|
||||
/// This means that the source instance is returned directly.
|
||||
/// This attribute is not inherited.
|
||||
/// <remarks>
|
||||
/// Note that when calling any of the generic <see cref="ISerializationManager.CopyTo{T}"/> and
|
||||
/// <see cref="ISerializationManager.CreateCopy{T}"/> methods, this attribute will only be respected
|
||||
/// if the generic parameter passed to the copying methods has this attribute.
|
||||
/// For example, if a copy method is called with a generic parameter T that is not annotated with this attribute,
|
||||
/// but the actual type of the source parameter is annotated with this attribute, it will not be copied by ref.
|
||||
/// Conversely, if the generic parameter T is annotated with this attribute, but the actual type of the source
|
||||
/// is an inheritor which is not annotated with this attribute, it will still be copied by ref.
|
||||
/// If the generic parameter T is a type derived from another that is annotated with the attribute,
|
||||
/// but it itself is not annotated with this attribute, source will not be copied by ref as this attribute
|
||||
/// is not inherited.
|
||||
/// <code>
|
||||
/// public class A {}
|
||||
///
|
||||
/// [CopyByRef]
|
||||
/// public class B : A {}
|
||||
///
|
||||
/// public class C : B {}
|
||||
///
|
||||
/// public class Copier(ISerializationManager manager)
|
||||
/// {
|
||||
/// var a = new A();
|
||||
/// var b = new B();
|
||||
/// var c = new C();
|
||||
///
|
||||
/// // false, not copied by ref
|
||||
/// manager.CreateCopy(a) == a
|
||||
///
|
||||
/// // false, not copied by ref
|
||||
/// manager.CreateCopy<A>(b) == b
|
||||
///
|
||||
/// // true, copied by ref
|
||||
/// manager.CreateCopy(b) == b
|
||||
///
|
||||
/// // false, not copied by ref
|
||||
/// manager.CreateCopy(c) == c
|
||||
///
|
||||
/// // true, copied by ref
|
||||
/// manager.CreateCopy<B>(c) == c
|
||||
/// }
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that when calling any of the generic <see cref="ISerializationManager.CopyTo{T}"/> and
|
||||
/// <see cref="ISerializationManager.CreateCopy{T}"/> methods, this attribute will only be respected
|
||||
/// if the generic parameter passed to the copying methods has this attribute.
|
||||
/// For example, if a copy method is called with a generic parameter T that is not annotated with this attribute,
|
||||
/// but the actual type of the source parameter is annotated with this attribute, it will not be copied by ref.
|
||||
/// Conversely, if the generic parameter T is annotated with this attribute, but the actual type of the source
|
||||
/// is an inheritor which is not annotated with this attribute, it will still be copied by ref.
|
||||
/// If the generic parameter T is a type derived from another that is annotated with the attribute,
|
||||
/// but it itself is not annotated with this attribute, source will not be copied by ref as this attribute
|
||||
/// is not inherited.
|
||||
/// <code>
|
||||
/// public class A {}
|
||||
/// <br/>
|
||||
/// [CopyByRef]
|
||||
/// public class B : A {}
|
||||
/// <br/>
|
||||
/// public class C : B {}
|
||||
/// <br/>
|
||||
/// public class Copier(ISerializationManager manager)
|
||||
/// {
|
||||
/// var a = new A();
|
||||
/// var b = new B();
|
||||
/// var c = new C();
|
||||
/// <br/>
|
||||
/// // false, not copied by ref
|
||||
/// manager.CreateCopy(a) == a
|
||||
/// <br/>
|
||||
/// // false, not copied by ref
|
||||
/// manager.CreateCopy<A>(b) == b
|
||||
/// <br/>
|
||||
/// // true, copied by ref
|
||||
/// manager.CreateCopy(b) == b
|
||||
/// <br/>
|
||||
/// // false, not copied by ref
|
||||
/// manager.CreateCopy(c) == c
|
||||
/// <br/>
|
||||
/// // true, copied by ref
|
||||
/// manager.CreateCopy<B>(c) == c
|
||||
/// }
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
[AttributeUsage(
|
||||
AttributeTargets.Class |
|
||||
AttributeTargets.Struct |
|
||||
|
||||
@@ -3,6 +3,13 @@ using JetBrains.Annotations;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Marks this type as being data-serializable.
|
||||
/// </summary>
|
||||
/// <include file='Docs.xml' path='entries/entry[@name="MeansDataDefinitionHaver"]/*'/>
|
||||
/// <include file='Docs.xml' path='entries/entry[@name="DataDefinitionExample"]/*'/>
|
||||
/// <seealso cref="DataFieldAttribute"/>
|
||||
/// <seealso cref="DataRecordAttribute"/>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
|
||||
[MeansDataDefinition]
|
||||
[MeansImplicitUse]
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
using Robust.Shared.ViewVariables;
|
||||
#if !ROBUST_ANALYZERS_TEST
|
||||
using JetBrains.Annotations;
|
||||
#endif
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks a field or property as being serializable/deserializable, also implying
|
||||
/// <see cref="ViewVariablesAttribute"/> with ReadWrite permissions.
|
||||
/// </summary>
|
||||
/// <include file='Docs.xml' path='entries/entry[@name="DataDefinitionExample"]/*'/>
|
||||
/// <seealso cref="DataDefinitionAttribute"/>
|
||||
/// <seealso cref="DataRecordAttribute"/>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
#if !ROBUST_ANALYZERS_TEST
|
||||
[MeansImplicitAssignment]
|
||||
@@ -20,12 +30,18 @@ namespace Robust.Shared.Serialization.Manager.Attributes
|
||||
public string? Tag { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this field being mapped is required for the component to function.
|
||||
/// Whether this field being mapped is required for the object to successfully deserialize.
|
||||
/// This will not guarantee that the field is mapped when the program is run,
|
||||
/// it is meant to be used as metadata information.
|
||||
/// </summary>
|
||||
public readonly bool Required;
|
||||
|
||||
/// <param name="tag">See <see cref="Tag"/>.</param>
|
||||
/// <param name="readOnly">See <see cref="DataFieldBaseAttribute.ReadOnly"/>.</param>
|
||||
/// <param name="priority">See <see cref="DataFieldBaseAttribute.Priority"/>.</param>
|
||||
/// <param name="required">See <see cref="Required"/>.</param>
|
||||
/// <param name="serverOnly">See <see cref="DataFieldBaseAttribute.ServerOnly"/>.</param>
|
||||
/// <param name="customTypeSerializer">See <see cref="DataFieldBaseAttribute.CustomTypeSerializer"/>.</param>
|
||||
public DataFieldAttribute(string? tag = null, bool readOnly = false, int priority = 1, bool required = false, bool serverOnly = false, Type? customTypeSerializer = null) : base(readOnly, priority, serverOnly, customTypeSerializer)
|
||||
{
|
||||
Tag = tag;
|
||||
@@ -40,11 +56,60 @@ namespace Robust.Shared.Serialization.Manager.Attributes
|
||||
|
||||
public abstract class DataFieldBaseAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an order datafields should be deserialized in. You rarely if ever need this functionality, and
|
||||
/// it has no effect on serialization.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [DataDefinition]
|
||||
/// public class TestClass
|
||||
/// {
|
||||
/// // This field is decoded first, as it has the highest priority.
|
||||
/// [DataField(priority: 3)]
|
||||
/// public FooData First = new();
|
||||
/// <br/>
|
||||
/// // This field has the default priority of 0, and is in the middle.
|
||||
/// [DataField]
|
||||
/// public bool Middle = false;
|
||||
/// <br/>
|
||||
/// // This field has the lowest priority, so it's dead last.
|
||||
/// [DataField(priority: -999)]
|
||||
/// public int DeadLast = 0;
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public readonly int Priority;
|
||||
|
||||
/// <summary>
|
||||
/// A specific <see cref="ITypeSerializer{TType,TNode}"/> implementation to use for parsing this type.
|
||||
/// This allows you to provide custom yaml parsing logic for a field, an example of this in regular use is
|
||||
/// <see cref="FlagSerializer{TTag}"/>.
|
||||
/// </summary>
|
||||
public readonly Type? CustomTypeSerializer;
|
||||
|
||||
/// <summary>
|
||||
/// Marks the datafield as only ever being <b>read/deserialized</b> from YAML, it will never be
|
||||
/// written/saved.
|
||||
///
|
||||
/// This is useful for data that is only ever used during, say, entity setup, and shouldn't be kept in live
|
||||
/// entities nor saved for them.
|
||||
/// </summary>
|
||||
public readonly bool ReadOnly;
|
||||
|
||||
/// <summary>
|
||||
/// Marks the datafield as server only, indicating to client code that it should not attempt to read or
|
||||
/// write this field because it may not understand the contained data.
|
||||
///
|
||||
/// This is useful for working with types that only exist on the server in otherwise shared data like a
|
||||
/// shared prototype.
|
||||
/// </summary>
|
||||
public readonly bool ServerOnly;
|
||||
|
||||
/// <param name="readOnly">See <see cref="ReadOnly"/>.</param>
|
||||
/// <param name="priority">See <see cref="Priority"/>.</param>
|
||||
/// <param name="serverOnly">See <see cref="ServerOnly"/>.</param>
|
||||
/// <param name="customTypeSerializer">See <see cref="CustomTypeSerializer"/>.</param>
|
||||
protected DataFieldBaseAttribute(bool readOnly = false, int priority = 1, bool serverOnly = false, Type? customTypeSerializer = null)
|
||||
{
|
||||
ReadOnly = readOnly;
|
||||
|
||||
@@ -4,9 +4,22 @@ using JetBrains.Annotations;
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Makes all properties in a record data fields with camel case naming.
|
||||
/// <seealso cref="DataFieldAttribute"/>
|
||||
/// Marks this type as being data-serializable and automatically marks all properties as data fields.
|
||||
/// </summary>
|
||||
/// <include file='Docs.xml' path='entries/entry[@name="MeansDataDefinitionHaver"]/*'/>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [DataRecord]
|
||||
/// public sealed record MyRecord(int Foo, bool Bar);
|
||||
/// </code>
|
||||
/// which has the serialized yaml equivalent of:
|
||||
/// <code>
|
||||
/// foo: 0
|
||||
/// bar: false
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <seealso cref="DataDefinitionAttribute"/>
|
||||
/// <seealso cref="DataFieldAttribute"/>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
|
||||
[MeansDataDefinition]
|
||||
[MeansDataRecord]
|
||||
|
||||
56
Robust.Shared/Serialization/Manager/Attributes/Docs.xml
Normal file
56
Robust.Shared/Serialization/Manager/Attributes/Docs.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<entries>
|
||||
<entry name="MeansDataDefinitionHaver"> <!-- Commentary on all MeansDataDefinition. -->
|
||||
<remarks>
|
||||
<para>
|
||||
Data-serializable types <b>must</b> be <see langword="partial"/>. They can be serialized/deserialized with <see cref="T:Robust.Shared.Serialization.Manager.ISerializationManager" />.
|
||||
Properties to be serialized should be annotated with <see cref="T:Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute"/>, though this is automatic for <see cref="T:Robust.Shared.Serialization.Manager.Attributes.DataRecordAttribute" />.
|
||||
</para>
|
||||
<para>
|
||||
This also allows this type to be used from <c>!type:name</c> annotations in YAML if its name is unique.
|
||||
Fields not marked with <see cref="T:Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute"/> or its
|
||||
relatives are never serialized.
|
||||
</para>
|
||||
<para>
|
||||
Has no relation to <see cref="T:System.SerializableAttribute"/>, which is only used with
|
||||
<see cref="T:Robust.Shared.Serialization.NetSerializableAttribute"/> in RobustToolbox games.
|
||||
</para>
|
||||
</remarks>
|
||||
</entry>
|
||||
<entry name="ImpliesDataDefinition">
|
||||
<summary>
|
||||
Also implies <see cref="T:Robust.Shared.Serialization.Manager.Attributes.DataDefinitionAttribute"/>, you don't
|
||||
need to specify it yourself.
|
||||
</summary>
|
||||
</entry>
|
||||
<entry name="ImpliesDataRecord">
|
||||
<summary>
|
||||
Also implies <see cref="T:Robust.Shared.Serialization.Manager.Attributes.DataRecordAttribute"/>, you don't
|
||||
need to specify it yourself.
|
||||
</summary>
|
||||
</entry>
|
||||
<entry name="DataDefinitionExample">
|
||||
<example>
|
||||
Starting with a definition in C#:
|
||||
<code>
|
||||
[DataDefinition] // Mark our type as being serializable by ISerializationManager.
|
||||
public partial class MyData
|
||||
{
|
||||
// Mark this field as being a data field, it'll be named "enabled" implicitly as we
|
||||
// didn't specify any names.
|
||||
// If a field is not required, it is best practice to specify a default value.
|
||||
[DataField]
|
||||
public bool Enabled = true;
|
||||
<br/> <!-- Yea so tip to the next person: Rider will eat your whitespace in code blocks. use breaks. -->
|
||||
[DataField(required: true)]
|
||||
public int Counter;
|
||||
}
|
||||
</code>
|
||||
This definition describes a YAML schema, which when serialized could look like this:
|
||||
<code>
|
||||
enabled: false
|
||||
counter: 3
|
||||
</code>
|
||||
</example>
|
||||
</entry>
|
||||
</entries>
|
||||
@@ -2,6 +2,30 @@ using System;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks all classes or interfaces that inherit from the one with this attribute with
|
||||
/// <see cref="DataDefinitionAttribute"/>, without requiring this be done manually.
|
||||
/// Cannot be reversed by inheritors!
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [ImplicitDataDefinitionForInheritors]
|
||||
/// public abstract class BaseClass
|
||||
/// {
|
||||
/// [DataField]
|
||||
/// public bool Enabled;
|
||||
/// }
|
||||
/// <br/>
|
||||
/// // Not only do we not need to mark this as a data definition,
|
||||
/// // we inherit our fields from our parent class as normal and can add our own fields.
|
||||
/// public sealed class MyClass : BaseClass
|
||||
/// {
|
||||
/// [DataField]
|
||||
/// public int Counter;
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <seealso cref="ImplicitDataRecordAttribute"/>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
|
||||
public sealed class ImplicitDataDefinitionForInheritorsAttribute : Attribute
|
||||
{
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Makes any inheritors data records.
|
||||
/// <seealso cref="DataRecordAttribute"/>
|
||||
/// Marks all classes or interfaces that inherit from the one with this attribute with
|
||||
/// <see cref="DataRecordAttribute"/>, without requiring this be done manually.
|
||||
/// Cannot be reversed by inheritors!
|
||||
/// </summary>
|
||||
/// <seealso cref="ImplicitDataDefinitionForInheritorsAttribute"/>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
|
||||
public sealed class ImplicitDataRecordAttribute : Attribute
|
||||
{
|
||||
|
||||
@@ -4,23 +4,42 @@ using JetBrains.Annotations;
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Inlines the datafield instead of putting it into its own node.
|
||||
/// Marks a field or property as being serializable/deserializable, including all of the fields from its type into
|
||||
/// the current data definition. Does not create a named field of its own, and should be used sparingly for
|
||||
/// conciseness and readability only.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// mapping:
|
||||
/// data1: 0
|
||||
/// data2: 0
|
||||
/// Becomes
|
||||
/// data1: 0
|
||||
/// data2: 0
|
||||
/// This should never be used in a way where the included/collapsed fields conflict in name with other fields!
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// otherField: 42
|
||||
/// myField:
|
||||
/// subfield1: foo
|
||||
/// subfield2: bar
|
||||
/// </code>
|
||||
/// becomes
|
||||
/// <code>
|
||||
/// otherField: 42
|
||||
/// subfield1: foo
|
||||
/// subfield2: bar
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
[MeansImplicitAssignment]
|
||||
[MeansImplicitUse(ImplicitUseKindFlags.Assign)]
|
||||
public sealed class IncludeDataFieldAttribute : DataFieldBaseAttribute
|
||||
{
|
||||
public IncludeDataFieldAttribute(bool readOnly = false, int priority = 1, bool serverOnly = false,
|
||||
Type? customTypeSerializer = null) : base(readOnly, priority, serverOnly, customTypeSerializer)
|
||||
/// <param name="readOnly">See <see cref="DataFieldBaseAttribute.ReadOnly"/>.</param>
|
||||
/// <param name="priority">See <see cref="DataFieldBaseAttribute.Priority"/>.</param>
|
||||
/// <param name="serverOnly">See <see cref="DataFieldBaseAttribute.ServerOnly"/>.</param>
|
||||
/// <param name="customTypeSerializer">See <see cref="DataFieldBaseAttribute.CustomTypeSerializer"/>.</param>
|
||||
public IncludeDataFieldAttribute(
|
||||
bool readOnly = false,
|
||||
int priority = 1,
|
||||
bool serverOnly = false,
|
||||
Type? customTypeSerializer = null
|
||||
) : base(readOnly, priority, serverOnly, customTypeSerializer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,11 @@ using JetBrains.Annotations;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks an attribute class as implying <see cref="DataDefinitionAttribute"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="DataFieldAttribute"/>
|
||||
/// <seealso cref="DataRecordAttribute"/>
|
||||
[BaseTypeRequired(typeof(Attribute))]
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class MeansDataDefinitionAttribute : Attribute
|
||||
|
||||
@@ -4,8 +4,9 @@ using JetBrains.Annotations;
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// <seealso cref="DataRecordAttribute"/>
|
||||
/// Marks an attribute class as implying <see cref="DataRecordAttribute"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="DataRecordAttribute"/>
|
||||
[BaseTypeRequired(typeof(Attribute))]
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class MeansDataRecordAttribute : Attribute
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
using System;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes
|
||||
{
|
||||
// TODO Serialization: find a way to constrain this to DataField only & make exclusive w/ AlwaysPush
|
||||
/// <summary>
|
||||
/// When added to a <see cref="DataFieldAttribute">DataField</see>, this makes it so that the value of the field
|
||||
/// is <b>never</b> given to a child when inheriting. For example with prototypes, this means the value of the
|
||||
/// field will not be given to a child if it inherits from a parent with the field set. This is useful for
|
||||
/// things like <see cref="AbstractDataFieldAttribute"/> where you do not want abstract-ness to pass on to the
|
||||
/// child.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class NeverPushInheritanceAttribute : Attribute
|
||||
{
|
||||
|
||||
@@ -3,7 +3,8 @@ using System;
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Used to denote that a type is not serializable to yaml, and should not be used as a data-field.
|
||||
/// Used to denote that a type is not serializable to yaml, and should not be used as a data-field.
|
||||
/// Types marked with this will cause an error early in serialization init if used as a field.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
internal sealed class NotYamlSerializableAttribute : Attribute;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers a <see cref="Robust.Shared.Serialization.TypeSerializers.Interfaces.ITypeSerializer"/> as a default serializer for its type
|
||||
/// Registers a <see cref="ITypeSerializer{TType,TNode}"/> as a default serializer for its type.
|
||||
/// This should only be used for serializers that should always be used unconditionally. If you're making a
|
||||
/// custom serializer, do not apply this attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
[MeansImplicitUse]
|
||||
|
||||
@@ -4,9 +4,9 @@ using Robust.Shared.Prototypes;
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// This attribute should be used on static string or string collection fields to validate that they correspond to
|
||||
/// valid YAML prototype ids. This attribute is not required for static <see cref="ProtoId{T}"/> and
|
||||
/// <see cref="EntProtoId"/> fields, as they automatically get validated.
|
||||
/// This attribute should be used on static string or string collection fields to validate that they correspond to
|
||||
/// valid YAML prototype ids. This attribute is not required for static <see cref="ProtoId{T}"/> and
|
||||
/// <see cref="EntProtoId"/> fields, as they automatically get validated.
|
||||
/// </summary>
|
||||
[Obsolete("Use a static readonly ProtoId<T> instead")]
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
|
||||
@@ -8,6 +8,22 @@ using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes or deserializes an integer from a set of known constants specified by an enum.
|
||||
/// This is very niche in utility, for integer fields where yaml should only ever be using named
|
||||
/// shorthands.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public enum MyConstants {
|
||||
/// Foo = 1,
|
||||
/// Bar = 2,
|
||||
/// Life = 42,
|
||||
/// }
|
||||
/// </code>
|
||||
/// Using this serializer, an integer field can then be deserialized from, say, <c>"Life"</c> and correctly
|
||||
/// be set to the value 42.
|
||||
/// </example>
|
||||
public sealed class ConstantSerializer<TTag> : ITypeSerializer<int, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
|
||||
@@ -9,6 +9,24 @@ using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes or deserializes <see cref="FlagsAttribute"/> marked enums, allowing you to specify a list of
|
||||
/// flags instead of writing magic numbers.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [Flags]
|
||||
/// public enum TestFlags {
|
||||
/// Foo = 1,
|
||||
/// Bar = 2,
|
||||
/// Baz = 4,
|
||||
/// }
|
||||
/// </code>
|
||||
/// which, using this serializer, can be deserialized from
|
||||
/// <code>
|
||||
/// [Foo, Baz]
|
||||
/// </code>
|
||||
/// </example>
|
||||
public sealed class FlagSerializer<TTag> : ITypeSerializer<int, ValueDataNode>, ITypeReader<int, SequenceDataNode>, ITypeCopyCreator<int>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Robust.Shared.Timing
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// Arguments of the GameLoop frame event.
|
||||
@@ -8,6 +10,10 @@
|
||||
/// <summary>
|
||||
/// Seconds passed since this event was last called.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Acceptable and simple to use for basic timing code, but accumulators/etc should prefer to use
|
||||
/// <see cref="TimeSpan"/>s and <see cref="IGameTiming.CurTime"/> to avoid loss of precision.
|
||||
/// </remarks>
|
||||
public float DeltaSeconds { get; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,8 +4,25 @@ using Robust.Shared.Serialization;
|
||||
namespace Robust.Shared.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// Wraps a game tick value.
|
||||
/// Represents a tick at some point in time over the game's runtime.
|
||||
/// The actual span of time a tick <b>is</b> depends on the <see cref="F:Robust.Shared.CVars.NetTickrate"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// While the game does use ticks for some timing, they are always an arbitrary time step. If you need to
|
||||
/// measure exact passage of time, you should use <see cref="TimeSpan"/>s instead in your reference frame
|
||||
/// (client, server, etc.) from <see cref="IGameTiming"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Ticks are appropriate for thinking purely relative to previous game ticks, for example tracking the last
|
||||
/// time modification occurred on a component for networking purposes.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The game can theoretically run out of ticks. At the default tickrate, this is after approximately 4.5 years.
|
||||
/// It is recommended to reboot the game before that happens.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="IGameTiming"/>
|
||||
[Serializable, NetSerializable]
|
||||
public readonly struct GameTick : IEquatable<GameTick>, IComparable<GameTick>
|
||||
{
|
||||
|
||||
@@ -217,7 +217,7 @@ namespace Robust.Shared.Timing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the simulation time.
|
||||
/// Resets the simulation time.
|
||||
/// </summary>
|
||||
public void ResetSimTime()
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Shared.Timing
|
||||
@@ -10,13 +11,18 @@ namespace Robust.Shared.Timing
|
||||
public interface IGameTiming
|
||||
{
|
||||
/// <summary>
|
||||
/// Is program execution inside of the simulation, or rendering?
|
||||
/// Is program execution inside the simulation, or outside of it (rendering, input handling, etc.)
|
||||
/// </summary>
|
||||
bool InSimulation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the simulation currently paused?
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When true, system update loops are not ran and relative time (like <see cref="CurTime"/>) does not
|
||||
/// advance. This is useful for fully idling a game server and can be automatically managed by
|
||||
/// <see cref="CVars.GameAutoPauseEmpty"/>.
|
||||
/// </remarks>
|
||||
bool Paused { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -26,7 +32,8 @@ namespace Robust.Shared.Timing
|
||||
TimeSpan CurTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The current real uptime of the simulation. Use this for UI and out of game timing.
|
||||
/// The current real uptime of the simulation. Use this for UI and out of game timing, it is not affected
|
||||
/// by pausing, timescale, or lag.
|
||||
/// </summary>
|
||||
TimeSpan RealTime { get; }
|
||||
|
||||
@@ -142,34 +149,44 @@ namespace Robust.Shared.Timing
|
||||
void StartFrame();
|
||||
|
||||
/// <summary>
|
||||
/// Is this the first time CurTick has been predicted?
|
||||
/// Is this the first time CurTick has been predicted?
|
||||
/// </summary>
|
||||
bool IsFirstTimePredicted { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if CurTick is ahead of LastRealTick, and <see cref="ApplyingState"/> is false.
|
||||
/// True if CurTick is ahead of LastRealTick, and <see cref="ApplyingState"/> is false.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This means the client is currently running ahead of the server, to fill in the gaps for the player and
|
||||
/// reduce latency while waiting for the next game state to arrive.
|
||||
/// </remarks>
|
||||
bool InPrediction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, the game is currently in the process of applying a game server-state.
|
||||
/// If true, the game is currently in the process of applying a game server-state.
|
||||
/// </summary>
|
||||
bool ApplyingState { get; }
|
||||
|
||||
string TickStamp => $"{CurTick}, predFirst: {IsFirstTimePredicted}, tickRem: {TickRemainder.TotalSeconds}, sim: {InSimulation}";
|
||||
|
||||
/// <summary>
|
||||
/// Statically-accessible version of <see cref="TickStamp"/>.
|
||||
/// Statically-accessible version of <see cref="TickStamp"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is intended as a debugging aid, and should not be used in regular committed code.
|
||||
/// This is intended as a debugging aid, and should not be used in regular committed code.
|
||||
/// </remarks>
|
||||
static string TickStampStatic => IoCManager.Resolve<IGameTiming>().TickStamp;
|
||||
|
||||
/// <summary>
|
||||
/// Resets the simulation time. This should be called on round restarts.
|
||||
/// Resets the simulation time completely. While functional, no mainstream RobustToolbox game currently uses
|
||||
/// this outside of client synchronization with the server and it may have quirks on existing titles.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To avoid potential desynchronization where some entities think they have changes from the far future,
|
||||
/// this should be accompanied by a full ECS reset using <see cref="IEntityManager.FlushEntities"/>.
|
||||
/// </remarks>
|
||||
void ResetSimTime();
|
||||
/// <inheritdoc cref="ResetSimTime()"/>
|
||||
void ResetSimTime((TimeSpan, GameTick) timeBase);
|
||||
|
||||
void SetTickRateAt(ushort tickRate, GameTick atTick);
|
||||
|
||||
@@ -4,8 +4,11 @@ namespace Robust.Shared.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a set of methods and properties that you can use to accurately
|
||||
/// measure elapsed time.
|
||||
/// measure elapsed time.<br/>
|
||||
/// <br/>
|
||||
/// This is legacy and of low utility (it's for mocking), prefer using <see cref="RStopwatch"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="Stopwatch"/>
|
||||
public interface IStopwatch
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -2,8 +2,19 @@
|
||||
|
||||
namespace Robust.Shared.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages <see cref="Timer"/>-based timing, allowing you to register new timers with optional cancellation.
|
||||
/// </summary>
|
||||
public interface ITimerManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers a timer with the manager, which will be executed on the main thread when its duration has
|
||||
/// elapsed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Due to the granularity of the game simulation, the wait time for timers is will (effectively) round to
|
||||
/// the nearest multiple of a tick, as they can only be processed on tick.
|
||||
/// </remarks>
|
||||
void AddTimer(Timer timer, CancellationToken cancellationToken = default);
|
||||
|
||||
void UpdateTimers(FrameEventArgs frameEventArgs);
|
||||
|
||||
@@ -19,6 +19,9 @@ public struct RStopwatch
|
||||
|
||||
private static readonly double TicksToTimeTicks = (double)TimeSpan.TicksPerSecond / SStopwatch.Frequency;
|
||||
|
||||
/// <summary>
|
||||
/// Creates and immediately starts a new stopwatch.
|
||||
/// </summary>
|
||||
public static RStopwatch StartNew()
|
||||
{
|
||||
RStopwatch watch = new();
|
||||
@@ -26,6 +29,12 @@ public struct RStopwatch
|
||||
return watch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a stopwatch if it wasn't already.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Starting the same stopwatch twice does nothing.
|
||||
/// </remarks>
|
||||
public void Start()
|
||||
{
|
||||
if (IsRunning)
|
||||
@@ -36,12 +45,21 @@ public struct RStopwatch
|
||||
IsRunning = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restarts the stopwatch, ensuring it is running regardless of the state it was in before.
|
||||
/// </summary>
|
||||
public void Restart()
|
||||
{
|
||||
IsRunning = true;
|
||||
_curTicks = SStopwatch.GetTimestamp();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the stopwatch, freezing its count if it is running.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Does nothing if the stopwatch wasn't already running.
|
||||
/// </remarks>
|
||||
public void Stop()
|
||||
{
|
||||
if (!IsRunning)
|
||||
@@ -51,12 +69,23 @@ public struct RStopwatch
|
||||
IsRunning = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completely resets the stopwatch to an unstarted state with no elapsed time.
|
||||
/// Strictly equivalent to <c>default</c>.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
this = default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The amount of elapsed time in <see cref="SStopwatch.Frequency"/>
|
||||
/// </summary>
|
||||
public readonly long ElapsedTicks => IsRunning ? SStopwatch.GetTimestamp() - _curTicks : _curTicks;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of elapsed time, in real time.
|
||||
/// </summary>
|
||||
public readonly TimeSpan Elapsed => new(ElapsedTimeTicks());
|
||||
|
||||
private readonly long ElapsedTimeTicks()
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
|
||||
namespace Robust.Shared.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a set of methods and properties that you can use to accurately
|
||||
/// measure elapsed time.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public sealed class Stopwatch : IStopwatch
|
||||
{
|
||||
private readonly System.Diagnostics.Stopwatch _stopwatch;
|
||||
@@ -18,23 +15,16 @@ namespace Robust.Shared.Timing
|
||||
_stopwatch = new System.Diagnostics.Stopwatch();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total elapsed time measured by the current instance.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public TimeSpan Elapsed => _stopwatch.Elapsed;
|
||||
|
||||
/// <summary>
|
||||
/// Starts, or resumes, measuring elapsed time for an interval.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public void Start()
|
||||
{
|
||||
_stopwatch.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops time interval measurement, resets the elapsed time to zero,
|
||||
/// and starts measuring elapsed time.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public void Restart()
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
|
||||
@@ -6,6 +6,13 @@ using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Shared.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// Non-serializable, but async friendly timers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Using these in Space Station 14 is discouraged, it has its own idioms that are all serialization friendly.
|
||||
/// </remarks>
|
||||
/// <seealso cref="ITimerManager"/>
|
||||
public sealed class Timer
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user