mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eada37378a | ||
|
|
0f1da1ba2a | ||
|
|
e0cdcd228e | ||
|
|
fdb5e014b5 | ||
|
|
cefcad775b | ||
|
|
e40feac1f1 | ||
|
|
3ef4ac7452 | ||
|
|
93bf1b09e7 | ||
|
|
a1e557e870 | ||
|
|
864adb7445 | ||
|
|
9e3f3f0c1c | ||
|
|
a40c4a435c | ||
|
|
17182dd0e8 | ||
|
|
d8b50044a2 | ||
|
|
4dc396e73d | ||
|
|
6ae0b0e892 | ||
|
|
7162ca3456 | ||
|
|
1b44c1a1b8 | ||
|
|
5b80b33e00 | ||
|
|
f05c1b2395 | ||
|
|
d9b2c73440 | ||
|
|
29a39f8e0a | ||
|
|
2d72a2bdb5 | ||
|
|
91da635631 | ||
|
|
68ab3d504a | ||
|
|
5187040a64 | ||
|
|
e0c63e7ce6 |
37
Robust.Client.Injectors/MathParsing.cs
Normal file
37
Robust.Client.Injectors/MathParsing.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Linq;
|
||||
using Pidgin;
|
||||
using static Pidgin.Parser;
|
||||
|
||||
|
||||
namespace Robust.Build.Tasks
|
||||
{
|
||||
public static class MathParsing
|
||||
{
|
||||
public static Parser<char, float> Single { get; } = Real.Select(c => (float) c);
|
||||
|
||||
public static Parser<char, float> Single1 { get; }
|
||||
= Single.Between(SkipWhitespaces);
|
||||
|
||||
public static Parser<char, (float, float)> Single2 { get; }
|
||||
= Single.Before(SkipWhitespaces).Repeat(2).Select(e =>
|
||||
{
|
||||
var arr = e.ToArray();
|
||||
return (arr[0], arr[1]);
|
||||
});
|
||||
|
||||
public static Parser<char, (float, float, float, float)> Single4 { get; }
|
||||
= Single.Before(SkipWhitespaces).Repeat(4).Select(e =>
|
||||
{
|
||||
var arr = e.ToArray();
|
||||
return (arr[0], arr[1], arr[2], arr[3]);
|
||||
});
|
||||
|
||||
public static Parser<char, float[]> Thickness { get; }
|
||||
= SkipWhitespaces.Then(
|
||||
OneOf(
|
||||
Try(Single1.Select(c => new[] {c})),
|
||||
Try(Single2.Select(c => new[] {c.Item1, c.Item2})),
|
||||
Try(Single4.Select(c => new[] {c.Item1, c.Item2, c.Item3, c.Item4}))
|
||||
));
|
||||
}
|
||||
}
|
||||
93
Robust.Client.Injectors/RXamlVecLikeConstAstNode.cs
Normal file
93
Robust.Client.Injectors/RXamlVecLikeConstAstNode.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using XamlX.Ast;
|
||||
using XamlX.Emit;
|
||||
using XamlX.IL;
|
||||
using XamlX.TypeSystem;
|
||||
|
||||
namespace Robust.Build.Tasks
|
||||
{
|
||||
public abstract class RXamlVecLikeConstAstNode<T>
|
||||
: XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
|
||||
where T : unmanaged
|
||||
{
|
||||
private readonly IXamlConstructor _constructor;
|
||||
protected readonly T[] Values;
|
||||
|
||||
public RXamlVecLikeConstAstNode(
|
||||
IXamlLineInfo lineInfo,
|
||||
IXamlType type, IXamlConstructor constructor,
|
||||
IXamlType componentType, T[] values)
|
||||
: base(lineInfo)
|
||||
{
|
||||
_constructor = constructor;
|
||||
Values = values;
|
||||
|
||||
var @params = constructor.Parameters;
|
||||
|
||||
if (@params.Count != values.Length)
|
||||
throw new ArgumentException("Invalid amount of parameters");
|
||||
|
||||
if (@params.Any(c => c != componentType))
|
||||
throw new ArgumentException("Invalid constructor: not all parameters match component type");
|
||||
|
||||
Type = new XamlAstClrTypeReference(lineInfo, type, false);
|
||||
}
|
||||
|
||||
public IXamlAstTypeReference Type { get; }
|
||||
|
||||
public virtual XamlILNodeEmitResult Emit(
|
||||
XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
|
||||
IXamlILEmitter codeGen)
|
||||
{
|
||||
codeGen.Newobj(_constructor);
|
||||
|
||||
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RXamlSingleVecLikeConstAstNode : RXamlVecLikeConstAstNode<float>
|
||||
{
|
||||
public RXamlSingleVecLikeConstAstNode(
|
||||
IXamlLineInfo lineInfo,
|
||||
IXamlType type, IXamlConstructor constructor,
|
||||
IXamlType componentType, float[] values)
|
||||
: base(lineInfo, type, constructor, componentType, values)
|
||||
{
|
||||
}
|
||||
|
||||
public override XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
|
||||
IXamlILEmitter codeGen)
|
||||
{
|
||||
foreach (var value in Values)
|
||||
{
|
||||
codeGen.Emit(OpCodes.Ldc_R4, value);
|
||||
}
|
||||
|
||||
return base.Emit(context, codeGen);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RXamlInt32VecLikeConstAstNode : RXamlVecLikeConstAstNode<int>
|
||||
{
|
||||
public RXamlInt32VecLikeConstAstNode(
|
||||
IXamlLineInfo lineInfo,
|
||||
IXamlType type, IXamlConstructor constructor,
|
||||
IXamlType componentType, int[] values)
|
||||
: base(lineInfo, type, constructor, componentType, values)
|
||||
{
|
||||
}
|
||||
|
||||
public override XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
|
||||
IXamlILEmitter codeGen)
|
||||
{
|
||||
foreach (var value in Values)
|
||||
{
|
||||
codeGen.Emit(OpCodes.Ldc_I4, value);
|
||||
}
|
||||
|
||||
return base.Emit(context, codeGen);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
Robust.Client.Injectors/RXamlWellKnownTypes.cs
Normal file
47
Robust.Client.Injectors/RXamlWellKnownTypes.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Linq;
|
||||
using XamlX.Transform;
|
||||
using XamlX.TypeSystem;
|
||||
|
||||
namespace Robust.Build.Tasks
|
||||
{
|
||||
class RXamlWellKnownTypes
|
||||
{
|
||||
public XamlTypeWellKnownTypes XamlIlTypes { get; }
|
||||
public IXamlType Single { get; }
|
||||
public IXamlType Int32 { get; }
|
||||
public IXamlType Vector2 { get; }
|
||||
public IXamlConstructor Vector2ConstructorFull { get; }
|
||||
public IXamlType Vector2i { get; }
|
||||
public IXamlConstructor Vector2iConstructorFull { get; }
|
||||
|
||||
public RXamlWellKnownTypes(TransformerConfiguration cfg)
|
||||
{
|
||||
var ts = cfg.TypeSystem;
|
||||
XamlIlTypes = cfg.WellKnownTypes;
|
||||
Single = ts.GetType("System.Single");
|
||||
Int32 = ts.GetType("System.Int32");
|
||||
|
||||
(Vector2, Vector2ConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Vector2", Single, 2);
|
||||
(Vector2i, Vector2iConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Vector2i", Int32, 2);
|
||||
|
||||
(IXamlType, IXamlConstructor) GetNumericTypeInfo(string name, IXamlType componentType, int componentCount)
|
||||
{
|
||||
var type = cfg.TypeSystem.GetType(name);
|
||||
var ctor = type.GetConstructor(Enumerable.Repeat(componentType, componentCount).ToList());
|
||||
|
||||
return (type, ctor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class RXamlWellKnownTypesExtensions
|
||||
{
|
||||
public static RXamlWellKnownTypes GetRobustTypes(this AstTransformationContext ctx)
|
||||
{
|
||||
if (ctx.TryGetItem<RXamlWellKnownTypes>(out var rv))
|
||||
return rv;
|
||||
ctx.SetItem(rv = new RXamlWellKnownTypes(ctx.Configuration));
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build.Framework" Version="16.8.0" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
|
||||
<PackageReference Include="Pidgin" Version="2.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -6,6 +6,7 @@ using Microsoft.Build.Framework;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Cecil.Rocks;
|
||||
using Pidgin;
|
||||
using XamlX;
|
||||
using XamlX.Ast;
|
||||
using XamlX.Emit;
|
||||
@@ -70,7 +71,7 @@ namespace Robust.Build.Tasks
|
||||
{
|
||||
XmlnsAttributes =
|
||||
{
|
||||
typeSystem.GetType("Robust.Client.UserInterface.XAML.XmlnsDefinitionAttribute"),
|
||||
typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute"),
|
||||
|
||||
},
|
||||
ContentAttributes =
|
||||
@@ -98,7 +99,7 @@ namespace Robust.Build.Tasks
|
||||
typeSystem,
|
||||
typeSystem.TargetAssembly,
|
||||
xamlLanguage,
|
||||
XamlXmlnsMappings.Resolve(typeSystem, xamlLanguage));
|
||||
XamlXmlnsMappings.Resolve(typeSystem, xamlLanguage), CustomValueConverter);
|
||||
|
||||
var contextDef = new TypeDefinition("CompiledRobustXaml", "XamlIlContext",
|
||||
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
|
||||
@@ -297,6 +298,41 @@ namespace Robust.Build.Tasks
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CustomValueConverter(
|
||||
AstTransformationContext context,
|
||||
IXamlAstValueNode node,
|
||||
IXamlType type,
|
||||
out IXamlAstValueNode result)
|
||||
{
|
||||
if (!(node is XamlAstTextNode textNode))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var text = textNode.Text;
|
||||
var types = context.GetRobustTypes();
|
||||
|
||||
if (type.Equals(types.Vector2))
|
||||
{
|
||||
var foo = MathParsing.Single2.Parse(text);
|
||||
|
||||
if (!foo.Success)
|
||||
throw new XamlLoadException($"Unable to parse \"{text}\" as a Vector2", node);
|
||||
|
||||
var (x, y) = foo.Value;
|
||||
|
||||
result = new RXamlSingleVecLikeConstAstNode(
|
||||
node,
|
||||
types.Vector2, types.Vector2ConstructorFull,
|
||||
types.Single, new[] {x, y});
|
||||
return true;
|
||||
}
|
||||
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public const string ContextNameScopeFieldName = "RobustNameScope";
|
||||
|
||||
private static void EmitNameScopeField(XamlLanguageTypeMappings xamlLanguage, CecilTypeSystem typeSystem, IXamlTypeBuilder<IXamlILEmitter> typeBuilder, IXamlILEmitter constructor)
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Robust.Client.NameGenerator
|
||||
{
|
||||
private const string AttributeName = "Robust.Client.AutoGenerated.GenerateTypedNameReferencesAttribute";
|
||||
private const string AttributeFile = "GenerateTypedNameReferencesAttribute";
|
||||
|
||||
private const string AttributeCode = @"// <auto-generated />
|
||||
using System;
|
||||
namespace Robust.Client.AutoGenerated
|
||||
@@ -54,8 +55,8 @@ namespace Robust.Client.AutoGenerated
|
||||
{
|
||||
var clrtype = objectNode.Type.GetClrType();
|
||||
var isControl = IsControl(clrtype);
|
||||
//clrtype.Interfaces.Any(i =>
|
||||
//i.IsInterface && i.FullName == "Robust.Client.UserInterface.IControl");
|
||||
//clrtype.Interfaces.Any(i =>
|
||||
//i.IsInterface && i.FullName == "Robust.Client.UserInterface.IControl");
|
||||
|
||||
if (!isControl)
|
||||
return node;
|
||||
@@ -80,9 +81,13 @@ namespace Robust.Client.AutoGenerated
|
||||
return node;
|
||||
}
|
||||
|
||||
public void Push(IXamlAstNode node) { }
|
||||
public void Push(IXamlAstNode node)
|
||||
{
|
||||
}
|
||||
|
||||
public void Pop() { }
|
||||
public void Pop()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateSourceCode(
|
||||
@@ -97,16 +102,20 @@ namespace Robust.Client.AutoGenerated
|
||||
var compiler =
|
||||
new XamlILCompiler(
|
||||
new TransformerConfiguration(typeSystem, typeSystem.Assemblies[0],
|
||||
new XamlLanguageTypeMappings(typeSystem)),
|
||||
new XamlLanguageTypeMappings(typeSystem)
|
||||
{
|
||||
XmlnsAttributes = {typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute")}
|
||||
}),
|
||||
new XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult>(), false);
|
||||
compiler.Transformers.Add(new TypeReferenceResolver());
|
||||
compiler.Transform(parsed);
|
||||
var initialRoot = (XamlAstObjectNode) parsed.Root;
|
||||
var names = NameVisitor.GetNames(initialRoot);
|
||||
var fieldAccess = classSymbol.IsSealed ? "private" : "protected";
|
||||
//var names = NameVisitor.GetNames((XamlAstObjectNode)XDocumentXamlParser.Parse(xamlFile).Root);
|
||||
var namedControls = names.Select(info => " " +
|
||||
$"protected global::{info.type} {info.name} => " +
|
||||
$"this.FindControl<global::{info.type}>(\"{info.name}\");");
|
||||
$"{fieldAccess} global::{info.type} {info.name} => " +
|
||||
$"this.FindControl<global::{info.type}>(\"{info.name}\");");
|
||||
return $@"// <auto-generated />
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -124,7 +133,7 @@ namespace {nameSpace}
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
var comp = (CSharpCompilation) context.Compilation;
|
||||
if(comp.GetTypeByMetadataName(AttributeName) == null)
|
||||
if (comp.GetTypeByMetadataName(AttributeName) == null)
|
||||
context.AddSource(AttributeFile, SourceText.From(AttributeCode, Encoding.UTF8));
|
||||
if (!(context.SyntaxReceiver is NameReferenceSyntaxReceiver receiver))
|
||||
{
|
||||
@@ -132,7 +141,7 @@ namespace {nameSpace}
|
||||
}
|
||||
|
||||
var symbols = UnpackAnnotatedTypes(context, comp, receiver);
|
||||
if(symbols == null)
|
||||
if (symbols == null)
|
||||
return;
|
||||
|
||||
foreach (var typeSymbol in symbols)
|
||||
@@ -168,7 +177,8 @@ namespace {nameSpace}
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true),
|
||||
Location.Create(xamlFileName, new TextSpan(0,0), new LinePositionSpan(new LinePosition(0,0),new LinePosition(0,0)))));
|
||||
Location.Create(xamlFileName, new TextSpan(0, 0),
|
||||
new LinePositionSpan(new LinePosition(0, 0), new LinePosition(0, 0)))));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -194,7 +204,8 @@ namespace {nameSpace}
|
||||
}
|
||||
}
|
||||
|
||||
private IReadOnlyList<INamedTypeSymbol> UnpackAnnotatedTypes(in GeneratorExecutionContext context, CSharpCompilation comp, NameReferenceSyntaxReceiver receiver)
|
||||
private IReadOnlyList<INamedTypeSymbol> UnpackAnnotatedTypes(in GeneratorExecutionContext context,
|
||||
CSharpCompilation comp, NameReferenceSyntaxReceiver receiver)
|
||||
{
|
||||
var options = (CSharpParseOptions) comp.SyntaxTrees[0].Options;
|
||||
var compilation =
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Client.Audio.Midi;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Debugging;
|
||||
@@ -10,6 +10,7 @@ using Robust.Client.Input;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Client.Placement;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Prototypes;
|
||||
using Robust.Client.Reflection;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.State;
|
||||
@@ -17,6 +18,7 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Client.ViewVariables;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -33,7 +35,7 @@ namespace Robust.Client
|
||||
{
|
||||
SharedIoC.RegisterIoC();
|
||||
|
||||
IoCManager.Register<IPrototypeManager, PrototypeManager>();
|
||||
IoCManager.Register<IPrototypeManager, ClientPrototypeManager>();
|
||||
IoCManager.Register<IEntityManager, ClientEntityManager>();
|
||||
IoCManager.Register<IComponentFactory, ClientComponentFactory>();
|
||||
IoCManager.Register<ITileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
@@ -59,6 +61,7 @@ namespace Robust.Client
|
||||
IoCManager.Register<ILightManager, LightManager>();
|
||||
IoCManager.Register<IDiscordRichPresence, DiscordRichPresence>();
|
||||
IoCManager.Register<IClientConsoleHost, ClientConsoleHost>();
|
||||
IoCManager.Register<IConsoleHost, ClientConsoleHost>();
|
||||
IoCManager.Register<IFontManager, FontManager>();
|
||||
IoCManager.Register<IFontManagerInternal, FontManager>();
|
||||
IoCManager.Register<IMidiManager, MidiManager>();
|
||||
|
||||
@@ -26,7 +26,6 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timers;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -163,6 +162,7 @@ namespace Robust.Client
|
||||
_serializer.Initialize();
|
||||
_inputManager.Initialize();
|
||||
_consoleHost.Initialize();
|
||||
_prototypeManager.Initialize();
|
||||
_prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes/"));
|
||||
_prototypeManager.Resync();
|
||||
_mapManager.Initialize();
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedIgnorePauseComponent))]
|
||||
public sealed class IgnorePauseComponent : SharedIgnorePauseComponent
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <param name="message">Arguments for this event.</param>
|
||||
/// <param name="replay">if true, current cmd state will not be checked or updated - use this for "replaying" an
|
||||
/// old input that was saved or buffered until further processing could be done</param>
|
||||
public bool HandleInputCommand(ICommonSession session, BoundKeyFunction function, FullInputCmdMessage message, bool replay = false)
|
||||
public bool HandleInputCommand(ICommonSession? session, BoundKeyFunction function, FullInputCmdMessage message, bool replay = false)
|
||||
{
|
||||
#if DEBUG
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Shared.GameStates;
|
||||
@@ -352,7 +353,10 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var component in _componentManager.GetNetComponents(createdEntity))
|
||||
{
|
||||
var state = component.GetComponentState();
|
||||
Debug.Assert(_players.LocalPlayer != null, "_players.LocalPlayer != null");
|
||||
|
||||
var player = _players.LocalPlayer.Session;
|
||||
var state = component.GetComponentState(player);
|
||||
|
||||
if (state.GetType() == typeof(ComponentState))
|
||||
{
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private GLFWCallbacks.WindowSizeCallback _windowSizeCallback = default!;
|
||||
private GLFWCallbacks.WindowContentScaleCallback _windowContentScaleCallback = default!;
|
||||
private GLFWCallbacks.WindowIconifyCallback _windowIconifyCallback = default!;
|
||||
private GLFWCallbacks.WindowFocusCallback _windowFocusCallback = default!;
|
||||
|
||||
private bool _glfwInitialized;
|
||||
|
||||
@@ -62,6 +63,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private Window* _glfwWindow;
|
||||
|
||||
private Vector2i _framebufferSize;
|
||||
private bool _isFocused;
|
||||
private Vector2i _windowSize;
|
||||
private Vector2i _prevWindowSize;
|
||||
private Vector2i _prevWindowPos;
|
||||
@@ -74,6 +76,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// NOTE: in engine we pretend the framebuffer size is the screen size..
|
||||
// For practical reasons like UI rendering.
|
||||
public override Vector2i ScreenSize => _framebufferSize;
|
||||
public override bool IsFocused => _isFocused;
|
||||
public Vector2 DefaultWindowScale => _windowScale;
|
||||
public Vector2 MouseScreenPosition => _lastMousePos;
|
||||
|
||||
@@ -231,6 +234,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GLFW.SetMouseButtonCallback(_glfwWindow, _mouseButtonCallback);
|
||||
GLFW.SetWindowContentScaleCallback(_glfwWindow, _windowContentScaleCallback);
|
||||
GLFW.SetWindowIconifyCallback(_glfwWindow, _windowIconifyCallback);
|
||||
GLFW.SetWindowFocusCallback(_glfwWindow, _windowFocusCallback);
|
||||
|
||||
GLFW.MakeContextCurrent(_glfwWindow);
|
||||
|
||||
@@ -548,6 +552,19 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGlfwWindowFocus(Window* window, bool focused)
|
||||
{
|
||||
try
|
||||
{
|
||||
_isFocused = focused;
|
||||
OnWindowFocused?.Invoke(new WindowFocusedEventArgs(focused));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CatchCallbackException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void StoreCallbacks()
|
||||
{
|
||||
_errorCallback = OnGlfwError;
|
||||
@@ -560,6 +577,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_windowSizeCallback = OnGlfwWindowSize;
|
||||
_windowContentScaleCallback = OnGlfwWindownContentScale;
|
||||
_windowIconifyCallback = OnGlfwWindowIconify;
|
||||
_windowFocusCallback = OnGlfwWindowFocus;
|
||||
}
|
||||
|
||||
public override void SetWindowTitle(string title)
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public override bool Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
|
||||
_configurationManager.OnValueChanged(CVars.DisplayOGLCheckErrors, b => _checkGLErrors = b, true);
|
||||
|
||||
if (!InitWindowing())
|
||||
@@ -152,6 +152,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
public override event Action<WindowResizedEventArgs>? OnWindowResized;
|
||||
|
||||
public override event Action<WindowFocusedEventArgs>? OnWindowFocused;
|
||||
|
||||
public void Screenshot(ScreenshotType type, Action<Image<Rgb24>> callback)
|
||||
{
|
||||
_queuedScreenshots.Add((type, callback));
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public IRenderWindow MainWindowRenderTarget { get; }
|
||||
public override Vector2i ScreenSize { get; } = (1280, 720);
|
||||
public Vector2 DefaultWindowScale => (1, 1);
|
||||
public override bool IsFocused => true;
|
||||
|
||||
public ShaderInstance InstanceShader(ClydeHandle handle)
|
||||
{
|
||||
@@ -79,6 +80,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
remove { }
|
||||
}
|
||||
|
||||
public override event Action<WindowFocusedEventArgs> OnWindowFocused
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
// Nada.
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace Robust.Client.Graphics
|
||||
protected bool VSync { get; private set; } = true;
|
||||
|
||||
public abstract Vector2i ScreenSize { get; }
|
||||
public abstract bool IsFocused { get; }
|
||||
|
||||
public abstract void SetWindowTitle(string title);
|
||||
|
||||
public virtual bool Initialize()
|
||||
@@ -45,6 +47,8 @@ namespace Robust.Client.Graphics
|
||||
|
||||
public abstract event Action<WindowResizedEventArgs> OnWindowResized;
|
||||
|
||||
public abstract event Action<WindowFocusedEventArgs> OnWindowFocused;
|
||||
|
||||
protected virtual void ReadConfig()
|
||||
{
|
||||
WindowMode = (WindowMode) _configurationManager.GetCVar(CVars.DisplayWindowMode);
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace Robust.Client.Graphics
|
||||
|
||||
Vector2i ScreenSize { get; }
|
||||
|
||||
bool IsFocused { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The default scale ratio for window contents, given to us by the OS.
|
||||
/// </summary>
|
||||
@@ -27,6 +29,8 @@ namespace Robust.Client.Graphics
|
||||
|
||||
event Action<WindowResizedEventArgs> OnWindowResized;
|
||||
|
||||
event Action<WindowFocusedEventArgs> OnWindowFocused;
|
||||
|
||||
Texture LoadTextureFromPNGStream(Stream stream, string? name = null,
|
||||
TextureLoadParameters? loadParams = null);
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ using YamlDotNet.RepresentationModel;
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
[Prototype("shader")]
|
||||
public sealed class ShaderPrototype : IPrototype, IIndexedPrototype
|
||||
public sealed class ShaderPrototype : IPrototype
|
||||
{
|
||||
[Dependency] private readonly IClydeInternal _clyde = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
|
||||
14
Robust.Client/Graphics/WindowFocusedEventArgs.cs
Normal file
14
Robust.Client/Graphics/WindowFocusedEventArgs.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
public class WindowFocusedEventArgs : EventArgs
|
||||
{
|
||||
public WindowFocusedEventArgs(bool focused)
|
||||
{
|
||||
Focused = focused;
|
||||
}
|
||||
|
||||
public bool Focused { get; }
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,11 @@ namespace Robust.Client.Player
|
||||
|
||||
LocalPlayer? LocalPlayer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after LocalPlayer is changed
|
||||
/// </summary>
|
||||
event Action<LocalPlayerChangedEventArgs>? LocalPlayerChanged;
|
||||
|
||||
int PlayerCount { get; }
|
||||
int MaxPlayers { get; }
|
||||
event EventHandler PlayerListUpdated;
|
||||
@@ -23,4 +28,15 @@ namespace Robust.Client.Player
|
||||
|
||||
void ApplyPlayerStates(IEnumerable<PlayerState>? list);
|
||||
}
|
||||
|
||||
public class LocalPlayerChangedEventArgs : EventArgs
|
||||
{
|
||||
public readonly LocalPlayer? OldPlayer;
|
||||
public readonly LocalPlayer? NewPlayer;
|
||||
public LocalPlayerChangedEventArgs(LocalPlayer? oldPlayer, LocalPlayer? newPlayer)
|
||||
{
|
||||
OldPlayer = oldPlayer;
|
||||
NewPlayer = newPlayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,21 @@ namespace Robust.Client.Player
|
||||
public int MaxPlayers => _client.GameInfo?.ServerMaxPlayers ?? 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables] public LocalPlayer? LocalPlayer { get; private set; }
|
||||
[ViewVariables]
|
||||
public LocalPlayer? LocalPlayer
|
||||
{
|
||||
get => _localPlayer;
|
||||
private set
|
||||
{
|
||||
if (_localPlayer == value) return;
|
||||
var oldValue = _localPlayer;
|
||||
_localPlayer = value;
|
||||
LocalPlayerChanged?.Invoke(new LocalPlayerChangedEventArgs(oldValue, _localPlayer));
|
||||
}
|
||||
}
|
||||
private LocalPlayer? _localPlayer;
|
||||
public event Action<LocalPlayerChangedEventArgs>? LocalPlayerChanged;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables] public IEnumerable<IPlayerSession> Sessions => _sessions.Values;
|
||||
|
||||
119
Robust.Client/Prototypes/ClientPrototypeManager.cs
Normal file
119
Robust.Client/Prototypes/ClientPrototypeManager.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Robust.Client.Prototypes
|
||||
{
|
||||
public sealed class ClientPrototypeManager : PrototypeManager
|
||||
{
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
|
||||
private readonly List<FileSystemWatcher> _watchers = new();
|
||||
private readonly TimeSpan _reloadDelay = TimeSpan.FromMilliseconds(10);
|
||||
private CancellationTokenSource _reloadToken = new();
|
||||
private readonly HashSet<ResourcePath> _reloadQueue = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
NetManager.RegisterNetMessage<MsgReloadPrototypes>(MsgReloadPrototypes.NAME, accept: NetMessageAccept.Server);
|
||||
|
||||
_clyde.OnWindowFocused += WindowFocusedChanged;
|
||||
|
||||
WatchResources();
|
||||
}
|
||||
|
||||
private void WindowFocusedChanged(WindowFocusedEventArgs args)
|
||||
{
|
||||
#if !FULL_RELEASE
|
||||
if (args.Focused && _reloadQueue.Count > 0)
|
||||
{
|
||||
Timer.Spawn(_reloadDelay, ReloadPrototypeQueue, _reloadToken.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
_reloadToken.Cancel();
|
||||
_reloadToken = new CancellationTokenSource();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private void ReloadPrototypeQueue()
|
||||
{
|
||||
var then = DateTime.Now;
|
||||
|
||||
var msg = NetManager.CreateNetMessage<MsgReloadPrototypes>();
|
||||
msg.Paths = _reloadQueue.ToArray();
|
||||
NetManager.ClientSendMessage(msg);
|
||||
|
||||
foreach (var path in _reloadQueue)
|
||||
{
|
||||
ReloadPrototypes(path);
|
||||
}
|
||||
|
||||
Logger.Info($"Reloaded prototypes in {(int) (DateTime.Now - then).TotalMilliseconds} ms");
|
||||
}
|
||||
|
||||
private void WatchResources()
|
||||
{
|
||||
#if !FULL_RELEASE
|
||||
foreach (var path in Resources.GetContentRoots().Select(r => r.ToString())
|
||||
.Where(r => Directory.Exists(r + "/Prototypes")).Select(p => p + "/Prototypes"))
|
||||
{
|
||||
var watcher = new FileSystemWatcher(path, "*.yml")
|
||||
{
|
||||
IncludeSubdirectories = true,
|
||||
NotifyFilter = NotifyFilters.LastWrite
|
||||
};
|
||||
|
||||
watcher.Changed += (_, args) =>
|
||||
{
|
||||
switch (args.ChangeType)
|
||||
{
|
||||
case WatcherChangeTypes.Renamed:
|
||||
case WatcherChangeTypes.Deleted:
|
||||
return;
|
||||
case WatcherChangeTypes.Created:
|
||||
// case WatcherChangeTypes.Deleted:
|
||||
case WatcherChangeTypes.Changed:
|
||||
case WatcherChangeTypes.All:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
TaskManager.RunOnMainThread(() =>
|
||||
{
|
||||
var file = new ResourcePath(args.FullPath);
|
||||
|
||||
foreach (var root in IoCManager.Resolve<IResourceManager>().GetContentRoots())
|
||||
{
|
||||
if (!file.TryRelativeTo(root, out var relative))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_reloadQueue.Add(relative);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
watcher.EnableRaisingEvents = true;
|
||||
_watchers.Add(watcher);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using System.Diagnostics.Contracts;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Maths;
|
||||
using Timer = Robust.Shared.Timers.Timer;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
|
||||
@@ -15,15 +15,36 @@ namespace Robust.Client.UserInterface.Controls
|
||||
private readonly Popup _popup;
|
||||
private readonly VBoxContainer _popupVBox;
|
||||
private readonly Label _label;
|
||||
private readonly TextureRect _triangle;
|
||||
|
||||
public int ItemCount => _buttonData.Count;
|
||||
|
||||
/// <summary>
|
||||
/// If true, hides the triangle that normally appears to the right of the button label
|
||||
/// </summary>
|
||||
public bool HideTriangle
|
||||
{
|
||||
get => _hideTriangle;
|
||||
set
|
||||
{
|
||||
_hideTriangle = value;
|
||||
_triangle.Visible = !_hideTriangle;
|
||||
}
|
||||
}
|
||||
private bool _hideTriangle;
|
||||
|
||||
/// <summary>
|
||||
/// StyleClasses to apply to the options that popup when clicking this button.
|
||||
/// </summary>
|
||||
public ICollection<string> OptionStyleClasses { get; }
|
||||
|
||||
public event Action<ItemSelectedEventArgs>? OnItemSelected;
|
||||
|
||||
public string Prefix { get; set; }
|
||||
|
||||
public OptionButton()
|
||||
{
|
||||
OptionStyleClasses = new List<string>();
|
||||
AddStyleClass(StyleClassButton);
|
||||
Prefix = "";
|
||||
OnPressed += OnPressedInternal;
|
||||
@@ -43,12 +64,13 @@ namespace Robust.Client.UserInterface.Controls
|
||||
};
|
||||
hBox.AddChild(_label);
|
||||
|
||||
var textureRect = new TextureRect
|
||||
_triangle = new TextureRect
|
||||
{
|
||||
StyleClasses = { StyleClassOptionTriangle },
|
||||
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||
Visible = !HideTriangle
|
||||
};
|
||||
hBox.AddChild(textureRect);
|
||||
hBox.AddChild(_triangle);
|
||||
}
|
||||
|
||||
public void AddItem(Texture icon, string label, int? id = null)
|
||||
@@ -73,6 +95,10 @@ namespace Robust.Client.UserInterface.Controls
|
||||
Text = label,
|
||||
ToggleMode = true
|
||||
};
|
||||
foreach (var styleClass in OptionStyleClasses)
|
||||
{
|
||||
button.AddStyleClass(styleClass);
|
||||
}
|
||||
button.OnPressed += ButtonOnPressed;
|
||||
var data = new ButtonData(label, button)
|
||||
{
|
||||
@@ -170,6 +196,10 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select by index rather than id. Throws exception if item with that index
|
||||
/// not in this control.
|
||||
/// </summary>
|
||||
public void Select(int idx)
|
||||
{
|
||||
if (_idMap.TryGetValue(SelectedId, out var prevIdx))
|
||||
@@ -182,11 +212,29 @@ namespace Robust.Client.UserInterface.Controls
|
||||
data.Button.Pressed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select by index rather than id.
|
||||
/// </summary>
|
||||
/// <returns>false if item with that index not in this control</returns>
|
||||
public bool TrySelect(int idx)
|
||||
{
|
||||
if (idx < 0 || idx >= _buttonData.Count) return false;
|
||||
Select(idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// throws exception if item with this ID is not in this control
|
||||
public void SelectId(int id)
|
||||
{
|
||||
Select(GetIdx(id));
|
||||
}
|
||||
|
||||
/// <returns>false if item with id not in this control</returns>
|
||||
public bool TrySelectId(int id)
|
||||
{
|
||||
return _idMap.TryGetValue(id, out var idx) && TrySelect(idx);
|
||||
}
|
||||
|
||||
public int GetIdx(int id)
|
||||
{
|
||||
return _idMap[id];
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
<cc:SS14Window xmlns:cc="clr-namespace:Robust.Client.UserInterface.CustomControls"
|
||||
xmlns:c="clr-namespace:Robust.Client.UserInterface.Controls">
|
||||
<c:PanelContainer StyleClasses="windowPanel"/>
|
||||
<c:VBoxContainer SeparationOverride="0">
|
||||
<c:PanelContainer Name="WindowHeader" StyleClasses="windowHeader">
|
||||
<c:HBoxContainer>
|
||||
<c:MarginContainer MarginLeftOverride="5" SizeFlagsHorizontal="FillExpand">
|
||||
<c:Label Name="TitleLabel" StyleIdentifier="foo" ClipText="True" Text="Exemplary Window Title Here" VAlign="Center" StyleClasses="windowTitle"/>
|
||||
</c:MarginContainer>
|
||||
<c:TextureButton Name="CloseButton" StyleClasses="windowCloseButton" SizeFlagsVertical="ShrinkCenter"></c:TextureButton>
|
||||
</c:HBoxContainer>
|
||||
</c:PanelContainer>
|
||||
<c:MarginContainer Name="ContentsContainer" MarginBottomOverride="10" MarginLeftOverride="10" MarginRightOverride="10" MarginTopOverride="10" RectClipContent="True" SizeFlagsVertical="FillExpand" />
|
||||
</c:VBoxContainer>
|
||||
</cc:SS14Window>
|
||||
<SS14Window xmlns="https://spacestation14.io">
|
||||
<PanelContainer StyleClasses="windowPanel" />
|
||||
<VBoxContainer SeparationOverride="0">
|
||||
<PanelContainer Name="WindowHeader" StyleClasses="windowHeader">
|
||||
<HBoxContainer>
|
||||
<MarginContainer MarginLeftOverride="5" SizeFlagsHorizontal="FillExpand">
|
||||
<Label Name="TitleLabel" StyleIdentifier="foo" ClipText="True"
|
||||
Text="{Loc Exemplary Window Title Here}" VAlign="Center" StyleClasses="windowTitle" />
|
||||
</MarginContainer>
|
||||
<TextureButton Name="CloseButton" StyleClasses="windowCloseButton" SizeFlagsVertical="ShrinkCenter" />
|
||||
</HBoxContainer>
|
||||
</PanelContainer>
|
||||
<MarginContainer Name="ContentsContainer" MarginBottomOverride="10" MarginLeftOverride="10"
|
||||
MarginRightOverride="10" MarginTopOverride="10" RectClipContent="True"
|
||||
SizeFlagsVertical="FillExpand" />
|
||||
</VBoxContainer>
|
||||
</SS14Window>
|
||||
|
||||
23
Robust.Client/UserInterface/LocExtension.cs
Normal file
23
Robust.Client/UserInterface/LocExtension.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Robust.Client.UserInterface
|
||||
{
|
||||
// TODO: Code a XAML compiler transformer to remove references to this type at compile time.
|
||||
// And just replace them with the Loc.GetString() call.
|
||||
[PublicAPI]
|
||||
public class LocExtension
|
||||
{
|
||||
public string Key { get; }
|
||||
|
||||
public LocExtension(string key)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
|
||||
public object ProvideValue()
|
||||
{
|
||||
return Loc.GetString(Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,6 @@ namespace Robust.Client.UserInterface.XAML
|
||||
{
|
||||
}
|
||||
|
||||
public class XmlnsDefinitionAttribute : Attribute
|
||||
{
|
||||
public XmlnsDefinitionAttribute(string xmlNamespace, string clrNamespace)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class UsableDuringInitializationAttribute : Attribute
|
||||
{
|
||||
public UsableDuringInitializationAttribute(bool usable)
|
||||
|
||||
16
Robust.Client/UserInterface/XAML/Binding.cs
Normal file
16
Robust.Client/UserInterface/XAML/Binding.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Avalonia.Data
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class Binding
|
||||
{
|
||||
public Binding()
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Data binding is not currently supported and this type exists only to make Rider work.");
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Robust.Client/UserInterface/XAML/Namespaces.cs
Normal file
6
Robust.Client/UserInterface/XAML/Namespaces.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using Avalonia.Metadata;
|
||||
|
||||
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml", "Robust.Client.UserInterface.XAML.XNamespace")]
|
||||
[assembly: XmlnsDefinition("https://spacestation14.io", "Robust.Client.UserInterface")]
|
||||
[assembly: XmlnsDefinition("https://spacestation14.io", "Robust.Client.UserInterface.Controls")]
|
||||
[assembly: XmlnsDefinition("https://spacestation14.io", "Robust.Client.UserInterface.CustomControls")]
|
||||
12
Robust.Client/UserInterface/XAML/NullExtension.cs
Normal file
12
Robust.Client/UserInterface/XAML/NullExtension.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using JetBrains.Annotations;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Robust.Client.UserInterface.XAML.XNamespace
|
||||
{
|
||||
// x:Null
|
||||
[UsedImplicitly]
|
||||
public sealed class NullExtension
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
28
Robust.Client/UserInterface/XAML/RiderNotes.md
Normal file
28
Robust.Client/UserInterface/XAML/RiderNotes.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# XamlUI notes about Rider
|
||||
|
||||
XAML support in both Rider and VS is pretty hardcoded for all supported frameworks. Rider seems less bad about this but they still hardcode a lot of type paths and do other silly stuff. Luckily, they support Avalonia. So... let's pretend to be Avalonia!
|
||||
|
||||
**JetBrains, if you're reading this, please don't sue me or take away our licenses pls I only reverse engineered your program to avoid wasting time on your issue tracker.**
|
||||
|
||||
## Where to find this info
|
||||
|
||||
The primary XAML support in Rider appears to be in `lib/ReSharperHost/JetBrains.ReSharper.Psi.Xaml.dll`. I recommend you decompile it with DotPeek to a project for navigating around. You can then look around the decompiled code (it's not obfuscated at all) to figure out how Rider does all this stuff.
|
||||
|
||||
**JetBrains, if you're reading this, please don't sue me or take away our licenses pls I only reverse engineered your program to avoid wasting time on your issue tracker.**
|
||||
|
||||
## Attributes
|
||||
|
||||
Attributes like `XmlnsAttributionAttribute` are hardcoded for full name with namespace. That's also where it stops, so we just define our own `Avalonia.Metadata.XmlnsDefinitionAttribute` and this fools Rider into recognizing it and working. Hooray!
|
||||
|
||||
## `http://schemas.microsoft.com/winfx/2006/xaml` Namespace
|
||||
|
||||
As far as I can tell, at least in Avalonia, the things in this namespace are not backed by any real .NET symbols and are just made up by Rider. This is a problem because it only does this making up if it detects that you're using Avalonia.
|
||||
|
||||
The heuristic for "are we using Avalonia" appears to be finding an assembly or project reference for `Avalonia.Base`. You can see this in `XamlModulePlatformCache.cs` in the decompiled ReSharper source code and also the `XamlPlatform` flags enum.
|
||||
|
||||
For the time being I decided to hold off on actually fully pretending we're Avalonia so did *not* commit to a reference to `Avalonia.Base` yet. Instead we just manually created stuff like a dummy `StaticExtension` class (with a constructor that always throws) and put it into the `http://schemas.microsoft.com/winfx/2006/xaml` namespace. This seems to satisfy Rider without interfering with the actual XamlIL compilation.
|
||||
|
||||
## Markup Extensions
|
||||
|
||||
Markup extensions have to be classes with a method of signature either `object ProvideValue(IServiceProvider)` or `object ProvideValue()`.
|
||||
It should be noted that Rider refuses to acknowledge markup extensions unless it finds Avalonia's `Avalonia.Data.Binding` type (or `Avalonia.Markup.Xaml.Data.Binding`). Yes, this is an actual requirement.
|
||||
22
Robust.Client/UserInterface/XAML/StaticExtension.cs
Normal file
22
Robust.Client/UserInterface/XAML/StaticExtension.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Robust.Client.UserInterface.XAML.XNamespace
|
||||
{
|
||||
// x:Static
|
||||
[UsedImplicitly]
|
||||
public sealed class StaticExtension
|
||||
{
|
||||
public StaticExtension(string _)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"This type only exists to make Rider work and should never be instantiated.");
|
||||
}
|
||||
|
||||
public static object ProvideValue()
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
22
Robust.Client/UserInterface/XAML/TypeExtension.cs
Normal file
22
Robust.Client/UserInterface/XAML/TypeExtension.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Robust.Client.UserInterface.XAML.XNamespace
|
||||
{
|
||||
// x:Type
|
||||
[UsedImplicitly]
|
||||
public sealed class TypeExtension
|
||||
{
|
||||
public TypeExtension(string _)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"This type only exists to make Rider work and should never be instantiated.");
|
||||
}
|
||||
|
||||
public static object ProvideValue()
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Robust.Client/UserInterface/XAML/XmlnsDefinitionAttribute.cs
Normal file
20
Robust.Client/UserInterface/XAML/XmlnsDefinitionAttribute.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Avalonia.Metadata
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
[PublicAPI]
|
||||
public sealed class XmlnsDefinitionAttribute : Attribute
|
||||
{
|
||||
public XmlnsDefinitionAttribute(string xmlNamespace, string clrNamespace)
|
||||
{
|
||||
XmlNamespace = xmlNamespace;
|
||||
ClrNamespace = clrNamespace;
|
||||
}
|
||||
|
||||
public string XmlNamespace { get; }
|
||||
public string ClrNamespace { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.ViewVariables.Traits;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Shared.Utility;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Instances
|
||||
{
|
||||
internal class ViewVariablesInstanceObject : ViewVariablesInstance
|
||||
{
|
||||
private TabContainer _tabs = default!;
|
||||
private Button _refreshButton = default!;
|
||||
private int _tabCount;
|
||||
|
||||
private readonly List<ViewVariablesTrait> _traits = new();
|
||||
|
||||
private CancellationTokenSource _refreshCancelToken = new();
|
||||
|
||||
public ViewVariablesRemoteSession? Session { get; private set; }
|
||||
public object? Object { get; private set; }
|
||||
|
||||
@@ -72,9 +78,10 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
name.SizeFlagsHorizontal = Control.SizeFlags.FillExpand;
|
||||
headBox.AddChild(name);
|
||||
|
||||
var button = new Button {Text = "Refresh"};
|
||||
button.OnPressed += _ => _refresh();
|
||||
headBox.AddChild(button);
|
||||
_refreshButton = new Button {Text = "Refresh", ToolTip = "RMB to toggle auto-refresh."};
|
||||
_refreshButton.OnPressed += _ => _refresh();
|
||||
_refreshButton.OnKeyBindDown += OnButtonKeybindDown;
|
||||
headBox.AddChild(_refreshButton);
|
||||
vBoxContainer.AddChild(headBox);
|
||||
}
|
||||
|
||||
@@ -82,10 +89,32 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
vBoxContainer.AddChild(_tabs);
|
||||
}
|
||||
|
||||
private void OnButtonKeybindDown(GUIBoundKeyEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Function == EngineKeyFunctions.UIRightClick)
|
||||
{
|
||||
_refreshButton.ToggleMode = !_refreshButton.ToggleMode;
|
||||
_refreshButton.Pressed = !_refreshButton.Pressed;
|
||||
|
||||
_refreshCancelToken.Cancel();
|
||||
|
||||
if (!_refreshButton.Pressed) return;
|
||||
|
||||
_refreshCancelToken = new CancellationTokenSource();
|
||||
Timer.SpawnRepeating(500, _refresh, _refreshCancelToken.Token);
|
||||
|
||||
} else if (eventArgs.Function == EngineKeyFunctions.UIClick)
|
||||
{
|
||||
_refreshButton.ToggleMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
|
||||
_refreshCancelToken.Cancel();
|
||||
|
||||
if (Session != null && !Session.Closed)
|
||||
{
|
||||
ViewVariablesManager.CloseSession(Session);
|
||||
|
||||
@@ -31,7 +31,6 @@ using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timers;
|
||||
using Serilog.Debugging;
|
||||
using Serilog.Sinks.Loki;
|
||||
using Stopwatch = Robust.Shared.Timing.Stopwatch;
|
||||
@@ -273,12 +272,17 @@ namespace Robust.Server
|
||||
|
||||
//IoCManager.Resolve<IMapLoader>().LoadedMapData +=
|
||||
// IoCManager.Resolve<IRobustMappedStringSerializer>().AddStrings;
|
||||
IoCManager.Resolve<IPrototypeManager>().LoadedData +=
|
||||
(yaml, name) => _stringSerializer.AddStrings(yaml);
|
||||
IoCManager.Resolve<IPrototypeManager>().LoadedData += (yaml, name) =>
|
||||
{
|
||||
if (!_stringSerializer.Locked)
|
||||
{
|
||||
_stringSerializer.AddStrings(yaml);
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize Tier 2 services
|
||||
IoCManager.Resolve<IGameTiming>().InSimulation = true;
|
||||
|
||||
|
||||
IoCManager.Resolve<INetConfigurationManager>().SetupNetworking();
|
||||
|
||||
_stateManager.Initialize();
|
||||
@@ -297,6 +301,7 @@ namespace Robust.Server
|
||||
// because of 'reasons' this has to be called after the last assembly is loaded
|
||||
// otherwise the prototypes will be cleared
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
prototypeManager.Initialize();
|
||||
prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes"));
|
||||
prototypeManager.Resync();
|
||||
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Server.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class AddCompCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "addcomp";
|
||||
public string Description => "Adds a component to an entity";
|
||||
public string Help => "addcomp <uid> <componentName>";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
shell.WriteLine("Wrong number of arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
var entityUid = EntityUid.Parse(args[0]);
|
||||
var componentName = args[1];
|
||||
|
||||
var compManager = IoCManager.Resolve<IComponentManager>();
|
||||
var compFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
var entity = entityManager.GetEntity(entityUid);
|
||||
var component = (Component) compFactory.GetComponent(componentName);
|
||||
|
||||
component.Owner = entity;
|
||||
|
||||
compManager.AddComponent(entity, component);
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
internal sealed class RemoveCompCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "rmcomp";
|
||||
public string Description => "Removes a component from an entity.";
|
||||
public string Help => "rmcomp <uid> <componentName>";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
shell.WriteLine("Wrong number of arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
var entityUid = EntityUid.Parse(args[0]);
|
||||
var componentName = args[1];
|
||||
|
||||
var compManager = IoCManager.Resolve<IComponentManager>();
|
||||
var compFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
var registration = compFactory.GetRegistration(componentName);
|
||||
|
||||
compManager.RemoveComponent(entityUid, registration.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Robust.Server/Console/Commands/AddComponentCommand.cs
Normal file
61
Robust.Server/Console/Commands/AddComponentCommand.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Server.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class AddComponentCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "addcomp";
|
||||
public string Description => "Adds a component to an entity";
|
||||
public string Help => $"{Command} <uid> <componentName>";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
shell.WriteLine($"Invalid amount of arguments.\n{Help}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var uid))
|
||||
{
|
||||
shell.WriteLine($"{uid} is not a valid entity uid.");
|
||||
return;
|
||||
}
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!entityManager.TryGetEntity(uid, out var entity))
|
||||
{
|
||||
shell.WriteLine($"No entity found with id {uid}.");
|
||||
return;
|
||||
}
|
||||
|
||||
var componentName = args[1];
|
||||
|
||||
var compManager = IoCManager.Resolve<IComponentManager>();
|
||||
var compFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
if (!compFactory.TryGetRegistration(componentName, out var registration, true))
|
||||
{
|
||||
shell.WriteLine($"No component found with name {componentName}.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity.HasComponent(registration.Type))
|
||||
{
|
||||
shell.WriteLine($"Entity {entity.Name} already has a {componentName} component.");
|
||||
}
|
||||
|
||||
var component = (Component) compFactory.GetComponent(registration.Type);
|
||||
|
||||
component.Owner = entity;
|
||||
compManager.AddComponent(entity, component);
|
||||
|
||||
shell.WriteLine($"Added {componentName} component to entity {entity.Name}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
using System.Globalization;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Server.Timing;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Server.Console.Commands
|
||||
{
|
||||
@@ -237,81 +236,6 @@ namespace Robust.Server.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
class PauseMapCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "pausemap";
|
||||
public string Description => "Pauses a map, pausing all simulation processing on it.";
|
||||
public string Help => "Usage: pausemap <map ID>";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("Need to supply a valid MapId"));
|
||||
return;
|
||||
}
|
||||
|
||||
var arg = args[0];
|
||||
var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture));
|
||||
|
||||
var pauseManager = IoCManager.Resolve<IPauseManager>();
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
if (!mapManager.MapExists(mapId))
|
||||
{
|
||||
shell.WriteLine("That map does not exist.");
|
||||
return;
|
||||
}
|
||||
pauseManager.SetMapPaused(mapId, true);
|
||||
}
|
||||
}
|
||||
|
||||
class UnpauseMapCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "unpausemap";
|
||||
public string Description => "unpauses a map, resuming all simulation processing on it.";
|
||||
public string Help => "Usage: unpausemap <map ID>";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("Need to supply a valid MapId"));
|
||||
return;
|
||||
}
|
||||
|
||||
var arg = args[0];
|
||||
var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture));
|
||||
|
||||
var pauseManager = IoCManager.Resolve<IPauseManager>();
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
if (!mapManager.MapExists(mapId))
|
||||
{
|
||||
shell.WriteLine("That map does not exist.");
|
||||
return;
|
||||
}
|
||||
pauseManager.SetMapPaused(mapId, false);
|
||||
}
|
||||
}
|
||||
|
||||
class QueryMapPausedCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "querymappaused";
|
||||
public string Description => "Check whether a map is paused or not.";
|
||||
public string Help => "Usage: querymappaused <map ID>";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var arg = args[0];
|
||||
var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture));
|
||||
|
||||
var pauseManager = IoCManager.Resolve<IPauseManager>();
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
if (!mapManager.MapExists(mapId))
|
||||
{
|
||||
shell.WriteLine("That map does not exist.");
|
||||
return;
|
||||
}
|
||||
shell.WriteLine(pauseManager.IsMapPaused(mapId).ToString());
|
||||
}
|
||||
}
|
||||
|
||||
class TpGridCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "tpgrid";
|
||||
|
||||
59
Robust.Server/Console/Commands/RemoveComponentCommand.cs
Normal file
59
Robust.Server/Console/Commands/RemoveComponentCommand.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Server.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class RemoveComponentCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "rmcomp";
|
||||
public string Description => "Removes a component from an entity.";
|
||||
public string Help => $"{Command} <uid> <componentName>";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
shell.WriteLine($"Invalid amount of arguments.\n{Help}.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var uid))
|
||||
{
|
||||
shell.WriteLine($"{uid} is not a valid entity uid.");
|
||||
return;
|
||||
}
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!entityManager.TryGetEntity(uid, out var entity))
|
||||
{
|
||||
shell.WriteLine($"No entity found with id {uid}.");
|
||||
return;
|
||||
}
|
||||
|
||||
var componentName = args[1];
|
||||
|
||||
var compManager = IoCManager.Resolve<IComponentManager>();
|
||||
var compFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
if (!compFactory.TryGetRegistration(componentName, out var registration, true))
|
||||
{
|
||||
shell.WriteLine($"No component found with name {componentName}.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entity.HasComponent(registration.Type))
|
||||
{
|
||||
shell.WriteLine($"No {componentName} component found on entity {entity.Name}.");
|
||||
return;
|
||||
}
|
||||
|
||||
compManager.RemoveComponent(uid, registration.Type);
|
||||
|
||||
shell.WriteLine($"Removed {componentName} component from entity {entity.Name}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,5 +30,10 @@ namespace Robust.Server.Console
|
||||
{
|
||||
return Implementation?.CanAdminMenu(session) ?? false;
|
||||
}
|
||||
|
||||
public bool CanAdminReloadPrototypes(IPlayerSession session)
|
||||
{
|
||||
return Implementation?.CanAdminReloadPrototypes(session) ?? false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ namespace Robust.Server.Console
|
||||
bool CanAdminPlace(IPlayerSession session);
|
||||
bool CanScript(IPlayerSession session);
|
||||
bool CanAdminMenu(IPlayerSession session);
|
||||
bool CanAdminReloadPrototypes(IPlayerSession session);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Robust.Shared.ViewVariables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
@@ -61,7 +62,7 @@ namespace Robust.Server.GameObjects
|
||||
return false;
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new AppearanceComponentState(data);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -256,7 +257,7 @@ namespace Robust.Server.GameObjects
|
||||
_entitiesWaitingResolve = null;
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new ContainerManagerComponentState(
|
||||
_allContainers.ToDictionary(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
@@ -63,7 +64,7 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new EyeComponentState(DrawFov, Zoom, Offset, Rotation);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -73,7 +74,7 @@ namespace Robust.Server.GameObjects
|
||||
serializer.DataField(ref _offset, "offset", Vector2.Zero);
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new PointLightComponentState(Enabled, Color, Radius, Offset);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Robust.Shared.GameObjects;
|
||||
using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -441,7 +442,7 @@ namespace Robust.Server.GameObjects
|
||||
Layers = layerData;
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new SpriteComponentState(Visible, DrawDepth, Scale, Rotation, Offset, Color, Directional,
|
||||
BaseRSIPath, Layers, RenderOrder);
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robust.Server.GameObjects
|
||||
/// <summary>
|
||||
/// Gets all entity states that have been modified after and including the provided tick.
|
||||
/// </summary>
|
||||
List<EntityState>? GetEntityStates(GameTick fromTick);
|
||||
List<EntityState>? GetEntityStates(GameTick fromTick, IPlayerSession player);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entity states within an AABB that have been modified after and including the provided tick.
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using Prometheus;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Server.Timing;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -13,6 +12,7 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Shared.GameObjects.TransformComponent;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
@@ -139,7 +139,7 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<EntityState>? GetEntityStates(GameTick fromTick)
|
||||
public List<EntityState>? GetEntityStates(GameTick fromTick, IPlayerSession player)
|
||||
{
|
||||
var stateEntities = new List<EntityState>();
|
||||
foreach (var entity in AllEntities)
|
||||
@@ -154,7 +154,7 @@ namespace Robust.Server.GameObjects
|
||||
if (entity.LastModifiedTick <= fromTick)
|
||||
continue;
|
||||
|
||||
stateEntities.Add(GetEntityState(ComponentManager, entity.Uid, fromTick));
|
||||
stateEntities.Add(GetEntityState(ComponentManager, entity.Uid, fromTick, player));
|
||||
}
|
||||
|
||||
// no point sending an empty collection
|
||||
@@ -336,7 +336,7 @@ namespace Robust.Server.GameObjects
|
||||
if (playerEnt == null)
|
||||
{
|
||||
// super-observer?
|
||||
return GetEntityStates(fromTick);
|
||||
return GetEntityStates(fromTick, player);
|
||||
}
|
||||
|
||||
var playerUid = playerEnt.Uid;
|
||||
@@ -390,7 +390,7 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
var state = GetEntityState(ComponentManager, uid, fromTick);
|
||||
var state = GetEntityState(ComponentManager, uid, fromTick, player);
|
||||
|
||||
if (checkedEnts.Add(uid))
|
||||
{
|
||||
@@ -404,14 +404,14 @@ namespace Robust.Server.GameObjects
|
||||
{
|
||||
// mover changed and can't be seen
|
||||
var idx = Array.FindIndex(state.ComponentStates,
|
||||
x => x is TransformComponent.TransformComponentState);
|
||||
x => x is TransformComponentState);
|
||||
|
||||
if (idx != -1)
|
||||
{
|
||||
// mover changed positional data and can't be seen
|
||||
var oldState =
|
||||
(TransformComponent.TransformComponentState) state.ComponentStates[idx];
|
||||
var newState = new TransformComponent.TransformComponentState(Vector2NaN,
|
||||
(TransformComponentState) state.ComponentStates[idx];
|
||||
var newState = new TransformComponentState(Vector2NaN,
|
||||
oldState.Rotation, oldState.ParentID, oldState.NoLocalRotation);
|
||||
state.ComponentStates[idx] = newState;
|
||||
seenMovers.Remove(uid);
|
||||
@@ -441,7 +441,7 @@ namespace Robust.Server.GameObjects
|
||||
{
|
||||
// mover can't be seen
|
||||
var oldState =
|
||||
(TransformComponent.TransformComponentState) entity.Transform.GetComponentState();
|
||||
(TransformComponentState) entity.Transform.GetComponentState(player);
|
||||
entityStates.Add(new EntityState(uid,
|
||||
new ComponentChanged[]
|
||||
{
|
||||
@@ -449,7 +449,7 @@ namespace Robust.Server.GameObjects
|
||||
},
|
||||
new ComponentState[]
|
||||
{
|
||||
new TransformComponent.TransformComponentState(Vector2NaN, oldState.Rotation,
|
||||
new TransformComponentState(Vector2NaN, oldState.Rotation,
|
||||
oldState.ParentID, oldState.NoLocalRotation)
|
||||
}));
|
||||
|
||||
@@ -517,7 +517,7 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
|
||||
// should this be lastSeen or fromTick?
|
||||
var entityState = GetEntityState(ComponentManager, uid, lastSeen);
|
||||
var entityState = GetEntityState(ComponentManager, uid, lastSeen, player);
|
||||
|
||||
checkedEnts.Add(uid);
|
||||
|
||||
@@ -577,7 +577,7 @@ namespace Robust.Server.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
var state = GetEntityState(ComponentManager, uid, fromTick);
|
||||
var state = GetEntityState(ComponentManager, uid, fromTick, player);
|
||||
|
||||
if (state.ComponentStates == null)
|
||||
{
|
||||
@@ -591,7 +591,7 @@ namespace Robust.Server.GameObjects
|
||||
seenMovers.Remove(uid);
|
||||
ClearLastSeenTick(lSeen, uid);
|
||||
|
||||
var idx = Array.FindIndex(state.ComponentStates, x => x is TransformComponent.TransformComponentState);
|
||||
var idx = Array.FindIndex(state.ComponentStates, x => x is TransformComponentState);
|
||||
|
||||
if (idx == -1)
|
||||
{
|
||||
@@ -599,9 +599,9 @@ namespace Robust.Server.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
var oldState = (TransformComponent.TransformComponentState) state.ComponentStates[idx];
|
||||
var oldState = (TransformComponentState) state.ComponentStates[idx];
|
||||
var newState =
|
||||
new TransformComponent.TransformComponentState(Vector2NaN, oldState.Rotation, oldState.ParentID, oldState.NoLocalRotation);
|
||||
new TransformComponentState(Vector2NaN, oldState.Rotation, oldState.ParentID, oldState.NoLocalRotation);
|
||||
state.ComponentStates[idx] = newState;
|
||||
|
||||
|
||||
@@ -630,7 +630,7 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
|
||||
var entity = GetEntity(uid);
|
||||
var state = GetEntityState(ComponentManager, uid, fromTick);
|
||||
var state = GetEntityState(ComponentManager, uid, fromTick, player);
|
||||
|
||||
if (state.ComponentStates == null || viewbox.Intersects(GetWorldAabbFromEntity(entity)))
|
||||
{
|
||||
@@ -643,7 +643,7 @@ namespace Robust.Server.GameObjects
|
||||
entityStates.Add(state);
|
||||
|
||||
var idx = Array.FindIndex(state.ComponentStates,
|
||||
x => x is TransformComponent.TransformComponentState);
|
||||
x => x is TransformComponentState);
|
||||
|
||||
if (idx == -1)
|
||||
{
|
||||
@@ -651,9 +651,9 @@ namespace Robust.Server.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
var oldState = (TransformComponent.TransformComponentState) state.ComponentStates[idx];
|
||||
var oldState = (TransformComponentState) state.ComponentStates[idx];
|
||||
var newState =
|
||||
new TransformComponent.TransformComponentState(Vector2NaN, oldState.Rotation,
|
||||
new TransformComponentState(Vector2NaN, oldState.Rotation,
|
||||
oldState.ParentID, oldState.NoLocalRotation);
|
||||
state.ComponentStates[idx] = newState;
|
||||
seenMovers.Remove(uid);
|
||||
@@ -881,8 +881,9 @@ namespace Robust.Server.GameObjects
|
||||
/// <param name="compMan">ComponentManager that contains the components for the entity.</param>
|
||||
/// <param name="entityUid">Uid of the entity to generate the state from.</param>
|
||||
/// <param name="fromTick">Only provide delta changes from this tick.</param>
|
||||
/// <param name="player">The player to generate this state for.</param>
|
||||
/// <returns>New entity State for the given entity.</returns>
|
||||
private static EntityState GetEntityState(IComponentManager compMan, EntityUid entityUid, GameTick fromTick)
|
||||
private static EntityState GetEntityState(IComponentManager compMan, EntityUid entityUid, GameTick fromTick, IPlayerSession player)
|
||||
{
|
||||
var compStates = new List<ComponentState>();
|
||||
var changed = new List<ComponentChanged>();
|
||||
@@ -899,7 +900,7 @@ namespace Robust.Server.GameObjects
|
||||
// As such, we can avoid sending this data in this case since the client "already has it".
|
||||
|
||||
if (comp.NetSyncEnabled && comp.LastModifiedTick != GameTick.Zero && comp.LastModifiedTick >= fromTick)
|
||||
compStates.Add(comp.GetComponentState());
|
||||
compStates.Add(comp.GetComponentState(player));
|
||||
|
||||
if (comp.CreationTick != GameTick.Zero && comp.CreationTick >= fromTick && !comp.Deleted)
|
||||
{
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace Robust.Server.GameStates
|
||||
}
|
||||
|
||||
var entStates = lastAck == GameTick.Zero || !PvsEnabled
|
||||
? _entityManager.GetEntityStates(lastAck)
|
||||
? _entityManager.GetEntityStates(lastAck, session)
|
||||
: _entityManager.UpdatePlayerSeenEntityStates(lastAck, session, _entityManager.MaxUpdateRange);
|
||||
var playerStates = _playerManager.GetPlayerStates(lastAck);
|
||||
var deletions = _entityManager.GetDeletedEntities(lastAck);
|
||||
|
||||
@@ -12,9 +12,9 @@ using Robust.Shared.GameObjects;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Timing;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using YamlDotNet.Core;
|
||||
|
||||
namespace Robust.Server.Maps
|
||||
|
||||
@@ -1,12 +1,50 @@
|
||||
using System;
|
||||
using Robust.Server.Console;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Server.Prototypes
|
||||
{
|
||||
public sealed class ServerPrototypeManager : PrototypeManager
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IConGroupController _conGroups = default!;
|
||||
|
||||
public ServerPrototypeManager() : base()
|
||||
{
|
||||
RegisterIgnore("shader");
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
NetManager.RegisterNetMessage<MsgReloadPrototypes>(MsgReloadPrototypes.NAME, HandleReloadPrototypes, NetMessageAccept.Server);
|
||||
}
|
||||
|
||||
private void HandleReloadPrototypes(MsgReloadPrototypes msg)
|
||||
{
|
||||
#if !FULL_RELEASE
|
||||
if (!_playerManager.TryGetSessionByChannel(msg.MsgChannel, out var player) ||
|
||||
!_conGroups.CanAdminReloadPrototypes(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var then = DateTime.Now;
|
||||
|
||||
foreach (var path in msg.Paths)
|
||||
{
|
||||
ReloadPrototypes(path);
|
||||
}
|
||||
|
||||
Logger.Info($"Reloaded prototypes in {(int) (DateTime.Now - then).TotalMilliseconds} ms");
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ using Robust.Server.Prototypes;
|
||||
using Robust.Server.Reflection;
|
||||
using Robust.Server.Scripting;
|
||||
using Robust.Server.ServerStatus;
|
||||
using Robust.Server.Timing;
|
||||
using Robust.Server.ViewVariables;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -20,6 +20,7 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Server
|
||||
{
|
||||
@@ -37,11 +38,11 @@ namespace Robust.Server
|
||||
IoCManager.Register<IComponentFactory, ServerComponentFactory>();
|
||||
IoCManager.Register<IConGroupController, ConGroupController>();
|
||||
IoCManager.Register<IServerConsoleHost, ServerConsoleHost>();
|
||||
IoCManager.Register<IConsoleHost, ServerConsoleHost>();
|
||||
IoCManager.Register<IEntityManager, ServerEntityManager>();
|
||||
IoCManager.Register<IEntityNetworkManager, ServerEntityNetworkManager>();
|
||||
IoCManager.Register<IServerEntityNetworkManager, ServerEntityNetworkManager>();
|
||||
IoCManager.Register<IMapLoader, MapLoader>();
|
||||
IoCManager.Register<IPauseManager, PauseManager>();
|
||||
IoCManager.Register<IPlacementManager, PlacementManager>();
|
||||
IoCManager.Register<IPlayerManager, PlayerManager>();
|
||||
IoCManager.Register<IPrototypeManager, ServerPrototypeManager>();
|
||||
|
||||
@@ -152,21 +152,21 @@ namespace Robust.Server.ServerStatus
|
||||
|
||||
private void RegisterCVars()
|
||||
{
|
||||
try
|
||||
var path = PathHelpers.ExecutableRelativeFile("build.json");
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
var buildInfo = File.ReadAllText(PathHelpers.ExecutableRelativeFile("build.json"));
|
||||
var info = JsonConvert.DeserializeObject<BuildInfo>(buildInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't replace cvars with contents of build.json if overriden by --cvar or such.
|
||||
SetCVarIfUnmodified(CVars.BuildEngineVersion, info.EngineVersion);
|
||||
SetCVarIfUnmodified(CVars.BuildForkId, info.ForkId);
|
||||
SetCVarIfUnmodified(CVars.BuildVersion, info.Version);
|
||||
SetCVarIfUnmodified(CVars.BuildDownloadUrl, info.Download ?? "");
|
||||
SetCVarIfUnmodified(CVars.BuildHash, info.Hash ?? "");
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
}
|
||||
var buildInfo = File.ReadAllText(path);
|
||||
var info = JsonConvert.DeserializeObject<BuildInfo>(buildInfo);
|
||||
|
||||
// Don't replace cvars with contents of build.json if overriden by --cvar or such.
|
||||
SetCVarIfUnmodified(CVars.BuildEngineVersion, info.EngineVersion);
|
||||
SetCVarIfUnmodified(CVars.BuildForkId, info.ForkId);
|
||||
SetCVarIfUnmodified(CVars.BuildVersion, info.Version);
|
||||
SetCVarIfUnmodified(CVars.BuildDownloadUrl, info.Download ?? "");
|
||||
SetCVarIfUnmodified(CVars.BuildHash, info.Hash ?? "");
|
||||
|
||||
void SetCVarIfUnmodified(CVarDef<string> cvar, string val)
|
||||
{
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Server.Timing
|
||||
{
|
||||
internal sealed class PauseManager : IPauseManager, IPostInjectInit
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
[ViewVariables] private readonly HashSet<MapId> _pausedMaps = new();
|
||||
[ViewVariables] private readonly HashSet<MapId> _unInitializedMaps = new();
|
||||
|
||||
public void SetMapPaused(MapId mapId, bool paused)
|
||||
{
|
||||
if (paused)
|
||||
{
|
||||
_pausedMaps.Add(mapId);
|
||||
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
|
||||
{
|
||||
entity.Paused = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_pausedMaps.Remove(mapId);
|
||||
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
|
||||
{
|
||||
entity.Paused = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DoMapInitialize(MapId mapId)
|
||||
{
|
||||
if (IsMapInitialized(mapId))
|
||||
{
|
||||
throw new ArgumentException("That map is already initialized.");
|
||||
}
|
||||
|
||||
_unInitializedMaps.Remove(mapId);
|
||||
|
||||
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
|
||||
{
|
||||
entity.RunMapInit();
|
||||
entity.Paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void DoGridMapInitialize(IMapGrid grid) => DoGridMapInitialize(grid.Index);
|
||||
public void DoGridMapInitialize(GridId gridId)
|
||||
{
|
||||
var mapId = _mapManager.GetGrid(gridId).ParentMapId;
|
||||
|
||||
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
|
||||
{
|
||||
if (entity.Transform.GridID != gridId)
|
||||
continue;
|
||||
|
||||
entity.RunMapInit();
|
||||
entity.Paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddUninitializedMap(MapId mapId)
|
||||
{
|
||||
_unInitializedMaps.Add(mapId);
|
||||
}
|
||||
|
||||
public bool IsMapPaused(MapId mapId) => _pausedMaps.Contains(mapId) || _unInitializedMaps.Contains(mapId);
|
||||
public bool IsGridPaused(IMapGrid grid) => IsMapPaused(grid.ParentMapId);
|
||||
|
||||
public bool IsGridPaused(GridId gridId)
|
||||
{
|
||||
var grid = _mapManager.GetGrid(gridId);
|
||||
return IsGridPaused(grid);
|
||||
}
|
||||
|
||||
public bool IsMapInitialized(MapId mapId)
|
||||
{
|
||||
return !_unInitializedMaps.Contains(mapId);
|
||||
}
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
_mapManager.MapDestroyed += (sender, args) =>
|
||||
{
|
||||
_pausedMaps.Remove(args.Map);
|
||||
_unInitializedMaps.Add(args.Map);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Server.Timing
|
||||
{
|
||||
public static class PauseManagerExt
|
||||
{
|
||||
[Pure]
|
||||
[Obsolete("Use IEntity.Paused directly")]
|
||||
public static bool IsEntityPaused(this IPauseManager manager, IEntity entity)
|
||||
{
|
||||
return entity.Paused;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,6 @@ namespace Robust.Shared.Scripting
|
||||
"Robust.Shared.IoC",
|
||||
"Robust.Shared.Maths",
|
||||
"Robust.Shared.GameObjects",
|
||||
"Robust.Shared.Interfaces.GameObjects",
|
||||
"Robust.Shared.Map",
|
||||
"Robust.Shared.Prototypes"
|
||||
};
|
||||
|
||||
@@ -767,14 +767,14 @@ namespace Robust.Shared.ContentPack
|
||||
var dllName = $"{simpleName}.dll";
|
||||
foreach (var diskLoadPath in _diskLoadPaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
var path = Path.Combine(diskLoadPath, dllName);
|
||||
return new PEReader(File.OpenRead(path));
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
var path = Path.Combine(diskLoadPath, dllName);
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return new PEReader(File.OpenRead(path));
|
||||
}
|
||||
|
||||
var extraStream = _parent.ExtraRobustLoader?.Invoke(dllName);
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
var prevDir = new DirectoryInfo(prevPath);
|
||||
var found = false;
|
||||
foreach (var info in prevDir.GetFileSystemInfos())
|
||||
foreach (var info in prevDir.EnumerateFileSystemInfos())
|
||||
{
|
||||
if (!string.Equals(info.Name, segment, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
|
||||
@@ -116,6 +116,12 @@ namespace Robust.Shared.ContentPack
|
||||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="path"/> is null.</exception>
|
||||
IEnumerable<ResourcePath> ContentFindFiles(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of paths to all top-level content directories
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IEnumerable<ResourcePath> GetContentRoots();
|
||||
|
||||
/// <summary>
|
||||
/// Read a file from the mounted content paths to a string.
|
||||
/// </summary>
|
||||
@@ -168,4 +174,4 @@ namespace Robust.Shared.ContentPack
|
||||
return new StreamReader(stream, encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +305,17 @@ namespace Robust.Shared.ContentPack
|
||||
AddRoot(ResourcePath.Root, loader);
|
||||
}
|
||||
|
||||
public IEnumerable<ResourcePath> GetContentRoots()
|
||||
{
|
||||
foreach (var (_, root) in _contentRoots)
|
||||
{
|
||||
if (root is DirLoader loader)
|
||||
{
|
||||
yield return new ResourcePath(loader.GetPath(new ResourcePath(@"/")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsPathValid(ResourcePath path)
|
||||
{
|
||||
var asString = path.ToString();
|
||||
|
||||
@@ -330,6 +330,7 @@ Types:
|
||||
System.Globalization:
|
||||
CompareOptions: { }
|
||||
CultureInfo: { All: True }
|
||||
DateTimeStyles: { All: True } # Enum
|
||||
TextInfo:
|
||||
Methods:
|
||||
- "bool get_IsRightToLeft()"
|
||||
@@ -894,6 +895,7 @@ Types:
|
||||
MidpointRounding: { } # Enum
|
||||
MulticastDelegate:
|
||||
Inherit: Allow
|
||||
NotImplementedException: { All: True }
|
||||
NotSupportedException: { All: True }
|
||||
Nullable: { All: True }
|
||||
Nullable`1: { All: True }
|
||||
|
||||
@@ -202,8 +202,9 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public virtual void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null) { }
|
||||
|
||||
/// <param name="player"></param>
|
||||
/// <inheritdoc />
|
||||
public virtual ComponentState GetComponentState()
|
||||
public virtual ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
if (NetID == null)
|
||||
throw new InvalidOperationException($"Cannot make state for component without Net ID: {GetType()}");
|
||||
|
||||
@@ -5,6 +5,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -132,8 +133,9 @@ namespace Robust.Shared.GameObjects
|
||||
serializer.DataField(ref _mass, "mass", 1.0f);
|
||||
}
|
||||
|
||||
/// <param name="player"></param>
|
||||
/// <inheritdoc />
|
||||
public override ComponentState GetComponentState()
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new PhysicsComponentState(_canCollide, _status, _physShapes, _isHard, _mass, LinearVelocity, AngularVelocity, Anchored);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Robust.Shared.Localization.Macros;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -33,7 +34,7 @@ namespace Robust.Shared.GameObjects
|
||||
serializer.DataField(this, x => x.Proper, "proper", false);
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new GrammarComponentState(Proper);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using Robust.Server.Timing;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedIgnorePauseComponent))]
|
||||
public sealed class IgnorePauseComponent : SharedIgnorePauseComponent
|
||||
public class IgnorePauseComponent : Component
|
||||
{
|
||||
public override string Name => "IgnorePause";
|
||||
|
||||
public override void OnAdd()
|
||||
{
|
||||
base.OnAdd();
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -73,7 +74,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new OccluderComponentState(Enabled, BoundingBox);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -39,8 +40,9 @@ namespace Robust.Shared.GameObjects
|
||||
_mapIndex = MapId.Nullspace;
|
||||
}
|
||||
|
||||
/// <param name="player"></param>
|
||||
/// <inheritdoc />
|
||||
public override ComponentState GetComponentState()
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new MapComponentState(_mapIndex);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -61,8 +62,9 @@ namespace Robust.Shared.GameObjects
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
/// <param name="player"></param>
|
||||
/// <inheritdoc />
|
||||
public override ComponentState GetComponentState()
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new MapGridComponentState(_gridIndex, Grid.HasGravity);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -137,8 +138,9 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="player"></param>
|
||||
/// <inheritdoc />
|
||||
public override ComponentState GetComponentState()
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new MetaDataComponentState(_entityName, _entityDescription, EntityPrototype?.ID);
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public abstract class SharedIgnorePauseComponent : Component
|
||||
{
|
||||
public override string Name => "IgnorePause";
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.IoC;
|
||||
using Timer = Robust.Shared.Timers.Timer;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Timer = Robust.Shared.Timers.Timer;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Shared.Containers;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -698,8 +699,9 @@ namespace Robust.Shared.GameObjects
|
||||
serializer.DataField(ref _noLocalRotation, "noRot", false);
|
||||
}
|
||||
|
||||
/// <param name="player"></param>
|
||||
/// <inheritdoc />
|
||||
public override ComponentState GetComponentState()
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new TransformComponentState(_localPosition, LocalRotation, _parent, _noLocalRotation);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Robust.Shared.GameObjects
|
||||
get => _paused;
|
||||
set
|
||||
{
|
||||
if (_paused == value || value && HasComponent<SharedIgnorePauseComponent>())
|
||||
if (_paused == value || value && HasComponent<IgnorePauseComponent>())
|
||||
return;
|
||||
|
||||
_paused = value;
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// Whether the Owner has been paused.
|
||||
/// </summary>
|
||||
bool Paused { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Whether the client should synchronize component additions and removals.
|
||||
/// If this is false and the component gets added or removed server side, the client will not do the same.
|
||||
@@ -125,8 +125,9 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Get the component's state for replicating on the client.
|
||||
/// </summary>
|
||||
/// <param name="player"></param>
|
||||
/// <returns>ComponentState object</returns>
|
||||
ComponentState GetComponentState();
|
||||
ComponentState GetComponentState(ICommonSession player);
|
||||
|
||||
/// <summary>
|
||||
/// Handles an incoming component state from the server.
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a component that has "map initialization" behavior.
|
||||
/// Basically irreversible behavior that moves the map from "map editor" to playable,
|
||||
/// like spawning preset objects.
|
||||
/// Defines a component that has "map initialization" behavior.
|
||||
/// Basically irreversible behavior that moves the map from "map editor" to playable,
|
||||
/// like spawning preset objects.
|
||||
/// </summary>
|
||||
public interface IMapInit
|
||||
{
|
||||
42
Robust.Shared/Network/Messages/MsgReloadPrototypes.cs
Normal file
42
Robust.Shared/Network/Messages/MsgReloadPrototypes.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Lidgren.Network;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgReloadPrototypes : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgReloadPrototypes);
|
||||
|
||||
public MsgReloadPrototypes(INetChannel channel) : base(NAME, GROUP)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public ResourcePath[] Paths = default!;
|
||||
|
||||
public override void ReadFromBuffer(NetIncomingMessage buffer)
|
||||
{
|
||||
var count = buffer.ReadInt32();
|
||||
Paths = new ResourcePath[count];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
Paths[i] = new ResourcePath(buffer.ReadString());
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteToBuffer(NetOutgoingMessage buffer)
|
||||
{
|
||||
buffer.Write(Paths.Length);
|
||||
|
||||
foreach (var path in Paths)
|
||||
{
|
||||
buffer.Write(path.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,9 +179,33 @@ namespace Robust.Shared.Network
|
||||
// Well they're in. Kick a connected client with the same GUID if we have to.
|
||||
if (_assignedUserIds.TryGetValue(userId, out var existing))
|
||||
{
|
||||
existing.Disconnect("Another connection has been made with your account.");
|
||||
// Have to wait until they're properly off the server to avoid any collisions.
|
||||
await AwaitDisconnectAsync(existing);
|
||||
if (_awaitingDisconnectToConnect.Contains(userId))
|
||||
{
|
||||
connection.Disconnect("Stop trying to connect multiple times at once.");
|
||||
return;
|
||||
}
|
||||
|
||||
_awaitingDisconnectToConnect.Add(userId);
|
||||
try
|
||||
{
|
||||
existing.Disconnect("Another connection has been made with your account.");
|
||||
// Have to wait until they're properly off the server to avoid any collisions.
|
||||
await AwaitDisconnectAsync(existing);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_awaitingDisconnectToConnect.Remove(userId);
|
||||
}
|
||||
}
|
||||
|
||||
if (connection.Status == NetConnectionStatus.Disconnecting ||
|
||||
connection.Status == NetConnectionStatus.Disconnected)
|
||||
{
|
||||
Logger.InfoS("net",
|
||||
"{ConnectionEndpoint} disconnected during handshake",
|
||||
connection.RemoteEndPoint, userName, userId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = peer.Peer.CreateMessage();
|
||||
@@ -242,8 +266,12 @@ namespace Robust.Shared.Network
|
||||
|
||||
private Task AwaitDisconnectAsync(NetConnection connection)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object?>();
|
||||
_awaitingDisconnect.Add(connection, tcs);
|
||||
if (!_awaitingDisconnect.TryGetValue(connection, out var tcs))
|
||||
{
|
||||
tcs = new TaskCompletionSource<object?>();
|
||||
_awaitingDisconnect.Add(connection, tcs);
|
||||
}
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,6 +135,8 @@ namespace Robust.Shared.Network
|
||||
private readonly Dictionary<NetConnection, TaskCompletionSource<object?>> _awaitingDisconnect
|
||||
= new();
|
||||
|
||||
private readonly HashSet<NetUserId> _awaitingDisconnectToConnect = new HashSet<NetUserId>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Port => _config.GetCVar(CVars.NetPort);
|
||||
|
||||
|
||||
@@ -870,7 +870,7 @@ namespace Robust.Shared.Physics
|
||||
|
||||
public void Query<TState>(ref TState state, QueryCallback<TState> callback, in Box2 aabb)
|
||||
{
|
||||
using var stack = new GrowableStack<Proxy>(stackalloc Proxy[256]);
|
||||
var stack = new GrowableStack<Proxy>(stackalloc Proxy[256]);
|
||||
stack.Push(_root);
|
||||
|
||||
ref var baseRef = ref _nodes[0];
|
||||
@@ -915,7 +915,7 @@ namespace Robust.Shared.Physics
|
||||
{
|
||||
// NOTE: This is not Box2D's normal ray cast function, since our rays have infinite length.
|
||||
|
||||
using var stack = new GrowableStack<Proxy>(stackalloc Proxy[256]);
|
||||
var stack = new GrowableStack<Proxy>(stackalloc Proxy[256]);
|
||||
|
||||
stack.Push(_root);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
@@ -6,6 +6,7 @@ using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -18,7 +19,7 @@ namespace Robust.Shared.Prototypes
|
||||
/// Prototype that represents game entities.
|
||||
/// </summary>
|
||||
[Prototype("entity")]
|
||||
public class EntityPrototype : IPrototype, IIndexedPrototype, ISyncingPrototype
|
||||
public class EntityPrototype : IPrototype, ISyncingPrototype
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
|
||||
@@ -257,6 +258,11 @@ namespace Robust.Shared.Prototypes
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Children.Clear();
|
||||
}
|
||||
|
||||
// Resolve inheritance.
|
||||
public bool Sync(IPrototypeManager manager, int stage)
|
||||
{
|
||||
@@ -424,6 +430,68 @@ namespace Robust.Shared.Prototypes
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateEntity(Entity entity)
|
||||
{
|
||||
bool HasBeenModified(string name, YamlMappingNode data, EntityPrototype prototype, IComponent currentComponent, IComponentFactory factory)
|
||||
{
|
||||
var component = factory.GetComponent(name);
|
||||
prototype.CurrentDeserializingComponent = name;
|
||||
ObjectSerializer ser = YamlObjectSerializer.NewReader(data, new PrototypeSerializationContext(prototype));
|
||||
component.ExposeData(ser);
|
||||
return component == (Component) currentComponent;
|
||||
}
|
||||
|
||||
if (Name != entity.Prototype?.Name)
|
||||
{
|
||||
Logger.Error($"Reloaded prototype used to update entity did not match entity's existing prototype: Expected '{entity.Prototype?.Name}', got '{Name}'");
|
||||
return;
|
||||
}
|
||||
|
||||
var factory = IoCManager.Resolve<IComponentFactory>();
|
||||
var componentManager = IoCManager.Resolve<IComponentManager>();
|
||||
var oldPrototype = entity.Prototype;
|
||||
|
||||
var oldPrototypeComponents = oldPrototype.Components.Keys
|
||||
.Where(n => n != "Transform" && n != "MetaData")
|
||||
.Select(name => (name, factory.GetRegistration(name).Type))
|
||||
.ToList();
|
||||
var newPrototypeComponents = Components.Keys
|
||||
.Where(n => n != "Transform" && n != "MetaData")
|
||||
.Select(name => (name, factory.GetRegistration(name).Type))
|
||||
.ToList();
|
||||
|
||||
var ignoredComponents = new List<string>();
|
||||
|
||||
// Find components to be removed, and remove them
|
||||
foreach (var (name, type) in oldPrototypeComponents.Except(newPrototypeComponents))
|
||||
{
|
||||
if (!HasBeenModified(name, oldPrototype.Components[name], oldPrototype, entity.GetComponent(type),
|
||||
factory) && Components.Keys.Contains(name))
|
||||
{
|
||||
ignoredComponents.Add(name);
|
||||
Logger.Debug(name);
|
||||
continue;
|
||||
}
|
||||
componentManager.RemoveComponent(entity.Uid, type);
|
||||
}
|
||||
componentManager.CullRemovedComponents();
|
||||
|
||||
// Add new components
|
||||
foreach (var (name, type) in newPrototypeComponents.Where(t => !ignoredComponents.Contains(t.name)).Except(oldPrototypeComponents))
|
||||
{
|
||||
var data = Components[name];
|
||||
var component = (Component) factory.GetComponent(name);
|
||||
ObjectSerializer ser = YamlObjectSerializer.NewReader(data, new PrototypeSerializationContext(this));
|
||||
CurrentDeserializingComponent = name;
|
||||
component.Owner = entity;
|
||||
component.ExposeData(ser);
|
||||
entity.AddComponent(component);
|
||||
}
|
||||
|
||||
// Update entity metadata
|
||||
entity.MetaData.EntityPrototype = this;
|
||||
}
|
||||
|
||||
internal static void LoadEntity(EntityPrototype? prototype, Entity entity, IComponentFactory factory, IEntityLoadContext? context)
|
||||
{
|
||||
YamlObjectSerializer.Context? defaultContext = null;
|
||||
|
||||
@@ -3,30 +3,24 @@
|
||||
namespace Robust.Shared.Prototypes
|
||||
{
|
||||
/// <summary>
|
||||
/// An IPrototype is a prototype that can be loaded from the global YAML prototypes.
|
||||
/// An IPrototype is a prototype that can be loaded from the global YAML prototypes.
|
||||
/// </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.
|
||||
/// 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>
|
||||
public interface IPrototype
|
||||
{
|
||||
/// <summary>
|
||||
/// Load data from the YAML mappings in the prototype files.
|
||||
/// </summary>
|
||||
void LoadFrom(YamlMappingNode mapping);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension on <see cref="IPrototype"/> that allows it to be "indexed" by a string ID.
|
||||
/// </summary>
|
||||
public interface IIndexedPrototype
|
||||
{
|
||||
/// <summary>
|
||||
/// An ID for this prototype instance.
|
||||
/// If this is a duplicate, an error will be thrown.
|
||||
/// </summary>
|
||||
string ID { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Load data from the YAML mappings in the prototype files.
|
||||
/// </summary>
|
||||
void LoadFrom(YamlMappingNode mapping);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -35,6 +29,8 @@ namespace Robust.Shared.Prototypes
|
||||
/// </summary>
|
||||
public interface ISyncingPrototype
|
||||
{
|
||||
void Reset();
|
||||
|
||||
/// <summary>
|
||||
/// Sync and update cross-referencing data.
|
||||
/// Syncing works in stages, each time it will be called with the stage it's currently on.
|
||||
|
||||
@@ -4,11 +4,16 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.IoC.Exceptions;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.Core;
|
||||
@@ -21,6 +26,8 @@ namespace Robust.Shared.Prototypes
|
||||
/// </summary>
|
||||
public interface IPrototypeManager
|
||||
{
|
||||
void Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Return an IEnumerable to iterate all prototypes of a certain type.
|
||||
/// </summary>
|
||||
@@ -28,6 +35,7 @@ namespace Robust.Shared.Prototypes
|
||||
/// Thrown if the type of prototype is not registered.
|
||||
/// </exception>
|
||||
IEnumerable<T> EnumeratePrototypes<T>() where T : class, IPrototype;
|
||||
|
||||
/// <summary>
|
||||
/// Return an IEnumerable to iterate all prototypes of a certain type.
|
||||
/// </summary>
|
||||
@@ -35,31 +43,44 @@ namespace Robust.Shared.Prototypes
|
||||
/// Thrown if the type of prototype is not registered.
|
||||
/// </exception>
|
||||
IEnumerable<IPrototype> EnumeratePrototypes(Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Index for a <see cref="IIndexedPrototype"/> by ID.
|
||||
/// Index for a <see cref="IPrototype"/> by ID.
|
||||
/// </summary>
|
||||
/// <exception cref="KeyNotFoundException">
|
||||
/// Thrown if the type of prototype is not registered.
|
||||
/// </exception>
|
||||
T Index<T>(string id) where T : class, IIndexedPrototype;
|
||||
T Index<T>(string id) where T : class, IPrototype;
|
||||
|
||||
/// <summary>
|
||||
/// Index for a <see cref="IIndexedPrototype"/> by ID.
|
||||
/// Index for a <see cref="IPrototype"/> by ID.
|
||||
/// </summary>
|
||||
/// <exception cref="KeyNotFoundException">
|
||||
/// Thrown if the ID does not exist or the type of prototype is not registered.
|
||||
/// </exception>
|
||||
IIndexedPrototype Index(Type type, string id);
|
||||
bool HasIndex<T>(string id) where T : IIndexedPrototype;
|
||||
bool TryIndex<T>(string id, out T prototype) where T : IIndexedPrototype;
|
||||
IPrototype Index(Type type, string id);
|
||||
bool HasIndex<T>(string id) where T : IPrototype;
|
||||
bool TryIndex<T>(string id, out T prototype) where T : IPrototype;
|
||||
|
||||
/// <summary>
|
||||
/// Load prototypes from files in a directory, recursively.
|
||||
/// </summary>
|
||||
void LoadDirectory(ResourcePath path);
|
||||
void LoadFromStream(TextReader stream);
|
||||
Task<List<IPrototype>> LoadDirectory(ResourcePath path);
|
||||
|
||||
List<IPrototype> LoadFromStream(TextReader stream);
|
||||
|
||||
List<IPrototype> LoadString(string str);
|
||||
|
||||
/// <summary>
|
||||
/// Clear out all prototypes and reset to a blank slate.
|
||||
/// </summary>
|
||||
void Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Performs a reload on all prototypes, updating the game state accordingly
|
||||
/// </summary>
|
||||
void ReloadPrototypes(ResourcePath file);
|
||||
|
||||
/// <summary>
|
||||
/// Syncs all inter-prototype data. Call this when operations adding new prototypes are done.
|
||||
/// </summary>
|
||||
@@ -78,7 +99,6 @@ namespace Robust.Shared.Prototypes
|
||||
void RegisterType(Type protoClass);
|
||||
|
||||
event Action<YamlStream, string>? LoadedData;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -102,25 +122,40 @@ namespace Robust.Shared.Prototypes
|
||||
{
|
||||
[Dependency] private readonly IReflectionManager ReflectionManager = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _dynamicTypeFactory = default!;
|
||||
[Dependency] private readonly IResourceManager _resources = default!;
|
||||
[Dependency] public readonly IResourceManager Resources = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] public readonly ITaskManager TaskManager = default!;
|
||||
[Dependency] public readonly INetManager NetManager = default!;
|
||||
|
||||
private readonly Dictionary<string, Type> prototypeTypes = new();
|
||||
|
||||
private bool _initialized;
|
||||
private bool _hasEverBeenReloaded;
|
||||
private bool _hasEverResynced;
|
||||
|
||||
#region IPrototypeManager members
|
||||
private readonly Dictionary<Type, List<IPrototype>> prototypes = new();
|
||||
private readonly Dictionary<Type, Dictionary<string, IIndexedPrototype>> indexedPrototypes = new();
|
||||
private readonly Dictionary<Type, Dictionary<string, IPrototype>> prototypes = new();
|
||||
|
||||
private readonly HashSet<string> IgnoredPrototypeTypes = new();
|
||||
|
||||
public virtual void Initialize()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(PrototypeManager)} has already been initialized.");
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
public IEnumerable<T> EnumeratePrototypes<T>() where T : class, IPrototype
|
||||
{
|
||||
if (!_hasEverBeenReloaded)
|
||||
{
|
||||
throw new InvalidOperationException("No prototypes have been loaded yet.");
|
||||
}
|
||||
return prototypes[typeof(T)].Select((IPrototype p) => (T)p);
|
||||
|
||||
return prototypes[typeof(T)].Values.Select(p => (T) p);
|
||||
}
|
||||
|
||||
public IEnumerable<IPrototype> EnumeratePrototypes(Type type)
|
||||
@@ -129,10 +164,11 @@ namespace Robust.Shared.Prototypes
|
||||
{
|
||||
throw new InvalidOperationException("No prototypes have been loaded yet.");
|
||||
}
|
||||
return prototypes[type];
|
||||
|
||||
return prototypes[type].Values;
|
||||
}
|
||||
|
||||
public T Index<T>(string id) where T : class, IIndexedPrototype
|
||||
public T Index<T>(string id) where T : class, IPrototype
|
||||
{
|
||||
if (!_hasEverBeenReloaded)
|
||||
{
|
||||
@@ -140,7 +176,7 @@ namespace Robust.Shared.Prototypes
|
||||
}
|
||||
try
|
||||
{
|
||||
return (T)indexedPrototypes[typeof(T)][id];
|
||||
return (T)prototypes[typeof(T)][id];
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
@@ -148,24 +184,60 @@ namespace Robust.Shared.Prototypes
|
||||
}
|
||||
}
|
||||
|
||||
public IIndexedPrototype Index(Type type, string id)
|
||||
public IPrototype Index(Type type, string id)
|
||||
{
|
||||
if (!_hasEverBeenReloaded)
|
||||
{
|
||||
throw new InvalidOperationException("No prototypes have been loaded yet.");
|
||||
}
|
||||
return indexedPrototypes[type][id];
|
||||
|
||||
return prototypes[type][id];
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
prototypes.Clear();
|
||||
prototypeTypes.Clear();
|
||||
indexedPrototypes.Clear();
|
||||
prototypes.Clear();
|
||||
}
|
||||
|
||||
public virtual async void ReloadPrototypes(ResourcePath file)
|
||||
{
|
||||
#if !FULL_RELEASE
|
||||
var changed = await LoadFile(file.ToRootedPath(), true);
|
||||
Resync();
|
||||
|
||||
foreach (var prototype in changed)
|
||||
{
|
||||
if (prototype is not EntityPrototype entityPrototype)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var entity in _entityManager.GetEntities(new PredicateEntityQuery(e => e.Prototype != null && e.Prototype.ID == entityPrototype.ID)))
|
||||
{
|
||||
entityPrototype.UpdateEntity((Entity) entity);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Resync()
|
||||
{
|
||||
// TODO Make this smarter and only resync changed prototypes
|
||||
if (_hasEverResynced)
|
||||
{
|
||||
foreach (var prototypeList in prototypes.Values)
|
||||
{
|
||||
foreach (var prototype in prototypeList.Values)
|
||||
{
|
||||
if (prototype is ISyncingPrototype syncing)
|
||||
{
|
||||
syncing.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Type type in prototypeTypes.Values.Where(t => typeof(ISyncingPrototype).IsAssignableFrom(t)))
|
||||
{
|
||||
// This list is the list of prototypes we're syncing.
|
||||
@@ -177,17 +249,18 @@ namespace Robust.Shared.Prototypes
|
||||
// When we get to the end, do the whole thing again!
|
||||
// Yes this is ridiculously overengineered BUT IT PERFORMS WELL.
|
||||
// I hope.
|
||||
List<ISyncingPrototype> currentRun = prototypes[type].Select(p => (ISyncingPrototype)p).ToList();
|
||||
int stage = 0;
|
||||
List<ISyncingPrototype> currentRun = prototypes[type].Values.Select(p => (ISyncingPrototype) p).ToList();
|
||||
|
||||
var stage = 0;
|
||||
// Outer loop to iterate stages.
|
||||
while (currentRun.Count > 0)
|
||||
{
|
||||
// Increase positions to iterate over list.
|
||||
// If we need to stick, i gets reduced down below.
|
||||
for (int i = 0; i < currentRun.Count; i++)
|
||||
for (var i = 0; i < currentRun.Count; i++)
|
||||
{
|
||||
ISyncingPrototype prototype = currentRun[i];
|
||||
bool result = prototype.Sync(this, stage);
|
||||
var result = prototype.Sync(this, stage);
|
||||
// Keep prototype and move on to next one if it returns true.
|
||||
// Thus it stays in the list for next stage.
|
||||
if (result)
|
||||
@@ -204,80 +277,128 @@ namespace Robust.Shared.Prototypes
|
||||
stage++;
|
||||
}
|
||||
}
|
||||
|
||||
_hasEverResynced = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void LoadDirectory(ResourcePath path)
|
||||
public async Task<List<IPrototype>> LoadDirectory(ResourcePath path)
|
||||
{
|
||||
var sawmill = Logger.GetSawmill("eng");
|
||||
var changedPrototypes = new List<IPrototype>();
|
||||
|
||||
_hasEverBeenReloaded = true;
|
||||
var yamlStreams = _resources.ContentFindFiles(path).ToList().AsParallel()
|
||||
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith("."))
|
||||
.Select(filePath =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using var reader = new StreamReader(_resources.ContentFileRead(filePath), EncodingHelpers.UTF8);
|
||||
var yamlStream = new YamlStream();
|
||||
yamlStream.Load(reader);
|
||||
var streams = Resources.ContentFindFiles(path).ToList().AsParallel()
|
||||
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith("."));
|
||||
|
||||
var result = ((YamlStream? yamlStream, ResourcePath?))(yamlStream, filePath);
|
||||
|
||||
LoadedData?.Invoke(yamlStream, filePath.ToString());
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (YamlException e)
|
||||
{
|
||||
sawmill.Error("YamlException whilst loading prototypes from {0}: {1}", filePath, e.Message);
|
||||
return (null, null);
|
||||
}
|
||||
})
|
||||
.Where(p => p.yamlStream != null) // Filter out loading errors.
|
||||
.ToList();
|
||||
|
||||
foreach (var (stream, filePath) in yamlStreams)
|
||||
foreach (var resourcePath in streams)
|
||||
{
|
||||
for (var i = 0; i < stream!.Documents.Count; i++)
|
||||
var filePrototypes = await LoadFile(resourcePath);
|
||||
changedPrototypes.AddRange(filePrototypes);
|
||||
}
|
||||
|
||||
return changedPrototypes;
|
||||
}
|
||||
|
||||
private Task<StreamReader?> ReadFile(ResourcePath file, bool @throw = true)
|
||||
{
|
||||
var retries = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
var reader = new StreamReader(Resources.ContentFileRead(file), EncodingHelpers.UTF8);
|
||||
|
||||
return Task.FromResult<StreamReader?>(reader);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
if (retries > 10)
|
||||
{
|
||||
LoadFromDocument(stream.Documents[i]);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorS("eng", $"Exception whilst loading prototypes from {filePath}#{i}:\n{e}");
|
||||
if (@throw)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
Logger.Error($"Error reloading prototypes in file {file}.", e);
|
||||
return Task.FromResult<StreamReader?>(null);
|
||||
}
|
||||
|
||||
retries++;
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadFromStream(TextReader stream)
|
||||
public async Task<List<IPrototype>> LoadFile(ResourcePath file, bool overwrite = false)
|
||||
{
|
||||
var changedPrototypes = new List<IPrototype>();
|
||||
|
||||
try
|
||||
{
|
||||
using var reader = await ReadFile(file, !overwrite);
|
||||
var yamlStream = new YamlStream();
|
||||
yamlStream.Load(reader);
|
||||
|
||||
LoadedData?.Invoke(yamlStream, file.ToString());
|
||||
|
||||
for (var i = 0; i < yamlStream.Documents.Count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var documentPrototypes = LoadFromDocument(yamlStream.Documents[i], overwrite);
|
||||
changedPrototypes.AddRange(documentPrototypes);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorS("eng", $"Exception whilst loading prototypes from {file}#{i}:\n{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (YamlException e)
|
||||
{
|
||||
var sawmill = Logger.GetSawmill("eng");
|
||||
sawmill.Error("YamlException whilst loading prototypes from {0}: {1}", file, e.Message);
|
||||
}
|
||||
|
||||
return changedPrototypes;
|
||||
}
|
||||
|
||||
public List<IPrototype> LoadFromStream(TextReader stream)
|
||||
{
|
||||
var changedPrototypes = new List<IPrototype>();
|
||||
_hasEverBeenReloaded = true;
|
||||
var yaml = new YamlStream();
|
||||
yaml.Load(stream);
|
||||
|
||||
for (int i = 0; i < yaml.Documents.Count; i++)
|
||||
for (var i = 0; i < yaml.Documents.Count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadFromDocument(yaml.Documents[i]);
|
||||
var documentPrototypes = LoadFromDocument(yaml.Documents[i]);
|
||||
changedPrototypes.AddRange(documentPrototypes);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new PrototypeLoadException(string.Format("Failed to load prototypes from document#{0}", i), e);
|
||||
throw new PrototypeLoadException($"Failed to load prototypes from document#{i}", e);
|
||||
}
|
||||
}
|
||||
|
||||
LoadedData?.Invoke(yaml, "anonymous prototypes YAML stream");
|
||||
|
||||
return changedPrototypes;
|
||||
}
|
||||
|
||||
public List<IPrototype> LoadString(string str)
|
||||
{
|
||||
return LoadFromStream(new StreamReader(str));
|
||||
}
|
||||
|
||||
#endregion IPrototypeManager members
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
ReflectionManager.OnAssemblyAdded += (_, __) => ReloadPrototypeTypes();
|
||||
ReflectionManager.OnAssemblyAdded += (_, _) => ReloadPrototypeTypes();
|
||||
ReloadPrototypeTypes();
|
||||
}
|
||||
|
||||
@@ -290,9 +411,11 @@ namespace Robust.Shared.Prototypes
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadFromDocument(YamlDocument document)
|
||||
private List<IPrototype> LoadFromDocument(YamlDocument document, bool overwrite = false)
|
||||
{
|
||||
var rootNode = (YamlSequenceNode)document.RootNode;
|
||||
var changedPrototypes = new List<IPrototype>();
|
||||
var rootNode = (YamlSequenceNode) document.RootNode;
|
||||
|
||||
foreach (YamlMappingNode node in rootNode.Cast<YamlMappingNode>())
|
||||
{
|
||||
var type = node.GetNode("type").AsString();
|
||||
@@ -302,38 +425,41 @@ namespace Robust.Shared.Prototypes
|
||||
{
|
||||
continue;
|
||||
}
|
||||
throw new PrototypeLoadException(string.Format("Unknown prototype type: '{0}'", type));
|
||||
|
||||
throw new PrototypeLoadException($"Unknown prototype type: '{type}'");
|
||||
}
|
||||
|
||||
var prototypeType = prototypeTypes[type];
|
||||
var prototype = _dynamicTypeFactory.CreateInstanceUnchecked<IPrototype>(prototypeType);
|
||||
|
||||
prototype.LoadFrom(node);
|
||||
prototypes[prototypeType].Add(prototype);
|
||||
var indexedPrototype = prototype as IIndexedPrototype;
|
||||
if (indexedPrototype != null)
|
||||
changedPrototypes.Add(prototype);
|
||||
|
||||
var id = prototype.ID;
|
||||
|
||||
if (!overwrite && prototypes[prototypeType].ContainsKey(id))
|
||||
{
|
||||
var id = indexedPrototype.ID;
|
||||
if (indexedPrototypes[prototypeType].ContainsKey(id))
|
||||
{
|
||||
throw new PrototypeLoadException(string.Format("Duplicate ID: '{0}'", id));
|
||||
}
|
||||
indexedPrototypes[prototypeType][id] = (IIndexedPrototype)prototype;
|
||||
throw new PrototypeLoadException($"Duplicate ID: '{id}'");
|
||||
}
|
||||
|
||||
prototypes[prototypeType][id] = prototype;
|
||||
}
|
||||
|
||||
return changedPrototypes;
|
||||
}
|
||||
|
||||
public bool HasIndex<T>(string id) where T : IIndexedPrototype
|
||||
public bool HasIndex<T>(string id) where T : IPrototype
|
||||
{
|
||||
if (!indexedPrototypes.TryGetValue(typeof(T), out var index))
|
||||
if (!prototypes.TryGetValue(typeof(T), out var index))
|
||||
{
|
||||
throw new UnknownPrototypeException(id);
|
||||
}
|
||||
return index.ContainsKey(id);
|
||||
}
|
||||
|
||||
public bool TryIndex<T>(string id, [MaybeNullWhen(false)] out T prototype) where T : IIndexedPrototype
|
||||
public bool TryIndex<T>(string id, [MaybeNullWhen(false)] out T prototype) where T : IPrototype
|
||||
{
|
||||
if (!indexedPrototypes.TryGetValue(typeof(T), out var index))
|
||||
if (!prototypes.TryGetValue(typeof(T), out var index))
|
||||
{
|
||||
throw new UnknownPrototypeException(id);
|
||||
}
|
||||
@@ -370,11 +496,10 @@ namespace Robust.Shared.Prototypes
|
||||
}
|
||||
|
||||
prototypeTypes[attribute.Type] = type;
|
||||
prototypes[type] = new List<IPrototype>();
|
||||
|
||||
if (typeof(IIndexedPrototype).IsAssignableFrom(type))
|
||||
if (typeof(IPrototype).IsAssignableFrom(type))
|
||||
{
|
||||
indexedPrototypes[type] = new Dictionary<string, IIndexedPrototype>();
|
||||
prototypes[type] = new Dictionary<string, IPrototype>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,11 +523,6 @@ namespace Robust.Shared.Prototypes
|
||||
public PrototypeLoadException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Robust.Shared.Reflection
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Attribute to search for.</typeparam>
|
||||
/// <returns>Enumeration of all types with the specified attribute.</returns>
|
||||
IEnumerable<Type> FindTypesWithAttribute<T>();
|
||||
IEnumerable<Type> FindTypesWithAttribute<T>() where T : Attribute;
|
||||
|
||||
/// <summary>
|
||||
/// Loads assemblies into the manager and get all the types.
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.Reflection
|
||||
@@ -158,7 +159,7 @@ namespace Robust.Shared.Reflection
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Type> FindTypesWithAttribute<T>()
|
||||
public IEnumerable<Type> FindTypesWithAttribute<T>() where T : Attribute
|
||||
{
|
||||
var types = new List<Type>();
|
||||
|
||||
@@ -217,7 +218,21 @@ namespace Robust.Shared.Reflection
|
||||
Type? found = null;
|
||||
foreach (var derivedType in GetAllChildren(baseType))
|
||||
{
|
||||
if (derivedType.Name == typeName && (derivedType.IsPublic))
|
||||
if (!derivedType.IsPublic)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (derivedType.Name == typeName)
|
||||
{
|
||||
found = derivedType;
|
||||
break;
|
||||
}
|
||||
|
||||
var serializedAttribute = derivedType.GetCustomAttribute<SerializedTypeAttribute>();
|
||||
|
||||
if (serializedAttribute != null &&
|
||||
serializedAttribute.SerializeName == typeName)
|
||||
{
|
||||
found = derivedType;
|
||||
break;
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace Robust.Shared.Serialization
|
||||
[PublicAPI]
|
||||
internal interface IRobustMappedStringSerializer
|
||||
{
|
||||
bool Locked { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The type serializer to register with NetSerializer.
|
||||
/// </summary>
|
||||
|
||||
@@ -133,6 +133,7 @@ namespace Robust.Shared.Serialization
|
||||
| RegexOptions.IgnorePatternWhitespace
|
||||
);
|
||||
|
||||
public bool Locked => _dict.Locked;
|
||||
|
||||
public ITypeSerializer TypeSerializer => this;
|
||||
|
||||
|
||||
18
Robust.Shared/Serialization/SerializedTypeAttribute.cs
Normal file
18
Robust.Shared/Serialization/SerializedTypeAttribute.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.Serialization
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public class SerializedTypeAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of this type in serialized files.
|
||||
/// </summary>
|
||||
public string SerializeName { get; }
|
||||
|
||||
public SerializedTypeAttribute(string serializeName)
|
||||
{
|
||||
SerializeName = serializeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace Robust.Shared.Serialization
|
||||
_formatter = formatter;
|
||||
}
|
||||
|
||||
public override object NodeToType(Type _type, YamlNode node, YamlObjectSerializer serializer)
|
||||
public override object NodeToType(Type type, YamlNode node, YamlObjectSerializer serializer)
|
||||
{
|
||||
return _formatter.FromCustomFormat(serializer.NodeToType(_formatter.Format, node));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
@@ -38,6 +39,7 @@ namespace Robust.Shared.Serialization
|
||||
{
|
||||
{ typeof(Color), new ColorSerializer() },
|
||||
{ typeof(Vector2), new Vector2Serializer() },
|
||||
{ typeof(Vector2i), new Vector2iSerializer() },
|
||||
{ typeof(Vector3), new Vector3Serializer() },
|
||||
{ typeof(Vector4), new Vector4Serializer() },
|
||||
{ typeof(Angle), new AngleSerializer() },
|
||||
@@ -510,6 +512,24 @@ namespace Robust.Shared.Serialization
|
||||
return newList;
|
||||
}
|
||||
|
||||
if (TryGenericImmutableListType(type, out var immutableListType))
|
||||
{
|
||||
var listNode = (YamlSequenceNode) node;
|
||||
var elems = listNode.Children;
|
||||
|
||||
var newList = Array.CreateInstance(immutableListType, elems.Count);
|
||||
|
||||
for (var i = 0; i < elems.Count; i++)
|
||||
{
|
||||
newList.SetValue(NodeToType(immutableListType, elems[i]), i);
|
||||
}
|
||||
|
||||
var list = typeof(ImmutableList);
|
||||
var add = list.GetMethod("CreateRange")!.MakeGenericMethod(immutableListType);
|
||||
|
||||
return add.Invoke(null, new object?[] {newList})!;
|
||||
}
|
||||
|
||||
// Dictionary<K,V>/IReadOnlyDictionary<K,V>
|
||||
if (TryGenericReadDictType(type, out var keyType, out var valType, out var dictType))
|
||||
{
|
||||
@@ -584,12 +604,8 @@ namespace Robust.Shared.Serialization
|
||||
// IExposeData.
|
||||
if (typeof(IExposeData).IsAssignableFrom(type))
|
||||
{
|
||||
if (!(node is YamlMappingNode mapNode))
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot read from IExposeData on non-mapping node. Type: '{type}'");
|
||||
}
|
||||
|
||||
var concreteType = type;
|
||||
|
||||
if (type.IsAbstract || type.IsInterface)
|
||||
{
|
||||
var tag = node.Tag;
|
||||
@@ -607,6 +623,12 @@ namespace Robust.Shared.Serialization
|
||||
}
|
||||
|
||||
var instance = (IExposeData)Activator.CreateInstance(concreteType)!;
|
||||
|
||||
if (node is not YamlMappingNode mapNode)
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
// TODO: Might be worth it to cut down on allocations here by using ourselves instead of creating a fork.
|
||||
// Seems doable.
|
||||
if (_context != null)
|
||||
@@ -639,7 +661,7 @@ namespace Robust.Shared.Serialization
|
||||
throw new ArgumentException($"Type {type.FullName} is not supported.", nameof(type));
|
||||
}
|
||||
|
||||
public T NodeToType<T>(YamlNode node)
|
||||
public T NodeToType<T>(YamlNode node, string name)
|
||||
{
|
||||
return (T) NodeToType(typeof(T), node);
|
||||
}
|
||||
@@ -723,6 +745,28 @@ namespace Robust.Shared.Serialization
|
||||
return node;
|
||||
}
|
||||
|
||||
if (TryGenericImmutableListType(type, out var immutableListType))
|
||||
{
|
||||
var node = new YamlSequenceNode {Tag = TagSkipTag};
|
||||
|
||||
foreach (var entry in (IEnumerable) obj)
|
||||
{
|
||||
if (entry == null)
|
||||
{
|
||||
throw new ArgumentException("Cannot serialize null value inside list.");
|
||||
}
|
||||
|
||||
var entryNode = TypeToNode(entry);
|
||||
|
||||
// write the concrete type tag
|
||||
AssignTag<object?>(immutableListType, entry, null, entryNode);
|
||||
|
||||
node.Add(entryNode);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// Dictionary<K,V>
|
||||
if (TryGenericDictType(type, out var keyType, out var valType)
|
||||
|| TryGenericReadOnlyDictType(type, out keyType, out valType))
|
||||
@@ -861,7 +905,8 @@ namespace Robust.Shared.Serialization
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TryGenericListType(type!, out _))
|
||||
if (TryGenericListType(type!, out _) ||
|
||||
TryGenericImmutableListType(type!, out _))
|
||||
{
|
||||
var listA = (IList) a;
|
||||
var listB = (IList) b!;
|
||||
@@ -1023,6 +1068,21 @@ namespace Robust.Shared.Serialization
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGenericImmutableListType(Type type, [NotNullWhen(true)] out Type? listType)
|
||||
{
|
||||
var isImmutableList = type.GetTypeInfo().IsGenericType &&
|
||||
type.GetGenericTypeDefinition() == typeof(ImmutableList<>);
|
||||
|
||||
if (isImmutableList)
|
||||
{
|
||||
listType = type.GetGenericArguments()[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
listType = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGenericReadOnlyDictType(Type type, [NotNullWhen(true)] out Type? keyType, [NotNullWhen(true)] out Type? valType)
|
||||
{
|
||||
var isDict = type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>);
|
||||
@@ -1287,6 +1347,20 @@ namespace Robust.Shared.Serialization
|
||||
}
|
||||
}
|
||||
|
||||
class Vector2iSerializer : TypeSerializer
|
||||
{
|
||||
public override object NodeToType(Type type, YamlNode node, YamlObjectSerializer serializer)
|
||||
{
|
||||
return node.AsVector2i();
|
||||
}
|
||||
|
||||
public override YamlNode TypeToNode(object obj, YamlObjectSerializer serializer)
|
||||
{
|
||||
var vec = (Vector2i)obj;
|
||||
return new YamlScalarNode($"{vec.X.ToString(CultureInfo.InvariantCulture)},{vec.Y.ToString(CultureInfo.InvariantCulture)}");
|
||||
}
|
||||
}
|
||||
|
||||
class Vector3Serializer : TypeSerializer
|
||||
{
|
||||
public override object NodeToType(Type type, YamlNode node, YamlObjectSerializer serializer)
|
||||
|
||||
@@ -13,7 +13,6 @@ using Robust.Shared.Physics;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Sandboxing;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timers;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared
|
||||
@@ -35,6 +34,7 @@ namespace Robust.Shared
|
||||
IoCManager.Register<ILogManager, LogManager>();
|
||||
IoCManager.Register<IMapManager, MapManager>();
|
||||
IoCManager.Register<IMapManagerInternal, MapManager>();
|
||||
IoCManager.Register<IPauseManager, PauseManager>();
|
||||
IoCManager.Register<IModLoader, ModLoader>();
|
||||
IoCManager.Register<IModLoaderInternal, ModLoader>();
|
||||
IoCManager.Register<INetManager, NetManager>();
|
||||
|
||||
@@ -123,8 +123,6 @@ namespace Robust.Shared.Timing
|
||||
FrameEventArgs realFrameEvent;
|
||||
FrameEventArgs simFrameEvent;
|
||||
|
||||
_timing.ResetRealTime();
|
||||
|
||||
while (Running)
|
||||
{
|
||||
// maximum number of ticks to queue before the loop slows down.
|
||||
|
||||
@@ -92,18 +92,13 @@ namespace Robust.Shared.Timing
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_netManager.IsServer)
|
||||
{
|
||||
return RealTime;
|
||||
}
|
||||
|
||||
var clientNetManager = (IClientNetManager) _netManager;
|
||||
if (clientNetManager.ServerChannel == null)
|
||||
var offset = GetServerOffset();
|
||||
if (offset == null)
|
||||
{
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
return clientNetManager.ServerChannel.RemoteTime;
|
||||
return RealTime + offset.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,15 +231,6 @@ namespace Robust.Shared.Timing
|
||||
_cachedCurTimeInfo = (newTime, CurTick);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the real uptime of the server.
|
||||
/// </summary>
|
||||
public void ResetRealTime()
|
||||
{
|
||||
_realTimer.Restart();
|
||||
_lastRealTime = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the simulation time.
|
||||
/// </summary>
|
||||
@@ -256,6 +242,35 @@ namespace Robust.Shared.Timing
|
||||
Paused = true;
|
||||
}
|
||||
|
||||
public TimeSpan RealLocalToServer(TimeSpan local)
|
||||
{
|
||||
var offset = GetServerOffset();
|
||||
if (offset == null)
|
||||
return TimeSpan.Zero;
|
||||
|
||||
return local + offset.Value;
|
||||
}
|
||||
|
||||
public TimeSpan RealServerToLocal(TimeSpan server)
|
||||
{
|
||||
var offset = GetServerOffset();
|
||||
if (offset == null)
|
||||
return TimeSpan.Zero;
|
||||
|
||||
return server - offset.Value;
|
||||
}
|
||||
|
||||
private TimeSpan? GetServerOffset()
|
||||
{
|
||||
if (_netManager.IsServer)
|
||||
{
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
var clientNetManager = (IClientNetManager) _netManager;
|
||||
return clientNetManager.ServerChannel?.RemoteTimeOffset;
|
||||
}
|
||||
|
||||
public bool IsFirstTimePredicted { get; private set; } = true;
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -123,11 +123,6 @@ namespace Robust.Shared.Timing
|
||||
/// </summary>
|
||||
void StartFrame();
|
||||
|
||||
/// <summary>
|
||||
/// Resets the real uptime of the server.
|
||||
/// </summary>
|
||||
void ResetRealTime();
|
||||
|
||||
/// <summary>
|
||||
/// Is this the first time CurTick has been predicted?
|
||||
/// </summary>
|
||||
@@ -162,5 +157,8 @@ namespace Robust.Shared.Timing
|
||||
/// Resets the simulation time. This should be called on round restarts.
|
||||
/// </summary>
|
||||
void ResetSimTime();
|
||||
|
||||
TimeSpan RealLocalToServer(TimeSpan local);
|
||||
TimeSpan RealServerToLocal(TimeSpan server);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using JetBrains.Annotations;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Robust.Server.Timing
|
||||
namespace Robust.Shared.Timing
|
||||
{
|
||||
public interface IPauseManager
|
||||
{
|
||||
@@ -26,4 +26,4 @@ namespace Robust.Server.Timing
|
||||
[Pure]
|
||||
bool IsMapInitialized(MapId mapId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Threading;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.Timers
|
||||
namespace Robust.Shared.Timing
|
||||
{
|
||||
public interface ITimerManager
|
||||
{
|
||||
179
Robust.Shared/Timing/PauseManager.cs
Normal file
179
Robust.Shared/Timing/PauseManager.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.Timing
|
||||
{
|
||||
internal sealed class PauseManager : IPauseManager, IPostInjectInit
|
||||
{
|
||||
[Dependency] private readonly IConsoleHost _conhost = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
[ViewVariables] private readonly HashSet<MapId> _pausedMaps = new();
|
||||
[ViewVariables] private readonly HashSet<MapId> _unInitializedMaps = new();
|
||||
|
||||
public void SetMapPaused(MapId mapId, bool paused)
|
||||
{
|
||||
if (paused)
|
||||
{
|
||||
_pausedMaps.Add(mapId);
|
||||
|
||||
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
|
||||
{
|
||||
entity.Paused = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_pausedMaps.Remove(mapId);
|
||||
|
||||
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
|
||||
{
|
||||
entity.Paused = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DoMapInitialize(MapId mapId)
|
||||
{
|
||||
if (IsMapInitialized(mapId))
|
||||
throw new ArgumentException("That map is already initialized.");
|
||||
|
||||
_unInitializedMaps.Remove(mapId);
|
||||
|
||||
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
|
||||
{
|
||||
entity.RunMapInit();
|
||||
entity.Paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void DoGridMapInitialize(IMapGrid grid)
|
||||
{
|
||||
DoGridMapInitialize(grid.Index);
|
||||
}
|
||||
|
||||
public void DoGridMapInitialize(GridId gridId)
|
||||
{
|
||||
var mapId = _mapManager.GetGrid(gridId).ParentMapId;
|
||||
|
||||
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
|
||||
{
|
||||
if (entity.Transform.GridID != gridId)
|
||||
continue;
|
||||
|
||||
entity.RunMapInit();
|
||||
entity.Paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddUninitializedMap(MapId mapId)
|
||||
{
|
||||
_unInitializedMaps.Add(mapId);
|
||||
}
|
||||
|
||||
public bool IsMapPaused(MapId mapId)
|
||||
{
|
||||
return _pausedMaps.Contains(mapId) || _unInitializedMaps.Contains(mapId);
|
||||
}
|
||||
|
||||
public bool IsGridPaused(IMapGrid grid)
|
||||
{
|
||||
return IsMapPaused(grid.ParentMapId);
|
||||
}
|
||||
|
||||
public bool IsGridPaused(GridId gridId)
|
||||
{
|
||||
var grid = _mapManager.GetGrid(gridId);
|
||||
return IsGridPaused(grid);
|
||||
}
|
||||
|
||||
public bool IsMapInitialized(MapId mapId)
|
||||
{
|
||||
return !_unInitializedMaps.Contains(mapId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PostInject()
|
||||
{
|
||||
_mapManager.MapDestroyed += (_, args) =>
|
||||
{
|
||||
_pausedMaps.Remove(args.Map);
|
||||
_unInitializedMaps.Add(args.Map);
|
||||
};
|
||||
|
||||
if(_conhost.IsClient)
|
||||
return;
|
||||
|
||||
_conhost.RegisterCommand("pausemap",
|
||||
"Pauses a map, pausing all simulation processing on it.",
|
||||
"pausemap <map ID>",
|
||||
(shell, _, args) =>
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteError("Need to supply a valid MapId");
|
||||
return;
|
||||
}
|
||||
|
||||
string? arg = args[0];
|
||||
var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture));
|
||||
|
||||
if (!_mapManager.MapExists(mapId))
|
||||
{
|
||||
shell.WriteError("That map does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
SetMapPaused(mapId, true);
|
||||
});
|
||||
|
||||
_conhost.RegisterCommand("querymappaused",
|
||||
"Check whether a map is paused or not.",
|
||||
"querymappaused <map ID>",
|
||||
(shell, _, args) =>
|
||||
{
|
||||
string? arg = args[0];
|
||||
var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture));
|
||||
|
||||
if (!_mapManager.MapExists(mapId))
|
||||
{
|
||||
shell.WriteError("That map does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteLine(IsMapPaused(mapId).ToString());
|
||||
});
|
||||
|
||||
_conhost.RegisterCommand("unpausemap",
|
||||
"unpauses a map, resuming all simulation processing on it.",
|
||||
"Usage: unpausemap <map ID>",
|
||||
(shell, _, args) =>
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("Need to supply a valid MapId"));
|
||||
return;
|
||||
}
|
||||
|
||||
string? arg = args[0];
|
||||
var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture));
|
||||
|
||||
if (!_mapManager.MapExists(mapId))
|
||||
{
|
||||
shell.WriteLine("That map does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
SetMapPaused(mapId, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using System.Threading.Tasks;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Shared.Timers
|
||||
namespace Robust.Shared.Timing
|
||||
{
|
||||
public class Timer
|
||||
{
|
||||
@@ -2,9 +2,8 @@
|
||||
using System.Threading;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.Timers
|
||||
namespace Robust.Shared.Timing
|
||||
{
|
||||
internal sealed class TimerManager : ITimerManager
|
||||
{
|
||||
@@ -15,7 +15,8 @@ namespace Robust.Shared.Utility
|
||||
private static readonly Parser<char, char> ParseEscapeSequence =
|
||||
Char('\\').Then(OneOf(
|
||||
Char('\\'),
|
||||
Char(TagBegin)));
|
||||
Char(TagBegin),
|
||||
Char(TagEnd)));
|
||||
|
||||
private static readonly Parser<char, TagText> ParseTagText =
|
||||
ParseEscapeSequence.Or(Token(c => c != TagBegin && c != '\\'))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user