mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5af7e60043 | ||
|
|
ab823dcd12 | ||
|
|
b3976eb8d7 | ||
|
|
91759cdd3c | ||
|
|
b000c3178b | ||
|
|
63b3ecdd13 | ||
|
|
a183a98f75 | ||
|
|
bded7ad228 | ||
|
|
75c4f48496 | ||
|
|
49fe2d59cf | ||
|
|
8a5b7f5146 | ||
|
|
ae4f470e1f |
5
MSBuild/Robust.Analyzers.targets
Normal file
5
MSBuild/Robust.Analyzers.targets
Normal file
@@ -0,0 +1,5 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Analyzers\Robust.Analyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,2 +1,3 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<Import Project="Robust.Analyzers.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<PropertyGroup>
|
||||
<!-- Avoid MSBuild adding a None entry for XAML files because they'd show up TWICE in the project view. -->
|
||||
<DefaultItemExcludes>**/*.xaml</DefaultItemExcludes>
|
||||
<RobustUseExternalMSBuild>true</RobustUseExternalMSBuild>
|
||||
<_RobustUseExternalMSBuild>$(RobustUseExternalMSBuild)</_RobustUseExternalMSBuild>
|
||||
<_RobustUseExternalMSBuild Condition="'$(_RobustForceInternalMSBuild)' == 'true'">false</_RobustUseExternalMSBuild>
|
||||
|
||||
92
Robust.Analyzers/ExplicitInterfaceAnalyzer.cs
Normal file
92
Robust.Analyzers/ExplicitInterfaceAnalyzer.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Document = Microsoft.CodeAnalysis.Document;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
{
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class ExplicitInterfaceAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
public readonly SyntaxKind[] ExcludedModifiers =
|
||||
{
|
||||
SyntaxKind.VirtualKeyword,
|
||||
SyntaxKind.AbstractKeyword,
|
||||
SyntaxKind.OverrideKeyword
|
||||
};
|
||||
|
||||
public const string DiagnosticId = "RA0000";
|
||||
|
||||
private const string Title = "No explicit interface specified";
|
||||
private const string MessageFormat = "No explicit interface specified";
|
||||
private const string Description = "Make sure to specify the interface in your method-declaration.";
|
||||
private const string Category = "Usage";
|
||||
|
||||
[SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
|
||||
|
||||
private const string RequiresExplicitImplementationAttributeMetadataName =
|
||||
"Robust.Shared.Analyzers.RequiresExplicitImplementationAttribute";
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.MethodDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.PropertyDeclaration);
|
||||
}
|
||||
|
||||
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
ISymbol symbol;
|
||||
Location location;
|
||||
switch (context.Node)
|
||||
{
|
||||
//we already have a explicit interface specified, no need to check further
|
||||
case MethodDeclarationSyntax methodDecl when methodDecl.ExplicitInterfaceSpecifier != null || methodDecl.Modifiers.Any(m => ExcludedModifiers.Contains(m.Kind())):
|
||||
return;
|
||||
case PropertyDeclarationSyntax propertyDecl when propertyDecl.ExplicitInterfaceSpecifier != null || propertyDecl.Modifiers.Any(m => ExcludedModifiers.Contains(m.Kind())):
|
||||
return;
|
||||
|
||||
case MethodDeclarationSyntax methodDecl:
|
||||
symbol = context.SemanticModel.GetDeclaredSymbol(methodDecl);
|
||||
location = methodDecl.Identifier.GetLocation();
|
||||
break;
|
||||
case PropertyDeclarationSyntax propertyDecl:
|
||||
symbol = context.SemanticModel.GetDeclaredSymbol(propertyDecl);
|
||||
location = propertyDecl.Identifier.GetLocation();
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
var attrSymbol = context.Compilation.GetTypeByMetadataName(RequiresExplicitImplementationAttributeMetadataName);
|
||||
|
||||
var isInterfaceMember = symbol?.ContainingType.AllInterfaces.Any(
|
||||
i =>
|
||||
i.GetMembers().Any(m => SymbolEqualityComparer.Default.Equals(symbol, symbol.ContainingType.FindImplementationForInterfaceMember(m)))
|
||||
&& i.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol))
|
||||
) ?? false;
|
||||
|
||||
if (isInterfaceMember)
|
||||
{
|
||||
//we do not have an explicit interface specified. bad!
|
||||
var diagnostic = Diagnostic.Create(
|
||||
Rule,
|
||||
location);
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Robust.Analyzers/Robust.Analyzers.csproj
Normal file
13
Robust.Analyzers/Robust.Analyzers.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="3.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
150
Robust.Analyzers/SerializableAnalyzer.cs
Normal file
150
Robust.Analyzers/SerializableAnalyzer.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
{
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class SerializableAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
// Metadata of the analyzer
|
||||
public const string DiagnosticId = "RA0001";
|
||||
|
||||
// You could use LocalizedString but it's a little more complicated for this sample
|
||||
private const string Title = "Class not marked as (Net)Serializable";
|
||||
private const string MessageFormat = "Class not marked as (Net)Serializable";
|
||||
private const string Description = "The class should be marked as (Net)Serializable.";
|
||||
private const string Category = "Usage";
|
||||
|
||||
private const string RequiresSerializableAttributeMetadataName = "Robust.Shared.Analyzers.RequiresSerializableAttribute";
|
||||
private const string SerializableAttributeMetadataName = "System.SerializableAttribute";
|
||||
private const string NetSerializableAttributeMetadataName = "Robust.Shared.Serialization.NetSerializableAttribute";
|
||||
|
||||
[SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration);
|
||||
}
|
||||
|
||||
private bool Marked(INamedTypeSymbol namedTypeSymbol, INamedTypeSymbol attrSymbol)
|
||||
{
|
||||
if (namedTypeSymbol == null) return false;
|
||||
if (namedTypeSymbol.GetAttributes()
|
||||
.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol))) return true;
|
||||
return Marked(namedTypeSymbol.BaseType, attrSymbol);
|
||||
}
|
||||
|
||||
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
var attrSymbol = context.Compilation.GetTypeByMetadataName(RequiresSerializableAttributeMetadataName);
|
||||
var classDecl = (ClassDeclarationSyntax) context.Node;
|
||||
var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDecl);
|
||||
if (classSymbol == null) return;
|
||||
|
||||
if (Marked(classSymbol, attrSymbol))
|
||||
{
|
||||
var attributes = classSymbol.GetAttributes();
|
||||
var serAttr = context.Compilation.GetTypeByMetadataName(SerializableAttributeMetadataName);
|
||||
var netSerAttr = context.Compilation.GetTypeByMetadataName(NetSerializableAttributeMetadataName);
|
||||
|
||||
var hasSerAttr = attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, serAttr));
|
||||
var hasNetSerAttr =
|
||||
attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, netSerAttr));
|
||||
|
||||
if (!hasSerAttr || !hasNetSerAttr)
|
||||
{
|
||||
var requiredAttributes = new List<string>();
|
||||
if(!hasSerAttr) requiredAttributes.Add(SerializableAttributeMetadataName);
|
||||
if(!hasNetSerAttr) requiredAttributes.Add(NetSerializableAttributeMetadataName);
|
||||
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(
|
||||
Rule,
|
||||
classDecl.Identifier.GetLocation(),
|
||||
ImmutableDictionary.CreateRange(new Dictionary<string, string>()
|
||||
{
|
||||
{
|
||||
"requiredAttributes", string.Join(",", requiredAttributes)
|
||||
}
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
||||
public class SerializableCodeFixProvider : CodeFixProvider
|
||||
{
|
||||
private const string Title = "Annotate class as (Net)Serializable.";
|
||||
|
||||
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
|
||||
foreach (var diagnostic in context.Diagnostics)
|
||||
{
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var classDecl = root.FindToken(span.Start).Parent.AncestorsAndSelf().OfType<ClassDeclarationSyntax>().First();
|
||||
|
||||
if(!diagnostic.Properties.TryGetValue("requiredAttributes", out var requiredAttributes)) return;
|
||||
|
||||
context.RegisterCodeFix(
|
||||
CodeAction.Create(
|
||||
Title,
|
||||
c => FixAsync(context.Document, classDecl, requiredAttributes, c),
|
||||
Title),
|
||||
diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Document> FixAsync(Document document, ClassDeclarationSyntax classDecl,
|
||||
string requiredAttributes, CancellationToken cancellationToken)
|
||||
{
|
||||
var attributes = new List<AttributeSyntax>();
|
||||
var namespaces = new List<string>();
|
||||
foreach (var attribute in requiredAttributes.Split(','))
|
||||
{
|
||||
var tempSplit = attribute.Split('.');
|
||||
namespaces.Add(string.Join(".",tempSplit.Take(tempSplit.Length-1)));
|
||||
var @class = tempSplit.Last();
|
||||
@class = @class.Substring(0, @class.Length - 9); //cut out "Attribute" at the end
|
||||
attributes.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName(@class)));
|
||||
}
|
||||
|
||||
var newClassDecl =
|
||||
classDecl.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(attributes)));
|
||||
|
||||
var root = (CompilationUnitSyntax) await document.GetSyntaxRootAsync(cancellationToken);
|
||||
root = root.ReplaceNode(classDecl, newClassDecl);
|
||||
|
||||
foreach (var ns in namespaces)
|
||||
{
|
||||
if(root.Usings.Any(u => u.Name.ToString() == ns)) continue;
|
||||
root = root.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(ns)));
|
||||
}
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(SerializableAnalyzer.DiagnosticId);
|
||||
|
||||
public override FixAllProvider GetFixAllProvider()
|
||||
{
|
||||
return WellKnownFixAllProviders.BatchFixer;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using Robust.Client.Log;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
@@ -14,13 +13,17 @@ namespace Robust.Client.Console
|
||||
{
|
||||
public class AddStringArgs : EventArgs
|
||||
{
|
||||
public Color Color { get; }
|
||||
public string Text { get; }
|
||||
|
||||
public AddStringArgs(string text, Color color)
|
||||
public bool Local { get; }
|
||||
|
||||
public bool Error { get; }
|
||||
|
||||
public AddStringArgs(string text, bool local, bool error)
|
||||
{
|
||||
Text = text;
|
||||
Color = color;
|
||||
Local = local;
|
||||
Error = error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +40,6 @@ namespace Robust.Client.Console
|
||||
/// <inheritdoc cref="IClientConsoleHost" />
|
||||
internal class ClientConsoleHost : ConsoleHost, IClientConsoleHost
|
||||
{
|
||||
private static readonly Color _msgColor = new(65, 105, 225);
|
||||
|
||||
private bool _requestedCommands;
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -63,46 +64,51 @@ namespace Robust.Client.Console
|
||||
SendServerCommandRequest();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<AddStringArgs>? AddString;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<AddFormattedMessageArgs>? AddFormatted;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddFormattedLine(FormattedMessage message)
|
||||
{
|
||||
AddFormatted?.Invoke(this, new AddFormattedMessageArgs(message));
|
||||
}
|
||||
|
||||
public override void WriteLine(ICommonSession? session, string text, Color color)
|
||||
/// <inheritdoc />
|
||||
public override void WriteError(ICommonSession? session, string text)
|
||||
{
|
||||
AddString?.Invoke(this, new AddStringArgs(text, color));
|
||||
OutputText(text, true, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ExecuteCommand(ICommonSession? session, string command)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(command))
|
||||
return;
|
||||
|
||||
// echo the command locally
|
||||
WriteLine(null, "> " + command, Color.Lime);
|
||||
WriteError(null, "> " + command);
|
||||
|
||||
//Commands are processed locally and then sent to the server to be processed there again.
|
||||
var args = new List<string>();
|
||||
|
||||
CommandParsing.ParseArguments(command, args);
|
||||
|
||||
var commandname = args[0];
|
||||
var commandName = args[0];
|
||||
|
||||
if (AvailableCommands.ContainsKey(commandname))
|
||||
if (AvailableCommands.ContainsKey(commandName))
|
||||
{
|
||||
var command1 = AvailableCommands[commandname];
|
||||
var command1 = AvailableCommands[commandName];
|
||||
args.RemoveAt(0);
|
||||
command1.Execute(new ConsoleShell(this, null), command, args.ToArray());
|
||||
}
|
||||
else if (!NetManager.IsConnected) WriteLine(null, "Unknown command: " + commandname, Color.Red);
|
||||
else if (!NetManager.IsConnected)
|
||||
WriteError(null, "Unknown command: " + commandName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a command directly to the server.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public override void RemoteExecuteCommand(ICommonSession? session, string command)
|
||||
{
|
||||
if (!NetManager.IsConnected)
|
||||
@@ -113,9 +119,10 @@ namespace Robust.Client.Console
|
||||
NetManager.ClientSendMessage(msg);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteLine(ICommonSession? session, string text)
|
||||
{
|
||||
WriteLine(null, text, Color.White);
|
||||
OutputText(text, true, false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -124,6 +131,11 @@ namespace Robust.Client.Console
|
||||
// We don't have anything to dispose.
|
||||
}
|
||||
|
||||
private void OutputText(string text, bool local, bool error)
|
||||
{
|
||||
AddString?.Invoke(this, new AddStringArgs(text, local, error));
|
||||
}
|
||||
|
||||
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)
|
||||
{
|
||||
SendServerCommandRequest();
|
||||
@@ -131,14 +143,14 @@ namespace Robust.Client.Console
|
||||
|
||||
private void HandleConCmdAck(MsgConCmdAck msg)
|
||||
{
|
||||
WriteLine(null, "< " + msg.Text, _msgColor);
|
||||
OutputText("< " + msg.Text, false, msg.Error);
|
||||
}
|
||||
|
||||
private void HandleConCmdReg(MsgConCmdReg msg)
|
||||
{
|
||||
foreach (var cmd in msg.Commands)
|
||||
{
|
||||
var commandName = cmd.Name;
|
||||
string? commandName = cmd.Name;
|
||||
|
||||
// Do not do duplicate commands.
|
||||
if (AvailableCommands.ContainsKey(commandName))
|
||||
@@ -176,12 +188,6 @@ namespace Robust.Client.Console
|
||||
[Reflect(false)]
|
||||
internal class ServerDummyCommand : IConsoleCommand
|
||||
{
|
||||
public string Command { get; }
|
||||
|
||||
public string Description { get; }
|
||||
|
||||
public string Help { get; }
|
||||
|
||||
internal ServerDummyCommand(string command, string help, string description)
|
||||
{
|
||||
Command = command;
|
||||
@@ -189,6 +195,12 @@ namespace Robust.Client.Console
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public string Command { get; }
|
||||
|
||||
public string Description { get; }
|
||||
|
||||
public string Help { get; }
|
||||
|
||||
// Always forward to server.
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
if (args.Length < 1 || args.Length > 2)
|
||||
{
|
||||
shell.WriteLine("Must provide exactly one or two arguments.", Color.Red);
|
||||
shell.WriteError("Must provide exactly one or two arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
if (!configManager.IsCVarRegistered(name))
|
||||
{
|
||||
shell.WriteLine($"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.", Color.Red);
|
||||
shell.WriteError($"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,9 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
Color[] colors = { Color.Green, Color.Blue, Color.Red };
|
||||
var random = IoCManager.Resolve<IRobustRandom>();
|
||||
for (int x = 0; x < 50; x++)
|
||||
{
|
||||
shell.WriteLine("filling...", colors[random.Next(0, colors.Length)]);
|
||||
shell.WriteLine("filling...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
foreach (var e in entityManager.GetEntities().OrderBy(e => e.Uid))
|
||||
{
|
||||
shell.WriteLine($"entity {e.Uid}, {e.Prototype?.ID}, {e.Transform.Coordinates}.", Color.White);
|
||||
shell.WriteLine($"entity {e.Uid}, {e.Prototype?.ID}, {e.Transform.Coordinates}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,16 +92,16 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
message.Append($", NSE: {registration.NetworkSynchronizeExistence}, references:");
|
||||
|
||||
shell.WriteLine(message.ToString(), Color.White);
|
||||
shell.WriteLine(message.ToString());
|
||||
|
||||
foreach (var type in registration.References)
|
||||
{
|
||||
shell.WriteLine($" {type}", Color.White);
|
||||
shell.WriteLine($" {type}");
|
||||
}
|
||||
}
|
||||
catch (UnknownComponentException)
|
||||
{
|
||||
shell.WriteLine($"No registration found for '{args[0]}'", Color.Red);
|
||||
shell.WriteError($"No registration found for '{args[0]}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,13 +215,13 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
if (!int.TryParse(args[0], out var id))
|
||||
{
|
||||
shell.WriteLine($"{args[0]} is not a valid integer.",Color.Red);
|
||||
shell.WriteError($"{args[0]} is not a valid integer.");
|
||||
return;
|
||||
}
|
||||
|
||||
var mgr = IoCManager.Resolve<IDebugDrawingManager>();
|
||||
mgr.DebugDrawRays = !mgr.DebugDrawRays;
|
||||
shell.WriteLine("Toggled showing rays to:" + mgr.DebugDrawRays.ToString(), Color.Green);
|
||||
shell.WriteError("Toggled showing rays to:" + mgr.DebugDrawRays.ToString());
|
||||
mgr.DebugRayLifetime = TimeSpan.FromSeconds((double)int.Parse(args[0], CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
@@ -257,7 +257,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
if ((!new Regex(@"^c?[0-9]+$").IsMatch(args[0])))
|
||||
{
|
||||
shell.WriteLine("Malformed UID", Color.Red);
|
||||
shell.WriteError("Malformed UID");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ namespace Robust.Client.Console.Commands
|
||||
var entmgr = IoCManager.Resolve<IEntityManager>();
|
||||
if (!entmgr.TryGetEntity(uid, out var entity))
|
||||
{
|
||||
shell.WriteLine("That entity does not exist. Sorry lad.", Color.Red);
|
||||
shell.WriteError("That entity does not exist. Sorry lad.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -310,13 +310,13 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
if (!int.TryParse(args[0], out var id))
|
||||
{
|
||||
shell.WriteLine($"{args[0]} is not a valid integer.",Color.Red);
|
||||
shell.WriteError($"{args[0]} is not a valid integer.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!new Regex(@"^-?[0-9]+,-?[0-9]+$").IsMatch(indices))
|
||||
{
|
||||
shell.WriteLine("mapIndicies must be of form x<int>,y<int>", Color.Red);
|
||||
shell.WriteError("mapIndicies must be of form x<int>,y<int>");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
else
|
||||
{
|
||||
shell.WriteLine("given offset type is not defined", Color.Red);
|
||||
shell.WriteError("given offset type is not defined");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
else
|
||||
{
|
||||
shell.WriteLine("grid does not exist", Color.Red);
|
||||
shell.WriteError("grid does not exist");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,7 +368,7 @@ namespace Robust.Client.Console.Commands
|
||||
var client = IoCManager.Resolve<IBaseClient>();
|
||||
client.PlayerNameOverride = args[0];
|
||||
|
||||
shell.WriteLine($"Overriding player name to \"{args[0]}\".", Color.White);
|
||||
shell.WriteLine($"Overriding player name to \"{args[0]}\".");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,7 +395,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
catch(ArgumentException)
|
||||
{
|
||||
shell.WriteLine("Unable to find type", Color.Red);
|
||||
shell.WriteError("Unable to find type");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -432,7 +432,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
catch(ArgumentException)
|
||||
{
|
||||
shell.WriteLine("Unable to find type", Color.Red);
|
||||
shell.WriteError("Unable to find type");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -472,7 +472,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
else
|
||||
{
|
||||
shell.WriteLine($"No grid exists with id {id}",Color.Red);
|
||||
shell.WriteError($"No grid exists with id {id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -759,7 +759,7 @@ namespace Robust.Client.Console.Commands
|
||||
if (int.TryParse(args[0], out int result))
|
||||
GC.Collect(result);
|
||||
else
|
||||
shell.WriteLine("Failed to parse argument.",Color.Red);
|
||||
shell.WriteError("Failed to parse argument.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
shell.WriteLine("That type does not exist", Color.Red);
|
||||
shell.WriteError("That type does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Robust.Client.Console.Commands
|
||||
if (!IoCManager.Resolve<IClientNetManager>().IsConnected)
|
||||
{
|
||||
// No server so nothing to respond with unknown command.
|
||||
shell.WriteLine("Unknown command: " + commandname, Color.Red);
|
||||
shell.WriteError("Unknown command: " + commandname);
|
||||
return;
|
||||
}
|
||||
// TODO: Maybe have a server side help?
|
||||
@@ -39,7 +39,7 @@ namespace Robust.Client.Console.Commands
|
||||
break;
|
||||
|
||||
default:
|
||||
shell.WriteLine("Invalid amount of arguments.", Color.Red);
|
||||
shell.WriteError("Invalid amount of arguments.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
shell.WriteLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
|
||||
shell.WriteError("Invalid argument amount. Expected 2 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
if (args.Length != 3)
|
||||
{
|
||||
shell.WriteLine("Invalid argument amount. Expected 3 arguments.", Color.Red);
|
||||
shell.WriteError("Invalid argument amount. Expected 3 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Robust.Client.Console.Commands
|
||||
var mgr = IoCManager.Resolve<IScriptClient>();
|
||||
if (!mgr.CanScript)
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("You do not have server side scripting permission."), Color.Red);
|
||||
shell.WriteError(Loc.GetString("You do not have server side scripting permission."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -356,4 +356,76 @@ namespace Robust.Client.GameObjects.EntitySystems
|
||||
|
||||
event Action PlaybackDone;
|
||||
}
|
||||
|
||||
public static class AudioSystemExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file following an entity.
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="entity">The entity "emitting" the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
|
||||
public static IPlayingAudioStream? Play(
|
||||
this IEntity entity,
|
||||
string filename,
|
||||
AudioParams? audioParams,
|
||||
AudioSystem? audioSystem = null)
|
||||
{
|
||||
audioSystem ??= EntitySystem.Get<AudioSystem>();
|
||||
return audioSystem.Play(filename, entity, audioParams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio stream following an entity.
|
||||
/// </summary>
|
||||
/// <param name="stream">The audio stream to play.</param>
|
||||
/// <param name="entity">The entity "emitting" the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
|
||||
public static IPlayingAudioStream? Play(
|
||||
this IEntity entity,
|
||||
AudioStream stream,
|
||||
AudioParams? audioParams = null,
|
||||
AudioSystem? audioSystem = null)
|
||||
{
|
||||
audioSystem ??= EntitySystem.Get<AudioSystem>();
|
||||
return audioSystem.Play(stream, entity, audioParams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file at a static position.
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
|
||||
public static IPlayingAudioStream? Play(
|
||||
this EntityCoordinates coordinates,
|
||||
string filename,
|
||||
AudioParams? audioParams = null,
|
||||
AudioSystem? audioSystem = null)
|
||||
{
|
||||
audioSystem ??= EntitySystem.Get<AudioSystem>();
|
||||
return audioSystem.Play(filename, coordinates, audioParams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio stream at a static position.
|
||||
/// </summary>
|
||||
/// <param name="stream">The audio stream to play.</param>
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
|
||||
public static IPlayingAudioStream? Play(
|
||||
this EntityCoordinates coordinates,
|
||||
AudioStream stream,
|
||||
AudioParams? audioParams = null,
|
||||
AudioSystem? audioSystem = null)
|
||||
{
|
||||
audioSystem ??= EntitySystem.Get<AudioSystem>();
|
||||
return audioSystem.Play(stream, coordinates, audioParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,13 +233,13 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
|
||||
shell.WriteError("Invalid argument amount. Expected 2 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!byte.TryParse(args[0], out var iValue))
|
||||
{
|
||||
shell.WriteLine("Invalid argument: Needs to be 0 or 1.", Color.Red);
|
||||
shell.WriteError("Invalid argument: Needs to be 0 or 1.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
|
||||
shell.WriteError("Invalid argument amount. Expected 2 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
|
||||
shell.WriteError("Invalid argument amount. Expected 2 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Robust.Client.Interfaces.Input
|
||||
public bool CanRepeat;
|
||||
public bool AllowSubCombs;
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref Function, "function", default);
|
||||
serializer.DataField(ref Type, "type", KeyBindingType.State);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -94,7 +94,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
CommandBar.OnTextEntered += CommandEntered;
|
||||
CommandBar.OnHistoryChanged += OnHistoryChanged;
|
||||
|
||||
_consoleHost.AddString += (_, args) => AddLine(args.Text, args.Color);
|
||||
_consoleHost.AddString += (_, args) => AddLine(args.Text, DetermineColor(args.Local, args.Error));
|
||||
_consoleHost.AddFormatted += (_, args) => AddFormattedLine(args.Message);
|
||||
_consoleHost.ClearText += (_, args) => Clear();
|
||||
|
||||
@@ -103,6 +103,11 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
searchResults = new List<string>();
|
||||
}
|
||||
|
||||
private Color DetermineColor(bool local, bool error)
|
||||
{
|
||||
return Color.White;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
@@ -5,7 +5,6 @@ using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -15,9 +14,9 @@ namespace Robust.Server.Console
|
||||
/// <inheritdoc cref="IServerConsoleHost" />
|
||||
internal class ServerConsoleHost : ConsoleHost, IServerConsoleHost
|
||||
{
|
||||
[Dependency] private readonly IConGroupController _groupController = default!;
|
||||
[Dependency] private readonly IPlayerManager _players = default!;
|
||||
[Dependency] private readonly ISystemConsoleManager _systemConsole = default!;
|
||||
[Dependency] private readonly IConGroupController _groupController = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ExecuteCommand(ICommonSession? session, string command)
|
||||
@@ -26,6 +25,53 @@ namespace Robust.Server.Console
|
||||
ExecuteInShell(shell, command);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void RemoteExecuteCommand(ICommonSession? session, string command)
|
||||
{
|
||||
//TODO: Server -> Client remote execute, just like how the client forwards the command
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteLine(ICommonSession? session, string text)
|
||||
{
|
||||
if (session is IPlayerSession playerSession)
|
||||
OutputText(playerSession, text, false);
|
||||
else
|
||||
OutputText(null, text, false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteError(ICommonSession? session, string text)
|
||||
{
|
||||
if (session is IPlayerSession playerSession)
|
||||
OutputText(playerSession, text, true);
|
||||
else
|
||||
OutputText(null, text, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
RegisterCommand("sudo", "sudo make me a sandwich", "sudo <command>",(shell, _, args) =>
|
||||
{
|
||||
string command = args[0];
|
||||
var cArgs = args[1..].Select(CommandParsing.Escape);
|
||||
|
||||
var localShell = shell.ConsoleHost.LocalShell;
|
||||
var sudoShell = new SudoShell(this, localShell, shell);
|
||||
ExecuteInShell(sudoShell, $"{command} {string.Join(' ', cArgs)}");
|
||||
});
|
||||
|
||||
LoadConsoleCommands();
|
||||
|
||||
// setup networking with clients
|
||||
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
|
||||
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME);
|
||||
|
||||
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME,
|
||||
message => HandleRegistrationRequest(message.MsgChannel));
|
||||
}
|
||||
|
||||
private void ExecuteInShell(IConsoleShell shell, string command)
|
||||
{
|
||||
try
|
||||
@@ -37,7 +83,7 @@ namespace Robust.Server.Console
|
||||
if (args.Count == 0)
|
||||
return;
|
||||
|
||||
var cmdName = args[0];
|
||||
string? cmdName = args[0];
|
||||
|
||||
if (AvailableCommands.TryGetValue(cmdName, out var conCmd)) // command registered
|
||||
{
|
||||
@@ -49,7 +95,7 @@ namespace Robust.Server.Console
|
||||
conCmd.Execute(shell, command, args.ToArray());
|
||||
}
|
||||
else
|
||||
shell.WriteLine($"Unknown command: '{cmdName}'");
|
||||
shell.WriteError($"Unknown command: '{cmdName}'");
|
||||
}
|
||||
else // system console
|
||||
{
|
||||
@@ -58,56 +104,15 @@ namespace Robust.Server.Console
|
||||
}
|
||||
}
|
||||
else
|
||||
shell.WriteLine($"Unknown command: '{cmdName}'");
|
||||
shell.WriteError($"Unknown command: '{cmdName}'");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogManager.GetSawmill(SawmillName).Warning($"{FormatPlayerString(shell.Player)}: ExecuteError - {command}:\n{e}");
|
||||
shell.WriteLine($"There was an error while executing the command: {e}");
|
||||
shell.WriteError($"There was an error while executing the command: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
public override void RemoteExecuteCommand(ICommonSession? session, string command)
|
||||
{
|
||||
//TODO: Server -> Client remote execute, just like how the client forwards the command
|
||||
}
|
||||
|
||||
public override void WriteLine(ICommonSession? session, string text)
|
||||
{
|
||||
if (session is IPlayerSession playerSession)
|
||||
SendText(playerSession, text);
|
||||
else
|
||||
SendText(null as IPlayerSession, text);
|
||||
}
|
||||
|
||||
public override void WriteLine(ICommonSession? session, string text, Color color)
|
||||
{
|
||||
//TODO: Make colors work.
|
||||
WriteLine(session, text);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
RegisterCommand("sudo", "sudo make me a sandwich", "sudo <command>", (shell, _, args) =>
|
||||
{
|
||||
var command = args[0];
|
||||
var cArgs = args[1..].Select(CommandParsing.Escape);
|
||||
|
||||
var localShell = shell.ConsoleHost.LocalShell;
|
||||
var sudoShell = new SudoShell(this, localShell, shell);
|
||||
ExecuteInShell(sudoShell, $"{command} {string.Join(' ', cArgs)}");
|
||||
});
|
||||
|
||||
LoadConsoleCommands();
|
||||
|
||||
// setup networking with clients
|
||||
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
|
||||
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME);
|
||||
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME,
|
||||
message => HandleRegistrationRequest(message.MsgChannel));
|
||||
}
|
||||
|
||||
private void HandleRegistrationRequest(INetChannel senderConnection)
|
||||
{
|
||||
var netMgr = IoCManager.Resolve<IServerNetManager>();
|
||||
@@ -115,6 +120,7 @@ namespace Robust.Server.Console
|
||||
|
||||
var counter = 0;
|
||||
message.Commands = new MsgConCmdReg.Command[RegisteredCommands.Count];
|
||||
|
||||
foreach (var command in RegisteredCommands.Values)
|
||||
{
|
||||
message.Commands[counter++] = new MsgConCmdReg.Command
|
||||
@@ -130,7 +136,7 @@ namespace Robust.Server.Console
|
||||
|
||||
private void ProcessCommand(MsgConCmd message)
|
||||
{
|
||||
var text = message.Text;
|
||||
string? text = message.Text;
|
||||
var sender = message.MsgChannel;
|
||||
var session = _players.GetSessionByChannel(sender);
|
||||
|
||||
@@ -139,34 +145,19 @@ namespace Robust.Server.Console
|
||||
ExecuteCommand(session, text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a text string to the remote player.
|
||||
/// </summary>
|
||||
/// <param name="session">
|
||||
/// Remote player to send the text message to. If this is null, the text is sent to the local
|
||||
/// console.
|
||||
/// </param>
|
||||
/// <param name="text">Text message to send.</param>
|
||||
private void SendText(IPlayerSession? session, string text)
|
||||
private void OutputText(IPlayerSession? session, string text, bool error)
|
||||
{
|
||||
if (session != null)
|
||||
SendText(session.ConnectedClient, text);
|
||||
{
|
||||
var replyMsg = NetManager.CreateNetMessage<MsgConCmdAck>();
|
||||
replyMsg.Error = error;
|
||||
replyMsg.Text = text;
|
||||
NetManager.ServerSendMessage(replyMsg, session.ConnectedClient);
|
||||
}
|
||||
else
|
||||
_systemConsole.Print(text + "\n");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a text string to the remote console.
|
||||
/// </summary>
|
||||
/// <param name="target">Net channel to send the text string to.</param>
|
||||
/// <param name="text">Text message to send.</param>
|
||||
private void SendText(INetChannel target, string text)
|
||||
{
|
||||
var replyMsg = NetManager.CreateNetMessage<MsgConCmdAck>();
|
||||
replyMsg.Text = text;
|
||||
NetManager.ServerSendMessage(replyMsg, target);
|
||||
}
|
||||
|
||||
private static string FormatPlayerString(IBaseSession? session)
|
||||
{
|
||||
return session != null ? $"{session.Name}" : "[HOST]";
|
||||
@@ -205,10 +196,10 @@ namespace Robust.Server.Console
|
||||
_sudoer.WriteLine(text);
|
||||
}
|
||||
|
||||
public void WriteLine(string text, Color color)
|
||||
public void WriteError(string text)
|
||||
{
|
||||
_owner.WriteLine(text, color);
|
||||
_sudoer.WriteLine(text, color);
|
||||
_owner.WriteError(text);
|
||||
_sudoer.WriteError(text);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
|
||||
@@ -289,7 +289,7 @@ namespace Robust.Server.GameObjects.Components.Container
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref Entities, "entities", new List<EntityUid>());
|
||||
serializer.DataField(ref Type, "type", null);
|
||||
|
||||
@@ -7,6 +7,7 @@ using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using static Robust.Server.GameObjects.EntitySystems.AudioSystem;
|
||||
|
||||
namespace Robust.Server.GameObjects.EntitySystems
|
||||
{
|
||||
@@ -43,13 +44,15 @@ namespace Robust.Server.GameObjects.EntitySystems
|
||||
Identifier = id
|
||||
};
|
||||
|
||||
if(sessions == null)
|
||||
if (sessions == null)
|
||||
RaiseNetworkEvent(msg);
|
||||
else
|
||||
{
|
||||
foreach (var session in sessions)
|
||||
{
|
||||
RaiseNetworkEvent(msg, session.ConnectedClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private uint CacheIdentifier()
|
||||
@@ -90,7 +93,7 @@ namespace Robust.Server.GameObjects.EntitySystems
|
||||
players.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
RaiseNetworkEvent(msg, player.ConnectedClient);
|
||||
}
|
||||
|
||||
@@ -135,7 +138,7 @@ namespace Robust.Server.GameObjects.EntitySystems
|
||||
players.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
RaiseNetworkEvent(msg, player.ConnectedClient);
|
||||
}
|
||||
|
||||
@@ -177,7 +180,7 @@ namespace Robust.Server.GameObjects.EntitySystems
|
||||
players.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
RaiseNetworkEvent(msg, player.ConnectedClient);
|
||||
}
|
||||
|
||||
@@ -223,4 +226,49 @@ namespace Robust.Server.GameObjects.EntitySystems
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class AudioSystemExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Play an audio file following an entity.
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="entity">The entity "emitting" the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
/// <param name="range">The max range at which the audio will be heard. Less than or equal to 0 to send to every player.</param>
|
||||
/// <param name="excludedSession">Sessions that won't receive the audio message.</param>
|
||||
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
|
||||
public static void PlaySoundFrom(
|
||||
this IEntity entity,
|
||||
string filename,
|
||||
AudioParams? audioParams = null,
|
||||
int range = AudioDistanceRange,
|
||||
IPlayerSession? excludedSession = null,
|
||||
AudioSystem? audioSystem = null)
|
||||
{
|
||||
audioSystem ??= EntitySystem.Get<AudioSystem>();
|
||||
audioSystem.PlayFromEntity(filename, entity, audioParams, range, excludedSession);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file at a static position.
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
/// <param name="range">The max range at which the audio will be heard. Less than or equal to 0 to send to every player.</param>
|
||||
/// <param name="excludedSession">Session that won't receive the audio message.</param>
|
||||
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
|
||||
public static void PlaySoundFrom(
|
||||
this EntityCoordinates coordinates,
|
||||
string filename,
|
||||
AudioParams? audioParams = null,
|
||||
int range = AudioDistanceRange,
|
||||
IPlayerSession? excludedSession = null,
|
||||
AudioSystem? audioSystem = null)
|
||||
{
|
||||
audioSystem ??= EntitySystem.Get<AudioSystem>();
|
||||
audioSystem.PlayAtCoords(filename, coordinates, audioParams, range, excludedSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.Analyzers
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Interface)]
|
||||
public class RequiresExplicitImplementationAttribute : Attribute { }
|
||||
}
|
||||
7
Robust.Shared/Analyzers/RequiresSerializableAttribute.cs
Normal file
7
Robust.Shared/Analyzers/RequiresSerializableAttribute.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.Analyzers
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class RequiresSerializableAttribute : Attribute { }
|
||||
}
|
||||
@@ -55,7 +55,7 @@ namespace Robust.Shared.Audio
|
||||
/// </summary>
|
||||
public static readonly AudioParams Default = new(0, 1, "Master", 62.5f, 1, AudioMixTarget.Stereo, false, 0f);
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
Volume = serializer.ReadDataField("volume", 0f);
|
||||
PitchScale = serializer.ReadDataField("pitchscale", 1f);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Interfaces.Resources;
|
||||
@@ -23,14 +23,14 @@ namespace Robust.Shared.Console.Commands
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
shell.WriteLine("No file specified!", Color.Red);
|
||||
shell.WriteError("No file specified!");
|
||||
return;
|
||||
}
|
||||
|
||||
var path = new ResourcePath(args[0]).ToRootedPath();
|
||||
if (!res.UserData.Exists(path))
|
||||
{
|
||||
shell.WriteLine("File does not exist.", Color.Red);
|
||||
shell.WriteError("File does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Robust.Shared.Console
|
||||
|
||||
//TODO: IConsoleOutput for [e#1225]
|
||||
public abstract void WriteLine(ICommonSession? session, string text);
|
||||
public abstract void WriteLine(ICommonSession? session, string text, Color color);
|
||||
public abstract void WriteError(ICommonSession? session, string text);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearLocalConsole()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
namespace Robust.Shared.Console
|
||||
@@ -6,6 +5,18 @@ namespace Robust.Shared.Console
|
||||
/// <inheritdoc />
|
||||
public class ConsoleShell : IConsoleShell
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="ConsoleShell"/>.
|
||||
/// </summary>
|
||||
/// <param name="host">Console Host that owns this shell.</param>
|
||||
/// <param name="session">Player Session that this shell represents. If this is null, then
|
||||
/// the shell is representing the local console.</param>
|
||||
public ConsoleShell(IConsoleHost host, ICommonSession? session)
|
||||
{
|
||||
ConsoleHost = host;
|
||||
Player = session;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IConsoleHost ConsoleHost { get; }
|
||||
|
||||
@@ -15,12 +26,6 @@ namespace Robust.Shared.Console
|
||||
/// <inheritdoc />
|
||||
public ICommonSession? Player { get; }
|
||||
|
||||
public ConsoleShell(IConsoleHost host, ICommonSession? session)
|
||||
{
|
||||
ConsoleHost = host;
|
||||
Player = session;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ExecuteCommand(string command)
|
||||
{
|
||||
@@ -40,9 +45,9 @@ namespace Robust.Shared.Console
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void WriteLine(string text, Color color)
|
||||
public void WriteError(string text)
|
||||
{
|
||||
ConsoleHost.WriteLine(Player, text, color);
|
||||
ConsoleHost.WriteError(Player, text);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -110,8 +110,7 @@ namespace Robust.Shared.Console
|
||||
/// console.
|
||||
/// </param>
|
||||
/// <param name="text">Text message to send.</param>
|
||||
/// <param name="color">Foreground color of the text.</param>
|
||||
void WriteLine(ICommonSession? session, string text, Color color);
|
||||
void WriteError(ICommonSession? session, string text);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all text from the local console.
|
||||
|
||||
@@ -56,11 +56,10 @@ namespace Robust.Shared.Console
|
||||
void WriteLine(string text);
|
||||
|
||||
/// <summary>
|
||||
/// Write a line with a specific color to the console window.
|
||||
/// Write an error line to the console window.
|
||||
/// </summary>
|
||||
/// <param name="text">Line of text to write.</param>
|
||||
/// <param name="color">Foreground color of the string of text.</param>
|
||||
void WriteLine(string text, Color color);
|
||||
void WriteError(string text);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the entire console of text.
|
||||
|
||||
@@ -45,6 +45,11 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, ComponentRegistration> names = new();
|
||||
|
||||
/// <summary>
|
||||
/// Mapping of lowercase component names to their registration.
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, string> _lowerCaseNames = new();
|
||||
|
||||
/// <summary>
|
||||
/// Mapping of network ID to type.
|
||||
/// </summary>
|
||||
@@ -70,7 +75,7 @@ namespace Robust.Shared.GameObjects
|
||||
Register(typeof(T), overwrite);
|
||||
}
|
||||
|
||||
private void Register(Type type, bool overwrite=false)
|
||||
private void Register(Type type, bool overwrite = false)
|
||||
{
|
||||
if (types.ContainsKey(type))
|
||||
{
|
||||
@@ -82,6 +87,7 @@ namespace Robust.Shared.GameObjects
|
||||
var dummy = (IComponent)Activator.CreateInstance(type)!;
|
||||
|
||||
var name = dummy.Name;
|
||||
var lowerCaseName = name.ToLowerInvariant();
|
||||
var netID = dummy.NetID;
|
||||
var netSyncExist = dummy.NetworkSynchronizeExistence;
|
||||
|
||||
@@ -105,6 +111,14 @@ namespace Robust.Shared.GameObjects
|
||||
RemoveComponent(name);
|
||||
}
|
||||
|
||||
if (_lowerCaseNames.ContainsKey(lowerCaseName))
|
||||
{
|
||||
if (!overwrite)
|
||||
{
|
||||
throw new InvalidOperationException($"{lowerCaseName} is already registered, previous: {_lowerCaseNames[lowerCaseName]}");
|
||||
}
|
||||
}
|
||||
|
||||
if (netID != null && netIDs.ContainsKey(netID.Value))
|
||||
{
|
||||
if (!overwrite)
|
||||
@@ -117,6 +131,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
var registration = new ComponentRegistration(name, type, netID, netSyncExist);
|
||||
names[name] = registration;
|
||||
_lowerCaseNames[lowerCaseName] = name;
|
||||
types[type] = registration;
|
||||
if (netID != null)
|
||||
{
|
||||
@@ -169,6 +184,7 @@ namespace Robust.Shared.GameObjects
|
||||
var registration = names[name];
|
||||
|
||||
names.Remove(registration.Name);
|
||||
_lowerCaseNames.Remove(registration.Name.ToLowerInvariant());
|
||||
types.Remove(registration.Type);
|
||||
if (registration.NetID != null)
|
||||
{
|
||||
@@ -176,8 +192,13 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
public ComponentAvailability GetComponentAvailability(string componentName)
|
||||
public ComponentAvailability GetComponentAvailability(string componentName, bool ignoreCase = false)
|
||||
{
|
||||
if (ignoreCase && _lowerCaseNames.TryGetValue(componentName, out var lowerCaseName))
|
||||
{
|
||||
componentName = lowerCaseName;
|
||||
}
|
||||
|
||||
if (names.ContainsKey(componentName))
|
||||
{
|
||||
return ComponentAvailability.Available;
|
||||
@@ -209,8 +230,13 @@ namespace Robust.Shared.GameObjects
|
||||
return _typeFactory.CreateInstanceUnchecked<T>(types[typeof(T)].Type);
|
||||
}
|
||||
|
||||
public IComponent GetComponent(string componentName)
|
||||
public IComponent GetComponent(string componentName, bool ignoreCase = false)
|
||||
{
|
||||
if (ignoreCase && _lowerCaseNames.TryGetValue(componentName, out var lowerCaseName))
|
||||
{
|
||||
componentName = lowerCaseName;
|
||||
}
|
||||
|
||||
return _typeFactory.CreateInstanceUnchecked<IComponent>(GetRegistration(componentName).Type);
|
||||
}
|
||||
|
||||
@@ -219,8 +245,13 @@ namespace Robust.Shared.GameObjects
|
||||
return _typeFactory.CreateInstanceUnchecked<IComponent>(GetRegistration(netId).Type);
|
||||
}
|
||||
|
||||
public IComponentRegistration GetRegistration(string componentName)
|
||||
public IComponentRegistration GetRegistration(string componentName, bool ignoreCase = false)
|
||||
{
|
||||
if (ignoreCase && _lowerCaseNames.TryGetValue(componentName, out var lowerCaseName))
|
||||
{
|
||||
componentName = lowerCaseName;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return names[componentName];
|
||||
@@ -265,8 +296,13 @@ namespace Robust.Shared.GameObjects
|
||||
return GetRegistration(component.GetType());
|
||||
}
|
||||
|
||||
public bool TryGetRegistration(string componentName, [NotNullWhen(true)] out IComponentRegistration? registration)
|
||||
public bool TryGetRegistration(string componentName, [NotNullWhen(true)] out IComponentRegistration? registration, bool ignoreCase = false)
|
||||
{
|
||||
if (ignoreCase && _lowerCaseNames.TryGetValue(componentName, out var lowerCaseName))
|
||||
{
|
||||
componentName = lowerCaseName;
|
||||
}
|
||||
|
||||
if (names.TryGetValue(componentName, out var tempRegistration))
|
||||
{
|
||||
registration = tempRegistration;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using Robust.Shared.Serialization;
|
||||
using System;
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[RequiresSerializable]
|
||||
[Serializable, NetSerializable]
|
||||
public class ComponentState
|
||||
{
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace Robust.Shared.GameObjects.Components.Renderable
|
||||
};
|
||||
}
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref Shader, "shader", null);
|
||||
serializer.DataField(ref TexturePath, "texture", null);
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Robust.Shared.GameObjects.Components.Timers
|
||||
{
|
||||
entity
|
||||
.EnsureComponent<TimerComponent>()
|
||||
.Spawn(milliseconds, onFired, cancellationToken);
|
||||
.SpawnRepeating(milliseconds, onFired, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -100,7 +100,7 @@ namespace Robust.Shared.GameObjects.Components.Timers
|
||||
{
|
||||
entity
|
||||
.EnsureComponent<TimerComponent>()
|
||||
.Spawn(duration, onFired, cancellationToken);
|
||||
.SpawnRepeating(duration, onFired, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Robust.Shared.GameObjects.Components.UserInterface
|
||||
public object UiKey { get; private set; } = default!;
|
||||
public string ClientType { get; private set; } = default!;
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
UiKey = serializer.ReadStringEnumKey("key");
|
||||
ClientType = serializer.ReadDataField<string>("type");
|
||||
|
||||
@@ -96,6 +96,8 @@ namespace Robust.Shared.GameObjects.Systems
|
||||
|
||||
foreach (var physics in _queuedDeletions)
|
||||
{
|
||||
// If an entity was swapped from awake -> sleep -> awake then it's still relevant.
|
||||
if (!physics.Deleted && physics.Awake) continue;
|
||||
_awakeBodies.Remove(physics);
|
||||
_predictedAwakeBodies.Remove(physics);
|
||||
_controllers.Remove(physics);
|
||||
|
||||
@@ -54,12 +54,13 @@ namespace Robust.Shared.Interfaces.GameObjects
|
||||
/// </summary>
|
||||
IEnumerable<Type> AllRegisteredTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Get whether a component is available right now.
|
||||
/// </summary>
|
||||
/// <param name="componentName">The name of the component to check.</param>
|
||||
/// <param name="ignoreCase">Whether or not to ignore casing on <see cref="componentName"/></param>
|
||||
/// <returns>The availability of the component.</returns>
|
||||
ComponentAvailability GetComponentAvailability(string componentName);
|
||||
ComponentAvailability GetComponentAvailability(string componentName, bool ignoreCase = false);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a prototype to be available for spawning.
|
||||
@@ -109,11 +110,12 @@ namespace Robust.Shared.Interfaces.GameObjects
|
||||
/// Gets a new component instantiated of the specified <see cref="IComponent.Name"/>.
|
||||
/// </summary>
|
||||
/// <param name="componentName">name of component to make</param>
|
||||
/// <param name="ignoreCase">Whether or not to ignore casing on <see cref="componentName"/></param>
|
||||
/// <returns>A Component</returns>
|
||||
/// <exception cref="UnknownComponentException">
|
||||
/// Thrown if no component exists with the given name <see cref="componentName"/>.
|
||||
/// </exception>
|
||||
IComponent GetComponent(string componentName);
|
||||
IComponent GetComponent(string componentName, bool ignoreCase = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a new component instantiated of the specified network ID.
|
||||
@@ -129,10 +131,11 @@ namespace Robust.Shared.Interfaces.GameObjects
|
||||
/// Gets the registration belonging to a component, throwing an exception if it does not exist.
|
||||
/// </summary>
|
||||
/// <param name="componentName">The name of the component.</param>
|
||||
/// <param name="ignoreCase">Whether or not to ignore casing on <see cref="componentName"/></param>
|
||||
/// <exception cref="UnknownComponentException">
|
||||
/// Thrown if no component exists with the given name <see cref="componentName"/>.
|
||||
/// </exception>
|
||||
IComponentRegistration GetRegistration(string componentName);
|
||||
IComponentRegistration GetRegistration(string componentName, bool ignoreCase = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the registration belonging to a component, throwing an exception if it does not exist.
|
||||
@@ -179,8 +182,9 @@ namespace Robust.Shared.Interfaces.GameObjects
|
||||
/// </summary>
|
||||
/// <param name="componentName">The name of the component.</param>
|
||||
/// <param name="registration">The registration if found, null otherwise.</param>
|
||||
/// <param name="ignoreCase">Whether or not to ignore casing on <see cref="componentName"/></param>
|
||||
/// <returns>true it found, false otherwise.</returns>
|
||||
bool TryGetRegistration(string componentName, [NotNullWhen(true)] out IComponentRegistration? registration);
|
||||
bool TryGetRegistration(string componentName, [NotNullWhen(true)] out IComponentRegistration? registration, bool ignoreCase = false);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the registration belonging to a component.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.Interfaces.Serialization
|
||||
@@ -5,6 +6,7 @@ namespace Robust.Shared.Interfaces.Serialization
|
||||
/// <summary>
|
||||
/// Interface for the "expose data" system, which is basically our method of handling data serialization.
|
||||
/// </summary>
|
||||
[RequiresExplicitImplementation]
|
||||
public interface IExposeData
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -9,6 +9,9 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.IoC
|
||||
{
|
||||
public delegate T DependencyFactoryDelegate<out T>()
|
||||
where T : class;
|
||||
|
||||
/// <inheritdoc />
|
||||
internal class DependencyCollection : IDependencyCollection
|
||||
{
|
||||
@@ -26,6 +29,8 @@ namespace Robust.Shared.IoC
|
||||
/// </summary>
|
||||
private readonly Dictionary<Type, Type> _resolveTypes = new();
|
||||
|
||||
private readonly Dictionary<Type, DependencyFactoryDelegate<object>> _resolveFactories = new();
|
||||
|
||||
// To do injection of common types like components, we make DynamicMethods to do the actual injecting.
|
||||
// This is way faster than reflection and should be allocation free outside setup.
|
||||
private readonly Dictionary<Type, (InjectorDelegate? @delegate, object[]? services)> _injectorCache =
|
||||
@@ -34,11 +39,18 @@ namespace Robust.Shared.IoC
|
||||
/// <inheritdoc />
|
||||
public void Register<TInterface, TImplementation>(bool overwrite = false)
|
||||
where TImplementation : class, TInterface, new()
|
||||
{
|
||||
Register<TInterface, TImplementation>(() => new TImplementation(), overwrite);
|
||||
}
|
||||
|
||||
public void Register<TInterface, TImplementation>(DependencyFactoryDelegate<TImplementation> factory, bool overwrite = false)
|
||||
where TImplementation : class, TInterface
|
||||
{
|
||||
var interfaceType = typeof(TInterface);
|
||||
CheckRegisterInterface(interfaceType, typeof(TImplementation), overwrite);
|
||||
|
||||
_resolveTypes[interfaceType] = typeof(TImplementation);
|
||||
_resolveFactories[typeof(TImplementation)] = factory;
|
||||
}
|
||||
|
||||
[AssertionMethod]
|
||||
@@ -96,6 +108,7 @@ namespace Robust.Shared.IoC
|
||||
|
||||
_services.Clear();
|
||||
_resolveTypes.Clear();
|
||||
_resolveFactories.Clear();
|
||||
_injectorCache.Clear();
|
||||
}
|
||||
|
||||
@@ -156,7 +169,8 @@ namespace Robust.Shared.IoC
|
||||
|
||||
try
|
||||
{
|
||||
var instance = Activator.CreateInstance(value)!;
|
||||
// Yay for delegate covariance
|
||||
object instance = _resolveFactories[value].Invoke();
|
||||
_services[key] = instance;
|
||||
injectList.Add(instance);
|
||||
}
|
||||
@@ -166,6 +180,10 @@ namespace Robust.Shared.IoC
|
||||
}
|
||||
}
|
||||
|
||||
// Because we only ever construct an instance once per registration, there is no need to keep the factory
|
||||
// delegates. Also we need to free the delegates because lambdas capture variables.
|
||||
_resolveFactories.Clear();
|
||||
|
||||
// Graph built, go over ones that need injection.
|
||||
foreach (var implementation in injectList)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Robust.Shared.IoC.Exceptions;
|
||||
|
||||
@@ -43,6 +43,24 @@ namespace Robust.Shared.IoC
|
||||
void Register<TInterface, TImplementation>(bool overwrite = false)
|
||||
where TImplementation : class, TInterface, new();
|
||||
|
||||
/// <summary>
|
||||
/// Registers an interface to an implementation, to make it accessible to <see cref="DependencyCollection.Resolve{T}"/>
|
||||
/// <see cref="IDependencyCollection.BuildGraph"/> MUST be called after this method to make the new interface available.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInterface">The type that will be resolvable.</typeparam>
|
||||
/// <typeparam name="TImplementation">The type that will be constructed as implementation.</typeparam>
|
||||
/// <param name="factory">A factory method to construct the instance of the implementation.</param>
|
||||
/// <param name="overwrite">
|
||||
/// If true, do not throw an <see cref="InvalidOperationException"/> if an interface is already registered,
|
||||
/// replace the current implementation instead.
|
||||
/// </param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if <paramref name="overwrite"/> is false and <typeparamref name="TInterface"/> has been registered before,
|
||||
/// or if an already instantiated interface (by <see cref="DependencyCollection.BuildGraph"/>) is attempting to be overwritten.
|
||||
/// </exception>
|
||||
void Register<TInterface, TImplementation>(DependencyFactoryDelegate<TImplementation> factory, bool overwrite = false)
|
||||
where TImplementation : class, TInterface;
|
||||
|
||||
/// <summary>
|
||||
/// Registers an interface to an existing instance of an implementation,
|
||||
/// making it accessible to <see cref="IDependencyCollection.Resolve{T}"/>.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Robust.Shared.IoC.Exceptions;
|
||||
using Robust.Shared.IoC.Exceptions;
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Threading;
|
||||
@@ -103,6 +103,29 @@ namespace Robust.Shared.IoC
|
||||
_container.Value!.Register<TInterface, TImplementation>(overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an interface to an implementation, to make it accessible to <see cref="Resolve{T}"/>
|
||||
/// <see cref="BuildGraph"/> MUST be called after this method to make the new interface available.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInterface">The type that will be resolvable.</typeparam>
|
||||
/// <typeparam name="TImplementation">The type that will be constructed as implementation.</typeparam>
|
||||
/// <param name="factory">A factory method to construct the instance of the implementation.</param>
|
||||
/// <param name="overwrite">
|
||||
/// If true, do not throw an <see cref="InvalidOperationException"/> if an interface is already registered,
|
||||
/// replace the current implementation instead.
|
||||
/// </param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if <paramref name="overwrite"/> is false and <typeparamref name="TInterface"/> has been registered before,
|
||||
/// or if an already instantiated interface (by <see cref="BuildGraph"/>) is attempting to be overwritten.
|
||||
/// </exception>
|
||||
public static void Register<TInterface, TImplementation>(DependencyFactoryDelegate<TImplementation> factory, bool overwrite = false)
|
||||
where TImplementation : class, TInterface
|
||||
{
|
||||
DebugTools.Assert(_container.IsValueCreated, NoContextAssert);
|
||||
|
||||
_container.Value!.Register<TInterface, TImplementation>(factory, overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an interface to an existing instance of an implementation,
|
||||
/// making it accessible to <see cref="IDependencyCollection.Resolve{T}"/>.
|
||||
|
||||
@@ -6,7 +6,7 @@ using Serilog.Events;
|
||||
|
||||
namespace Robust.Shared.Log
|
||||
{
|
||||
public sealed class FileLogHandler : ILogHandler, IDisposable
|
||||
internal sealed class FileLogHandler : ILogHandler, IDisposable
|
||||
{
|
||||
private readonly TextWriter writer;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
@@ -83,7 +84,7 @@ namespace Robust.Shared.Map
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _gridId, "grid", GridId.Invalid);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Lidgren.Network;
|
||||
using Lidgren.Network;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
|
||||
#nullable disable
|
||||
@@ -14,6 +14,7 @@ namespace Robust.Shared.Network.Messages
|
||||
#endregion
|
||||
|
||||
public string Text { get; set; }
|
||||
public bool Error { get; set; }
|
||||
|
||||
public override void ReadFromBuffer(NetIncomingMessage buffer)
|
||||
{
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
// #define DEBUG_DYNAMIC_TREE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@@ -268,6 +270,9 @@ namespace Robust.Shared.Physics
|
||||
/// </summary>
|
||||
public Proxy CreateProxy(in Box2 aabb, T userData)
|
||||
{
|
||||
// Also catches NaN fuckery.
|
||||
Assert(aabb.Right >= aabb.Left && aabb.Top >= aabb.Bottom);
|
||||
|
||||
ref var proxy = ref AllocateNode(out var proxyId);
|
||||
|
||||
// Fatten the aabb.
|
||||
@@ -292,6 +297,8 @@ namespace Robust.Shared.Physics
|
||||
public bool MoveProxy(Proxy proxy, in Box2 aabb, Vector2 displacement)
|
||||
{
|
||||
Assert(0 <= proxy && proxy < Capacity);
|
||||
// Also catches NaN fuckery.
|
||||
Assert(aabb.Right >= aabb.Left && aabb.Top >= aabb.Bottom);
|
||||
|
||||
ref var leafNode = ref _nodes[proxy];
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Robust.Shared.Physics
|
||||
private readonly ExtractAabbDelegate _extractAabb;
|
||||
|
||||
// avoids "Collection was modified; enumeration operation may not execute."
|
||||
private IDictionary<T, Proxy> _nodeLookup;
|
||||
private Dictionary<T, Proxy> _nodeLookup;
|
||||
private readonly B2DynamicTree<T> _b2Tree;
|
||||
|
||||
public DynamicTree(ExtractAabbDelegate extractAabbFunc, IEqualityComparer<T>? comparer = null, float aabbExtendSize = 1f / 32, int capacity = 256, Func<int, int>? growthFunc = null)
|
||||
@@ -134,6 +134,12 @@ namespace Robust.Shared.Physics
|
||||
|
||||
var box = _extractAabb(item);
|
||||
|
||||
if (CheckNaNs(box))
|
||||
{
|
||||
_nodeLookup[item] = Proxy.Free;
|
||||
return true;
|
||||
}
|
||||
|
||||
proxy = _b2Tree.CreateProxy(box, item);
|
||||
_nodeLookup[item] = proxy;
|
||||
|
||||
@@ -162,7 +168,11 @@ namespace Robust.Shared.Physics
|
||||
return false;
|
||||
}
|
||||
|
||||
_b2Tree.DestroyProxy(proxy);
|
||||
if (proxy != Proxy.Free)
|
||||
{
|
||||
_b2Tree.DestroyProxy(proxy);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -178,6 +188,25 @@ namespace Robust.Shared.Physics
|
||||
}
|
||||
|
||||
var newBox = _extractAabb(item);
|
||||
|
||||
if (CheckNaNs(newBox))
|
||||
{
|
||||
if (proxy == Proxy.Free)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_b2Tree.DestroyProxy(proxy);
|
||||
_nodeLookup[item] = Proxy.Free;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (proxy == Proxy.Free)
|
||||
{
|
||||
_nodeLookup[item] = _b2Tree.CreateProxy(newBox, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
return _b2Tree.MoveProxy(proxy, newBox, Vector2.Zero);
|
||||
}
|
||||
|
||||
@@ -304,6 +333,14 @@ namespace Robust.Shared.Physics
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool AddOrUpdate(T item) => Update(item) || Add(item);
|
||||
|
||||
private static bool CheckNaNs(in Box2 box)
|
||||
{
|
||||
return float.IsNaN(box.Left)
|
||||
|| float.IsNaN(box.Top)
|
||||
|| float.IsNaN(box.Bottom)
|
||||
|| float.IsNaN(box.Right);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG_DYNAMIC_TREE")]
|
||||
[Conditional("DEBUG_DYNAMIC_TREE_ASSERTS")]
|
||||
[DebuggerNonUserCode]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -80,7 +81,7 @@ namespace Robust.Shared.Physics
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _collisionLayer, "layer", 0, WithFormat.Flags<CollisionLayer>());
|
||||
serializer.DataField(ref _collisionMask, "mask", 0, WithFormat.Flags<CollisionMask>());
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -61,7 +62,7 @@ namespace Robust.Shared.Physics
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _collisionLayer, "layer", 0, WithFormat.Flags<CollisionLayer>());
|
||||
serializer.DataField(ref _collisionMask, "mask", 0, WithFormat.Flags<CollisionMask>());
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -64,7 +65,7 @@ namespace Robust.Shared.Physics
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _collisionLayer, "layer", 0, WithFormat.Flags<CollisionLayer>());
|
||||
serializer.DataField(ref _collisionMask, "mask", 0, WithFormat.Flags<CollisionMask>());
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -478,8 +477,8 @@ namespace Robust.Shared.Serialization
|
||||
}
|
||||
|
||||
// val enum
|
||||
if (type.IsEnum)
|
||||
return Enum.Parse(type, node.ToString());
|
||||
if (underlyingType.IsEnum)
|
||||
return Enum.Parse(underlyingType, node.ToString());
|
||||
|
||||
// IReadOnlyList<T>/IReadOnlyCollection<T>
|
||||
if (TryGenericReadOnlyCollectionType(type, out var collectionType))
|
||||
@@ -546,6 +545,33 @@ namespace Robust.Shared.Serialization
|
||||
return newSet;
|
||||
}
|
||||
|
||||
// KeyValuePair<K, V>
|
||||
if (TryGenericKeyValuePairType(type, out var kvpKeyType, out var kvpValType))
|
||||
{
|
||||
var pairType = typeof(KeyValuePair<,>).MakeGenericType(kvpKeyType, kvpValType);
|
||||
var pairNode = (YamlMappingNode) node;
|
||||
|
||||
switch (pairNode.Children.Count)
|
||||
{
|
||||
case 0:
|
||||
return Activator.CreateInstance(pairType)!;
|
||||
case 1:
|
||||
{
|
||||
using var enumerator = pairNode.GetEnumerator();
|
||||
enumerator.MoveNext();
|
||||
var yamlPair = enumerator.Current;
|
||||
var keyValue = NodeToType(kvpKeyType, yamlPair.Key);
|
||||
var valValue = NodeToType(kvpValType, yamlPair.Value);
|
||||
var pair = Activator.CreateInstance(pairType, keyValue, valValue)!;
|
||||
|
||||
return pair;
|
||||
}
|
||||
default:
|
||||
throw new InvalidOperationException(
|
||||
$"Cannot read KeyValuePair from mapping node with more than one child.");
|
||||
}
|
||||
}
|
||||
|
||||
// Hand it to the context.
|
||||
if (_context != null && _context.TryNodeToType(node, type, out var contextObj))
|
||||
{
|
||||
@@ -749,6 +775,21 @@ namespace Robust.Shared.Serialization
|
||||
return node;
|
||||
}
|
||||
|
||||
if (TryGenericKeyValuePairType(type, out var kvpKeyType, out var kvpValType))
|
||||
{
|
||||
var node = new YamlMappingNode {Tag = TagSkipTag};
|
||||
dynamic pair = obj;
|
||||
var keyNode = TypeToNode(pair.Key);
|
||||
var valNode = TypeToNode(pair.Value);
|
||||
|
||||
// write the concrete type tag
|
||||
AssignTag<object?>(kvpValType, pair, null, valNode);
|
||||
|
||||
node.Add(keyNode, valNode);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// Hand it to the context.
|
||||
if (_context != null && _context.TryTypeToNode(obj, out var contextNode))
|
||||
{
|
||||
@@ -896,6 +937,24 @@ namespace Robust.Shared.Serialization
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TryGenericKeyValuePairType(type!, out _, out _))
|
||||
{
|
||||
dynamic tupleA = a;
|
||||
dynamic tupleB = b!;
|
||||
|
||||
if (!IsSerializedEqual(tupleA.Key, tupleB.Key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsSerializedEqual(tupleA.Value, tupleB.Value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof(IExposeData).IsAssignableFrom(type))
|
||||
{
|
||||
// Serialize both, see if output matches.
|
||||
@@ -1035,6 +1094,27 @@ namespace Robust.Shared.Serialization
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGenericKeyValuePairType(
|
||||
Type type,
|
||||
[NotNullWhen(true)] out Type? keyType,
|
||||
[NotNullWhen(true)] out Type? valType)
|
||||
{
|
||||
var isPair = type.GetTypeInfo().IsGenericType &&
|
||||
type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>);
|
||||
|
||||
if (isPair)
|
||||
{
|
||||
var genArgs = type.GetGenericArguments();
|
||||
keyType = genArgs[0];
|
||||
valType = genArgs[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
keyType = default;
|
||||
valType = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract class TypeSerializer
|
||||
{
|
||||
public abstract object NodeToType(Type type, YamlNode node, YamlObjectSerializer serializer);
|
||||
|
||||
118
Robust.UnitTesting/Shared/GameObjects/ComponentFactory_Tests.cs
Normal file
118
Robust.UnitTesting/Shared/GameObjects/ComponentFactory_Tests.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
|
||||
// ReSharper disable AccessToStaticMemberViaDerivedType
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(ComponentFactory))]
|
||||
public class ComponentFactory_Tests : RobustUnitTest
|
||||
{
|
||||
private const string TestComponentName = "A";
|
||||
private const string LowercaseTestComponentName = "a";
|
||||
private const string NonexistentComponentName = "B";
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void OneTimeSetUp()
|
||||
{
|
||||
IoCManager.Resolve<IComponentFactory>().Register<TestComponent>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetComponentAvailabilityTest()
|
||||
{
|
||||
var componentFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
// Should not exist
|
||||
Assert.That(componentFactory.GetComponentAvailability(NonexistentComponentName), Is.EqualTo(ComponentAvailability.Unknown));
|
||||
Assert.That(componentFactory.GetComponentAvailability(NonexistentComponentName, true), Is.EqualTo(ComponentAvailability.Unknown));
|
||||
|
||||
// Normal casing, do not ignore case, should exist
|
||||
Assert.That(componentFactory.GetComponentAvailability(TestComponentName), Is.EqualTo(ComponentAvailability.Available));
|
||||
|
||||
// Normal casing, ignore case, should exist
|
||||
Assert.That(componentFactory.GetComponentAvailability(TestComponentName, true), Is.EqualTo(ComponentAvailability.Available));
|
||||
|
||||
// Lower casing, do not ignore case, should not exist
|
||||
Assert.That(componentFactory.GetComponentAvailability(LowercaseTestComponentName), Is.EqualTo(ComponentAvailability.Unknown));
|
||||
|
||||
// Lower casing, ignore case, should exist
|
||||
Assert.That(componentFactory.GetComponentAvailability(LowercaseTestComponentName, true), Is.EqualTo(ComponentAvailability.Available));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetComponentTest()
|
||||
{
|
||||
var componentFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
// Should not exist
|
||||
Assert.Throws<UnknownComponentException>(() => componentFactory.GetComponent(NonexistentComponentName));
|
||||
Assert.Throws<UnknownComponentException>(() => componentFactory.GetComponent(NonexistentComponentName, true));
|
||||
|
||||
// Normal casing, do not ignore case, should exist
|
||||
Assert.IsInstanceOf<TestComponent>(componentFactory.GetComponent(TestComponentName));
|
||||
|
||||
// Normal casing, ignore case, should exist
|
||||
Assert.IsInstanceOf<TestComponent>(componentFactory.GetComponent(TestComponentName, true));
|
||||
|
||||
// Lower casing, do not ignore case, should not exist
|
||||
Assert.Throws<UnknownComponentException>(() => componentFactory.GetComponent(LowercaseTestComponentName));
|
||||
|
||||
// Lower casing, ignore case, should exist
|
||||
Assert.IsInstanceOf<TestComponent>(componentFactory.GetComponent(LowercaseTestComponentName, true));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetRegistrationTest()
|
||||
{
|
||||
var componentFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
// Should not exist
|
||||
Assert.Throws<UnknownComponentException>(() => componentFactory.GetRegistration(NonexistentComponentName));
|
||||
Assert.Throws<UnknownComponentException>(() => componentFactory.GetRegistration(NonexistentComponentName, true));
|
||||
|
||||
// Normal casing, do not ignore case, should exist
|
||||
Assert.DoesNotThrow(() => componentFactory.GetRegistration(TestComponentName));
|
||||
|
||||
// Normal casing, ignore case, should exist
|
||||
Assert.DoesNotThrow(() => componentFactory.GetRegistration(TestComponentName, true));
|
||||
|
||||
// Lower casing, do not ignore case, should not exist
|
||||
Assert.Throws<UnknownComponentException>(() => componentFactory.GetRegistration(LowercaseTestComponentName));
|
||||
|
||||
// Lower casing, ignore case, should exist
|
||||
Assert.DoesNotThrow(() => componentFactory.GetRegistration(LowercaseTestComponentName, true));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetRegistrationTest()
|
||||
{
|
||||
var componentFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
// Should not exist
|
||||
Assert.False(componentFactory.TryGetRegistration(NonexistentComponentName, out _));
|
||||
Assert.False(componentFactory.TryGetRegistration(NonexistentComponentName, out _, true));
|
||||
|
||||
// Normal casing, do not ignore case, should exist
|
||||
Assert.True(componentFactory.TryGetRegistration(TestComponentName, out _));
|
||||
|
||||
// Normal casing, ignore case, should exist
|
||||
Assert.True(componentFactory.TryGetRegistration(TestComponentName, out _, true));
|
||||
|
||||
// Lower casing, do not ignore case, should not exist
|
||||
Assert.False(componentFactory.TryGetRegistration(LowercaseTestComponentName, out _));
|
||||
|
||||
// Lower casing, ignore case, should exist
|
||||
Assert.True(componentFactory.TryGetRegistration(LowercaseTestComponentName, out _, true));
|
||||
}
|
||||
|
||||
private class TestComponent : Component
|
||||
{
|
||||
public override string Name => TestComponentName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -47,6 +47,18 @@ namespace Robust.UnitTesting.Shared.IoC
|
||||
Assert.That(IoCManager.ResolveType(typeof(TestFieldInjection)), Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IoCRegisterFactory()
|
||||
{
|
||||
var newInstance = new TestFieldInjection();
|
||||
IoCManager.Register<TestFieldInjection, TestFieldInjection>(() => newInstance);
|
||||
|
||||
IoCManager.BuildGraph(); // Actually calls the factory
|
||||
|
||||
var result = IoCManager.Resolve<TestFieldInjection>();
|
||||
Assert.That(result, Is.EqualTo(newInstance));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IoCTestOverwrite()
|
||||
{
|
||||
|
||||
@@ -3,15 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
// ReSharper disable AccessToStaticMemberViaDerivedType
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Serialization
|
||||
{
|
||||
@@ -327,6 +325,103 @@ namespace Robust.UnitTesting.Shared.Serialization
|
||||
private readonly string SerializedSetYaml = "dataSet:\n- 1\n- 2\n- 3\n...\n";
|
||||
private readonly HashSet<int> SerializableSet = new() { 1, 2, 3 };
|
||||
|
||||
[Test]
|
||||
public void SerializePairTest()
|
||||
{
|
||||
var mapping = new YamlMappingNode();
|
||||
var pair = SerializablePair;
|
||||
|
||||
var writer = YamlObjectSerializer.NewWriter(mapping);
|
||||
|
||||
writer.DataField(ref pair, "dataPair", new KeyValuePair<string, int>("val0", 0));
|
||||
|
||||
var result = NodeToYamlText(mapping);
|
||||
Assert.That(result, Is.EqualTo(SerializedPairYaml));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeserializePairTest()
|
||||
{
|
||||
KeyValuePair<string, int> data = default;
|
||||
var rootNode = YamlTextToNode(SerializedPairYaml);
|
||||
var serializer = YamlObjectSerializer.NewReader(rootNode);
|
||||
|
||||
serializer.DataField(ref data, "dataPair", new KeyValuePair<string, int>("val0", 0));
|
||||
|
||||
Assert.That(data, Is.Not.EqualTo(default(KeyValuePair<string, int>)));
|
||||
Assert.That(data.Key, Is.EqualTo(SerializablePair.Key));
|
||||
Assert.That(data.Value, Is.EqualTo(SerializablePair.Value));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SerializeDefaultPairTest()
|
||||
{
|
||||
var mapping = new YamlMappingNode();
|
||||
var pair = SerializableDefaultPair;
|
||||
|
||||
var writer = YamlObjectSerializer.NewWriter(mapping);
|
||||
|
||||
writer.DataField(ref pair, "dataPair", new KeyValuePair<int, int>(0, 0));
|
||||
|
||||
var result = NodeToYamlText(mapping);
|
||||
Assert.That(result, Is.EqualTo(SerializedDefaultPairYaml));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeserializeDefaultPairTest()
|
||||
{
|
||||
KeyValuePair<int, int> data = default;
|
||||
var rootNode = YamlTextToNode(SerializedDefaultPairYaml);
|
||||
var serializer = YamlObjectSerializer.NewReader(rootNode);
|
||||
|
||||
serializer.DataField(ref data, "dataPair", new KeyValuePair<int, int>(0, 0));
|
||||
|
||||
Assert.That(data, Is.EqualTo(default(KeyValuePair<int, int>)));
|
||||
Assert.That(data.Key, Is.EqualTo(SerializableDefaultPair.Key));
|
||||
Assert.That(data.Value, Is.EqualTo(SerializableDefaultPair.Value));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeserializeNoPairTest()
|
||||
{
|
||||
KeyValuePair<int, int> data = default;
|
||||
var rootNode = YamlTextToNode(SerializedNoPairYaml);
|
||||
var serializer = YamlObjectSerializer.NewReader(rootNode);
|
||||
|
||||
serializer.DataField(ref data, "dataPair", new KeyValuePair<int, int>(0, 0));
|
||||
|
||||
Assert.That(data, Is.EqualTo(default(KeyValuePair<int, int>)));
|
||||
Assert.That(data.Key, Is.EqualTo(SerializedNoPair.Key));
|
||||
Assert.That(data.Value, Is.EqualTo(SerializedNoPair.Value));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SerializedEqualPairTest()
|
||||
{
|
||||
var pair = new KeyValuePair<string, int>("val0", 0);
|
||||
var pair2 = new KeyValuePair<string, int>("val0", 0);
|
||||
|
||||
Assert.That(YamlObjectSerializer.IsSerializedEqual(pair, pair2), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SerializedNotEqualPairTest()
|
||||
{
|
||||
var pair = new KeyValuePair<string, int>("val0", 0);
|
||||
var pair2 = new KeyValuePair<string, int>("val0", 1);
|
||||
|
||||
Assert.That(YamlObjectSerializer.IsSerializedEqual(pair, pair2), Is.False);
|
||||
}
|
||||
|
||||
private readonly string SerializedPairYaml = "dataPair:\n val1: 1\n...\n";
|
||||
private readonly KeyValuePair<string, int> SerializablePair = new("val1", 1);
|
||||
|
||||
private readonly string SerializedDefaultPairYaml = "{}\n...\n";
|
||||
private readonly KeyValuePair<int, int> SerializableDefaultPair = new(0, 0);
|
||||
|
||||
private readonly string SerializedNoPairYaml = "dataPair: {}\n...\n";
|
||||
private readonly KeyValuePair<int, int> SerializedNoPair = new(0, 0);
|
||||
|
||||
[Test]
|
||||
public void NullablePrimitiveSerializeNullTest()
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@ EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ManagedHttpListener", "ManagedHttpListener", "{15D28C35-25F6-4EA8-8D53-29DA7C8A24A7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.HttpListener", "ManagedHttpListener\src\System.Net.HttpListener.csproj", "{C3EB43AF-31FD-48F5-A4FB-552D0F13948B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Client.Injectors", "Robust.Client.Injectors\Robust.Client.Injectors.csproj", "{EEF2C805-5E03-41EA-A916-49C1DD15EF41}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Client.NameGenerator", "Robust.Client.NameGenerator\Robust.Client.NameGenerator.csproj", "{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}"
|
||||
@@ -44,6 +45,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.IL.Cecil", "XamlX\src
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.Runtime", "XamlX\src\XamlX.Runtime\XamlX.Runtime.csproj", "{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Analyzers", "Robust.Analyzers\Robust.Analyzers.csproj", "{3173712A-9E75-4685-B657-9AF9B7D54EFB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -188,6 +191,14 @@ Global
|
||||
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3173712A-9E75-4685-B657-9AF9B7D54EFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3173712A-9E75-4685-B657-9AF9B7D54EFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3173712A-9E75-4685-B657-9AF9B7D54EFB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3173712A-9E75-4685-B657-9AF9B7D54EFB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3173712A-9E75-4685-B657-9AF9B7D54EFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3173712A-9E75-4685-B657-9AF9B7D54EFB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3173712A-9E75-4685-B657-9AF9B7D54EFB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3173712A-9E75-4685-B657-9AF9B7D54EFB}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
Reference in New Issue
Block a user