mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
148 Commits
v0.8.41
...
ISerializa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9216ff9e4b | ||
|
|
f1857f3c53 | ||
|
|
04b7181a78 | ||
|
|
fa1de04df7 | ||
|
|
33251222cd | ||
|
|
70c1e8680f | ||
|
|
0d214769e6 | ||
|
|
495bbe891a | ||
|
|
5f68f569c3 | ||
|
|
52c883cb1f | ||
|
|
4b20bae597 | ||
|
|
64ff046ceb | ||
|
|
5f2566a97d | ||
|
|
c3735d1d5e | ||
|
|
749e547773 | ||
|
|
af8a010f43 | ||
|
|
a8a29e814f | ||
|
|
c3cb5406f6 | ||
|
|
73f26d93ca | ||
|
|
5e86a99060 | ||
|
|
ce9070b966 | ||
|
|
aa5bcefaf2 | ||
|
|
2df267fc28 | ||
|
|
306fcce337 | ||
|
|
3683b66ef8 | ||
|
|
08a52fb892 | ||
|
|
fd3c54b373 | ||
|
|
adee05b009 | ||
|
|
a66d40eb19 | ||
|
|
d065a96e01 | ||
|
|
ef7709aa78 | ||
|
|
305b330075 | ||
|
|
bec4297ce1 | ||
|
|
18b21b3d60 | ||
|
|
9b263417b9 | ||
|
|
13da0a7925 | ||
|
|
bb28db2412 | ||
|
|
489d150ae0 | ||
|
|
077dbaf933 | ||
|
|
26f83ac7a2 | ||
|
|
822009b429 | ||
|
|
e07a4e516c | ||
|
|
9ec38b5538 | ||
|
|
97da770978 | ||
|
|
3acbc8235d | ||
|
|
a32359d5d4 | ||
|
|
78fd39aee2 | ||
|
|
fca5c14c67 | ||
|
|
f60e3a14ef | ||
|
|
503a7032f9 | ||
|
|
918bbd3f01 | ||
|
|
a1441d5051 | ||
|
|
58d6189c40 | ||
|
|
58defed1d2 | ||
|
|
27a94384d0 | ||
|
|
4b8f5815db | ||
|
|
d830eef435 | ||
|
|
e7c4bf7341 | ||
|
|
162e646404 | ||
|
|
93049fcacf | ||
|
|
5b90db4beb | ||
|
|
7692ff736b | ||
|
|
f25ad8ece9 | ||
|
|
1aaf3b9250 | ||
|
|
11f6cff6df | ||
|
|
c2ea57c95a | ||
|
|
f61ae5da6b | ||
|
|
8ae9a5f2da | ||
|
|
3f7e89e006 | ||
|
|
cee4e4d62e | ||
|
|
14a3783760 | ||
|
|
b4607f7b1f | ||
|
|
5a28c16cae | ||
|
|
9e8bf861ea | ||
|
|
b540f04a7a | ||
|
|
bcaa7001ad | ||
|
|
3f0fba7b4e | ||
|
|
d7d7a53045 | ||
|
|
40daba9adf | ||
|
|
5665f8eb1c | ||
|
|
8f0d562f3e | ||
|
|
f3950e940e | ||
|
|
3fdca65cc9 | ||
|
|
4cfb9210d0 | ||
|
|
23d0b8a555 | ||
|
|
452b03d5a6 | ||
|
|
c8f2dab381 | ||
|
|
7409df07f8 | ||
|
|
973406b91d | ||
|
|
2e646fe2b6 | ||
|
|
6b902d22d4 | ||
|
|
83e6d52e58 | ||
|
|
025fdcd0b3 | ||
|
|
fe3ace92bd | ||
|
|
317070f167 | ||
|
|
3524363ad4 | ||
|
|
90287dbb09 | ||
|
|
9a2f35f2d3 | ||
|
|
3f10dbe770 | ||
|
|
8fcba93ada | ||
|
|
35366b5bc1 | ||
|
|
d8bf31ecc4 | ||
|
|
de4d255841 | ||
|
|
ec768c563f | ||
|
|
2114d2bc67 | ||
|
|
43478d0e45 | ||
|
|
561699b26e | ||
|
|
460923e54d | ||
|
|
c299104f0c | ||
|
|
224bebeeb5 | ||
|
|
52c8e8ecdf | ||
|
|
ac636b2703 | ||
|
|
a892f9ac99 | ||
|
|
2ebee8f40c | ||
|
|
43741dc5fc | ||
|
|
da9467b678 | ||
|
|
8f24a7b4fc | ||
|
|
ff48959e97 | ||
|
|
09e648d086 | ||
|
|
1d1db2e109 | ||
|
|
6a902286cd | ||
|
|
933724e4ee | ||
|
|
53034e6f05 | ||
|
|
681f77a796 | ||
|
|
6a31c5649c | ||
|
|
7bd90578dc | ||
|
|
2a7f1cbf48 | ||
|
|
c076be95a6 | ||
|
|
7b732dd68a | ||
|
|
3e34b1414e | ||
|
|
8456cd90d1 | ||
|
|
c4e63cfdc7 | ||
|
|
d716e1ff62 | ||
|
|
8e9acc9191 | ||
|
|
773a068b7d | ||
|
|
b37bfff155 | ||
|
|
a14cce222b | ||
|
|
c077e09436 | ||
|
|
213955566b | ||
|
|
b98cd3e6e1 | ||
|
|
987c8e8229 | ||
|
|
fcbb97ec55 | ||
|
|
1f6170c000 | ||
|
|
e438e53d36 | ||
|
|
792608f0d0 | ||
|
|
7b5678fd0f | ||
|
|
88c328a636 | ||
|
|
f9573530a8 |
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<PropertyGroup><Version>0.8.41</Version></PropertyGroup>
|
||||
<PropertyGroup><Version>0.8.67</Version></PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Robust.Generators
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
public static class Diagnostics
|
||||
{
|
||||
public static class Diagnostics
|
||||
{
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
}
|
||||
public const string IdExplicitInterface = "RA0000";
|
||||
public const string IdSerializable = "RA0001";
|
||||
public const string IdFriend = "RA0002";
|
||||
public const string IdExplicitVirtual = "RA0003";
|
||||
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
}
|
||||
|
||||
@@ -24,14 +24,14 @@ namespace Robust.Analyzers
|
||||
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);
|
||||
[SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new(
|
||||
Diagnostics.IdExplicitInterface,
|
||||
"No explicit interface specified",
|
||||
"No explicit interface specified",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
isEnabledByDefault: true,
|
||||
description: "Make sure to specify the interface in your method-declaration.");
|
||||
|
||||
private const string RequiresExplicitImplementationAttributeMetadataName =
|
||||
"Robust.Shared.Analyzers.RequiresExplicitImplementationAttribute";
|
||||
|
||||
176
Robust.Analyzers/ExplicitVirtualAnalyzer.cs
Normal file
176
Robust.Analyzers/ExplicitVirtualAnalyzer.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class ExplicitVirtualAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
internal const string Attribute = "Robust.Shared.Analyzers.VirtualAttribute";
|
||||
|
||||
[SuppressMessage("ReSharper", "RS2008")]
|
||||
private static readonly DiagnosticDescriptor Rule = new(
|
||||
Diagnostics.IdExplicitVirtual,
|
||||
"Class must be explicitly marked as [Virtual], abstract, static or sealed",
|
||||
"Class must be explicitly marked as [Virtual], abstract, static or sealed",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
isEnabledByDefault: true,
|
||||
description: "Class must be explicitly marked as [Virtual], abstract, static or sealed.");
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration);
|
||||
}
|
||||
|
||||
private static bool HasAttribute(INamedTypeSymbol namedTypeSymbol, INamedTypeSymbol attrSymbol)
|
||||
{
|
||||
return namedTypeSymbol.GetAttributes()
|
||||
.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol));
|
||||
}
|
||||
|
||||
private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
var attrSymbol = context.Compilation.GetTypeByMetadataName(Attribute);
|
||||
var classDecl = (ClassDeclarationSyntax)context.Node;
|
||||
var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDecl);
|
||||
if (classSymbol == null)
|
||||
return;
|
||||
|
||||
if (classSymbol.IsSealed || classSymbol.IsAbstract || classSymbol.IsStatic)
|
||||
return;
|
||||
|
||||
if (HasAttribute(classSymbol, attrSymbol))
|
||||
return;
|
||||
|
||||
var diag = Diagnostic.Create(Rule, classDecl.Keyword.GetLocation());
|
||||
context.ReportDiagnostic(diag);
|
||||
}
|
||||
}
|
||||
|
||||
// Doesn't work as I'd hoped: Roslyn doesn't provide an API for global usings and I can't get batch changes to work.
|
||||
/*
|
||||
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
||||
public sealed class ExplicitVirtualCodeFixProvider : CodeFixProvider
|
||||
{
|
||||
private const string TitleSealed = "Annotate class as sealed.";
|
||||
private const string TitleVirtual = "Annotate class as [Virtual].";
|
||||
private const string TitleAbstract = "Annotate class as abstract.";
|
||||
private const string TitleStatic = "Annotate class as static.";
|
||||
|
||||
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();
|
||||
|
||||
context.RegisterCodeFix(
|
||||
CodeAction.Create(
|
||||
TitleVirtual,
|
||||
c => FixVirtualAsync(context.Document, classDecl, c),
|
||||
TitleVirtual),
|
||||
diagnostic);
|
||||
|
||||
context.RegisterCodeFix(
|
||||
CodeAction.Create(
|
||||
TitleStatic,
|
||||
c => FixStaticAsync(context.Document, classDecl, c),
|
||||
TitleStatic),
|
||||
diagnostic);
|
||||
|
||||
context.RegisterCodeFix(
|
||||
CodeAction.Create(
|
||||
TitleSealed,
|
||||
c => FixSealedAsync(context.Document, classDecl, c),
|
||||
TitleSealed),
|
||||
diagnostic);
|
||||
|
||||
context.RegisterCodeFix(
|
||||
CodeAction.Create(
|
||||
TitleAbstract,
|
||||
c => FixAbstractAsync(context.Document, classDecl, c),
|
||||
TitleAbstract),
|
||||
diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Document> FixVirtualAsync(
|
||||
Document document,
|
||||
ClassDeclarationSyntax classDecl,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var ns = "Robust.Shared.Analyzers";
|
||||
var attrib = SyntaxFactory.Attribute(SyntaxFactory.ParseName("Virtual"));
|
||||
|
||||
var newClassDecl = classDecl.AddAttributeLists(
|
||||
SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(new[] { attrib })));
|
||||
|
||||
var root = (CompilationUnitSyntax)await document.GetSyntaxRootAsync(cancellationToken);
|
||||
root = root.ReplaceNode(classDecl, newClassDecl);
|
||||
|
||||
var options = await document.GetOptionsAsync(cancellationToken);
|
||||
|
||||
if (root.Usings.All(u => u.Name.ToString() != ns))
|
||||
{
|
||||
root = root.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(ns)));
|
||||
}
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private async Task<Document> FixStaticAsync(
|
||||
Document document,
|
||||
ClassDeclarationSyntax classDecl,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var newClassDecl = classDecl.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword));
|
||||
|
||||
var root = (CompilationUnitSyntax)await document.GetSyntaxRootAsync(cancellationToken);
|
||||
root = root.ReplaceNode(classDecl, newClassDecl);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private async Task<Document> FixAbstractAsync(
|
||||
Document document,
|
||||
ClassDeclarationSyntax classDecl,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var newClassDecl = classDecl.AddModifiers(SyntaxFactory.Token(SyntaxKind.AbstractKeyword));
|
||||
|
||||
var root = (CompilationUnitSyntax)await document.GetSyntaxRootAsync(cancellationToken);
|
||||
root = root.ReplaceNode(classDecl, newClassDecl);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private async Task<Document> FixSealedAsync(
|
||||
Document document,
|
||||
ClassDeclarationSyntax classDecl,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var newClassDecl = classDecl.AddModifiers(SyntaxFactory.Token(SyntaxKind.SealedKeyword));
|
||||
|
||||
var root = (CompilationUnitSyntax)await document.GetSyntaxRootAsync(cancellationToken);
|
||||
root = root.ReplaceNode(classDecl, newClassDecl);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
|
||||
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(Diagnostics.IdExplicitVirtual);
|
||||
}
|
||||
*/
|
||||
@@ -14,15 +14,15 @@ namespace Robust.Analyzers
|
||||
{
|
||||
const string FriendAttribute = "Robust.Shared.Analyzers.FriendAttribute";
|
||||
|
||||
public const string DiagnosticId = "RA0002";
|
||||
|
||||
private const string Title = "Tried to access friend-only member";
|
||||
private const string MessageFormat = "Tried to access member \"{0}\" in class \"{1}\" which can only be accessed by friend classes";
|
||||
private const string Description = "Make sure to specify the accessing class in the friends attribute.";
|
||||
private const string Category = "Usage";
|
||||
|
||||
[SuppressMessage("ReSharper", "RS2008")]
|
||||
private static readonly DiagnosticDescriptor Rule = new (DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, true, Description);
|
||||
private static readonly DiagnosticDescriptor Rule = new (
|
||||
Diagnostics.IdFriend,
|
||||
"Tried to access friend-only member",
|
||||
"Tried to access member \"{0}\" in class \"{1}\" which can only be accessed by friend classes",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to specify the accessing class in the friends attribute.");
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Generators;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>9</LangVersion>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -17,19 +17,21 @@ namespace Robust.Analyzers
|
||||
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);
|
||||
[SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new(
|
||||
Diagnostics.IdSerializable,
|
||||
"Class not marked as (Net)Serializable",
|
||||
"Class not marked as (Net)Serializable",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
isEnabledByDefault: true,
|
||||
description: "The class should be marked as (Net)Serializable.");
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
||||
|
||||
@@ -139,7 +141,8 @@ namespace Robust.Analyzers
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(SerializableAnalyzer.DiagnosticId);
|
||||
public sealed override ImmutableArray<string> FixableDiagnosticIds
|
||||
=> ImmutableArray.Create(Diagnostics.IdSerializable);
|
||||
|
||||
public override FixAllProvider GetFixAllProvider()
|
||||
{
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
namespace Robust.Benchmarks.NumericsHelpers
|
||||
{
|
||||
[Virtual]
|
||||
public class AddBenchmark
|
||||
{
|
||||
[Params(32, 128)]
|
||||
|
||||
@@ -4,7 +4,7 @@ using System;
|
||||
|
||||
namespace Robust.Benchmarks
|
||||
{
|
||||
internal class Program
|
||||
internal static class Program
|
||||
{
|
||||
// --allCategories=ctg1,ctg2
|
||||
// --anyCategories=ctg1,ctg2
|
||||
|
||||
@@ -9,7 +9,7 @@ using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Benchmarks.Serialization
|
||||
{
|
||||
public class BenchmarkIntSerializer : ITypeSerializer<int, ValueDataNode>
|
||||
public sealed class BenchmarkIntSerializer : ITypeSerializer<int, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Benchmarks.Serialization.Definitions;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
@@ -13,6 +14,7 @@ using YamlDotNet.RepresentationModel;
|
||||
namespace Robust.Benchmarks.Serialization.Copy
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
[Virtual]
|
||||
public class SerializationCopyBenchmark : SerializationBenchmark
|
||||
{
|
||||
public SerializationCopyBenchmark()
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Benchmarks.Serialization.Definitions
|
||||
{
|
||||
public class BenchmarkFlags
|
||||
public sealed class BenchmarkFlags
|
||||
{
|
||||
public const int Zero = 1 << 0;
|
||||
public const int ThirtyOne = 1 << 31;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Robust.Benchmarks.Serialization.Definitions
|
||||
{
|
||||
[DataDefinition]
|
||||
[Virtual]
|
||||
public class DataDefinitionWithString
|
||||
{
|
||||
[DataField("string")]
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
/// Taken from content.
|
||||
/// </summary>
|
||||
[Prototype("seed")]
|
||||
public class SeedDataDefinition : IPrototype
|
||||
public sealed class SeedDataDefinition : IPrototype
|
||||
{
|
||||
public const string Prototype = @"
|
||||
- type: seed
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
|
||||
namespace Robust.Benchmarks.Serialization.Initialize
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
[Virtual]
|
||||
public class SerializationInitializeBenchmark : SerializationBenchmark
|
||||
{
|
||||
[IterationCleanup]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Benchmarks.Serialization.Definitions;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
@@ -12,6 +13,7 @@ using YamlDotNet.RepresentationModel;
|
||||
namespace Robust.Benchmarks.Serialization.Read
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
[Virtual]
|
||||
public class SerializationReadBenchmark : SerializationBenchmark
|
||||
{
|
||||
public SerializationReadBenchmark()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Benchmarks.Serialization.Definitions;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Serialization.Markdown.Sequence;
|
||||
using Robust.Shared.Serialization.Markdown.Value;
|
||||
@@ -7,6 +8,7 @@ using Robust.Shared.Serialization.Markdown.Value;
|
||||
namespace Robust.Benchmarks.Serialization
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
[Virtual]
|
||||
public class SerializationArrayBenchmark : SerializationBenchmark
|
||||
{
|
||||
public SerializationArrayBenchmark()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Benchmarks.Serialization.Definitions;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
@@ -13,6 +14,7 @@ using YamlDotNet.RepresentationModel;
|
||||
namespace Robust.Benchmarks.Serialization.Write
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
[Virtual]
|
||||
public class SerializationWriteBenchmark : SerializationBenchmark
|
||||
{
|
||||
public SerializationWriteBenchmark()
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.Animations;
|
||||
|
||||
namespace Robust.Client.Animations
|
||||
{
|
||||
public class AnimationTrackControlProperty : AnimationTrackProperty
|
||||
public sealed class AnimationTrackControlProperty : AnimationTrackProperty
|
||||
{
|
||||
public string? Property { get; set; }
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Robust.Client.Audio.Midi
|
||||
void Shutdown();
|
||||
}
|
||||
|
||||
internal class MidiManager : IMidiManager
|
||||
internal sealed class MidiManager : IMidiManager
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
@@ -392,7 +392,7 @@ namespace Robust.Client.Audio.Midi
|
||||
/// <summary>
|
||||
/// This class is used to load soundfonts.
|
||||
/// </summary>
|
||||
private class ResourceLoaderCallbacks : SoundFontLoaderCallbacks
|
||||
private sealed class ResourceLoaderCallbacks : SoundFontLoaderCallbacks
|
||||
{
|
||||
private readonly Dictionary<int, Stream> _openStreams = new();
|
||||
private int _nextStreamId = 1;
|
||||
|
||||
@@ -180,7 +180,7 @@ namespace Robust.Client.Audio.Midi
|
||||
internal void InternalDispose();
|
||||
}
|
||||
|
||||
internal class MidiRenderer : IMidiRenderer
|
||||
internal sealed class MidiRenderer : IMidiRenderer
|
||||
{
|
||||
[Dependency] private readonly IClydeAudio _clydeAudio = default!;
|
||||
[Dependency] private readonly ITaskManager _taskManager = default!;
|
||||
|
||||
@@ -20,13 +20,12 @@ using Robust.Shared.Utility;
|
||||
namespace Robust.Client
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class BaseClient : IBaseClient
|
||||
public sealed class BaseClient : IBaseClient
|
||||
{
|
||||
[Dependency] private readonly IClientNetManager _net = default!;
|
||||
[Dependency] private readonly IPlayerManager _playMan = default!;
|
||||
[Dependency] private readonly INetConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IClientEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEntityLookup _entityLookup = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IDiscordRichPresence _discord = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
@@ -213,7 +212,6 @@ namespace Robust.Client
|
||||
{
|
||||
_entityManager.Startup();
|
||||
_mapManager.Startup();
|
||||
_entityLookup.Startup();
|
||||
|
||||
_timing.ResetSimTime();
|
||||
_timing.Paused = false;
|
||||
@@ -224,7 +222,6 @@ namespace Robust.Client
|
||||
IoCManager.Resolve<INetConfigurationManager>().FlushMessages();
|
||||
_gameStates.Reset();
|
||||
_playMan.Shutdown();
|
||||
_entityLookup.Shutdown();
|
||||
_entityManager.Shutdown();
|
||||
_mapManager.Shutdown();
|
||||
_discord.ClearPresence();
|
||||
@@ -291,7 +288,7 @@ namespace Robust.Client
|
||||
/// <summary>
|
||||
/// Event arguments for when something changed with the player.
|
||||
/// </summary>
|
||||
public class PlayerEventArgs : EventArgs
|
||||
public sealed class PlayerEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The session that triggered the event.
|
||||
@@ -310,7 +307,7 @@ namespace Robust.Client
|
||||
/// <summary>
|
||||
/// Event arguments for when the RunLevel has changed in the BaseClient.
|
||||
/// </summary>
|
||||
public class RunLevelChangedEventArgs : EventArgs
|
||||
public sealed class RunLevelChangedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// RunLevel that the BaseClient switched from.
|
||||
@@ -335,7 +332,7 @@ namespace Robust.Client
|
||||
/// <summary>
|
||||
/// Info about the server and player that is sent to the client while connecting.
|
||||
/// </summary>
|
||||
public class ServerInfo
|
||||
public sealed class ServerInfo
|
||||
{
|
||||
public ServerInfo(string serverName)
|
||||
{
|
||||
|
||||
@@ -43,11 +43,10 @@ namespace Robust.Client
|
||||
IoCManager.Register<IGameTiming, ClientGameTiming>();
|
||||
IoCManager.Register<IClientGameTiming, ClientGameTiming>();
|
||||
IoCManager.Register<IPrototypeManager, ClientPrototypeManager>();
|
||||
IoCManager.Register<IMapManager, ClientMapManager>();
|
||||
IoCManager.Register<IMapManagerInternal, ClientMapManager>();
|
||||
IoCManager.Register<IClientMapManager, ClientMapManager>();
|
||||
IoCManager.Register<IMapManager, NetworkedMapManager>();
|
||||
IoCManager.Register<IMapManagerInternal, NetworkedMapManager>();
|
||||
IoCManager.Register<INetworkedMapManager, NetworkedMapManager>();
|
||||
IoCManager.Register<IEntityManager, ClientEntityManager>();
|
||||
IoCManager.Register<IEntityLookup, EntityLookup>();
|
||||
IoCManager.Register<IReflectionManager, ClientReflectionManager>();
|
||||
IoCManager.Register<IConsoleHost, ClientConsoleHost>();
|
||||
IoCManager.Register<IClientConsoleHost, ClientConsoleHost>();
|
||||
@@ -72,7 +71,6 @@ namespace Robust.Client
|
||||
IoCManager.Register<IStateManager, StateManager>();
|
||||
IoCManager.Register<IUserInterfaceManager, UserInterfaceManager>();
|
||||
IoCManager.Register<IUserInterfaceManagerInternal, UserInterfaceManager>();
|
||||
IoCManager.Register<IDebugDrawing, DebugDrawing>();
|
||||
IoCManager.Register<ILightManager, LightManager>();
|
||||
IoCManager.Register<IDiscordRichPresence, DiscordRichPresence>();
|
||||
IoCManager.Register<IMidiManager, MidiManager>();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Robust.Client.Console
|
||||
{
|
||||
public class ClientConGroupController : IClientConGroupController
|
||||
public sealed class ClientConGroupController : IClientConGroupController
|
||||
{
|
||||
private IClientConGroupImplementation? _implementation;
|
||||
public event Action? ConGroupUpdated;
|
||||
|
||||
@@ -14,7 +14,7 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Console
|
||||
{
|
||||
public class AddStringArgs : EventArgs
|
||||
public sealed class AddStringArgs : EventArgs
|
||||
{
|
||||
public string Text { get; }
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Robust.Client.Console
|
||||
}
|
||||
}
|
||||
|
||||
public class AddFormattedMessageArgs : EventArgs
|
||||
public sealed class AddFormattedMessageArgs : EventArgs
|
||||
{
|
||||
public readonly FormattedMessage Message;
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Robust.Client.Console
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IClientConsoleHost" />
|
||||
internal class ClientConsoleHost : ConsoleHost, IClientConsoleHost
|
||||
internal sealed class ClientConsoleHost : ConsoleHost, IClientConsoleHost
|
||||
{
|
||||
[Dependency] private readonly IClientConGroupController _conGroup = default!;
|
||||
|
||||
@@ -109,13 +109,13 @@ namespace Robust.Client.Console
|
||||
if (AvailableCommands.ContainsKey(commandName))
|
||||
{
|
||||
var playerManager = IoCManager.Resolve<IPlayerManager>();
|
||||
|
||||
#if !DEBUG
|
||||
if (!_conGroup.CanCommand(commandName) && playerManager.LocalPlayer?.Session.Status > SessionStatus.Connecting)
|
||||
{
|
||||
WriteError(null, $"Insufficient perms for command: {commandName}");
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
var command1 = AvailableCommands[commandName];
|
||||
args.RemoveAt(0);
|
||||
var shell = new ConsoleShell(this, null);
|
||||
@@ -209,7 +209,7 @@ namespace Robust.Client.Console
|
||||
/// These dummies are made purely so list and help can list server-side commands.
|
||||
/// </summary>
|
||||
[Reflect(false)]
|
||||
internal class ServerDummyCommand : IConsoleCommand
|
||||
internal sealed class ServerDummyCommand : IConsoleCommand
|
||||
{
|
||||
internal ServerDummyCommand(string command, string help, string description)
|
||||
{
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public class SaveConfig : IConsoleCommand
|
||||
public sealed class SaveConfig : IConsoleCommand
|
||||
{
|
||||
public string Command => "saveconfig";
|
||||
public string Description => "Saves the client configuration to the config file";
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Console;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
class ClearCommand : IConsoleCommand
|
||||
sealed class ClearCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "cls";
|
||||
public string Help => "Clears the debug console of all messages.";
|
||||
@@ -18,7 +18,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
class FillCommand : IConsoleCommand
|
||||
sealed class FillCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "fill";
|
||||
public string Help => "Fills the console with some nonsense for debugging.";
|
||||
|
||||
@@ -30,7 +30,7 @@ using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
internal class DumpEntitiesCommand : IConsoleCommand
|
||||
internal sealed class DumpEntitiesCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "dumpentities";
|
||||
public string Help => "Dump entity list";
|
||||
@@ -47,7 +47,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class GetComponentRegistrationCommand : IConsoleCommand
|
||||
internal sealed class GetComponentRegistrationCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "getcomponentregistration";
|
||||
public string Help => "Usage: getcomponentregistration <componentName>";
|
||||
@@ -93,7 +93,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class ToggleMonitorCommand : IConsoleCommand
|
||||
internal sealed class ToggleMonitorCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "monitor";
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class ExceptionCommand : IConsoleCommand
|
||||
internal sealed class ExceptionCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "fuck";
|
||||
public string Help => "Throws an exception";
|
||||
@@ -160,7 +160,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class ShowPositionsCommand : IConsoleCommand
|
||||
internal sealed class ShowPositionsCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "showpos";
|
||||
public string Help => "";
|
||||
@@ -168,12 +168,12 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<IDebugDrawing>();
|
||||
var mgr = EntitySystem.Get<DebugDrawingSystem>();
|
||||
mgr.DebugPositions = !mgr.DebugPositions;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ShowRayCommand : IConsoleCommand
|
||||
internal sealed class ShowRayCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "showrays";
|
||||
public string Help => "Usage: showrays <raylifetime>";
|
||||
@@ -200,7 +200,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class DisconnectCommand : IConsoleCommand
|
||||
internal sealed class DisconnectCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "disconnect";
|
||||
public string Help => "";
|
||||
@@ -212,7 +212,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class EntityInfoCommand : IConsoleCommand
|
||||
internal sealed class EntityInfoCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "entfo";
|
||||
|
||||
@@ -264,7 +264,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class SnapGridGetCell : IConsoleCommand
|
||||
internal sealed class SnapGridGetCell : IConsoleCommand
|
||||
{
|
||||
public string Command => "sggcell";
|
||||
public string Help => "sggcell <gridID> <vector2i>\nThat vector2i param is in the form x<int>,y<int>.";
|
||||
@@ -313,7 +313,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class SetPlayerName : IConsoleCommand
|
||||
internal sealed class SetPlayerName : IConsoleCommand
|
||||
{
|
||||
public string Command => "overrideplayername";
|
||||
public string Description => "Changes the name used when attempting to connect to the server.";
|
||||
@@ -333,7 +333,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class LoadResource : IConsoleCommand
|
||||
internal sealed class LoadResource : IConsoleCommand
|
||||
{
|
||||
public string Command => "ldrsc";
|
||||
public string Description => "Pre-caches a resource.";
|
||||
@@ -370,7 +370,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class ReloadResource : IConsoleCommand
|
||||
internal sealed class ReloadResource : IConsoleCommand
|
||||
{
|
||||
public string Command => "rldrsc";
|
||||
public string Description => "Reloads a resource.";
|
||||
@@ -404,7 +404,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class GridTileCount : IConsoleCommand
|
||||
internal sealed class GridTileCount : IConsoleCommand
|
||||
{
|
||||
public string Command => "gridtc";
|
||||
public string Description => "Gets the tile count of a grid";
|
||||
@@ -438,7 +438,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class GuiDumpCommand : IConsoleCommand
|
||||
internal sealed class GuiDumpCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "guidump";
|
||||
public string Description => "Dump GUI tree to /guidump.txt in user data.";
|
||||
@@ -512,7 +512,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class UITestCommand : IConsoleCommand
|
||||
internal sealed class UITestCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "uitest";
|
||||
public string Description => "Open a dummy UI testing window";
|
||||
@@ -644,7 +644,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class SetClipboardCommand : IConsoleCommand
|
||||
internal sealed class SetClipboardCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "setclipboard";
|
||||
public string Description => "Sets the system clipboard";
|
||||
@@ -657,7 +657,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class GetClipboardCommand : IConsoleCommand
|
||||
internal sealed class GetClipboardCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "getclipboard";
|
||||
public string Description => "Gets the system clipboard";
|
||||
@@ -670,7 +670,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class ToggleLight : IConsoleCommand
|
||||
internal sealed class ToggleLight : IConsoleCommand
|
||||
{
|
||||
public string Command => "togglelight";
|
||||
public string Description => "Toggles light rendering.";
|
||||
@@ -684,7 +684,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class ToggleFOV : IConsoleCommand
|
||||
internal sealed class ToggleFOV : IConsoleCommand
|
||||
{
|
||||
public string Command => "togglefov";
|
||||
public string Description => "Toggles fov for client.";
|
||||
@@ -698,7 +698,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class ToggleHardFOV : IConsoleCommand
|
||||
internal sealed class ToggleHardFOV : IConsoleCommand
|
||||
{
|
||||
public string Command => "togglehardfov";
|
||||
public string Description => "Toggles hard fov for client (for debugging space-station-14#2353).";
|
||||
@@ -712,7 +712,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class ToggleShadows : IConsoleCommand
|
||||
internal sealed class ToggleShadows : IConsoleCommand
|
||||
{
|
||||
public string Command => "toggleshadows";
|
||||
public string Description => "Toggles shadow rendering.";
|
||||
@@ -725,7 +725,7 @@ namespace Robust.Client.Console.Commands
|
||||
mgr.DrawShadows = !mgr.DrawShadows;
|
||||
}
|
||||
}
|
||||
internal class ToggleLightBuf : IConsoleCommand
|
||||
internal sealed class ToggleLightBuf : IConsoleCommand
|
||||
{
|
||||
public string Command => "togglelightbuf";
|
||||
public string Description => "Toggles lighting rendering. This includes shadows but not FOV.";
|
||||
@@ -739,7 +739,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class GcCommand : IConsoleCommand
|
||||
internal sealed class GcCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "gc";
|
||||
public string Description => "Run the GC.";
|
||||
@@ -761,7 +761,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class GcFullCommand : IConsoleCommand
|
||||
internal sealed class GcFullCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "gcf";
|
||||
public string Description => "Run the GC, fully, compacting LOH and everything.";
|
||||
@@ -774,7 +774,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class GcModeCommand : IConsoleCommand
|
||||
internal sealed class GcModeCommand : IConsoleCommand
|
||||
{
|
||||
|
||||
public string Command => "gc_mode";
|
||||
@@ -816,7 +816,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
}
|
||||
|
||||
internal class SerializeStatsCommand : IConsoleCommand
|
||||
internal sealed class SerializeStatsCommand : IConsoleCommand
|
||||
{
|
||||
|
||||
public string Command => "szr_stats";
|
||||
@@ -836,7 +836,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
}
|
||||
|
||||
internal class ChunkInfoCommand : IConsoleCommand
|
||||
internal sealed class ChunkInfoCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "chunkinfo";
|
||||
public string Description => "Gets info about a chunk under your mouse cursor.";
|
||||
@@ -861,11 +861,11 @@ namespace Robust.Client.Console.Commands
|
||||
var chunkIndex = grid.LocalToChunkIndices(grid.MapToGrid(mousePos));
|
||||
var chunk = internalGrid.GetChunk(chunkIndex);
|
||||
|
||||
shell.WriteLine($"worldBounds: {chunk.CalcWorldAABB()} localBounds: {chunk.CalcLocalBounds()}");
|
||||
shell.WriteLine($"worldBounds: {internalGrid.CalcWorldAABB(chunk)} localBounds: {chunk.CachedBounds}");
|
||||
}
|
||||
}
|
||||
|
||||
internal class ReloadShadersCommand : IConsoleCommand
|
||||
internal sealed class ReloadShadersCommand : IConsoleCommand
|
||||
{
|
||||
|
||||
public string Command => "rldshader";
|
||||
@@ -1036,7 +1036,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
}
|
||||
|
||||
internal class ClydeDebugLayerCommand : IConsoleCommand
|
||||
internal sealed class ClydeDebugLayerCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "cldbglyr";
|
||||
public string Description => "Toggle fov and light debug layers";
|
||||
@@ -1061,7 +1061,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal class GetKeyInfoCommand : IConsoleCommand
|
||||
internal sealed class GetKeyInfoCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "keyinfo";
|
||||
public string Description => "Keys key info for a key";
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
public class GridChunkBBCommand : IConsoleCommand
|
||||
public sealed class GridChunkBBCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "showchunkbb";
|
||||
public string Description => "Displays chunk bounds for the purposes of rendering";
|
||||
|
||||
@@ -5,7 +5,7 @@ using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
class HelpCommand : IConsoleCommand
|
||||
sealed class HelpCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "help";
|
||||
public string Help => "When no arguments are provided, displays a generic help text. When an argument is passed, display the help text for the command with that name.";
|
||||
@@ -44,7 +44,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
class ListCommand : IConsoleCommand
|
||||
sealed class ListCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "list";
|
||||
public string Help => "Usage: list [filter]\n" +
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.Console;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
class LogSetLevelCommand : IConsoleCommand
|
||||
sealed class LogSetLevelCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "loglevel";
|
||||
public string Description => "Changes the log level for a provided sawmill.";
|
||||
@@ -40,7 +40,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
class TestLog : IConsoleCommand
|
||||
sealed class TestLog : IConsoleCommand
|
||||
{
|
||||
public string Command => "testlog";
|
||||
public string Description => "Writes a test log to a sawmill.";
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
class HardQuitCommand : IConsoleCommand
|
||||
sealed class HardQuitCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "hardquit";
|
||||
public string Description => "Kills the game client instantly.";
|
||||
@@ -16,7 +16,7 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
class QuitCommand : IConsoleCommand
|
||||
sealed class QuitCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "quit";
|
||||
public string Description => "Shuts down the game client gracefully.";
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.IoC;
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class SetInputContextCommand : IConsoleCommand
|
||||
public sealed class SetInputContextCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "setinputcontext";
|
||||
public string Description => "Sets the active input context.";
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
public class VelocitiesCommand : IConsoleCommand
|
||||
public sealed class VelocitiesCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "showvelocities";
|
||||
public string Description => "Displays your angular and linear velocities";
|
||||
|
||||
@@ -11,7 +11,7 @@ using static Robust.Shared.Network.Messages.MsgScriptCompletionResponse;
|
||||
|
||||
namespace Robust.Client.Console
|
||||
{
|
||||
public class Completions : DefaultWindow
|
||||
public sealed class Completions : DefaultWindow
|
||||
{
|
||||
private HistoryLineEdit _textBar;
|
||||
private ScrollContainer _suggestPanel = new()
|
||||
@@ -75,7 +75,7 @@ namespace Robust.Client.Console
|
||||
}
|
||||
|
||||
// Label and ghetto button.
|
||||
public class Entry : RichTextLabel
|
||||
public sealed class Entry : RichTextLabel
|
||||
{
|
||||
public readonly LiteResult Result;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Network.Messages;
|
||||
|
||||
namespace Robust.Client.Console
|
||||
{
|
||||
public partial class ScriptClient : IScriptClient
|
||||
public sealed partial class ScriptClient : IScriptClient
|
||||
{
|
||||
[Dependency] private readonly IClientConGroupController _conGroupController = default!;
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
|
||||
@@ -15,7 +15,7 @@ using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.Console
|
||||
{
|
||||
public class WatchWindow : DefaultWindow
|
||||
public sealed class WatchWindow : DefaultWindow
|
||||
{
|
||||
private readonly IReflectionManager _reflectionManager;
|
||||
|
||||
|
||||
@@ -6,17 +6,21 @@ using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class DebugDrawing : IDebugDrawing
|
||||
/// <summary>
|
||||
/// A collection of visual debug overlays for the client game.
|
||||
/// </summary>
|
||||
public sealed class DebugDrawingSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IEntityLookup _lookup = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
|
||||
private bool _debugPositions;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Toggles the visual overlay of the local origin for each entity on screen.
|
||||
/// </summary>
|
||||
public bool DebugPositions
|
||||
{
|
||||
get => _debugPositions;
|
||||
@@ -42,13 +46,13 @@ namespace Robust.Client.Debugging
|
||||
|
||||
private sealed class EntityPositionOverlay : Overlay
|
||||
{
|
||||
private readonly IEntityLookup _lookup;
|
||||
private readonly EntityLookupSystem _lookup;
|
||||
private readonly IEyeManager _eyeManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public EntityPositionOverlay(IEntityLookup lookup, IEyeManager eyeManager, IEntityManager entityManager)
|
||||
public EntityPositionOverlay(EntityLookupSystem lookup, IEyeManager eyeManager, IEntityManager entityManager)
|
||||
{
|
||||
_lookup = lookup;
|
||||
_eyeManager = eyeManager;
|
||||
@@ -61,13 +65,11 @@ namespace Robust.Client.Debugging
|
||||
|
||||
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
|
||||
var viewport = _eyeManager.GetWorldViewport();
|
||||
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var entity in _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, viewport))
|
||||
{
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(entity);
|
||||
|
||||
var center = transform.WorldPosition;
|
||||
var worldRotation = transform.WorldRotation;
|
||||
var (center, worldRotation) = xformQuery.GetComponent(entity).GetWorldPositionRotation();
|
||||
|
||||
var xLine = worldRotation.RotateVec(Vector2.UnitX);
|
||||
var yLine = worldRotation.RotateVec(Vector2.UnitY);
|
||||
@@ -209,8 +209,8 @@ namespace Robust.Client.Debugging
|
||||
|
||||
foreach (var fixture in _entityManager.GetComponent<FixturesComponent>(physBody.Owner).Fixtures.Values)
|
||||
{
|
||||
// Invalid shape - Box2D doesn't check for IsSensor
|
||||
if (physBody.BodyType == BodyType.Dynamic && fixture.Mass == 0f)
|
||||
// Invalid shape - Box2D doesn't check for IsSensor but we will for sanity.
|
||||
if (physBody.BodyType == BodyType.Dynamic && fixture.Mass == 0f && fixture.Hard)
|
||||
{
|
||||
DrawShape(worldHandle, fixture, xform, Color.Red.WithAlpha(AlphaModifier));
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Robust.Client.Debugging
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of visual debug overlays for the client game.
|
||||
/// </summary>
|
||||
public interface IDebugDrawing
|
||||
{
|
||||
/// <summary>
|
||||
/// Toggles the visual overlay of the local origin for each entity on screen.
|
||||
/// </summary>
|
||||
bool DebugPositions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace Robust.Client
|
||||
{
|
||||
internal partial class GameController
|
||||
{
|
||||
internal class LoaderEntryPoint : ILoaderEntryPoint
|
||||
internal sealed class LoaderEntryPoint : ILoaderEntryPoint
|
||||
{
|
||||
public void Main(IMainArgs args)
|
||||
{
|
||||
|
||||
@@ -50,7 +50,6 @@ namespace Robust.Client
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
[Dependency] private readonly ITimerManager _timerManager = default!;
|
||||
[Dependency] private readonly IClientEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEntityLookup _lookup = default!;
|
||||
[Dependency] private readonly IPlacementManager _placementManager = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
|
||||
[Dependency] private readonly IOverlayManagerInternal _overlayManager = default!;
|
||||
@@ -455,6 +454,7 @@ namespace Robust.Client
|
||||
private void Tick(FrameEventArgs frameEventArgs)
|
||||
{
|
||||
_modLoader.BroadcastUpdate(ModUpdateLevel.PreEngine, frameEventArgs);
|
||||
_console.CommandBufferExecute();
|
||||
_timerManager.UpdateTimers(frameEventArgs);
|
||||
_taskManager.ProcessPendingTasks();
|
||||
|
||||
@@ -470,7 +470,6 @@ namespace Robust.Client
|
||||
// The last real tick is the current tick! This way we won't be in "prediction" mode.
|
||||
_gameTiming.LastRealTick = _gameTiming.CurTick;
|
||||
_entityManager.TickUpdate(frameEventArgs.DeltaSeconds, noPredictions: false);
|
||||
_lookup.Update();
|
||||
}
|
||||
|
||||
_modLoader.BroadcastUpdate(ModUpdateLevel.PostEngine, frameEventArgs);
|
||||
@@ -510,7 +509,7 @@ namespace Robust.Client
|
||||
logManager.GetSawmill("discord").Level = LogLevel.Warning;
|
||||
logManager.GetSawmill("net.predict").Level = LogLevel.Info;
|
||||
logManager.GetSawmill("szr").Level = LogLevel.Info;
|
||||
logManager.GetSawmill("loc").Level = LogLevel.Error;
|
||||
logManager.GetSawmill("loc").Level = LogLevel.Warning;
|
||||
|
||||
#if DEBUG_ONLY_FCE_INFO
|
||||
#if DEBUG_ONLY_FCE_LOG
|
||||
@@ -575,7 +574,6 @@ namespace Robust.Client
|
||||
|
||||
_networkManager.Shutdown("Client shutting down");
|
||||
_midiManager.Shutdown();
|
||||
IoCManager.Resolve<IEntityLookup>().Shutdown();
|
||||
_entityManager.Shutdown();
|
||||
_clyde.Shutdown();
|
||||
_clydeAudio.Shutdown();
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client
|
||||
{
|
||||
public class GameControllerOptions
|
||||
public sealed class GameControllerOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether content sandboxing will be enabled & enforced.
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Reflection;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
internal class ClientComponentFactory : ComponentFactory
|
||||
internal sealed class ClientComponentFactory : ComponentFactory
|
||||
{
|
||||
public ClientComponentFactory(IDynamicTypeFactoryInternal typeFactory, IReflectionManager reflectionManager, IConsoleHost conHost)
|
||||
: base(typeFactory, reflectionManager, conHost)
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Runtime.Serialization;
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[Serializable]
|
||||
[Virtual]
|
||||
public class ComponentStateApplyException : Exception
|
||||
{
|
||||
public ComponentStateApplyException()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
@@ -14,6 +15,7 @@ public abstract class AppearanceVisualizer
|
||||
/// Initializes an entity to be managed by this appearance controller.
|
||||
/// DO NOT assume this is your only entity. Visualizers are shared.
|
||||
/// </summary>
|
||||
[Obsolete("Subscribe to your component being initialised instead.")]
|
||||
public virtual void InitializeEntity(EntityUid entity)
|
||||
{
|
||||
}
|
||||
@@ -23,6 +25,7 @@ public abstract class AppearanceVisualizer
|
||||
/// Update its visuals here.
|
||||
/// </summary>
|
||||
/// <param name="component">The appearance component of the entity that might need updating.</param>
|
||||
[Obsolete("Subscribe to AppearanceChangeEvent instead.")]
|
||||
public virtual void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -9,39 +10,10 @@ namespace Robust.Client.GameObjects;
|
||||
/// This is the client instance of <see cref="AppearanceComponent"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(AppearanceComponent))]
|
||||
[ComponentReference(typeof(AppearanceComponent)), Friend(typeof(AppearanceSystem))]
|
||||
public sealed class ClientAppearanceComponent : AppearanceComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
private bool _appearanceDirty;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("visuals")]
|
||||
internal List<AppearanceVisualizer> Visualizers = new();
|
||||
|
||||
protected override void MarkDirty()
|
||||
{
|
||||
if (_appearanceDirty)
|
||||
return;
|
||||
|
||||
EntitySystem.Get<AppearanceSystem>().EnqueueUpdate(this);
|
||||
_appearanceDirty = true;
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
foreach (var visual in Visualizers)
|
||||
{
|
||||
visual.InitializeEntity(Owner);
|
||||
}
|
||||
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
internal void UnmarkDirty()
|
||||
{
|
||||
_appearanceDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[ComponentReference(typeof(SharedEyeComponent))]
|
||||
public class EyeComponent : SharedEyeComponent
|
||||
public sealed class EyeComponent : SharedEyeComponent
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
@@ -12,7 +12,7 @@ using Robust.Shared.Utility;
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class IconComponent : Component, ISerializationHooks
|
||||
public sealed class IconComponent : Component, ISerializationHooks
|
||||
{
|
||||
public IDirectionalTextureProvider? Icon { get; private set; }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Defines data fields used in the <see cref="InputSystem"/>.
|
||||
/// </summary>
|
||||
public class InputComponent : Component
|
||||
public sealed class InputComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The context that will be made active for a client that attaches to this entity.
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[ComponentReference(typeof(OccluderComponent))]
|
||||
internal sealed class ClientOccluderComponent : OccluderComponent
|
||||
public sealed class ClientOccluderComponent : OccluderComponent
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
@@ -40,13 +40,14 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public void AnchorStateChanged()
|
||||
{
|
||||
SendDirty();
|
||||
var xform = _entityManager.GetComponent<TransformComponent>(Owner);
|
||||
SendDirty(xform);
|
||||
|
||||
if(!_entityManager.GetComponent<TransformComponent>(Owner).Anchored)
|
||||
if(!xform.Anchored)
|
||||
return;
|
||||
|
||||
var grid = _mapManager.GetGrid(_entityManager.GetComponent<TransformComponent>(Owner).GridID);
|
||||
_lastPosition = (_entityManager.GetComponent<TransformComponent>(Owner).GridID, grid.TileIndicesFor(_entityManager.GetComponent<TransformComponent>(Owner).Coordinates));
|
||||
var grid = _mapManager.GetGrid(xform.GridID);
|
||||
_lastPosition = (xform.GridID, grid.TileIndicesFor(xform.Coordinates));
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
@@ -56,9 +57,10 @@ namespace Robust.Client.GameObjects
|
||||
SendDirty();
|
||||
}
|
||||
|
||||
private void SendDirty()
|
||||
private void SendDirty(TransformComponent? xform = null)
|
||||
{
|
||||
if (_entityManager.GetComponent<TransformComponent>(Owner).Anchored)
|
||||
xform ??= _entityManager.GetComponent<TransformComponent>(Owner);
|
||||
if (xform.Anchored)
|
||||
{
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local,
|
||||
new OccluderDirtyEvent(Owner, _lastPosition));
|
||||
@@ -69,13 +71,29 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
Occluding = OccluderDir.None;
|
||||
|
||||
if (Deleted || !_entityManager.GetComponent<TransformComponent>(Owner).Anchored)
|
||||
if (Deleted)
|
||||
return;
|
||||
|
||||
// Content may want to override the default behavior for occlusion.
|
||||
var xform = _entityManager.GetComponent<TransformComponent>(Owner);
|
||||
var ev = new OccluderDirectionsEvent
|
||||
{
|
||||
Component = xform,
|
||||
};
|
||||
|
||||
_entityManager.EventBus.RaiseLocalEvent(Owner, ref ev);
|
||||
|
||||
if (ev.Handled)
|
||||
{
|
||||
Occluding = ev.Directions;
|
||||
return;
|
||||
}
|
||||
|
||||
var grid = _mapManager.GetGrid(_entityManager.GetComponent<TransformComponent>(Owner).GridID);
|
||||
var position = _entityManager.GetComponent<TransformComponent>(Owner).Coordinates;
|
||||
if (!xform.Anchored)
|
||||
return;
|
||||
|
||||
var grid = _mapManager.GetGrid(xform.GridID);
|
||||
var position = xform.Coordinates;
|
||||
void CheckDir(Direction dir, OccluderDir oclDir)
|
||||
{
|
||||
foreach (var neighbor in grid.GetInDir(position, dir))
|
||||
@@ -88,7 +106,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
var angle = _entityManager.GetComponent<TransformComponent>(Owner).LocalRotation;
|
||||
var angle = xform.LocalRotation;
|
||||
var dirRolling = angle.GetCardinalDir();
|
||||
// dirRolling starts at effective south
|
||||
|
||||
@@ -103,15 +121,28 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
CheckDir(dirRolling, OccluderDir.East);
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum OccluderDir : byte
|
||||
{
|
||||
None = 0,
|
||||
North = 1,
|
||||
East = 1 << 1,
|
||||
South = 1 << 2,
|
||||
West = 1 << 3,
|
||||
}
|
||||
[Flags]
|
||||
public enum OccluderDir : byte
|
||||
{
|
||||
None = 0,
|
||||
North = 1,
|
||||
East = 1 << 1,
|
||||
South = 1 << 2,
|
||||
West = 1 << 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised by occluders when trying to get occlusion directions.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public struct OccluderDirectionsEvent
|
||||
{
|
||||
public bool Handled = false;
|
||||
public OccluderDir Directions = OccluderDir.None;
|
||||
public TransformComponent Component = default!;
|
||||
|
||||
public OccluderDirectionsEvent() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedPointLightComponent))]
|
||||
public class PointLightComponent : SharedPointLightComponent, ISerializationHooks
|
||||
public sealed class PointLightComponent : SharedPointLightComponent, ISerializationHooks
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace Robust.Client.GameObjects
|
||||
internal RenderingTreeComponent? RenderTree { get; set; }
|
||||
}
|
||||
|
||||
public class PointLightRadiusChangedEvent : EntityEventArgs
|
||||
public sealed class PointLightRadiusChangedEvent : EntityEventArgs
|
||||
{
|
||||
public PointLightComponent PointLightComponent { get; }
|
||||
|
||||
|
||||
@@ -119,7 +119,8 @@ namespace Robust.Client.GameObjects
|
||||
/// This is useful to allow layer map configs to be defined in prototypes,
|
||||
/// while still allowing code to create configs if they're absent.
|
||||
/// </remarks>
|
||||
void LayerMapReserveBlank(object key);
|
||||
/// <returns>Index of the new layer.</returns>
|
||||
int LayerMapReserveBlank(object key);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a layer without texture (thus falling back to the error texture).
|
||||
@@ -145,8 +146,8 @@ namespace Robust.Client.GameObjects
|
||||
void RemoveLayer(int layer);
|
||||
void RemoveLayer(object layerKey);
|
||||
|
||||
void LayerSetShader(int layer, ShaderInstance shader);
|
||||
void LayerSetShader(object layerKey, ShaderInstance shader);
|
||||
void LayerSetShader(int layer, ShaderInstance shader, string? prototype = null);
|
||||
void LayerSetShader(object layerKey, ShaderInstance shader, string? prototype = null);
|
||||
void LayerSetShader(int layer, string shaderName);
|
||||
void LayerSetShader(object layerKey, string shaderName);
|
||||
|
||||
@@ -217,8 +218,8 @@ namespace Robust.Client.GameObjects
|
||||
int GetLayerDirectionCount(ISpriteLayer layer);
|
||||
|
||||
/// <summary>
|
||||
/// Calculate sprite bounding box in world-space coordinates.
|
||||
/// Calculate the rotated sprite bounding box in world-space coordinates.
|
||||
/// </summary>
|
||||
Box2 CalculateBoundingBox(Vector2 worldPos);
|
||||
Box2Rotated CalculateRotatedBoundingBox(Vector2 worldPosition, Angle worldRotation, IEye? eye = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
RSI.State.Direction EffectiveDirection(Angle worldRotation);
|
||||
|
||||
Vector2 LocalToLayer(Vector2 localPos);
|
||||
|
||||
/// <summary>
|
||||
/// Layer size in pixels.
|
||||
/// Don't account layer scale or sprite world transform.
|
||||
@@ -37,6 +35,6 @@ namespace Robust.Client.GameObjects
|
||||
/// Calculate layer bounding box in sprite local-space coordinates.
|
||||
/// </summary>
|
||||
/// <returns>Bounding box in sprite local-space coordinates.</returns>
|
||||
Box2 CalculateBoundingBox();
|
||||
Box2 CalculateBoundingBox(Angle worldAngle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Robust.Client.GameObjects
|
||||
private bool _enabled;
|
||||
}
|
||||
|
||||
public class SpriteBoundsOverlay : Overlay
|
||||
public sealed class SpriteBoundsOverlay : Overlay
|
||||
{
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
@@ -84,10 +84,16 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
foreach (var sprite in comp.SpriteTree.QueryAabb(localAABB))
|
||||
{
|
||||
var worldPos = _entityManager.GetComponent<TransformComponent>(sprite.Owner).WorldPosition;
|
||||
var bounds = sprite.CalculateBoundingBox(worldPos);
|
||||
var (worldPos, worldRot) = _entityManager.GetComponent<TransformComponent>(sprite.Owner).GetWorldPositionRotation();
|
||||
var bounds = sprite.CalculateRotatedBoundingBox(worldPos, worldRot);
|
||||
|
||||
// Get scaled down bounds used to indicate the "south" of a sprite.
|
||||
var localBound = bounds.Box;
|
||||
var smallLocal = localBound.Scale(0.2f).Translated(-new Vector2(0f, localBound.Extents.Y));
|
||||
var southIndicator = new Box2Rotated(smallLocal, bounds.Rotation, bounds.Origin);
|
||||
|
||||
handle.DrawRect(bounds, Color.Red.WithAlpha(0.2f));
|
||||
handle.DrawRect(bounds.Scale(0.2f).Translated(-new Vector2(0f, bounds.Extents.Y)), Color.Blue.WithAlpha(0.5f));
|
||||
handle.DrawRect(southIndicator, Color.Blue.WithAlpha(0.5f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
|
||||
using RSIDirection = Robust.Client.Graphics.RSI.State.Direction;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
@@ -31,6 +32,8 @@ namespace Robust.Client.GameObjects
|
||||
[Dependency] private readonly IResourceCache resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager prototypes = default!;
|
||||
[Dependency] private readonly IEntityManager entities = default!;
|
||||
[Dependency] private readonly IReflectionManager reflection = default!;
|
||||
[Dependency] private readonly IEyeManager eyeManager = default!;
|
||||
|
||||
[DataField("visible")]
|
||||
private bool _visible = true;
|
||||
@@ -72,7 +75,11 @@ namespace Robust.Client.GameObjects
|
||||
public Vector2 Scale
|
||||
{
|
||||
get => scale;
|
||||
set => scale = value;
|
||||
set
|
||||
{
|
||||
scale = value;
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
[DataField("rotation")]
|
||||
@@ -83,7 +90,11 @@ namespace Robust.Client.GameObjects
|
||||
public Angle Rotation
|
||||
{
|
||||
get => rotation;
|
||||
set => rotation = value;
|
||||
set
|
||||
{
|
||||
rotation = value;
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
[DataField("offset")]
|
||||
@@ -97,12 +108,18 @@ namespace Robust.Client.GameObjects
|
||||
public Vector2 Offset
|
||||
{
|
||||
get => offset;
|
||||
set => offset = value;
|
||||
set
|
||||
{
|
||||
offset = value;
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
[DataField("color")]
|
||||
private Color color = Color.White;
|
||||
|
||||
public Matrix3 LocalMatrix = Matrix3.Identity;
|
||||
|
||||
[Animatable]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Color Color
|
||||
@@ -134,113 +151,7 @@ namespace Robust.Client.GameObjects
|
||||
Layers.Clear();
|
||||
foreach (var layerDatum in value)
|
||||
{
|
||||
var anyTextureAttempted = false;
|
||||
var layer = new Layer(this);
|
||||
if (!string.IsNullOrWhiteSpace(layerDatum.RsiPath))
|
||||
{
|
||||
var path = TextureRoot / layerDatum.RsiPath;
|
||||
|
||||
if (IoCManager.Resolve<IResourceCache>().TryGetResource(path, out RSIResource? resource))
|
||||
{
|
||||
layer.RSI = resource.RSI;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.ErrorS(LogCategory, "Unable to load layer RSI '{0}'.", path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(layerDatum.State))
|
||||
{
|
||||
anyTextureAttempted = true;
|
||||
var theRsi = layer.RSI ?? BaseRSI;
|
||||
if (theRsi == null)
|
||||
{
|
||||
Logger.ErrorS(LogCategory,
|
||||
"Layer has no RSI to load states from. Cannot use 'state' property. ({0})",
|
||||
layerDatum.State);
|
||||
}
|
||||
else
|
||||
{
|
||||
var stateid = new RSI.StateId(layerDatum.State);
|
||||
layer.State = stateid;
|
||||
if (theRsi.TryGetState(stateid, out var state))
|
||||
{
|
||||
// Always use south because this layer will be cached in the serializer.
|
||||
layer.AnimationTimeLeft = state.GetDelay(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.ErrorS(LogCategory,
|
||||
$"State '{stateid}' not found in RSI: '{theRsi.Path}'.",
|
||||
stateid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(layerDatum.TexturePath))
|
||||
{
|
||||
anyTextureAttempted = true;
|
||||
if (layer.State.IsValid)
|
||||
{
|
||||
Logger.ErrorS(LogCategory,
|
||||
"Cannot specify 'texture' on a layer if it has an RSI state specified."
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
layer.Texture =
|
||||
IoCManager.Resolve<IResourceCache>().GetResource<TextureResource>(TextureRoot / layerDatum.TexturePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(layerDatum.Shader))
|
||||
{
|
||||
if (IoCManager.Resolve<IPrototypeManager>().TryIndex<ShaderPrototype>(layerDatum.Shader, out var prototype))
|
||||
{
|
||||
layer.Shader = prototype.Instance();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.ErrorS(LogCategory,
|
||||
"Shader prototype '{0}' does not exist.",
|
||||
layerDatum.Shader);
|
||||
}
|
||||
}
|
||||
|
||||
layer.Color = layerDatum.Color;
|
||||
layer.Rotation = layerDatum.Rotation;
|
||||
layer._offset = layerDatum.Offset;
|
||||
// If neither state: nor texture: were provided we assume that they want a blank invisible layer.
|
||||
layer.Visible = anyTextureAttempted && layerDatum.Visible;
|
||||
layer.Scale = layerDatum.Scale;
|
||||
|
||||
Layers.Add(layer);
|
||||
|
||||
if (layerDatum.MapKeys != null)
|
||||
{
|
||||
var index = Layers.Count - 1;
|
||||
foreach (var keyString in layerDatum.MapKeys)
|
||||
{
|
||||
object key;
|
||||
if (IoCManager.Resolve<IReflectionManager>().TryParseEnumReference(keyString, out var @enum))
|
||||
{
|
||||
key = @enum;
|
||||
}
|
||||
else
|
||||
{
|
||||
key = keyString;
|
||||
}
|
||||
|
||||
if (LayerMap.ContainsKey(key))
|
||||
{
|
||||
Logger.ErrorS(LogCategory, "Duplicate layer map key definition: {0}", key);
|
||||
continue;
|
||||
}
|
||||
|
||||
LayerMap.Add(key, index);
|
||||
}
|
||||
}
|
||||
AddLayer(layerDatum);
|
||||
}
|
||||
|
||||
_layerMapShared = true;
|
||||
@@ -336,11 +247,13 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(rsi))
|
||||
{
|
||||
var rsiPath = TextureRoot / rsi;
|
||||
if(IoCManager.Resolve<IResourceCache>().TryGetResource(rsiPath, out RSIResource? resource))
|
||||
if(resourceCache.TryGetResource(rsiPath, out RSIResource? resource))
|
||||
{
|
||||
BaseRSI = resource.RSI;
|
||||
}
|
||||
@@ -373,6 +286,8 @@ namespace Robust.Client.GameObjects
|
||||
LayerMap.Clear();
|
||||
LayerDatums = layerDatums;
|
||||
}
|
||||
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -390,6 +305,7 @@ namespace Robust.Client.GameObjects
|
||||
offset = other.offset;
|
||||
rotation = other.rotation;
|
||||
scale = other.scale;
|
||||
UpdateLocalMatrix();
|
||||
drawDepth = other.drawDepth;
|
||||
_screenLock = other._screenLock;
|
||||
_overrideDirection = other._overrideDirection;
|
||||
@@ -415,9 +331,14 @@ namespace Robust.Client.GameObjects
|
||||
RenderOrder = other.RenderOrder;
|
||||
}
|
||||
|
||||
internal void UpdateLocalMatrix()
|
||||
{
|
||||
LocalMatrix = Matrix3.CreateTransform(in offset, in rotation, in scale);
|
||||
}
|
||||
|
||||
public Matrix3 GetLocalMatrix()
|
||||
{
|
||||
return Matrix3.CreateTransform(in offset, in rotation, in scale);
|
||||
return LocalMatrix;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -462,14 +383,17 @@ namespace Robust.Client.GameObjects
|
||||
_layerMapShared = false;
|
||||
}
|
||||
|
||||
public void LayerMapReserveBlank(object key)
|
||||
public int LayerMapReserveBlank(object key)
|
||||
{
|
||||
if (LayerMapTryGet(key, out var _))
|
||||
if (LayerMapTryGet(key, out var index))
|
||||
{
|
||||
return;
|
||||
return index;
|
||||
}
|
||||
|
||||
LayerMapSet(key, AddBlankLayer());
|
||||
index = AddBlankLayer();
|
||||
LayerMapSet(key, index);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
public int AddBlankLayer(int? newIndex = null)
|
||||
@@ -478,6 +402,19 @@ namespace Robust.Client.GameObjects
|
||||
return AddLayer(layer, newIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new layer based on some <see cref="PrototypeLayerData"/>.
|
||||
/// </summary>
|
||||
public int AddLayer(PrototypeLayerData layerDatum, int? newIndex = null)
|
||||
{
|
||||
var layer = new Layer(this);
|
||||
|
||||
var index = AddLayer(layer, newIndex);
|
||||
|
||||
LayerSetData(index, layerDatum);
|
||||
return index;
|
||||
}
|
||||
|
||||
public int AddLayer(string texturePath, int? newIndex = null)
|
||||
{
|
||||
return AddLayer(new ResourcePath(texturePath), newIndex);
|
||||
@@ -653,7 +590,144 @@ namespace Robust.Client.GameObjects
|
||||
RemoveLayer(layer);
|
||||
}
|
||||
|
||||
public void LayerSetShader(int layer, ShaderInstance? shader)
|
||||
/// <summary>
|
||||
/// Fills in a layer's values using some <see cref="PrototypeLayerData"/>.
|
||||
/// </summary>
|
||||
public void LayerSetData(int index, PrototypeLayerData layerDatum)
|
||||
{
|
||||
if (Layers.Count <= index)
|
||||
{
|
||||
Logger.ErrorS(LogCategory, "Layer with index '{0}' does not exist, cannot set layer data! Trace:\n{1}",
|
||||
index, Environment.StackTrace);
|
||||
return;
|
||||
}
|
||||
|
||||
var layer = Layers[index];
|
||||
|
||||
var anyTextureAttempted = false;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(layerDatum.RsiPath))
|
||||
{
|
||||
var path = TextureRoot / layerDatum.RsiPath;
|
||||
|
||||
if (resourceCache.TryGetResource(path, out RSIResource? resource))
|
||||
{
|
||||
layer.RSI = resource.RSI;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.ErrorS(LogCategory, "Unable to load layer RSI '{0}'.", path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(layerDatum.State))
|
||||
{
|
||||
anyTextureAttempted = true;
|
||||
var theRsi = layer.RSI ?? BaseRSI;
|
||||
if (theRsi == null)
|
||||
{
|
||||
Logger.ErrorS(LogCategory,
|
||||
"Layer has no RSI to load states from. Cannot use 'state' property. ({0})",
|
||||
layerDatum.State);
|
||||
}
|
||||
else
|
||||
{
|
||||
var stateid = new RSI.StateId(layerDatum.State);
|
||||
layer.State = stateid;
|
||||
if (theRsi.TryGetState(stateid, out var state))
|
||||
{
|
||||
// Always use south because this layer will be cached in the serializer.
|
||||
layer.AnimationTimeLeft = state.GetDelay(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.ErrorS(LogCategory,
|
||||
$"State '{stateid}' not found in RSI: '{theRsi.Path}'.",
|
||||
stateid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(layerDatum.TexturePath))
|
||||
{
|
||||
anyTextureAttempted = true;
|
||||
if (layer.State.IsValid)
|
||||
{
|
||||
Logger.ErrorS(LogCategory,
|
||||
"Cannot specify 'texture' on a layer if it has an RSI state specified."
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
layer.Texture =
|
||||
resourceCache.GetResource<TextureResource>(TextureRoot / layerDatum.TexturePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(layerDatum.Shader))
|
||||
{
|
||||
if (prototypes.TryIndex<ShaderPrototype>(layerDatum.Shader, out var prototype))
|
||||
{
|
||||
layer.ShaderPrototype = layerDatum.Shader;
|
||||
layer.Shader = prototype.Instance();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.ErrorS(LogCategory,
|
||||
"Shader prototype '{0}' does not exist.",
|
||||
layerDatum.Shader);
|
||||
}
|
||||
}
|
||||
|
||||
if (layerDatum.MapKeys != null)
|
||||
{
|
||||
foreach (var keyString in layerDatum.MapKeys)
|
||||
{
|
||||
object key;
|
||||
if (reflection.TryParseEnumReference(keyString, out var @enum))
|
||||
{
|
||||
key = @enum;
|
||||
}
|
||||
else
|
||||
{
|
||||
key = keyString;
|
||||
}
|
||||
|
||||
if (LayerMap.TryGetValue(key, out var mappedIndex))
|
||||
{
|
||||
if (mappedIndex != index)
|
||||
Logger.ErrorS(LogCategory, "Duplicate layer map key definition: {0}", key);
|
||||
continue;
|
||||
}
|
||||
|
||||
_layerMapEnsurePrivate();
|
||||
LayerMap[key] = index;
|
||||
}
|
||||
}
|
||||
|
||||
layer.Color = layerDatum.Color;
|
||||
layer._rotation = layerDatum.Rotation;
|
||||
layer._offset = layerDatum.Offset;
|
||||
layer._scale = layerDatum.Scale;
|
||||
layer.UpdateLocalMatrix();
|
||||
|
||||
// If neither state: nor texture: were provided we assume that they want a blank invisible layer.
|
||||
layer.Visible = anyTextureAttempted && layerDatum.Visible;
|
||||
}
|
||||
|
||||
public void LayerSetData(object layerKey, PrototypeLayerData data)
|
||||
{
|
||||
if (!LayerMapTryGet(layerKey, out var layer))
|
||||
{
|
||||
Logger.ErrorS(LogCategory, "Layer with key '{0}' does not exist, cannot set shader! Trace:\n{1}",
|
||||
layerKey, Environment.StackTrace);
|
||||
return;
|
||||
}
|
||||
|
||||
LayerSetData(layer, data);
|
||||
}
|
||||
|
||||
public void LayerSetShader(int layer, ShaderInstance? shader, string? prototype = null)
|
||||
{
|
||||
if (Layers.Count <= layer)
|
||||
{
|
||||
@@ -664,9 +738,10 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var theLayer = Layers[layer];
|
||||
theLayer.Shader = shader;
|
||||
theLayer.ShaderPrototype = prototype;
|
||||
}
|
||||
|
||||
public void LayerSetShader(object layerKey, ShaderInstance shader)
|
||||
public void LayerSetShader(object layerKey, ShaderInstance shader, string? prototype = null)
|
||||
{
|
||||
if (!LayerMapTryGet(layerKey, out var layer))
|
||||
{
|
||||
@@ -675,7 +750,7 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
LayerSetShader(layer, shader);
|
||||
LayerSetShader(layer, shader, prototype);
|
||||
}
|
||||
|
||||
public void LayerSetShader(int layer, string shaderName)
|
||||
@@ -684,10 +759,12 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
Logger.ErrorS(LogCategory, "Shader prototype '{0}' does not exist. Trace:\n{1}", shaderName,
|
||||
Environment.StackTrace);
|
||||
|
||||
LayerSetShader(layer, null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// This will set the shader to null if it does not exist.
|
||||
LayerSetShader(layer, prototype?.Instance());
|
||||
LayerSetShader(layer, prototype.Instance(), shaderName);
|
||||
}
|
||||
|
||||
public void LayerSetShader(object layerKey, string shaderName)
|
||||
@@ -1148,7 +1225,7 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
Layers[layer].SetOffset(layerOffset);
|
||||
Layers[layer].Offset = layerOffset;
|
||||
}
|
||||
|
||||
public void LayerSetOffset(object layerKey, Vector2 layerOffset)
|
||||
@@ -1230,25 +1307,6 @@ namespace Robust.Client.GameObjects
|
||||
RenderInternal(drawingHandle, eyeRotation, worldRotation, worldPosition, overrideDir);
|
||||
}
|
||||
|
||||
private void CalcModelMatrix(int numDirs, Angle eyeRotation, Angle worldRotation, Vector2 worldPosition, out Matrix3 modelMatrix)
|
||||
{
|
||||
Angle angle;
|
||||
|
||||
if (_screenLock)
|
||||
{
|
||||
// Negate the eye rotation in the model matrix, so that later when the view matrix is applied the
|
||||
// sprite will be locked upright to the screen
|
||||
angle = new Angle(-eyeRotation.Theta);
|
||||
}
|
||||
else
|
||||
{
|
||||
angle = CalcRectWorldAngle(worldRotation + eyeRotation, numDirs) - eyeRotation;
|
||||
}
|
||||
|
||||
var sWorldRotation = angle;
|
||||
modelMatrix = Matrix3.CreateTransform(in worldPosition, in sWorldRotation);
|
||||
}
|
||||
|
||||
private void RenderInternal(DrawingHandleWorld drawingHandle, Angle eyeRotation, Angle worldRotation, Vector2 worldPosition, Direction? overrideDirection)
|
||||
{
|
||||
// Reduce the angles to fix math shenanigans
|
||||
@@ -1257,56 +1315,16 @@ namespace Robust.Client.GameObjects
|
||||
if (worldRotation.Theta < 0)
|
||||
worldRotation = new Angle(worldRotation.Theta + Math.Tau);
|
||||
|
||||
// sprite matrix, WITHOUT offset.
|
||||
// offset is applied after sprite numDirs snapping/rotation correction
|
||||
// --> apply at same time as layer offset
|
||||
var spriteMatrix = Matrix3.CreateTransform(Vector2.Zero, rotation, scale);
|
||||
// worldRotation + eyeRotation should be the angle of the entity on-screen. If no-rot is enabled this is just set to zero.
|
||||
// However, at some point later the eye-matrix is applied separately, so we subtract -eye rotation for now:
|
||||
var entityMatrix = Matrix3.CreateTransform(worldPosition, NoRotation ? -eyeRotation : worldRotation);
|
||||
|
||||
Matrix3.Multiply(ref LocalMatrix, ref entityMatrix, out var transform);
|
||||
|
||||
var angle = worldRotation + eyeRotation; // angle on-screen. Used to decide the direction of 4/8 directional RSIs
|
||||
foreach (var layer in Layers)
|
||||
{
|
||||
if (!layer.Visible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var numDirs = GetLayerDirectionCount(layer);
|
||||
var layerRotation = worldRotation + layer.Rotation;
|
||||
var layerPosition = worldPosition + layerRotation.RotateVec(layer._offset + offset);
|
||||
|
||||
CalcModelMatrix(numDirs, eyeRotation, layerRotation, layerPosition, out var modelMatrix);
|
||||
Matrix3.Multiply(ref spriteMatrix, ref modelMatrix, out var transformMatrix);
|
||||
drawingHandle.SetTransform(in transformMatrix);
|
||||
|
||||
RenderLayer(drawingHandle, layer, eyeRotation, layerRotation, overrideDirection);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderLayer(DrawingHandleWorld drawingHandle, Layer layer, Angle eyeRotation, Angle worldRotation, Direction? overrideDirection)
|
||||
{
|
||||
var texture = GetRenderTexture(layer, worldRotation + eyeRotation, overrideDirection);
|
||||
|
||||
if (layer.Shader != null)
|
||||
{
|
||||
drawingHandle.UseShader(layer.Shader);
|
||||
}
|
||||
|
||||
var layerColor = color * layer.Color;
|
||||
|
||||
var position = -(Vector2)texture.Size / (2f * EyeManager.PixelsPerMeter);
|
||||
var textureSize = texture.Size / (float)EyeManager.PixelsPerMeter;
|
||||
var quad = Box2.FromDimensions(position, textureSize);
|
||||
|
||||
// TODO: Implement layer-specific rotation and scale.
|
||||
// Apply these directly to the box.
|
||||
// Oh and when you do update Layer.LocalToLayer so content doesn't break.
|
||||
|
||||
// handle.Modulate changes the color
|
||||
// drawingHandle.SetTransform() is set above, turning the quad into local space vertices
|
||||
drawingHandle.DrawTextureRectRegion(texture, quad, layerColor);
|
||||
|
||||
if (layer.Shader != null)
|
||||
{
|
||||
drawingHandle.UseShader(null);
|
||||
layer.Render(drawingHandle, ref transform, angle, overrideDirection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1342,27 +1360,6 @@ namespace Robust.Client.GameObjects
|
||||
};
|
||||
}
|
||||
|
||||
private Texture GetRenderTexture(Layer layer, Angle worldRotation, Direction? overrideDirection)
|
||||
{
|
||||
var texture = layer.Texture;
|
||||
|
||||
if (layer.State.IsValid)
|
||||
{
|
||||
// Pull texture from RSI state instead.
|
||||
var rsi = layer.RSI ?? BaseRSI;
|
||||
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
|
||||
{
|
||||
state = GetFallbackState(resourceCache);
|
||||
}
|
||||
|
||||
var layerSpecificDir = layer.EffectiveDirection(state, worldRotation, overrideDirection);
|
||||
texture = state.GetFrame(layerSpecificDir, layer.AnimationFrame);
|
||||
}
|
||||
|
||||
texture ??= resourceCache.GetFallback<TextureResource>().Texture;
|
||||
return texture;
|
||||
}
|
||||
|
||||
public void FrameUpdate(float delta)
|
||||
{
|
||||
foreach (var t in Layers)
|
||||
@@ -1417,12 +1414,14 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
Visible = thestate.Visible;
|
||||
DrawDepth = thestate.DrawDepth;
|
||||
Scale = thestate.Scale;
|
||||
Rotation = thestate.Rotation;
|
||||
Offset = thestate.Offset;
|
||||
scale = thestate.Scale;
|
||||
rotation = thestate.Rotation;
|
||||
offset = thestate.Offset;
|
||||
UpdateLocalMatrix();
|
||||
Color = thestate.Color;
|
||||
RenderOrder = thestate.RenderOrder;
|
||||
|
||||
|
||||
if (thestate.BaseRsiPath != null && BaseRSI != null)
|
||||
{
|
||||
if (resourceCache.TryGetResource<RSIResource>(TextureRoot / thestate.BaseRsiPath, out var res))
|
||||
@@ -1438,43 +1437,8 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
// Maybe optimize this to NOT full clear.
|
||||
Layers.Clear();
|
||||
for (var i = 0; i < thestate.Layers.Count; i++)
|
||||
{
|
||||
var netlayer = thestate.Layers[i];
|
||||
var layer = new Layer(this)
|
||||
{
|
||||
// These are easy so do them here.
|
||||
Scale = netlayer.Scale,
|
||||
Rotation = netlayer.Rotation,
|
||||
Visible = netlayer.Visible,
|
||||
Color = netlayer.Color
|
||||
};
|
||||
Layers.Add(layer);
|
||||
|
||||
// Using the public API to handle errors.
|
||||
// Probably slow as crap.
|
||||
// Who am I kidding, DEFINITELY.
|
||||
if (netlayer.Shader != null)
|
||||
{
|
||||
LayerSetShader(i, netlayer.Shader);
|
||||
}
|
||||
|
||||
if (netlayer.RsiPath != null)
|
||||
{
|
||||
LayerSetRSI(i, netlayer.RsiPath);
|
||||
}
|
||||
|
||||
if (netlayer.TexturePath != null)
|
||||
{
|
||||
LayerSetTexture(i, netlayer.TexturePath);
|
||||
}
|
||||
else if (netlayer.State != null)
|
||||
{
|
||||
LayerSetState(i, netlayer.State);
|
||||
}
|
||||
}
|
||||
// Maybe optimize this to NOT fully clear the layers. (see LayerDatums setter function)
|
||||
LayerDatums = thestate.Layers;
|
||||
}
|
||||
|
||||
private void QueueUpdateIsInert()
|
||||
@@ -1553,35 +1517,61 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box2 CalculateBoundingBox(Vector2 worldPos)
|
||||
public Box2Rotated CalculateRotatedBoundingBox(Vector2 worldPosition, Angle worldRotation, IEye? eye = null)
|
||||
{
|
||||
// fast check for empty sprites
|
||||
if (Layers.Count == 0)
|
||||
return new Box2(worldPos, worldPos);
|
||||
if (!Visible || Layers.Count == 0)
|
||||
{
|
||||
return new Box2Rotated(new Box2(worldPosition, worldPosition), Angle.Zero, worldPosition);
|
||||
}
|
||||
|
||||
// We need to modify world rotation so that it lies between 0 and 2pi.
|
||||
// This matters for 4 or 8 directional sprites deciding which quadrant (octant?) they lie in.
|
||||
// the 0->2pi convention is set by the sprite-rendering code that selects the layers.
|
||||
// See RenderInternal().
|
||||
|
||||
worldRotation = worldRotation.Reduced();
|
||||
if (worldRotation.Theta < 0)
|
||||
worldRotation = new Angle(worldRotation.Theta + Math.Tau);
|
||||
|
||||
eye ??= eyeManager.CurrentEye;
|
||||
|
||||
// Need relative angle on screen for determining the sprite rsi direction.
|
||||
Angle relativeRotation = NoRotation
|
||||
? Angle.Zero
|
||||
: worldRotation + eye.Rotation;
|
||||
|
||||
// we need to calculate bounding box taking into account all nested layers
|
||||
// because layers can have offsets, scale or rotation we need to calculate a new BB
|
||||
// based on lowest bottomLeft and hightest topRight points from all layers
|
||||
var box = Layers[0].CalculateBoundingBox();
|
||||
// because layers can have offsets, scale or rotation, we need to calculate a new BB
|
||||
// based on lowest bottomLeft and highest topRight points from all layers
|
||||
var box = Layers[0].CalculateBoundingBox(relativeRotation);
|
||||
|
||||
for (int i = 1; i < Layers.Count; i++)
|
||||
{
|
||||
var layer = Layers[i];
|
||||
var layerBB = layer.CalculateBoundingBox();
|
||||
if (!layer.Visible) continue;
|
||||
var layerBB = layer.CalculateBoundingBox(relativeRotation);
|
||||
|
||||
box = box.Union(layerBB);
|
||||
}
|
||||
|
||||
// apply sprite transformations and calculate sprite bounding box
|
||||
// we can optimize it a bit, if sprite doesn't have rotation
|
||||
var spriteBox = box.Scale(Scale);
|
||||
var spriteHasRotation = !Rotation.EqualsApprox(Angle.Zero);
|
||||
var spriteBB = spriteHasRotation ?
|
||||
new Box2Rotated(spriteBox, Rotation).CalcBoundingBox() : spriteBox;
|
||||
// Next, what we do is take the box2 and apply the sprite's transform, and then the entity's transform. We
|
||||
// could do this via Matrix3.TransformBox, but that only yields bounding boxes. So instead we manually
|
||||
// transform our box by the combination of these matrices:
|
||||
|
||||
// move it all to world transform system (with sprite offset)
|
||||
var worldBB = spriteBB.Translated(Offset + worldPos);
|
||||
return worldBB;
|
||||
if (Scale != Vector2.One)
|
||||
box = box.Scale(Scale);
|
||||
|
||||
var adjustedOffset = NoRotation
|
||||
? (-eye.Rotation).RotateVec(Offset)
|
||||
: worldRotation.RotateVec(Offset);
|
||||
|
||||
Vector2 position = adjustedOffset + worldPosition;
|
||||
Angle finalRotation = NoRotation
|
||||
? Rotation - eye.Rotation
|
||||
: Rotation + worldRotation;
|
||||
|
||||
return new Box2Rotated(box.Translated(position), finalRotation, position);
|
||||
}
|
||||
|
||||
internal void UpdateBounds()
|
||||
@@ -1615,10 +1605,11 @@ namespace Robust.Client.GameObjects
|
||||
Flip = 3,
|
||||
}
|
||||
|
||||
public class Layer : ISpriteLayer
|
||||
public sealed class Layer : ISpriteLayer, ISerializationHooks
|
||||
{
|
||||
[ViewVariables] private readonly SpriteComponent _parent;
|
||||
|
||||
[ViewVariables] public string? ShaderPrototype;
|
||||
[ViewVariables] public ShaderInstance? Shader;
|
||||
[ViewVariables] public Texture? Texture;
|
||||
|
||||
@@ -1628,11 +1619,37 @@ namespace Robust.Client.GameObjects
|
||||
[ViewVariables] public float AnimationTime;
|
||||
[ViewVariables] public int AnimationFrame;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Vector2 Scale { get; set; } = Vector2.One;
|
||||
public Matrix3 LocalMatrix = Matrix3.Identity;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Angle Rotation { get; set; }
|
||||
public Vector2 Scale
|
||||
{
|
||||
get => _scale;
|
||||
set
|
||||
{
|
||||
if (_scale.EqualsApprox(value)) return;
|
||||
|
||||
_scale = value;
|
||||
UpdateLocalMatrix();
|
||||
_parent.UpdateBounds();
|
||||
}
|
||||
}
|
||||
internal Vector2 _scale = Vector2.One;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Angle Rotation
|
||||
{
|
||||
get => _rotation;
|
||||
set
|
||||
{
|
||||
if (_rotation.EqualsApprox(value)) return;
|
||||
|
||||
_rotation = value;
|
||||
UpdateLocalMatrix();
|
||||
_parent.UpdateBounds();
|
||||
}
|
||||
}
|
||||
internal Angle _rotation = Angle.Zero;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Visible = true;
|
||||
@@ -1652,6 +1669,7 @@ namespace Robust.Client.GameObjects
|
||||
if (_offset.EqualsApprox(value)) return;
|
||||
|
||||
_offset = value;
|
||||
UpdateLocalMatrix();
|
||||
_parent.UpdateBounds();
|
||||
}
|
||||
}
|
||||
@@ -1675,6 +1693,7 @@ namespace Robust.Client.GameObjects
|
||||
if (toClone.Shader != null)
|
||||
{
|
||||
Shader = toClone.Shader.Mutable ? toClone.Shader.Duplicate() : toClone.Shader;
|
||||
ShaderPrototype = toClone.ShaderPrototype;
|
||||
}
|
||||
Texture = toClone.Texture;
|
||||
RSI = toClone.RSI;
|
||||
@@ -1682,14 +1701,26 @@ namespace Robust.Client.GameObjects
|
||||
AnimationTimeLeft = toClone.AnimationTimeLeft;
|
||||
AnimationTime = toClone.AnimationTime;
|
||||
AnimationFrame = toClone.AnimationFrame;
|
||||
Scale = toClone.Scale;
|
||||
Rotation = toClone.Rotation;
|
||||
_scale = toClone.Scale;
|
||||
_rotation = toClone.Rotation;
|
||||
_offset = toClone.Offset;
|
||||
UpdateLocalMatrix();
|
||||
Visible = toClone.Visible;
|
||||
Color = toClone.Color;
|
||||
DirOffset = toClone.DirOffset;
|
||||
AutoAnimated = toClone.AutoAnimated;
|
||||
}
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
|
||||
internal void UpdateLocalMatrix()
|
||||
{
|
||||
LocalMatrix = Matrix3.CreateTransform(in _offset, in _rotation, in _scale);
|
||||
}
|
||||
|
||||
RSI? ISpriteLayer.Rsi { get => RSI; set => SetRsi(value); }
|
||||
RSI.StateId ISpriteLayer.RsiState { get => State; set => SetState(value); }
|
||||
Texture? ISpriteLayer.Texture { get => Texture; set => SetTexture(value); }
|
||||
@@ -1701,7 +1732,7 @@ namespace Robust.Client.GameObjects
|
||||
Color = Color,
|
||||
Rotation = Rotation,
|
||||
Scale = Scale,
|
||||
//todo Shader = Shader,
|
||||
Shader = ShaderPrototype,
|
||||
State = State.Name,
|
||||
Visible = Visible,
|
||||
RsiPath = RSI?.Path?.ToString(),
|
||||
@@ -1730,7 +1761,7 @@ namespace Robust.Client.GameObjects
|
||||
set => SetAutoAnimated(value);
|
||||
}
|
||||
|
||||
public RSI.State.Direction EffectiveDirection(Angle worldRotation)
|
||||
public RSIDirection EffectiveDirection(Angle worldRotation)
|
||||
{
|
||||
if (State == default)
|
||||
{
|
||||
@@ -1751,22 +1782,16 @@ namespace Robust.Client.GameObjects
|
||||
return default;
|
||||
}
|
||||
|
||||
public Vector2 LocalToLayer(Vector2 localPos)
|
||||
{
|
||||
// TODO: scale & rotation for layers is currently unimplemented.
|
||||
return localPos;
|
||||
}
|
||||
|
||||
public RSI.State.Direction EffectiveDirection(RSI.State state, Angle worldRotation,
|
||||
public RSIDirection EffectiveDirection(RSI.State state, Angle worldRotation,
|
||||
Direction? overrideDirection)
|
||||
{
|
||||
if (state.Directions == RSI.State.DirectionType.Dir1)
|
||||
{
|
||||
return RSI.State.Direction.South;
|
||||
return RSIDirection.South;
|
||||
}
|
||||
else
|
||||
{
|
||||
RSI.State.Direction dir;
|
||||
RSIDirection dir;
|
||||
if (overrideDirection != null)
|
||||
{
|
||||
dir = overrideDirection.Value.Convert(state.Directions);
|
||||
@@ -1898,11 +1923,6 @@ namespace Robust.Client.GameObjects
|
||||
_parent.QueueUpdateIsInert();
|
||||
}
|
||||
|
||||
public void SetOffset(Vector2 offset)
|
||||
{
|
||||
Offset = offset;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Vector2i PixelSize
|
||||
{
|
||||
@@ -1923,10 +1943,121 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box2 CalculateBoundingBox()
|
||||
public Box2 CalculateBoundingBox(Angle angle)
|
||||
{
|
||||
// TODO: scale & rotation for layers is currently unimplemented.
|
||||
return Box2.CenteredAround(Offset, PixelSize / EyeManager.PixelsPerMeter);
|
||||
// Other than some special cases for simple layers, this will basically just apply the matrix obtained
|
||||
// via GetLayerDrawMatrix() to this layer's bounding box.
|
||||
|
||||
var rsiState = GetActualState();
|
||||
|
||||
var dir = (rsiState == null || rsiState.Directions == RSI.State.DirectionType.Dir1)
|
||||
? RSIDirection.South
|
||||
: angle.ToRsiDirection(rsiState.Directions);
|
||||
|
||||
// special case for simple layers. The vast majority of layers are like this.
|
||||
if (_rotation == Angle.Zero)
|
||||
{
|
||||
var textureSize = PixelSize / EyeManager.PixelsPerMeter;
|
||||
|
||||
// this switch block is basically an explicit version of the `rsiDirectionMatrix` in `GetLayerDrawMatrix()`.
|
||||
var box = dir switch
|
||||
{
|
||||
// No rotation:
|
||||
RSIDirection.South or RSIDirection.North => Box2.CenteredAround(Offset, textureSize),
|
||||
// rotate 90 degrees:
|
||||
RSIDirection.East or RSIDirection.West => Box2.CenteredAround(Offset, (textureSize.Y, textureSize.X)),
|
||||
// rotated 45 degrees (any 45 degree rotated rectangle has a square bounding box with sides of length (x+y)/sqrt(2) )
|
||||
_ => Box2.CenteredAround(Offset, Vector2.One * (textureSize.X + textureSize.Y) / MathF.Sqrt(2))
|
||||
};
|
||||
|
||||
return _scale == Vector2.One ? box : box.Scale(_scale);
|
||||
}
|
||||
|
||||
// Welp we have some non-zero _rotation, so lets just apply the generalized layer transform and get the bounding box from where;
|
||||
GetLayerDrawMatrix(dir, out var layerDrawMatrix);
|
||||
return layerDrawMatrix.TransformBox(Box2.CentredAroundZero(PixelSize / EyeManager.PixelsPerMeter));
|
||||
}
|
||||
|
||||
internal RSI.State? GetActualState()
|
||||
{
|
||||
if (!State.IsValid)
|
||||
return null;
|
||||
|
||||
// Pull texture from RSI state
|
||||
var rsi = RSI ?? _parent.BaseRSI;
|
||||
if (rsi == null || !rsi.TryGetState(State, out var rsiState))
|
||||
{
|
||||
rsiState = GetFallbackState(_parent.resourceCache);
|
||||
}
|
||||
|
||||
return rsiState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given the apparent rotation of an entity on screen (world + eye rotation), get layer's matrix for drawing &
|
||||
/// relevant RSI direction.
|
||||
/// </summary>
|
||||
public void GetLayerDrawMatrix(RSIDirection dir, out Matrix3 layerDrawMatrix)
|
||||
{
|
||||
if (_parent.NoRotation)
|
||||
layerDrawMatrix = LocalMatrix;
|
||||
else
|
||||
{
|
||||
var rsiDirectionMatrix = Matrix3.CreateTransform(Vector2.Zero, -dir.Convert().ToAngle());
|
||||
Matrix3.Multiply(ref rsiDirectionMatrix, ref LocalMatrix, out layerDrawMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3 spriteMatrix, Angle angle, Direction? overrideDirection)
|
||||
{
|
||||
if (!Visible)
|
||||
return;
|
||||
|
||||
var rsiState = GetActualState();
|
||||
|
||||
var dir = (rsiState == null || rsiState.Directions == RSI.State.DirectionType.Dir1)
|
||||
? RSIDirection.South
|
||||
: angle.ToRsiDirection(rsiState.Directions);
|
||||
|
||||
// Set the drawing transform for this layer
|
||||
GetLayerDrawMatrix(dir, out var layerMatrix);
|
||||
Matrix3.Multiply(ref layerMatrix, ref spriteMatrix, out var transformMatrix);
|
||||
drawingHandle.SetTransform(in transformMatrix);
|
||||
|
||||
// The direction used to draw the sprite can differ from the one that the angle would naively suggest,
|
||||
// due to direction overrides or offsets.
|
||||
if (overrideDirection != null && rsiState != null)
|
||||
dir = overrideDirection.Value.Convert(rsiState.Directions);
|
||||
dir = dir.OffsetRsiDir(DirOffset);
|
||||
|
||||
// Get the correct directional texture from the state, and draw it!
|
||||
var texture = GetRenderTexture(rsiState, dir);
|
||||
RenderTexture(drawingHandle, texture);
|
||||
}
|
||||
|
||||
private void RenderTexture(DrawingHandleWorld drawingHandle, Texture texture)
|
||||
{
|
||||
if (Shader != null)
|
||||
drawingHandle.UseShader(Shader);
|
||||
|
||||
var layerColor = _parent.color * Color;
|
||||
|
||||
var position = -(Vector2)texture.Size / (2f * EyeManager.PixelsPerMeter);
|
||||
var textureSize = texture.Size / (float)EyeManager.PixelsPerMeter;
|
||||
var quad = Box2.FromDimensions(position, textureSize);
|
||||
|
||||
drawingHandle.DrawTextureRectRegion(texture, quad, layerColor);
|
||||
|
||||
if (Shader != null)
|
||||
drawingHandle.UseShader(null);
|
||||
}
|
||||
|
||||
private Texture GetRenderTexture(RSI.State? state, RSIDirection dir)
|
||||
{
|
||||
if (state == null)
|
||||
return Texture ?? _parent.resourceCache.GetFallback<TextureResource>().Texture;
|
||||
|
||||
return state.GetFrame(dir, AnimationFrame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2034,6 +2165,7 @@ namespace Robust.Client.GameObjects
|
||||
return results;
|
||||
}
|
||||
|
||||
[Obsolete("Use SpriteSystem")]
|
||||
public static IRsiStateLike GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache)
|
||||
{
|
||||
var icon = IconComponent.GetPrototypeIcon(prototype, resourceCache);
|
||||
|
||||
@@ -11,7 +11,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[ComponentReference(typeof(SharedUserInterfaceComponent))]
|
||||
public class ClientUserInterfaceComponent : SharedUserInterfaceComponent, ISerializationHooks
|
||||
public sealed class ClientUserInterfaceComponent : SharedUserInterfaceComponent, ISerializationHooks
|
||||
{
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactory _dynamicTypeFactory = default!;
|
||||
|
||||
@@ -62,6 +62,12 @@ namespace Robust.Client.GameObjects
|
||||
Play(component, animation, key);
|
||||
}
|
||||
|
||||
public void Play(EntityUid uid, AnimationPlayerComponent? component, Animation animation, string key)
|
||||
{
|
||||
component ??= EntityManager.EnsureComponent<AnimationPlayerComponent>(uid);
|
||||
Play(component, animation, key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start playing an animation.
|
||||
/// </summary>
|
||||
@@ -79,6 +85,14 @@ namespace Robust.Client.GameObjects
|
||||
component.PlayingAnimations.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool HasRunningAnimation(EntityUid uid, AnimationPlayerComponent? component, string key)
|
||||
{
|
||||
if (component == null)
|
||||
TryComp(uid, out component);
|
||||
|
||||
return component != null && component.PlayingAnimations.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool HasRunningAnimation(AnimationPlayerComponent component, string key)
|
||||
{
|
||||
return component.PlayingAnimations.ContainsKey(key);
|
||||
@@ -88,6 +102,18 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
component.PlayingAnimations.Remove(key);
|
||||
}
|
||||
|
||||
public void Stop(EntityUid uid, string key)
|
||||
{
|
||||
if (!TryComp<AnimationPlayerComponent>(uid, out var player)) return;
|
||||
player.PlayingAnimations.Remove(key);
|
||||
}
|
||||
|
||||
public void Stop(EntityUid uid, AnimationPlayerComponent? component, string key)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false)) return;
|
||||
component.PlayingAnimations.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class AnimationCompletedEvent : EntityEventArgs
|
||||
|
||||
@@ -1,17 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class AppearanceSystem : EntitySystem
|
||||
public sealed class AppearanceSystem : SharedAppearanceSystem
|
||||
{
|
||||
private readonly Queue<ClientAppearanceComponent> _queuedUpdates = new();
|
||||
|
||||
public void EnqueueUpdate(ClientAppearanceComponent component)
|
||||
public override void Initialize()
|
||||
{
|
||||
_queuedUpdates.Enqueue(component);
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ClientAppearanceComponent, ComponentInit>(OnAppearanceInit);
|
||||
SubscribeLocalEvent<ClientAppearanceComponent, ComponentHandleState>(OnAppearanceHandleState);
|
||||
}
|
||||
|
||||
private void OnAppearanceInit(EntityUid uid, ClientAppearanceComponent component, ComponentInit args)
|
||||
{
|
||||
foreach (var visual in component.Visualizers)
|
||||
{
|
||||
visual.InitializeEntity(uid);
|
||||
}
|
||||
|
||||
MarkDirty(component);
|
||||
}
|
||||
|
||||
private void OnAppearanceHandleState(EntityUid uid, ClientAppearanceComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not AppearanceComponentState actualState)
|
||||
return;
|
||||
|
||||
var stateDiff = component.AppearanceData.Count != actualState.Data.Count;
|
||||
|
||||
if (!stateDiff)
|
||||
{
|
||||
foreach (var (key, value) in component.AppearanceData)
|
||||
{
|
||||
if (!actualState.Data.TryGetValue(key, out var stateValue) ||
|
||||
!value.Equals(stateValue))
|
||||
{
|
||||
stateDiff = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!stateDiff) return;
|
||||
|
||||
component.AppearanceData = CloneAppearanceData(actualState.Data);
|
||||
MarkDirty(component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take in an appearance data dictionary and attempt to clone it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As some appearance data values are not simple value-type objects, this is not just a shallow clone.
|
||||
/// </remarks>
|
||||
private Dictionary<object, object> CloneAppearanceData(Dictionary<object, object> data)
|
||||
{
|
||||
Dictionary<object, object> newDict = new(data.Count);
|
||||
|
||||
foreach (var (key, value) in data)
|
||||
{
|
||||
if (value.GetType().IsValueType)
|
||||
newDict[key] = value;
|
||||
else if (value is ICloneable cloneable)
|
||||
newDict[key] = cloneable.Clone();
|
||||
else
|
||||
throw new NotSupportedException("Invalid object in appearance data dictionary. Appearance data must be cloneable");
|
||||
}
|
||||
|
||||
return newDict;
|
||||
}
|
||||
|
||||
public override void MarkDirty(AppearanceComponent component)
|
||||
{
|
||||
if (component.AppearanceDirty)
|
||||
return;
|
||||
|
||||
_queuedUpdates.Enqueue((ClientAppearanceComponent) component);
|
||||
component.AppearanceDirty = true;
|
||||
}
|
||||
|
||||
internal void UnmarkDirty(ClientAppearanceComponent component)
|
||||
{
|
||||
component.AppearanceDirty = false;
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
@@ -22,7 +100,7 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
|
||||
OnChangeData(appearance.Owner, appearance);
|
||||
appearance.UnmarkDirty();
|
||||
UnmarkDirty(appearance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,10 +108,30 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
if (!Resolve(uid, ref appearanceComponent, false)) return;
|
||||
|
||||
var ev = new AppearanceChangeEvent
|
||||
{
|
||||
Component = appearanceComponent,
|
||||
AppearanceData = appearanceComponent.AppearanceData,
|
||||
};
|
||||
|
||||
// Give it AppearanceData so we can still keep the friend attribute on the component.
|
||||
EntityManager.EventBus.RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
// Eventually visualizers would be nuked and we'd just make them components instead.
|
||||
foreach (var visualizer in appearanceComponent.Visualizers)
|
||||
{
|
||||
visualizer.OnChangeData(appearanceComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised whenever the appearance data for an entity changes.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public struct AppearanceChangeEvent
|
||||
{
|
||||
public AppearanceComponent Component;
|
||||
public IReadOnlyDictionary<object, object> AppearanceData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ using Robust.Shared.Utility;
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class AudioSystem : SharedAudioSystem, IAudioSystem
|
||||
public sealed class AudioSystem : SharedAudioSystem, IAudioSystem
|
||||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
@@ -420,7 +420,7 @@ namespace Robust.Client.GameObjects
|
||||
source.IsLooping = audioParams.Value.Loop;
|
||||
}
|
||||
|
||||
private class PlayingStream : IPlayingAudioStream
|
||||
private sealed class PlayingStream : IPlayingAudioStream
|
||||
{
|
||||
public uint? NetIdentifier;
|
||||
public IClydeAudioSource Source = default!;
|
||||
|
||||
@@ -72,10 +72,13 @@ namespace Robust.Client.GameObjects
|
||||
var sender = ev.Sender;
|
||||
if (EntityManager.EntityExists(sender) &&
|
||||
EntityManager.TryGetComponent(sender, out ClientOccluderComponent? iconSmooth)
|
||||
&& iconSmooth.Running)
|
||||
&& iconSmooth.Initialized)
|
||||
{
|
||||
var grid1 = _mapManager.GetGrid(EntityManager.GetComponent<TransformComponent>(sender).GridID);
|
||||
var coords = EntityManager.GetComponent<TransformComponent>(sender).Coordinates;
|
||||
var xform = EntityManager.GetComponent<TransformComponent>(sender);
|
||||
if (!_mapManager.TryGetGrid(xform.GridID, out var grid1))
|
||||
return;
|
||||
|
||||
var coords = xform.Coordinates;
|
||||
|
||||
_dirtyEntities.Enqueue(sender);
|
||||
AddValidEntities(grid1.GetInDir(coords, Direction.North));
|
||||
@@ -85,7 +88,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
// Entity is no longer valid, update around the last position it was at.
|
||||
if (ev.LastPosition.HasValue && _mapManager.TryGetGrid(ev.LastPosition.Value.grid, out var grid))
|
||||
else if (ev.LastPosition.HasValue && _mapManager.TryGetGrid(ev.LastPosition.Value.grid, out var grid))
|
||||
{
|
||||
var pos = ev.LastPosition.Value.pos;
|
||||
|
||||
|
||||
@@ -5,12 +5,14 @@ using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
using static Robust.Shared.Containers.ContainerManagerComponent;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public class ContainerSystem : SharedContainerSystem
|
||||
public sealed class ContainerSystem : SharedContainerSystem
|
||||
{
|
||||
[Dependency] private readonly IRobustSerializer _serializer = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!;
|
||||
@@ -59,7 +61,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
if (!cast.ContainerSet.Any(data => data.Id == id))
|
||||
{
|
||||
container.EmptyContainer(true);
|
||||
container.EmptyContainer(true, entMan: EntityManager);
|
||||
container.Shutdown();
|
||||
toDelete ??= new List<string>();
|
||||
toDelete.Add(id);
|
||||
@@ -131,12 +133,51 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
// If an entity is currently in the shadow realm, it means we probably left PVS and are now getting
|
||||
// back into range. We do not want to directly insert this entity, as IF the container and entity
|
||||
// transform states did not get sent simultaneously, the entity's transform will be modified by the
|
||||
// insert operation. This means it will then be reset to the shadow realm, causing it to be ejected
|
||||
// from the container. It would then subsequently be parented to the container without ever being
|
||||
// re-inserted, leading to the client seeing what should be hidden entities attached to
|
||||
// containers/players.
|
||||
if (Transform(entity).MapID == MapId.Nullspace)
|
||||
{
|
||||
AddExpectedEntity(entity, container);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!container.ContainedEntities.Contains(entity))
|
||||
container.Insert(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void HandleParentChanged(ref EntParentChangedMessage message)
|
||||
{
|
||||
base.HandleParentChanged(ref message);
|
||||
|
||||
// If an entity warped in from null-space (i.e., re-entered PVS) and got attached to a container, do the same checks as for newly initialized entities.
|
||||
if (message.OldParent != null && message.OldParent.Value.IsValid())
|
||||
return;
|
||||
|
||||
if (!ExpectedEntities.TryGetValue(message.Entity, out var container))
|
||||
return;
|
||||
|
||||
if (Transform(message.Entity).ParentUid != container.Owner)
|
||||
{
|
||||
// This container is expecting an entity... but it got parented to some other entity???
|
||||
// Ah well, the sever should send a new container state that updates expected entities so just ignore it for now.
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveExpectedEntity(message.Entity);
|
||||
|
||||
if (container.Deleted)
|
||||
return;
|
||||
|
||||
container.Insert(message.Entity);
|
||||
}
|
||||
|
||||
private IContainer ContainerFactory(ContainerManagerComponent component, string containerType, string id)
|
||||
{
|
||||
var type = _serializer.FindSerializedType(typeof(IContainer), containerType);
|
||||
@@ -169,68 +210,101 @@ namespace Robust.Client.GameObjects
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
var pointQuery = EntityManager.GetEntityQuery<PointLightComponent>();
|
||||
var spriteQuery = EntityManager.GetEntityQuery<SpriteComponent>();
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var toUpdate in _updateQueue)
|
||||
{
|
||||
if (EntityManager.Deleted(toUpdate))
|
||||
{
|
||||
if (Deleted(toUpdate))
|
||||
continue;
|
||||
}
|
||||
|
||||
UpdateEntityRecursively(toUpdate);
|
||||
UpdateEntityRecursively(toUpdate, xformQuery, pointQuery, spriteQuery);
|
||||
}
|
||||
|
||||
_updateQueue.Clear();
|
||||
}
|
||||
|
||||
private void UpdateEntityRecursively(EntityUid entity)
|
||||
private void UpdateEntityRecursively(
|
||||
EntityUid entity,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<PointLightComponent> pointQuery,
|
||||
EntityQuery<SpriteComponent> spriteQuery)
|
||||
{
|
||||
// TODO: Since we are recursing down,
|
||||
// we could cache ShowContents data here to speed it up for children.
|
||||
// Am lazy though.
|
||||
UpdateEntity(entity);
|
||||
// Recursively go up parents and containers to see whether both sprites and lights need to be occluded
|
||||
// Could maybe optimise this more by checking nearest parent that has sprite / light and whether it's container
|
||||
// occluded but this probably isn't a big perf issue.
|
||||
var xform = xformQuery.GetComponent(entity);
|
||||
var parent = xform.ParentUid;
|
||||
var child = entity;
|
||||
var spriteOccluded = false;
|
||||
var lightOccluded = false;
|
||||
|
||||
foreach (var child in EntityManager.GetComponent<TransformComponent>(entity).Children)
|
||||
while (parent.IsValid() && !spriteOccluded && !lightOccluded)
|
||||
{
|
||||
UpdateEntityRecursively(child.Owner);
|
||||
var parentXform = xformQuery.GetComponent(parent);
|
||||
if (TryComp<ContainerManagerComponent>(parent, out var manager) && manager.TryGetContainer(child, out var container))
|
||||
{
|
||||
spriteOccluded = spriteOccluded || !container.ShowContents;
|
||||
lightOccluded = lightOccluded || container.OccludesLight;
|
||||
}
|
||||
|
||||
child = parent;
|
||||
parent = parentXform.ParentUid;
|
||||
}
|
||||
|
||||
// Alright so
|
||||
// This is the CBT bit.
|
||||
// The issue is we need to go through the children and re-check whether they are or are not contained.
|
||||
// if they are contained then the occlusion values may need updating for all those children
|
||||
UpdateEntity(entity, xform, xformQuery, pointQuery, spriteQuery, spriteOccluded, lightOccluded);
|
||||
}
|
||||
|
||||
private void UpdateEntity(EntityUid entity)
|
||||
private void UpdateEntity(
|
||||
EntityUid entity,
|
||||
TransformComponent xform,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<PointLightComponent> pointQuery,
|
||||
EntityQuery<SpriteComponent> spriteQuery,
|
||||
bool spriteOccluded,
|
||||
bool lightOccluded)
|
||||
{
|
||||
if (EntityManager.TryGetComponent(entity, out SpriteComponent? sprite))
|
||||
if (spriteQuery.TryGetComponent(entity, out var sprite))
|
||||
{
|
||||
sprite.ContainerOccluded = false;
|
||||
|
||||
// We have to recursively scan for containers upwards in case of nested containers.
|
||||
var tempParent = entity;
|
||||
while (tempParent.TryGetContainer(out var container))
|
||||
{
|
||||
if (!container.ShowContents)
|
||||
{
|
||||
sprite.ContainerOccluded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
tempParent = container.Owner;
|
||||
}
|
||||
sprite.ContainerOccluded = spriteOccluded;
|
||||
}
|
||||
|
||||
if (EntityManager.TryGetComponent(entity, out PointLightComponent? light))
|
||||
if (pointQuery.TryGetComponent(entity, out var light))
|
||||
{
|
||||
light.ContainerOccluded = false;
|
||||
light.ContainerOccluded = lightOccluded;
|
||||
}
|
||||
|
||||
// We have to recursively scan for containers upwards in case of nested containers.
|
||||
var tempParent = entity;
|
||||
while (tempParent.TryGetContainer(out var container))
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
|
||||
// Try to avoid TryComp if we already know stuff is occluded.
|
||||
if ((!spriteOccluded || !lightOccluded) && TryComp<ContainerManagerComponent>(entity, out var manager))
|
||||
{
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
if (container.OccludesLight)
|
||||
// Thank god it's by value and not by ref.
|
||||
var childSpriteOccluded = spriteOccluded;
|
||||
var childLightOccluded = lightOccluded;
|
||||
|
||||
// We already know either sprite or light is not occluding so need to check container.
|
||||
if (manager.TryGetContainer(child.Value, out var container))
|
||||
{
|
||||
light.ContainerOccluded = true;
|
||||
break;
|
||||
childSpriteOccluded = childSpriteOccluded || !container.ShowContents;
|
||||
childLightOccluded = childLightOccluded || container.OccludesLight;
|
||||
}
|
||||
|
||||
tempParent = container.Owner;
|
||||
UpdateEntity(child.Value, xformQuery.GetComponent(child.Value), xformQuery, pointQuery, spriteQuery, childSpriteOccluded, childLightOccluded);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
UpdateEntity(child.Value, xformQuery.GetComponent(child.Value), xformQuery, pointQuery, spriteQuery, spriteOccluded, lightOccluded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Robust.Client.GameObjects
|
||||
if (_enabled)
|
||||
{
|
||||
_lightOverlay = new DebugLightOverlay(
|
||||
IoCManager.Resolve<IEntityLookup>(),
|
||||
EntitySystem.Get<EntityLookupSystem>(),
|
||||
IoCManager.Resolve<IEyeManager>(),
|
||||
IoCManager.Resolve<IMapManager>(),
|
||||
Get<RenderingTreeSystem>());
|
||||
@@ -44,7 +44,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private sealed class DebugLightOverlay : Overlay
|
||||
{
|
||||
private IEntityLookup _lookup;
|
||||
private EntityLookupSystem _lookup;
|
||||
private IEyeManager _eyeManager;
|
||||
private IMapManager _mapManager;
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public DebugLightOverlay(IEntityLookup lookup, IEyeManager eyeManager, IMapManager mapManager, RenderingTreeSystem tree)
|
||||
public DebugLightOverlay(EntityLookupSystem lookup, IEyeManager eyeManager, IMapManager mapManager, RenderingTreeSystem tree)
|
||||
{
|
||||
_lookup = lookup;
|
||||
_eyeManager = eyeManager;
|
||||
@@ -72,7 +72,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
foreach (var light in tree.LightTree)
|
||||
{
|
||||
var aabb = _lookup.GetWorldAabbFromEntity(light.Owner);
|
||||
var aabb = _lookup.GetWorldAABB(light.Owner);
|
||||
if (!aabb.Intersects(worldBounds)) continue;
|
||||
|
||||
args.WorldHandle.DrawRect(aabb, Color.Green.WithAlpha(0.1f));
|
||||
|
||||
@@ -15,7 +15,7 @@ using Robust.Shared.Enums;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public class EffectSystem : EntitySystem
|
||||
public sealed class EffectSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming gameTiming = default!;
|
||||
[Dependency] private readonly IResourceCache resourceCache = default!;
|
||||
@@ -94,7 +94,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
private class Effect
|
||||
private sealed class Effect
|
||||
{
|
||||
/// <summary>
|
||||
/// Effect Sprite
|
||||
@@ -323,7 +323,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
private readonly IPlayerManager _playerManager;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
||||
|
||||
private readonly ShaderInstance _unshadedShader;
|
||||
private readonly EffectSystem _owner;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Robust.Client.GameObjects
|
||||
/// Updates the position of every Eye every frame, so that the camera follows the player around.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class EyeUpdateSystem : EntitySystem
|
||||
public sealed class EyeUpdateSystem : EntitySystem
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
|
||||
@@ -10,7 +10,7 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public class GridChunkBoundsDebugSystem : EntitySystem
|
||||
public sealed class GridChunkBoundsDebugSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
@@ -4,11 +4,15 @@ using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -16,11 +20,13 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Client-side processing of all input commands through the simulation.
|
||||
/// </summary>
|
||||
public class InputSystem : SharedInputSystem
|
||||
public sealed class InputSystem : SharedInputSystem
|
||||
{
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IConsoleHost _conHost = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private readonly IPlayerCommandStates _cmdStates = new PlayerCommandStates();
|
||||
|
||||
@@ -108,6 +114,43 @@ namespace Robust.Client.GameObjects
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<PlayerAttachSysMessage>(OnAttachedEntityChanged);
|
||||
|
||||
_conHost.RegisterCommand("incmd",
|
||||
"Inserts an input command into the simulation",
|
||||
"incmd <KeyFunction> <d|u KeyState> <wxPos> <wyPos>",
|
||||
GenerateInputCommand);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_conHost.UnregisterCommand("incmd");
|
||||
}
|
||||
|
||||
private void GenerateInputCommand(IConsoleShell shell, string argstr, string[] args)
|
||||
{
|
||||
var localPlayer = _playerManager.LocalPlayer;
|
||||
if(localPlayer is null)
|
||||
return;
|
||||
|
||||
var pent = localPlayer.ControlledEntity;
|
||||
if(pent is null)
|
||||
return;
|
||||
|
||||
BoundKeyFunction keyFunction = new BoundKeyFunction(args[0]);
|
||||
BoundKeyState state = args[1] == "u" ? BoundKeyState.Up: BoundKeyState.Down;
|
||||
|
||||
var pxform = Transform(pent.Value);
|
||||
var wPos = pxform.WorldPosition + new Vector2(float.Parse(args[2]), float.Parse(args[3]));
|
||||
var coords = EntityCoordinates.FromMap(EntityManager, pent.Value, new MapCoordinates(wPos, pxform.MapID));
|
||||
|
||||
var funcId = _inputManager.NetworkBindMap.KeyFunctionID(keyFunction);
|
||||
|
||||
var message = new FullInputCmdMessage(_timing.CurTick, _timing.TickFraction, funcId, state,
|
||||
coords, new ScreenCoordinates(0, 0, default), EntityUid.Invalid);
|
||||
|
||||
HandleInputCommand(localPlayer.Session, keyFunction, message);
|
||||
}
|
||||
|
||||
private void OnAttachedEntityChanged(PlayerAttachSysMessage message)
|
||||
@@ -163,7 +206,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Entity system message that is raised when the player changes attached entities.
|
||||
/// </summary>
|
||||
public class PlayerAttachSysMessage : EntityEventArgs
|
||||
public sealed class PlayerAttachSysMessage : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// New entity the player is attached to.
|
||||
@@ -180,7 +223,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
public class PlayerAttachedEvent : EntityEventArgs
|
||||
public sealed class PlayerAttachedEvent : EntityEventArgs
|
||||
{
|
||||
public PlayerAttachedEvent(EntityUid entity)
|
||||
{
|
||||
@@ -190,7 +233,7 @@ namespace Robust.Client.GameObjects
|
||||
public EntityUid Entity { get; }
|
||||
}
|
||||
|
||||
public class PlayerDetachedEvent : EntityEventArgs
|
||||
public sealed class PlayerDetachedEvent : EntityEventArgs
|
||||
{
|
||||
public PlayerDetachedEvent(EntityUid entity)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public class MidiSystem : EntitySystem
|
||||
public sealed class MidiSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IMidiManager _midiManager = default!;
|
||||
|
||||
|
||||
@@ -113,10 +113,18 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void AnythingMoved(ref MoveEvent args)
|
||||
{
|
||||
AnythingMovedSubHandler(args.Sender);
|
||||
var pointQuery = EntityManager.GetEntityQuery<PointLightComponent>();
|
||||
var spriteQuery = EntityManager.GetEntityQuery<SpriteComponent>();
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
AnythingMovedSubHandler(args.Sender, xformQuery, pointQuery, spriteQuery);
|
||||
}
|
||||
|
||||
private void AnythingMovedSubHandler(EntityUid uid, TransformComponent? xform = null)
|
||||
private void AnythingMovedSubHandler(
|
||||
EntityUid uid,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<PointLightComponent> pointQuery,
|
||||
EntityQuery<SpriteComponent> spriteQuery)
|
||||
{
|
||||
// To avoid doing redundant updates (and we don't need to update a grid's children ever)
|
||||
if (!_checkedChildren.Add(uid) || EntityManager.HasComponent<RenderingTreeComponent>(uid)) return;
|
||||
@@ -125,17 +133,19 @@ namespace Robust.Client.GameObjects
|
||||
// WHATEVER YOU DO, DON'T REPLACE THIS WITH SPAMMING EVENTS UNLESS YOU HAVE A GUARANTEE IT WON'T LAG THE GC.
|
||||
// (Struct-based events ok though)
|
||||
// Ironically this was lagging the GC lolz
|
||||
if (EntityManager.TryGetComponent(uid, out SpriteComponent? sprite))
|
||||
if (spriteQuery.TryGetComponent(uid, out var sprite))
|
||||
QueueSpriteUpdate(sprite);
|
||||
|
||||
if (EntityManager.TryGetComponent(uid, out PointLightComponent? light))
|
||||
if (pointQuery.TryGetComponent(uid, out var light))
|
||||
QueueLightUpdate(light);
|
||||
|
||||
if (!Resolve(uid, ref xform)) return;
|
||||
if (!xformQuery.TryGetComponent(uid, out var xform)) return;
|
||||
|
||||
foreach (var child in xform.ChildEntities)
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
AnythingMovedSubHandler(child);
|
||||
AnythingMovedSubHandler(child.Value, xformQuery, pointQuery, spriteQuery);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,23 +258,21 @@ namespace Robust.Client.GameObjects
|
||||
EntityManager.EnsureComponent<RenderingTreeComponent>(_mapManager.GetGrid(gridId).GridEntityId);
|
||||
}
|
||||
|
||||
// TODO: Pass in TransformComponent directly: mainly interested in getting this shit working atm.
|
||||
private RenderingTreeComponent? GetRenderTree(EntityUid entity)
|
||||
private RenderingTreeComponent? GetRenderTree(EntityUid entity, EntityQuery<TransformComponent> xforms)
|
||||
{
|
||||
var lookups = EntityManager.GetEntityQuery<RenderingTreeComponent>();
|
||||
|
||||
if (!EntityManager.EntityExists(entity) ||
|
||||
!EntityManager.TryGetComponent(entity, out TransformComponent? xform) ||
|
||||
!xforms.TryGetComponent(entity, out var xform) ||
|
||||
xform.MapID == MapId.Nullspace ||
|
||||
EntityManager.HasComponent<RenderingTreeComponent>(entity)) return null;
|
||||
lookups.HasComponent(entity)) return null;
|
||||
|
||||
var parent = xform.ParentUid;
|
||||
|
||||
while (true)
|
||||
while (parent.IsValid())
|
||||
{
|
||||
if (!parent.IsValid())
|
||||
break;
|
||||
|
||||
if (EntityManager.TryGetComponent(parent, out RenderingTreeComponent? comp)) return comp;
|
||||
parent = EntityManager.GetComponent<TransformComponent>(parent).ParentUid;
|
||||
if (lookups.TryGetComponent(parent, out var comp)) return comp;
|
||||
parent = xforms.GetComponent(parent).ParentUid;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -279,6 +287,8 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
_checkedChildren.Clear();
|
||||
|
||||
var xforms = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var sprite in _spriteQueue)
|
||||
{
|
||||
sprite.TreeUpdateQueued = false;
|
||||
@@ -289,9 +299,9 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
var oldMapTree = sprite.RenderTree;
|
||||
var newMapTree = GetRenderTree(sprite.Owner);
|
||||
var newMapTree = GetRenderTree(sprite.Owner, xforms);
|
||||
// TODO: Temp PVS guard
|
||||
var xform = EntityManager.GetComponent<TransformComponent>(sprite.Owner);
|
||||
var xform = xforms.GetComponent(sprite.Owner);
|
||||
var (worldPos, worldRot) = xform.GetWorldPositionRotation();
|
||||
|
||||
if (float.IsNaN(worldPos.X) || float.IsNaN(worldPos.Y))
|
||||
@@ -300,7 +310,7 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
var aabb = SpriteAabbFunc(sprite, worldPos, worldRot);
|
||||
var aabb = SpriteAabbFunc(sprite, worldPos, worldRot, xforms);
|
||||
|
||||
// If we're on a new map then clear the old one.
|
||||
if (oldMapTree != newMapTree)
|
||||
@@ -327,9 +337,9 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
var oldMapTree = light.RenderTree;
|
||||
var newMapTree = GetRenderTree(light.Owner);
|
||||
var newMapTree = GetRenderTree(light.Owner, xforms);
|
||||
// TODO: Temp PVS guard
|
||||
var worldPos = EntityManager.GetComponent<TransformComponent>(light.Owner).WorldPosition;
|
||||
var worldPos = xforms.GetComponent(light.Owner).WorldPosition;
|
||||
|
||||
if (float.IsNaN(worldPos.X) || float.IsNaN(worldPos.Y))
|
||||
{
|
||||
@@ -344,7 +354,7 @@ namespace Robust.Client.GameObjects
|
||||
Logger.WarningS(LoggerSawmill, $"Light radius for {light.Owner} set above max radius of {MaxLightRadius}. This may lead to pop-in.");
|
||||
}
|
||||
|
||||
var aabb = LightAabbFunc(light, worldPos);
|
||||
var aabb = LightAabbFunc(light, worldPos, xforms);
|
||||
|
||||
// If we're on a new map then clear the old one.
|
||||
if (oldMapTree != newMapTree)
|
||||
@@ -366,69 +376,40 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private Box2 SpriteAabbFunc(in SpriteComponent value)
|
||||
{
|
||||
var xform = EntityManager.GetComponent<TransformComponent>(value.Owner);
|
||||
var xforms = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
var xform = xforms.GetComponent(value.Owner);
|
||||
var (worldPos, worldRot) = xform.GetWorldPositionRotation();
|
||||
var bounds = new Box2Rotated(value.CalculateBoundingBox(worldPos), worldRot, worldPos);
|
||||
var tree = GetRenderTree(value.Owner);
|
||||
|
||||
if (tree == null)
|
||||
{
|
||||
return bounds.CalcBoundingBox();
|
||||
}
|
||||
else
|
||||
{
|
||||
return EntityManager.GetComponent<TransformComponent>(tree.Owner).InvWorldMatrix.TransformBox(bounds);
|
||||
}
|
||||
return SpriteAabbFunc(value, worldPos, worldRot, xforms);
|
||||
}
|
||||
|
||||
private Box2 LightAabbFunc(in PointLightComponent value)
|
||||
{
|
||||
var worldPos = EntityManager.GetComponent<TransformComponent>(value.Owner).WorldPosition;
|
||||
var tree = GetRenderTree(value.Owner);
|
||||
var xforms = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
var worldPos = xforms.GetComponent(value.Owner).WorldPosition;
|
||||
var tree = GetRenderTree(value.Owner, xforms);
|
||||
var boxSize = value.Radius * 2;
|
||||
|
||||
Vector2 localPos;
|
||||
if (tree == null)
|
||||
{
|
||||
localPos = worldPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Need a way to just cache this InvWorldMatrix
|
||||
localPos = EntityManager.GetComponent<TransformComponent>(tree.Owner).InvWorldMatrix.Transform(worldPos);
|
||||
}
|
||||
var localPos = tree == null ? worldPos : xforms.GetComponent(tree.Owner).InvWorldMatrix.Transform(worldPos);
|
||||
return Box2.CenteredAround(localPos, (boxSize, boxSize));
|
||||
}
|
||||
|
||||
private Box2 SpriteAabbFunc(SpriteComponent value, Vector2 worldPos, Angle worldRot)
|
||||
private Box2 SpriteAabbFunc(SpriteComponent value, Vector2 worldPos, Angle worldRot, EntityQuery<TransformComponent> xforms)
|
||||
{
|
||||
var bounds = new Box2Rotated(value.CalculateBoundingBox(worldPos), worldRot, worldPos);
|
||||
var tree = GetRenderTree(value.Owner);
|
||||
var bounds = value.CalculateRotatedBoundingBox(worldPos, worldRot);
|
||||
var tree = GetRenderTree(value.Owner, xforms);
|
||||
|
||||
if (tree == null)
|
||||
{
|
||||
return bounds.CalcBoundingBox();
|
||||
}
|
||||
else
|
||||
{
|
||||
return EntityManager.GetComponent<TransformComponent>(tree.Owner).InvWorldMatrix.TransformBox(bounds);
|
||||
}
|
||||
return tree == null ? bounds.CalcBoundingBox() : xforms.GetComponent(tree.Owner).InvWorldMatrix.TransformBox(bounds);
|
||||
}
|
||||
|
||||
private Box2 LightAabbFunc(PointLightComponent value, Vector2 worldPos)
|
||||
private Box2 LightAabbFunc(PointLightComponent value, Vector2 worldPos, EntityQuery<TransformComponent> xforms)
|
||||
{
|
||||
// Lights are circles so don't need entity's rotation
|
||||
var tree = GetRenderTree(value.Owner);
|
||||
var tree = GetRenderTree(value.Owner, xforms);
|
||||
var boxSize = value.Radius * 2;
|
||||
|
||||
Vector2 localPos;
|
||||
if (tree == null)
|
||||
{
|
||||
localPos = worldPos;
|
||||
} else
|
||||
{
|
||||
localPos = EntityManager.GetComponent<TransformComponent>(tree.Owner).InvWorldMatrix.Transform(worldPos);
|
||||
}
|
||||
var localPos = tree == null ? worldPos : xforms.GetComponent(tree.Owner).InvWorldMatrix.Transform(worldPos);
|
||||
return Box2.CenteredAround(localPos, (boxSize, boxSize));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
public sealed partial class SpriteSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
|
||||
[Pure]
|
||||
public Texture Frame0(SpriteSpecifier specifier)
|
||||
{
|
||||
return RsiStateLike(specifier).Default;
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public IRsiStateLike RsiStateLike(SpriteSpecifier specifier)
|
||||
{
|
||||
switch (specifier)
|
||||
{
|
||||
case SpriteSpecifier.Texture tex:
|
||||
return tex.GetTexture(_resourceCache);
|
||||
|
||||
case SpriteSpecifier.Rsi rsi:
|
||||
return GetState(rsi);
|
||||
|
||||
case SpriteSpecifier.EntityPrototype prototypeIcon:
|
||||
if (!_proto.TryIndex<EntityPrototype>(prototypeIcon.EntityPrototypeId, out var prototype))
|
||||
{
|
||||
Logger.Error("Failed to load PrototypeIcon {0}", prototypeIcon.EntityPrototypeId);
|
||||
return SpriteComponent.GetFallbackState(_resourceCache);
|
||||
}
|
||||
|
||||
return SpriteComponent.GetPrototypeIcon(prototype, _resourceCache);
|
||||
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public IRsiStateLike GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache)
|
||||
{
|
||||
var icon = IconComponent.GetPrototypeIcon(prototype, _resourceCache);
|
||||
if (icon != null) return icon;
|
||||
|
||||
if (!prototype.Components.ContainsKey("Sprite"))
|
||||
{
|
||||
return SpriteComponent.GetFallbackState(resourceCache);
|
||||
}
|
||||
|
||||
var dummy = Spawn(prototype.ID, MapCoordinates.Nullspace);
|
||||
var spriteComponent = EnsureComp<SpriteComponent>(dummy);
|
||||
var result = spriteComponent.Icon ?? SpriteComponent.GetFallbackState(resourceCache);
|
||||
Del(dummy);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public RSI.State GetState(SpriteSpecifier.Rsi rsiSpecifier)
|
||||
{
|
||||
if (_resourceCache.TryGetResource<RSIResource>(
|
||||
SharedSpriteComponent.TextureRoot / rsiSpecifier.RsiPath,
|
||||
out var theRsi) &&
|
||||
theRsi.RSI.TryGetState(rsiSpecifier.RsiState, out var state))
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
Logger.Error("Failed to load RSI {0}", rsiSpecifier.RsiPath);
|
||||
return SpriteComponent.GetFallbackState(_resourceCache);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.GameObjects
|
||||
/// Updates the layer animation for every visible sprite.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class SpriteSystem : EntitySystem
|
||||
public sealed partial class SpriteSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly RenderingTreeSystem _treeSystem = default!;
|
||||
@@ -53,9 +53,11 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
var xforms = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var comp in _treeSystem.GetRenderTrees(currentMap, pvsBounds))
|
||||
{
|
||||
var bounds = EntityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(pvsBounds);
|
||||
var bounds = xforms.GetComponent(comp.Owner).InvWorldMatrix.TransformBox(pvsBounds);
|
||||
|
||||
comp.SpriteTree.QueryAabb(ref frameTime, (ref float state, in SpriteComponent value) =>
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public class VelocityDebugSystem : EntitySystem
|
||||
public sealed class VelocityDebugSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
20
Robust.Client/GameObjects/EntitySystems/VisualizerSystem.cs
Normal file
20
Robust.Client/GameObjects/EntitySystems/VisualizerSystem.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
/// <summary>
|
||||
/// An abstract entity system inheritor for systems that deal with
|
||||
/// appearance data, replacing <see cref="AppearanceVisualizer"/>.
|
||||
/// </summary>
|
||||
public abstract class VisualizerSystem<T> : EntitySystem
|
||||
where T: Component
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<T, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
}
|
||||
|
||||
protected virtual void OnAppearanceChange(EntityUid uid, T component, ref AppearanceChangeEvent args) {}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Shared;
|
||||
@@ -29,7 +28,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
/// <inheritdoc />
|
||||
[UsedImplicitly]
|
||||
public class ClientGameStateManager : IClientGameStateManager
|
||||
public sealed class ClientGameStateManager : IClientGameStateManager
|
||||
{
|
||||
private GameStateProcessor _processor = default!;
|
||||
|
||||
@@ -46,11 +45,10 @@ namespace Robust.Client.GameStates
|
||||
|
||||
[Dependency] private readonly IComponentFactory _compFactory = default!;
|
||||
[Dependency] private readonly IClientEntityManagerInternal _entities = default!;
|
||||
[Dependency] private readonly IEntityLookup _lookup = default!;
|
||||
[Dependency] private readonly IPlayerManager _players = default!;
|
||||
[Dependency] private readonly IClientNetManager _network = default!;
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IClientMapManager _mapManager = default!;
|
||||
[Dependency] private readonly INetworkedMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IClientGameTiming _timing = default!;
|
||||
[Dependency] private readonly INetConfigurationManager _config = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
@@ -60,6 +58,8 @@ namespace Robust.Client.GameStates
|
||||
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
|
||||
#endif
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int MinBufferSize => _processor.MinBufferSize;
|
||||
|
||||
@@ -87,6 +87,7 @@ namespace Robust.Client.GameStates
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
_sawmill = Logger.GetSawmill(CVars.NetPredict.Name);
|
||||
_processor = new GameStateProcessor(_timing);
|
||||
|
||||
_network.RegisterNetMessage<MsgState>(HandleStateMessage);
|
||||
@@ -144,7 +145,7 @@ namespace Robust.Client.GameStates
|
||||
_pendingInputs.Enqueue(message);
|
||||
|
||||
_inputManager.NetworkBindMap.TryGetKeyFunction(message.InputFunctionId, out var boundFunc);
|
||||
Logger.DebugS(CVars.NetPredict.Name,
|
||||
_sawmill.Debug(
|
||||
$"CL> SENT tick={_timing.CurTick}, sub={_timing.TickFraction}, seq={_nextInputCmdSeq}, func={boundFunc.FunctionName}, state={message.State}");
|
||||
_nextInputCmdSeq++;
|
||||
}
|
||||
@@ -238,7 +239,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
if (_lastProcessedSeq < curState.LastProcessedInput)
|
||||
{
|
||||
Logger.DebugS(CVars.NetPredict.Name, $"SV> RCV tick={_timing.CurTick}, seq={_lastProcessedSeq}");
|
||||
_sawmill.Debug($"SV> RCV tick={_timing.CurTick}, seq={_lastProcessedSeq}");
|
||||
_lastProcessedSeq = curState.LastProcessedInput;
|
||||
}
|
||||
}
|
||||
@@ -257,8 +258,7 @@ namespace Robust.Client.GameStates
|
||||
var inCmd = _pendingInputs.Dequeue();
|
||||
|
||||
_inputManager.NetworkBindMap.TryGetKeyFunction(inCmd.InputFunctionId, out var boundFunc);
|
||||
Logger.DebugS(CVars.NetPredict.Name,
|
||||
$"SV> seq={inCmd.InputSequence}, func={boundFunc.FunctionName}, state={inCmd.State}");
|
||||
_sawmill.Debug($"SV> seq={inCmd.InputSequence}, func={boundFunc.FunctionName}, state={inCmd.State}");
|
||||
}
|
||||
|
||||
while (_pendingSystemMessages.Count > 0 && _pendingSystemMessages.Peek().sequence <= _lastProcessedSeq)
|
||||
@@ -274,7 +274,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
if (_pendingInputs.Count > 0)
|
||||
{
|
||||
Logger.DebugS(CVars.NetPredict.Name, "CL> Predicted:");
|
||||
_sawmill.Debug("CL> Predicted:");
|
||||
}
|
||||
|
||||
var pendingInputEnumerator = _pendingInputs.GetEnumerator();
|
||||
@@ -299,7 +299,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
_inputManager.NetworkBindMap.TryGetKeyFunction(inputCmd.InputFunctionId, out var boundFunc);
|
||||
|
||||
Logger.DebugS(CVars.NetPredict.Name,
|
||||
_sawmill.Debug(
|
||||
$" seq={inputCmd.InputSequence}, sub={inputCmd.SubTick}, dTick={tick}, func={boundFunc.FunctionName}, " +
|
||||
$"state={inputCmd.State}");
|
||||
|
||||
@@ -331,8 +331,6 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
_entities.TickUpdate((float) _timing.TickPeriod.TotalSeconds, noPredictions: !IsPredictionEnabled);
|
||||
|
||||
_lookup.Update();
|
||||
}
|
||||
|
||||
private void ResetPredictedEntities(GameTick curTick)
|
||||
@@ -347,7 +345,11 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.DebugS(CVars.NetPredict.Name, $"Entity {entity} was made dirty.");
|
||||
// Check log level first to avoid the string alloc.
|
||||
if (_sawmill.Level <= LogLevel.Debug)
|
||||
{
|
||||
_sawmill.Debug($"Entity {entity} was made dirty.");
|
||||
}
|
||||
|
||||
if (!_processor.TryGetLastServerStates(entity, out var last))
|
||||
{
|
||||
@@ -365,7 +367,7 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.DebugS(CVars.NetPredict.Name, $" And also its component {comp.GetType()}");
|
||||
_sawmill.Debug($" And also its component {comp.GetType()}");
|
||||
// TODO: Handle interpolation.
|
||||
var handleState = new ComponentHandleState(compState, null);
|
||||
_entities.EventBus.RaiseComponentEvent(comp, ref handleState);
|
||||
@@ -632,7 +634,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
public class GameStateAppliedArgs : EventArgs
|
||||
public sealed class GameStateAppliedArgs : EventArgs
|
||||
{
|
||||
public GameState AppliedState { get; }
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ using Robust.Shared.Utility;
|
||||
namespace Robust.Client.GameStates
|
||||
{
|
||||
/// <inheritdoc />
|
||||
internal class GameStateProcessor : IGameStateProcessor
|
||||
internal sealed class GameStateProcessor : IGameStateProcessor
|
||||
{
|
||||
private readonly IGameTiming _timing;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Robust.Client.GameStates
|
||||
/// A network entity report that lists all entities as they are updated through game states.
|
||||
/// https://developer.valvesoftware.com/wiki/Networking_Entities#cl_entityreport
|
||||
/// </summary>
|
||||
class NetEntityOverlay : Overlay
|
||||
sealed class NetEntityOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
@@ -244,7 +244,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
private class NetEntityReportCommand : IConsoleCommand
|
||||
private sealed class NetEntityReportCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "net_entityreport";
|
||||
public string Help => "net_entityreport <0|1>";
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// Visual debug overlay for the network diagnostic graph.
|
||||
/// </summary>
|
||||
internal class NetGraphOverlay : Overlay
|
||||
internal sealed class NetGraphOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
@@ -239,7 +239,7 @@ namespace Robust.Client.GameStates
|
||||
base.DisposeBehavior();
|
||||
}
|
||||
|
||||
private class NetShowGraphCommand : IConsoleCommand
|
||||
private sealed class NetShowGraphCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "net_graph";
|
||||
public string Help => "net_graph <0|1>";
|
||||
@@ -275,7 +275,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
private class NetWatchEntCommand : IConsoleCommand
|
||||
private sealed class NetWatchEntCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "net_watchent";
|
||||
public string Help => "net_watchent <0|EntityUid>";
|
||||
|
||||
@@ -11,7 +11,7 @@ using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.GameStates
|
||||
{
|
||||
internal class NetInterpOverlay : Overlay
|
||||
internal sealed class NetInterpOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
@@ -38,7 +38,7 @@ namespace Robust.Client.GameStates
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(boundingBox.Owner);
|
||||
|
||||
// if not on the same map, continue
|
||||
if (transform.MapID != _eyeManager.CurrentMap || boundingBox.Owner.IsInContainer())
|
||||
if (transform.MapID != _eyeManager.CurrentMap || boundingBox.Owner.IsInContainer(_entityManager))
|
||||
continue;
|
||||
|
||||
// This entity isn't lerping, no need to draw debug info for it
|
||||
@@ -65,7 +65,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
private class NetShowInterpCommand : IConsoleCommand
|
||||
private sealed class NetShowInterpCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "net_draw_interp";
|
||||
public string Help => "net_draw_interp <0|1>";
|
||||
|
||||
@@ -18,7 +18,7 @@ using Vector2 = Robust.Shared.Maths.Vector2;
|
||||
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
internal partial class ClydeAudio : IClydeAudio, IClydeAudioInternal
|
||||
internal sealed partial class ClydeAudio : IClydeAudio, IClydeAudioInternal
|
||||
{
|
||||
private ALDevice _openALDevice;
|
||||
private ALContext _openALContext;
|
||||
@@ -224,7 +224,7 @@ namespace Robust.Client.Graphics.Audio
|
||||
_openALSawmill.Error("Failed to generate source. Too many simultaneous audio streams?");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
// TODO: This really shouldn't be indexing based on the ClydeHandle...
|
||||
AL.Source(source, ALSourcei.Buffer, _audioSampleBuffers[(int) stream.ClydeHandle!.Value.Value].BufferHandle);
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Robust.Client.Graphics.Audio
|
||||
/// <summary>
|
||||
/// Hey look, it's ClydeAudio.AudioSource's evil twin brother!
|
||||
/// </summary>
|
||||
[Virtual]
|
||||
internal class DummyAudioSource : IClydeAudioSource
|
||||
{
|
||||
public static DummyAudioSource Instance { get; } = new();
|
||||
|
||||
@@ -7,6 +7,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
/// <inheritdoc />
|
||||
[Virtual]
|
||||
public class Eye : IEye
|
||||
{
|
||||
private Vector2 _scale = Vector2.One/2f;
|
||||
|
||||
@@ -153,7 +153,7 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
public class CurrentEyeChangedEvent : EntityEventArgs
|
||||
public sealed class CurrentEyeChangedEvent : EntityEventArgs
|
||||
{
|
||||
public IEye? Old { get; }
|
||||
public IEye New { get; }
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// A fixed eye is an eye which is fixed to one point, its position.
|
||||
/// </summary>
|
||||
public class FixedEye : Eye
|
||||
public sealed class FixedEye : Eye
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@@ -18,8 +18,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private readonly Dictionary<GridId, Dictionary<Vector2i, MapChunkData>> _mapChunkData =
|
||||
new();
|
||||
|
||||
private int _verticesPerChunk(IMapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * 4;
|
||||
private int _indicesPerChunk(IMapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * GetQuadBatchIndexCount();
|
||||
private int _verticesPerChunk(MapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * 4;
|
||||
private int _indicesPerChunk(MapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * GetQuadBatchIndexCount();
|
||||
|
||||
private void _drawGrids(Viewport viewport, Box2Rotated worldBounds, IEye eye)
|
||||
{
|
||||
@@ -78,7 +78,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
private void _updateChunkMesh(IMapGrid grid, IMapChunk chunk)
|
||||
private void _updateChunkMesh(IMapGrid grid, MapChunk chunk)
|
||||
{
|
||||
var data = _mapChunkData[grid.Index];
|
||||
|
||||
@@ -91,25 +91,36 @@ namespace Robust.Client.Graphics.Clyde
|
||||
Span<Vertex2D> vertexBuffer = stackalloc Vertex2D[_verticesPerChunk(chunk)];
|
||||
|
||||
var i = 0;
|
||||
foreach (var tile in chunk)
|
||||
var cSz = grid.ChunkSize;
|
||||
var cScaled = chunk.Indices * cSz;
|
||||
for (ushort x = 0; x < cSz; x++)
|
||||
{
|
||||
var regionMaybe = _tileDefinitionManager.TileAtlasRegion(tile.Tile);
|
||||
if (regionMaybe == null)
|
||||
for (ushort y = 0; y < cSz; y++)
|
||||
{
|
||||
continue;
|
||||
var tile = chunk.GetTile(x, y);
|
||||
if (tile.IsEmpty)
|
||||
continue;
|
||||
|
||||
var regionMaybe = _tileDefinitionManager.TileAtlasRegion(tile.TypeId);
|
||||
if (regionMaybe == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var region = regionMaybe.Value;
|
||||
var gx = x + cScaled.X;
|
||||
var gy = y + cScaled.Y;
|
||||
|
||||
var vIdx = i * 4;
|
||||
vertexBuffer[vIdx + 0] = new Vertex2D(gx, gy, region.Left, region.Bottom);
|
||||
vertexBuffer[vIdx + 1] = new Vertex2D(gx + 1, gy, region.Right, region.Bottom);
|
||||
vertexBuffer[vIdx + 2] = new Vertex2D(gx + 1, gy + 1, region.Right, region.Top);
|
||||
vertexBuffer[vIdx + 3] = new Vertex2D(gx, gy + 1, region.Left, region.Top);
|
||||
var nIdx = i * GetQuadBatchIndexCount();
|
||||
var tIdx = (ushort)(i * 4);
|
||||
QuadBatchIndexWrite(indexBuffer, ref nIdx, tIdx);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
var region = regionMaybe.Value;
|
||||
|
||||
var vIdx = i * 4;
|
||||
vertexBuffer[vIdx + 0] = new Vertex2D(tile.X, tile.Y, region.Left, region.Bottom);
|
||||
vertexBuffer[vIdx + 1] = new Vertex2D(tile.X + 1, tile.Y, region.Right, region.Bottom);
|
||||
vertexBuffer[vIdx + 2] = new Vertex2D(tile.X + 1, tile.Y + 1, region.Right, region.Top);
|
||||
vertexBuffer[vIdx + 3] = new Vertex2D(tile.X, tile.Y + 1, region.Left, region.Top);
|
||||
var nIdx = i * GetQuadBatchIndexCount();
|
||||
var tIdx = (ushort) (i * 4);
|
||||
QuadBatchIndexWrite(indexBuffer, ref nIdx, tIdx);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
GL.BindVertexArray(datum.VAO);
|
||||
@@ -122,7 +133,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
datum.TileCount = i;
|
||||
}
|
||||
|
||||
private MapChunkData _initChunkBuffers(IMapGrid grid, IMapChunk chunk)
|
||||
private MapChunkData _initChunkBuffers(IMapGrid grid, MapChunk chunk)
|
||||
{
|
||||
var vao = GenVertexArray();
|
||||
BindVertexArray(vao);
|
||||
@@ -159,7 +170,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return datum;
|
||||
}
|
||||
|
||||
private bool _isChunkDirty(IMapGrid grid, IMapChunk chunk)
|
||||
private bool _isChunkDirty(IMapGrid grid, MapChunk chunk)
|
||||
{
|
||||
var data = _mapChunkData[grid.Index];
|
||||
return !data.TryGetValue(chunk.Indices, out var datum) || datum.Dirty;
|
||||
@@ -214,7 +225,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_mapChunkData.Remove(gridId);
|
||||
}
|
||||
|
||||
private class MapChunkData
|
||||
private sealed class MapChunkData
|
||||
{
|
||||
public bool Dirty;
|
||||
public readonly uint VAO;
|
||||
|
||||
@@ -24,11 +24,17 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
public ClydeDebugLayers DebugLayers { get; set; }
|
||||
|
||||
private readonly RefList<(SpriteComponent sprite, Matrix3 worldMatrix, Angle worldRotation, float yWorldPos)>
|
||||
private readonly RefList<(SpriteComponent sprite, Vector2 worldPos, Angle worldRotation, Box2 spriteScreenBB)>
|
||||
_drawingSpriteList
|
||||
=
|
||||
new();
|
||||
|
||||
// TODO allow this scale to be passed with PostShader as variable
|
||||
/// <summary>
|
||||
/// Some shaders that enlarge the final sprite, like emission or highlight effects, need to use a slightly larger render target.
|
||||
/// </summary>
|
||||
public static float PostShadeScale = 1.25f;
|
||||
|
||||
public void Render()
|
||||
{
|
||||
CheckTransferringScreenshots();
|
||||
@@ -217,9 +223,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
RenderOverlays(viewport, OverlaySpace.WorldSpaceBelowEntities, worldAABB, worldBounds);
|
||||
|
||||
var screenSize = viewport.Size;
|
||||
eye.GetViewMatrix(out var eyeMatrix, eye.Scale);
|
||||
|
||||
ProcessSpriteEntities(mapId, eyeMatrix, worldBounds, _drawingSpriteList);
|
||||
ProcessSpriteEntities(mapId, viewport, eye, worldBounds, _drawingSpriteList);
|
||||
|
||||
var worldOverlays = new List<Overlay>();
|
||||
|
||||
@@ -276,30 +281,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
break;
|
||||
}
|
||||
|
||||
var matrix = entry.worldMatrix;
|
||||
var worldPosition = new Vector2(matrix.R0C2, matrix.R1C2);
|
||||
|
||||
RenderTexture? entityPostRenderTarget = null;
|
||||
Vector2i roundedPos = default;
|
||||
if (entry.sprite.PostShader != null)
|
||||
{
|
||||
// calculate world bounding box
|
||||
var spriteBB = entry.sprite.CalculateBoundingBox(worldPosition);
|
||||
var spriteLB = spriteBB.BottomLeft;
|
||||
var spriteRT = spriteBB.TopRight;
|
||||
|
||||
// finally we can calculate screen bounding in pixels
|
||||
var screenLB = viewport.WorldToLocal(spriteLB);
|
||||
var screenRT = viewport.WorldToLocal(spriteRT);
|
||||
|
||||
// we need to scale RT a for effects like emission or highlight
|
||||
// scale can be passed with PostShader as variable in future
|
||||
var postShadeScale = 1.25f;
|
||||
var screenSpriteSize = (Vector2i) ((screenRT - screenLB) * postShadeScale).Rounded();
|
||||
|
||||
// Rotate the vector by the eye angle, otherwise the bounding box will be incorrect
|
||||
screenSpriteSize = (Vector2i) eye.Rotation.RotateVec(screenSpriteSize).Rounded();
|
||||
screenSpriteSize.Y = -screenSpriteSize.Y;
|
||||
// get the size of the sprite on screen, scaled slightly to allow for shaders that increase the final sprite size.
|
||||
var screenSpriteSize = (Vector2i) (entry.spriteScreenBB.Size * PostShadeScale).Rounded();
|
||||
|
||||
// I'm not 100% sure why it works, but without it post-shader
|
||||
// can be lower or upper by 1px than original sprite depending on sprite rotation or scale
|
||||
@@ -322,16 +309,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Calculate viewport so that the entity thinks it's drawing to the same position,
|
||||
// which is necessary for light application,
|
||||
// but it's ACTUALLY drawing into the center of the render target.
|
||||
var spritePos = spriteBB.Center;
|
||||
var screenPos = viewport.WorldToLocal(spritePos);
|
||||
var (roundedX, roundedY) = roundedPos = (Vector2i) screenPos;
|
||||
var flippedPos = new Vector2i(roundedX, screenSize.Y - roundedY);
|
||||
roundedPos = (Vector2i) entry.spriteScreenBB.Center;
|
||||
var flippedPos = new Vector2i(roundedPos.X, screenSize.Y - roundedPos.Y);
|
||||
flippedPos -= entityPostRenderTarget.Size / 2;
|
||||
_renderHandle.Viewport(Box2i.FromDimensions(-flippedPos, screenSize));
|
||||
}
|
||||
}
|
||||
|
||||
entry.sprite.Render(_renderHandle.DrawingHandleWorld, eye.Rotation, in entry.worldRotation, in worldPosition);
|
||||
entry.sprite.Render(_renderHandle.DrawingHandleWorld, eye.Rotation, in entry.worldRotation, in entry.worldPos);
|
||||
|
||||
if (entry.sprite.PostShader != null && entityPostRenderTarget != null)
|
||||
{
|
||||
@@ -369,28 +354,37 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void ProcessSpriteEntities(MapId map, Matrix3 eyeMatrix, Box2Rotated worldBounds,
|
||||
RefList<(SpriteComponent sprite, Matrix3 matrix, Angle worldRot, float yWorldPos)> list)
|
||||
private void ProcessSpriteEntities(MapId map, Viewport view, IEye eye, Box2Rotated worldBounds,
|
||||
RefList<(SpriteComponent sprite, Vector2 worldPos, Angle worldRot, Box2 spriteScreenBB)> list)
|
||||
{
|
||||
var xforms = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
// Construct a matrix equivalent for Viewport.WorldToLocal()
|
||||
eye.GetViewMatrix(out var viewMatrix, view.RenderScale);
|
||||
var uiProjmatrix = Matrix3.Identity;
|
||||
uiProjmatrix.R0C0 = EyeManager.PixelsPerMeter;
|
||||
uiProjmatrix.R1C1 = -EyeManager.PixelsPerMeter;
|
||||
uiProjmatrix.R0C2 = view.Size.X / 2f;
|
||||
uiProjmatrix.R1C2 = view.Size.Y / 2f;
|
||||
var worldToLocal = viewMatrix * uiProjmatrix;
|
||||
|
||||
foreach (var comp in _entitySystemManager.GetEntitySystem<RenderingTreeSystem>().GetRenderTrees(map, worldBounds))
|
||||
{
|
||||
var bounds = _entityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(worldBounds);
|
||||
var bounds = xforms.GetComponent(comp.Owner).InvWorldMatrix.TransformBox(worldBounds);
|
||||
|
||||
comp.SpriteTree.QueryAabb(ref list, (
|
||||
ref RefList<(SpriteComponent sprite, Matrix3 matrix, Angle worldRot, float yWorldPos)> state,
|
||||
ref RefList<(SpriteComponent sprite, Vector2 worldPos, Angle worldRot, Box2 spriteScreenBB)> state,
|
||||
in SpriteComponent value) =>
|
||||
{
|
||||
var entity = value.Owner;
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(entity);
|
||||
var transform = xforms.GetComponent(entity);
|
||||
|
||||
ref var entry = ref state.AllocAdd();
|
||||
entry.sprite = value;
|
||||
entry.worldRot = transform.WorldRotation;
|
||||
entry.matrix = transform.WorldMatrix;
|
||||
var eyePos = eyeMatrix.Transform(new Vector2(entry.matrix.R0C2, entry.matrix.R1C2));
|
||||
// Didn't use the bounds from the query as that has to be re-calculated (and is probably more expensive than this).
|
||||
var bounds = value.CalculateBoundingBox(eyePos);
|
||||
entry.yWorldPos = eyePos.Y - bounds.Extents.Y;
|
||||
(entry.worldPos, entry.worldRot) = transform.GetWorldPositionRotation();
|
||||
|
||||
var spriteWorldBB = value.CalculateRotatedBoundingBox(entry.worldPos, entry.worldRot, eye);
|
||||
entry.spriteScreenBB = worldToLocal.TransformBox(spriteWorldBB);
|
||||
return true;
|
||||
|
||||
}, bounds, true);
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
PixelInternalFormat.Srgb8Alpha8 => 4,
|
||||
PixelInternalFormat.R11fG11fB10f => 4,
|
||||
PixelInternalFormat.R32f => 4,
|
||||
PixelInternalFormat.Rg32f => 4,
|
||||
PixelInternalFormat.Rg32f => 8,
|
||||
PixelInternalFormat.R8 => 1,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
@@ -514,13 +514,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var enlargedBounds = worldAABB.Enlarged(renderingTreeSystem.MaxLightRadius);
|
||||
|
||||
// Use worldbounds for this one as we only care if the light intersects our actual bounds
|
||||
var state = (this, worldAABB, count: 0);
|
||||
var state = (this, worldAABB, count: 0, shadowCastingCount: 0);
|
||||
var xforms = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var comp in renderingTreeSystem.GetRenderTrees(map, enlargedBounds))
|
||||
{
|
||||
var bounds = _entityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(worldBounds);
|
||||
var bounds = xforms.GetComponent(comp.Owner).InvWorldMatrix.TransformBox(worldBounds);
|
||||
|
||||
comp.LightTree.QueryAabb(ref state, (ref (Clyde clyde, Box2 worldAABB, int count) state, in PointLightComponent light) =>
|
||||
comp.LightTree.QueryAabb(ref state, (ref (Clyde clyde, Box2 worldAABB, int count, int shadowCastingCount) state, in PointLightComponent light) =>
|
||||
{
|
||||
if (state.count >= LightsToRenderListSize)
|
||||
{
|
||||
@@ -528,7 +529,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return false;
|
||||
}
|
||||
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(light.Owner);
|
||||
var transform = xforms.GetComponent(light.Owner);
|
||||
|
||||
if (float.IsNaN(transform.LocalPosition.X) || float.IsNaN(transform.LocalPosition.Y)) return true;
|
||||
|
||||
@@ -542,6 +543,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the light is a shadow casting light, keep a separate track of that
|
||||
if (light.CastShadows) state.shadowCastingCount++;
|
||||
|
||||
float distanceSquared = (state.worldAABB.Center - lightPos).LengthSquared;
|
||||
state.clyde._lightsToRenderList[state.count++] = (light, lightPos, distanceSquared);
|
||||
|
||||
@@ -549,17 +553,29 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}, bounds);
|
||||
}
|
||||
|
||||
if (state.count > _maxLightsPerScene)
|
||||
if (state.shadowCastingCount > _maxLightsPerScene)
|
||||
{
|
||||
// There are too many lights to fit in the scene.
|
||||
// There are too many lights casting shadows to fit in the scene.
|
||||
// This check must occur before occluder expansion, or else bad things happen.
|
||||
// Sort lights by distance.
|
||||
|
||||
// First, partition the array based on whether the lights are shadow casting or not
|
||||
// (non shadow casting lights should be the first partition, shadow casting lights the second)
|
||||
Array.Sort(_lightsToRenderList, 0, state.count, Comparer<(PointLightComponent light, Vector2 pos, float distanceSquared)>.Create((x, y) =>
|
||||
{
|
||||
if (x.light.CastShadows && !y.light.CastShadows) return 1;
|
||||
else if (!x.light.CastShadows && y.light.CastShadows) return -1;
|
||||
else return 0;
|
||||
}));
|
||||
|
||||
// Next, sort just the shadow casting lights by distance.
|
||||
Array.Sort(_lightsToRenderList, state.count - state.shadowCastingCount, state.shadowCastingCount, Comparer<(PointLightComponent light, Vector2 pos, float distanceSquared)>.Create((x, y) =>
|
||||
{
|
||||
return x.distanceSquared.CompareTo(y.distanceSquared);
|
||||
}));
|
||||
// Then effectively delete the furthest lights.
|
||||
state.count = _maxLightsPerScene;
|
||||
|
||||
// Then effectively delete the furthest lights, by setting the end of the array to exclude N
|
||||
// number of shadow casting lights (where N is the number above the max number per scene.)
|
||||
state.count -= state.shadowCastingCount - _maxLightsPerScene;
|
||||
}
|
||||
|
||||
// When culling occluders later, we can't just remove any occluders outside the worldBounds.
|
||||
@@ -871,21 +887,24 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var ii = 0;
|
||||
var imi = 0;
|
||||
|
||||
var xforms = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var comp in occluderSystem.GetOccluderTrees(map, expandedBounds))
|
||||
{
|
||||
var treeBounds = _entityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(expandedBounds);
|
||||
var treeBounds = xforms.GetComponent(comp.Owner).InvWorldMatrix.TransformBox(expandedBounds);
|
||||
|
||||
comp.Tree.QueryAabb((in OccluderComponent sOccluder) =>
|
||||
{
|
||||
var occluder = (ClientOccluderComponent)sOccluder;
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(occluder.Owner);
|
||||
if (!occluder.Enabled)
|
||||
var transform = xforms.GetComponent(sOccluder.Owner);
|
||||
if (!sOccluder.Enabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var occluder = (ClientOccluderComponent)sOccluder;
|
||||
|
||||
var worldTransform = transform.WorldMatrix;
|
||||
var box = occluder.BoundingBox;
|
||||
var box = sOccluder.BoundingBox;
|
||||
|
||||
var tl = worldTransform.Transform(box.TopLeft);
|
||||
var tr = worldTransform.Transform(box.TopRight);
|
||||
|
||||
@@ -994,9 +994,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private sealed class SpriteDrawingOrderComparer : IComparer<int>
|
||||
{
|
||||
private readonly RefList<(SpriteComponent, Matrix3, Angle, float)> _drawList;
|
||||
private readonly RefList<(SpriteComponent, Vector2, Angle, Box2)> _drawList;
|
||||
|
||||
public SpriteDrawingOrderComparer(RefList<(SpriteComponent, Matrix3, Angle, float)> drawList)
|
||||
public SpriteDrawingOrderComparer(RefList<(SpriteComponent, Vector2, Angle, Box2)> drawList)
|
||||
{
|
||||
_drawList = drawList;
|
||||
}
|
||||
@@ -1019,7 +1019,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return cmp;
|
||||
}
|
||||
|
||||
cmp = _drawList[y].Item4.CompareTo(_drawList[x].Item4);
|
||||
// compare the top of the sprite's BB for y-sorting. Because screen coordinates are flipped, the "top" of the BB is actually the "bottom".
|
||||
cmp = _drawList[x].Item4.Top.CompareTo(_drawList[y].Item4.Top);
|
||||
|
||||
if (cmp != 0)
|
||||
{
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private readonly ConcurrentQueue<ClydeHandle> _deadShaderInstances = new();
|
||||
|
||||
private class LoadedShader
|
||||
private sealed class LoadedShader
|
||||
{
|
||||
public GLShaderProgram Program = default!;
|
||||
public bool HasLighting = true;
|
||||
@@ -42,7 +42,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public string? Name;
|
||||
}
|
||||
|
||||
private class LoadedShaderInstance
|
||||
private sealed class LoadedShaderInstance
|
||||
{
|
||||
public ClydeHandle ShaderHandle;
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (Eye == null)
|
||||
return default;
|
||||
|
||||
var eye = (IEye) Eye;
|
||||
var eye = Eye;
|
||||
var newPoint = point;
|
||||
|
||||
eye.GetViewMatrix(out var viewMatrix, RenderScale);
|
||||
|
||||
@@ -246,7 +246,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Nada.
|
||||
}
|
||||
|
||||
private class DummyCursor : ICursor
|
||||
private sealed class DummyCursor : ICursor
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
@@ -254,6 +254,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
[Virtual]
|
||||
private class DummyAudioSource : IClydeAudioSource
|
||||
{
|
||||
public static DummyAudioSource Instance { get; } = new();
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
/// <summary>
|
||||
/// Represents an OpenGL buffer object.
|
||||
/// </summary>
|
||||
[Virtual]
|
||||
private class GLBuffer
|
||||
{
|
||||
private readonly Clyde _clyde;
|
||||
@@ -178,7 +179,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
/// Subtype of buffers so that we can have a generic constructor.
|
||||
/// Functionally equivalent to <see cref="GLBuffer"/> otherwise.
|
||||
/// </summary>
|
||||
private class GLBuffer<T> : GLBuffer where T : unmanaged
|
||||
private sealed class GLBuffer<T> : GLBuffer where T : unmanaged
|
||||
{
|
||||
public GLBuffer(Clyde clyde, BufferTarget type, BufferUsageHint usage, Span<T> initialize,
|
||||
string? name = null)
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
internal partial class Clyde
|
||||
{
|
||||
private class GLShader
|
||||
private sealed class GLShader
|
||||
{
|
||||
private readonly Clyde _clyde;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
/// You've been warned:
|
||||
/// using things like <see cref="SetUniformTexture" /> if this buffer isn't bound WILL mess things up!
|
||||
/// </summary>
|
||||
private class GLShaderProgram
|
||||
private sealed class GLShaderProgram
|
||||
{
|
||||
private readonly sbyte?[] _uniformIntCache = new sbyte?[UniCount];
|
||||
private readonly Dictionary<string, int> _uniformCache = new();
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
/// <summary>
|
||||
/// Represents some set of uniforms that can be backed by a uniform buffer or by regular uniforms.
|
||||
/// </summary>
|
||||
private class GLUniformBuffer<T> where T : unmanaged, IAppliableUniformSet
|
||||
private sealed class GLUniformBuffer<T> where T : unmanaged, IAppliableUniformSet
|
||||
{
|
||||
private readonly Clyde _clyde;
|
||||
private readonly int _index;
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
[Serializable]
|
||||
[PublicAPI]
|
||||
[Virtual]
|
||||
internal class ShaderCompilationException : Exception
|
||||
{
|
||||
public ShaderCompilationException()
|
||||
|
||||
@@ -104,6 +104,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[Virtual]
|
||||
public class GlfwException : Exception
|
||||
{
|
||||
public GlfwException()
|
||||
|
||||
@@ -2,7 +2,7 @@ using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
public class StyleBoxEmpty : StyleBox
|
||||
public sealed class StyleBoxEmpty : StyleBox
|
||||
{
|
||||
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
public class StyleBoxFlat : StyleBox
|
||||
public sealed class StyleBoxFlat : StyleBox
|
||||
{
|
||||
public Color BackgroundColor { get; set; }
|
||||
public Color BorderColor { get; set; }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user