mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80f3aae30c | ||
|
|
98b1862433 | ||
|
|
d2311c193f | ||
|
|
f05ed96461 | ||
|
|
dc23dfaf4d | ||
|
|
62315f7c2e | ||
|
|
b2d121e780 | ||
|
|
fb4b029122 | ||
|
|
66239d23ea | ||
|
|
dbb45f1c13 | ||
|
|
6284e16b64 | ||
|
|
f6c55085fe | ||
|
|
30eafd26e7 | ||
|
|
63423d96b4 | ||
|
|
474334aff2 | ||
|
|
be102f86bf | ||
|
|
d7962c7190 | ||
|
|
7fe9385c3b | ||
|
|
a9d9d1348a | ||
|
|
4eaf624555 | ||
|
|
e37c131fb4 | ||
|
|
9df4606492 | ||
|
|
3be8070274 |
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -25,4 +25,7 @@
|
||||
|
||||
<!-- analyzer -->
|
||||
<Import Project="Robust.Analyzers.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
|
||||
|
||||
<!-- serialization generator -->
|
||||
<Import Project="Robust.Serialization.Generator.targets" />
|
||||
</Project>
|
||||
|
||||
5
MSBuild/Robust.Serialization.Generator.targets
Normal file
5
MSBuild/Robust.Serialization.Generator.targets
Normal file
@@ -0,0 +1,5 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Serialization.Generator\Robust.Serialization.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -54,6 +54,62 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 150.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Remove the Id field from Fixtures as the Id is already stored on FixturesComponent.
|
||||
|
||||
### New features
|
||||
|
||||
* Add AbstractDictionarySerializer for abstract classes.
|
||||
* Add many new spawn functions for entities for common operations.
|
||||
|
||||
|
||||
## 149.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix serialization sharing instances when copying data definitions and not assigning null when the source is null.
|
||||
* Fixed resizing a window to be bigger than its set maxsize crashing the client.
|
||||
|
||||
|
||||
## 149.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Data definitions must now be partial, their data fields must not be readonly and their data field properties must have a setter.
|
||||
|
||||
### Internal
|
||||
|
||||
* Copying data definitions through the serialization manager is now faster and consumes less memory.
|
||||
|
||||
|
||||
## 148.4.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add recursive PVS overrides and remove IsOverride()
|
||||
|
||||
|
||||
## 148.3.0
|
||||
|
||||
### New features
|
||||
|
||||
* Happy eyeballs delay can be configured.
|
||||
* Added more colors.
|
||||
* Allow pre-startup components to be shut down.
|
||||
* Added tile texture reload command.
|
||||
* Add implementation of Random.Pick(ValueList<T> ..).
|
||||
* Add IntegrationInstance fields for common dependencies.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Prevent invalid prototypes from being spawned.
|
||||
* Change default value of EntityLastModifiedTick from zero to one.
|
||||
* Make DiscordRichPresence icon CVars server-side with replication.
|
||||
|
||||
|
||||
## 148.2.0
|
||||
|
||||
### New features
|
||||
|
||||
@@ -558,3 +558,6 @@ cmd-vfs_ls-help = Usage: vfs_list <path>
|
||||
|
||||
cmd-vfs_ls-err-args = Need exactly 1 argument.
|
||||
cmd-vfs_ls-hint-path = <path>
|
||||
|
||||
cmd-reloadtiletextures-desc = Reloads the tile texture atlas to allow hot reloading tile sprites
|
||||
cmd-reloadtiletextures-help = Usage: reloadtiletextures
|
||||
|
||||
293
Robust.Analyzers/DataDefinitionAnalyzer.cs
Normal file
293
Robust.Analyzers/DataDefinitionAnalyzer.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
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 DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string DataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataDefinitionAttribute";
|
||||
private const string ImplicitDataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.ImplicitDataDefinitionForInheritorsAttribute";
|
||||
private const string DataFieldBaseNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataFieldBaseAttribute";
|
||||
|
||||
private static readonly DiagnosticDescriptor DataDefinitionPartialRule = new(
|
||||
Diagnostics.IdDataDefinitionPartial,
|
||||
"Type must be partial",
|
||||
"Type {0} is a DataDefinition but is not partial.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to mark any type that is a data definition as partial."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor NestedDataDefinitionPartialRule = new(
|
||||
Diagnostics.IdNestedDataDefinitionPartial,
|
||||
"Type must be partial",
|
||||
"Type {0} contains nested data definition {1} but is not partial.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to mark any type containing a nested data definition as partial."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor DataFieldWritableRule = new(
|
||||
Diagnostics.IdDataFieldWritable,
|
||||
"Data field must not be readonly",
|
||||
"Data field {0} in data definition {1} is readonly.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to remove the readonly modifier."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor DataFieldPropertyWritableRule = new(
|
||||
Diagnostics.IdDataFieldPropertyWritable,
|
||||
"Data field property must have a setter",
|
||||
"Data field property {0} in data definition {1} does not have a setter.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to add a setter."
|
||||
);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
|
||||
DataDefinitionPartialRule, NestedDataDefinitionPartialRule, DataFieldWritableRule, DataFieldPropertyWritableRule
|
||||
);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.ClassDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.StructDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.RecordDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.RecordStructDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.InterfaceDeclaration);
|
||||
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataField, SyntaxKind.FieldDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataFieldProperty, SyntaxKind.PropertyDeclaration);
|
||||
}
|
||||
|
||||
private void AnalyzeDataDefinition(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not TypeDeclarationSyntax declaration)
|
||||
return;
|
||||
|
||||
var type = context.SemanticModel.GetDeclaredSymbol(declaration)!;
|
||||
if (!IsDataDefinition(type))
|
||||
return;
|
||||
|
||||
if (!IsPartial(declaration))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataDefinitionPartialRule, declaration.Keyword.GetLocation(), type.Name));
|
||||
}
|
||||
|
||||
var containingType = type.ContainingType;
|
||||
while (containingType != null)
|
||||
{
|
||||
var containingTypeDeclaration = (TypeDeclarationSyntax) containingType.DeclaringSyntaxReferences[0].GetSyntax();
|
||||
if (!IsPartial(containingTypeDeclaration))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(NestedDataDefinitionPartialRule, containingTypeDeclaration.Keyword.GetLocation(), containingType.Name, type.Name));
|
||||
}
|
||||
|
||||
containingType = containingType.ContainingType;
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeDataField(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not FieldDeclarationSyntax field)
|
||||
return;
|
||||
|
||||
var typeDeclaration = field.FirstAncestorOrSelf<TypeDeclarationSyntax>();
|
||||
if (typeDeclaration == null)
|
||||
return;
|
||||
|
||||
var type = context.SemanticModel.GetDeclaredSymbol(typeDeclaration)!;
|
||||
if (!IsDataDefinition(type))
|
||||
return;
|
||||
|
||||
foreach (var variable in field.Declaration.Variables)
|
||||
{
|
||||
var fieldSymbol = context.SemanticModel.GetDeclaredSymbol(variable);
|
||||
if (fieldSymbol == null)
|
||||
continue;
|
||||
|
||||
if (IsReadOnlyDataField(type, fieldSymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldWritableRule, context.Node.GetLocation(), fieldSymbol.Name, type.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeDataFieldProperty(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not PropertyDeclarationSyntax property)
|
||||
return;
|
||||
|
||||
var typeDeclaration = property.FirstAncestorOrSelf<TypeDeclarationSyntax>();
|
||||
if (typeDeclaration == null)
|
||||
return;
|
||||
|
||||
var type = context.SemanticModel.GetDeclaredSymbol(typeDeclaration)!;
|
||||
if (!IsDataDefinition(type) || type.IsRecord || type.IsValueType)
|
||||
return;
|
||||
|
||||
var propertySymbol = context.SemanticModel.GetDeclaredSymbol(property);
|
||||
if (propertySymbol == null)
|
||||
return;
|
||||
|
||||
if (IsReadOnlyDataField(type, propertySymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldPropertyWritableRule, context.Node.GetLocation(), propertySymbol.Name, type.Name));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsReadOnlyDataField(ITypeSymbol type, ISymbol field)
|
||||
{
|
||||
if (!IsDataField(field, out _, out _))
|
||||
return false;
|
||||
|
||||
return IsReadOnlyMember(type, field);
|
||||
}
|
||||
|
||||
private static bool IsPartial(TypeDeclarationSyntax type)
|
||||
{
|
||||
return type.Modifiers.IndexOf(SyntaxKind.PartialKeyword) != -1;
|
||||
}
|
||||
|
||||
private static bool IsDataDefinition(ITypeSymbol? type)
|
||||
{
|
||||
if (type == null)
|
||||
return false;
|
||||
|
||||
return HasAttribute(type, DataDefinitionNamespace) ||
|
||||
IsImplicitDataDefinition(type);
|
||||
}
|
||||
|
||||
private static bool IsDataField(ISymbol member, out ITypeSymbol type, out AttributeData attribute)
|
||||
{
|
||||
// TODO data records and other attributes
|
||||
if (member is IFieldSymbol field)
|
||||
{
|
||||
foreach (var attr in field.GetAttributes())
|
||||
{
|
||||
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
||||
{
|
||||
type = field.Type;
|
||||
attribute = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (member is IPropertySymbol property)
|
||||
{
|
||||
foreach (var attr in property.GetAttributes())
|
||||
{
|
||||
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
||||
{
|
||||
type = property.Type;
|
||||
attribute = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type = null!;
|
||||
attribute = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool Inherits(ITypeSymbol type, string parent)
|
||||
{
|
||||
foreach (var baseType in GetBaseTypes(type))
|
||||
{
|
||||
if (baseType.ToDisplayString() == parent)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsReadOnlyMember(ITypeSymbol type, ISymbol member)
|
||||
{
|
||||
if (member is IFieldSymbol field)
|
||||
{
|
||||
return field.IsReadOnly;
|
||||
}
|
||||
else if (member is IPropertySymbol property)
|
||||
{
|
||||
if (property.SetMethod == null)
|
||||
return true;
|
||||
|
||||
if (property.SetMethod.IsInitOnly)
|
||||
return type.IsReferenceType;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HasAttribute(ITypeSymbol type, string attributeName)
|
||||
{
|
||||
foreach (var attribute in type.GetAttributes())
|
||||
{
|
||||
if (attribute.AttributeClass?.ToDisplayString() == attributeName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsImplicitDataDefinition(ITypeSymbol type)
|
||||
{
|
||||
if (HasAttribute(type, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
|
||||
foreach (var baseType in GetBaseTypes(type))
|
||||
{
|
||||
if (HasAttribute(baseType, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var @interface in type.AllInterfaces)
|
||||
{
|
||||
if (IsImplicitDataDefinitionInterface(@interface))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsImplicitDataDefinitionInterface(ITypeSymbol @interface)
|
||||
{
|
||||
if (HasAttribute(@interface, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
|
||||
foreach (var subInterface in @interface.AllInterfaces)
|
||||
{
|
||||
if (HasAttribute(subInterface, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static IEnumerable<ITypeSymbol> GetBaseTypes(ITypeSymbol type)
|
||||
{
|
||||
var baseType = type.BaseType;
|
||||
while (baseType != null)
|
||||
{
|
||||
yield return baseType;
|
||||
baseType = baseType.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
168
Robust.Analyzers/DataDefinitionFixer.cs
Normal file
168
Robust.Analyzers/DataDefinitionFixer.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
#nullable enable
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxKind;
|
||||
using static Robust.Analyzers.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
||||
public sealed class DefinitionFixer : CodeFixProvider
|
||||
{
|
||||
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
|
||||
IdDataDefinitionPartial, IdNestedDataDefinitionPartial, IdDataFieldWritable, IdDataFieldPropertyWritable
|
||||
);
|
||||
|
||||
public override Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||
{
|
||||
foreach (var diagnostic in context.Diagnostics)
|
||||
{
|
||||
switch (diagnostic.Id)
|
||||
{
|
||||
case IdDataDefinitionPartial:
|
||||
return RegisterPartialTypeFix(context, diagnostic);
|
||||
case IdNestedDataDefinitionPartial:
|
||||
return RegisterPartialTypeFix(context, diagnostic);
|
||||
case IdDataFieldWritable:
|
||||
return RegisterDataFieldFix(context, diagnostic);
|
||||
case IdDataFieldPropertyWritable:
|
||||
return RegisterDataFieldPropertyFix(context, diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override FixAllProvider GetFixAllProvider()
|
||||
{
|
||||
return WellKnownFixAllProviders.BatchFixer;
|
||||
}
|
||||
|
||||
private static async Task RegisterPartialTypeFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var token = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<TypeDeclarationSyntax>().First();
|
||||
|
||||
if (token == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Make type partial",
|
||||
c => MakeDataDefinitionPartial(context.Document, token, c),
|
||||
"Make type partial"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task<Document> MakeDataDefinitionPartial(Document document, TypeDeclarationSyntax declaration, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
var token = SyntaxFactory.Token(PartialKeyword);
|
||||
var newDeclaration = declaration.AddModifiers(token);
|
||||
|
||||
root = root!.ReplaceNode(declaration, newDeclaration);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private static async Task RegisterDataFieldFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var field = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<FieldDeclarationSyntax>().FirstOrDefault();
|
||||
|
||||
if (field == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Make data field writable",
|
||||
c => MakeFieldWritable(context.Document, field, c),
|
||||
"Make data field writable"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task RegisterDataFieldPropertyFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var property = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<PropertyDeclarationSyntax>().FirstOrDefault();
|
||||
|
||||
if (property == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Make data field writable",
|
||||
c => MakePropertyWritable(context.Document, property, c),
|
||||
"Make data field writable"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task<Document> MakeFieldWritable(Document document, FieldDeclarationSyntax declaration, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
var token = declaration.Modifiers.First(t => t.IsKind(ReadOnlyKeyword));
|
||||
var newDeclaration = declaration.WithModifiers(declaration.Modifiers.Remove(token));
|
||||
|
||||
root = root!.ReplaceNode(declaration, newDeclaration);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private static async Task<Document> MakePropertyWritable(Document document, PropertyDeclarationSyntax declaration, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
var newDeclaration = declaration;
|
||||
var privateSet = newDeclaration
|
||||
.AccessorList?
|
||||
.Accessors
|
||||
.FirstOrDefault(s => s.IsKind(SetAccessorDeclaration) || s.IsKind(InitAccessorDeclaration));
|
||||
|
||||
if (newDeclaration.AccessorList != null && privateSet != null)
|
||||
{
|
||||
newDeclaration = newDeclaration.WithAccessorList(
|
||||
newDeclaration.AccessorList.WithAccessors(
|
||||
newDeclaration.AccessorList.Accessors.Remove(privateSet)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
AccessorDeclarationSyntax setter;
|
||||
if (declaration.Modifiers.Any(m => m.IsKind(PrivateKeyword)))
|
||||
{
|
||||
setter = SyntaxFactory.AccessorDeclaration(
|
||||
SetAccessorDeclaration,
|
||||
default,
|
||||
default,
|
||||
SyntaxFactory.Token(SetKeyword),
|
||||
default,
|
||||
default,
|
||||
SyntaxFactory.Token(SemicolonToken)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
setter = SyntaxFactory.AccessorDeclaration(
|
||||
SetAccessorDeclaration,
|
||||
default,
|
||||
SyntaxFactory.TokenList(SyntaxFactory.Token(PrivateKeyword)),
|
||||
SyntaxFactory.Token(SetKeyword),
|
||||
default,
|
||||
default,
|
||||
SyntaxFactory.Token(SemicolonToken)
|
||||
);
|
||||
}
|
||||
|
||||
newDeclaration = newDeclaration.AddAccessorListAccessors(setter);
|
||||
|
||||
root = root!.ReplaceNode(declaration, newDeclaration);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,10 @@ public static class Diagnostics
|
||||
public const string IdValueEventSubscribedByRef = "RA0014";
|
||||
public const string IdByRefEventRaisedByValue = "RA0015";
|
||||
public const string IdValueEventRaisedByRef = "RA0016";
|
||||
public const string IdDataDefinitionPartial = "RA0017";
|
||||
public const string IdNestedDataDefinitionPartial = "RA0018";
|
||||
public const string IdDataFieldWritable = "RA0019";
|
||||
public const string IdDataFieldPropertyWritable = "RA0020";
|
||||
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.UnitTesting.Server;
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[Virtual]
|
||||
public class AddRemoveComponentBenchmark
|
||||
public partial class AddRemoveComponentBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -48,7 +48,7 @@ public class AddRemoveComponentBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
public class ComponentIteratorBenchmark
|
||||
public partial class ComponentIteratorBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -69,7 +69,7 @@ public class ComponentIteratorBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Analyzers;
|
||||
@@ -9,7 +8,7 @@ using Robust.UnitTesting.Server;
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[Virtual]
|
||||
public class GetComponentBenchmark
|
||||
public partial class GetComponentBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -55,7 +54,7 @@ public class GetComponentBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.UnitTesting.Server;
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[Virtual]
|
||||
public class SpawnDeleteEntityBenchmark
|
||||
public partial class SpawnDeleteEntityBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -56,7 +56,7 @@ public class SpawnDeleteEntityBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
{
|
||||
[DataDefinition]
|
||||
[Virtual]
|
||||
public class DataDefinitionWithString
|
||||
public partial class DataDefinitionWithString
|
||||
{
|
||||
[DataField("string")]
|
||||
public string StringField { get; init; } = default!;
|
||||
public string StringField { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace Robust.Benchmarks.Serialization.Definitions
|
||||
{
|
||||
[DataDefinition]
|
||||
public sealed class SealedDataDefinitionWithString
|
||||
public sealed partial class SealedDataDefinitionWithString
|
||||
{
|
||||
[DataField("string")]
|
||||
public string StringField { get; init; } = default!;
|
||||
public string StringField { get; private set; } = default!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -10,8 +11,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
/// Arbitrarily large data definition for benchmarks.
|
||||
/// Taken from content.
|
||||
/// </summary>
|
||||
[Prototype("seed")]
|
||||
public sealed class SeedDataDefinition : IPrototype
|
||||
public sealed partial class SeedDataDefinition : Component
|
||||
{
|
||||
public const string Prototype = @"
|
||||
- type: seed
|
||||
@@ -106,7 +106,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
public struct SeedChemQuantity
|
||||
public partial struct SeedChemQuantity
|
||||
{
|
||||
[DataField("Min")]
|
||||
public int Min;
|
||||
|
||||
@@ -68,6 +68,7 @@ namespace Robust.Client
|
||||
deps.Register<IComponentFactory, ComponentFactory>();
|
||||
deps.Register<ITileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<IClydeTileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<ClydeTileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<GameController, GameController>();
|
||||
deps.Register<IGameController, GameController>();
|
||||
deps.Register<IGameControllerInternal, GameController>();
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Physics;
|
||||
namespace Robust.Client.ComponentTrees;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class LightTreeComponent: Component, IComponentTreeComponent<PointLightComponent>
|
||||
public sealed partial class LightTreeComponent: Component, IComponentTreeComponent<PointLightComponent>
|
||||
{
|
||||
public DynamicTree<ComponentTreeEntry<PointLightComponent>> Tree { get; set; } = default!;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Physics;
|
||||
namespace Robust.Client.ComponentTrees;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class SpriteTreeComponent: Component, IComponentTreeComponent<SpriteComponent>
|
||||
public sealed partial class SpriteTreeComponent: Component, IComponentTreeComponent<SpriteComponent>
|
||||
{
|
||||
public DynamicTree<ComponentTreeEntry<SpriteComponent>> Tree { get; set; } = default!;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.GameObjects
|
||||
/// Plays back <see cref="Animation"/>s on entities.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class AnimationPlayerComponent : Component
|
||||
public sealed partial class AnimationPlayerComponent : Component
|
||||
{
|
||||
// TODO: Give this component a friend someday. Way too much content shit to change atm ._.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.GameObjects;
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(GenericVisualizerSystem))]
|
||||
public sealed class GenericVisualizerComponent : Component
|
||||
public sealed partial class GenericVisualizerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a nested dictionary that maps appearance data keys -> sprite layer keys -> appearance data values -> layer data.
|
||||
|
||||
@@ -10,7 +10,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent, ComponentReference(typeof(SharedEyeComponent))]
|
||||
public sealed class EyeComponent : SharedEyeComponent
|
||||
public sealed partial class EyeComponent : SharedEyeComponent
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace Robust.Client.GameObjects;
|
||||
/// updated.
|
||||
/// </remarks>
|
||||
[RegisterComponent]
|
||||
public sealed class IconComponent : Component
|
||||
public sealed partial class IconComponent : Component
|
||||
{
|
||||
[IncludeDataField]
|
||||
public readonly SpriteSpecifier.Rsi Icon = default!;
|
||||
public SpriteSpecifier.Rsi Icon = default!;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Client.GameObjects
|
||||
/// Defines data fields used in the <see cref="InputSystem"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class InputComponent : Component
|
||||
public sealed partial class InputComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The context that will be made active for a client that attaches to this entity.
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedPointLightComponent))]
|
||||
public sealed class PointLightComponent : SharedPointLightComponent, IComponentTreeEntry<PointLightComponent>
|
||||
public sealed partial class PointLightComponent : SharedPointLightComponent, IComponentTreeEntry<PointLightComponent>
|
||||
{
|
||||
public EntityUid? TreeUid { get; set; }
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public interface IRenderableComponent : IComponent
|
||||
public partial interface IRenderableComponent : IComponent
|
||||
{
|
||||
int DrawDepth { get; set; }
|
||||
float Bottom { get; }
|
||||
|
||||
@@ -32,7 +32,7 @@ using static Robust.Shared.Serialization.TypeSerializers.Implementations.SpriteS
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class SpriteComponent : Component, IComponentDebug, ISerializationHooks, IComponentTreeEntry<SpriteComponent>, IAnimationProperties
|
||||
public sealed partial class SpriteComponent : Component, IComponentDebug, ISerializationHooks, IComponentTreeEntry<SpriteComponent>, IAnimationProperties
|
||||
{
|
||||
[Dependency] private readonly IResourceCache resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager prototypes = default!;
|
||||
|
||||
@@ -7,7 +7,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent, ComponentReference(typeof(SharedUserInterfaceComponent))]
|
||||
public sealed class ClientUserInterfaceComponent : SharedUserInterfaceComponent
|
||||
public sealed partial class ClientUserInterfaceComponent : SharedUserInterfaceComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
internal readonly Dictionary<Enum, PrototypeData> _interfaces = new();
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
while (chunkEnumerator.MoveNext(out var chunk))
|
||||
{
|
||||
foreach (var fixture in chunk.Fixtures)
|
||||
foreach (var fixture in chunk.Fixtures.Values)
|
||||
{
|
||||
var poly = (PolygonShape) fixture.Shape;
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
public struct StencilParameters
|
||||
public partial struct StencilParameters
|
||||
{
|
||||
public StencilParameters()
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
[ViewVariables] private ShaderKind Kind;
|
||||
[ViewVariables] private Dictionary<string, object>? _params;
|
||||
@@ -95,19 +95,19 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
|
||||
[DataField("kind", required: true)]
|
||||
private readonly string _rawKind = default!;
|
||||
private string _rawKind = default!;
|
||||
|
||||
[DataField("path")]
|
||||
private readonly ResPath? _path;
|
||||
private ResPath? _path;
|
||||
|
||||
[DataField("params")]
|
||||
private readonly Dictionary<string, string>? _paramMapping;
|
||||
private Dictionary<string, string>? _paramMapping;
|
||||
|
||||
[DataField("light_mode")]
|
||||
private readonly string? _rawMode;
|
||||
private string? _rawMode;
|
||||
|
||||
[DataField("blend_mode")]
|
||||
private readonly string? _rawBlendMode;
|
||||
private string? _rawBlendMode;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.Serialization.Manager.Attributes;
|
||||
namespace Robust.Client.Input
|
||||
{
|
||||
[DataDefinition]
|
||||
public sealed class KeyBindingRegistration
|
||||
public sealed partial class KeyBindingRegistration
|
||||
{
|
||||
[DataField("function")]
|
||||
public BoundKeyFunction Function;
|
||||
|
||||
@@ -3,13 +3,16 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Utility;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
@@ -52,8 +55,11 @@ namespace Robust.Client.Map
|
||||
_genTextureAtlas();
|
||||
}
|
||||
|
||||
private void _genTextureAtlas()
|
||||
internal void _genTextureAtlas()
|
||||
{
|
||||
_tileRegions.Clear();
|
||||
_tileTextureAtlas = null;
|
||||
|
||||
var defList = TileDefs.Where(t => t.Sprite != null).ToList();
|
||||
|
||||
// If there are no tile definitions, we do nothing.
|
||||
@@ -144,4 +150,17 @@ namespace Robust.Client.Map
|
||||
_tileTextureAtlas = Texture.LoadFromImage(sheet, "Tile Atlas");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ReloadTileTexturesCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly ClydeTileDefinitionManager _tile = default!;
|
||||
|
||||
public override string Command => "reloadtiletextures";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
_tile._genTextureAtlas();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,6 @@ namespace Robust.Client.Physics;
|
||||
/// Simple component used to tag entities that have physics prediction enabled.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class PredictedPhysicsComponent : Component
|
||||
public sealed partial class PredictedPhysicsComponent : Component
|
||||
{
|
||||
}
|
||||
|
||||
@@ -715,7 +715,7 @@ namespace Robust.Client.UserInterface
|
||||
maxH = MathHelper.Clamp(maxConstraint, minH, maxH);
|
||||
|
||||
minConstraint = float.IsNaN(setH) ? 0 : setH;
|
||||
minH = MathHelper.Clamp(maxH, minConstraint, minH);
|
||||
minH = MathHelper.Clamp(minConstraint, minH, maxH);
|
||||
|
||||
return new Vector2(
|
||||
Math.Clamp(avail.X, minW, maxW),
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace Robust.Client.UserInterface.RichText;
|
||||
public sealed class FontPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
[DataField("path", required: true)]
|
||||
public ResPath Path { get; } = default!;
|
||||
public ResPath Path { get; private set; } = default!;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public sealed class UITheme : IPrototype
|
||||
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
[DataField("path")]
|
||||
private ResPath _path;
|
||||
@@ -70,7 +70,7 @@ public sealed class UITheme : IPrototype
|
||||
|
||||
if (!texturePath.EndsWith(".png"))
|
||||
texturePath = $"{texturePath}.png";
|
||||
|
||||
|
||||
var resPath = new ResPath(texturePath);
|
||||
if (resPath.IsRelative)
|
||||
{
|
||||
|
||||
6
Robust.Serialization.Generator/DataDefinition.cs
Normal file
6
Robust.Serialization.Generator/DataDefinition.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Robust.Serialization.Generator;
|
||||
|
||||
public sealed record DataDefinition(ITypeSymbol Type, string GenericTypeName, List<DataField> Fields, bool HasHooks, bool InvalidFields);
|
||||
17
Robust.Serialization.Generator/DataDefinitionComparer.cs
Normal file
17
Robust.Serialization.Generator/DataDefinitionComparer.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Robust.Serialization.Generator;
|
||||
|
||||
public sealed class DataDefinitionComparer : IEqualityComparer<TypeDeclarationSyntax>
|
||||
{
|
||||
public bool Equals(TypeDeclarationSyntax x, TypeDeclarationSyntax y)
|
||||
{
|
||||
return x.Equals(y);
|
||||
}
|
||||
|
||||
public int GetHashCode(TypeDeclarationSyntax type)
|
||||
{
|
||||
return type.GetHashCode();
|
||||
}
|
||||
}
|
||||
14
Robust.Serialization.Generator/DataField.cs
Normal file
14
Robust.Serialization.Generator/DataField.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Robust.Serialization.Generator;
|
||||
|
||||
public sealed record DataField(
|
||||
ISymbol Symbol,
|
||||
ITypeSymbol Type,
|
||||
(INamedTypeSymbol Serializer, CustomSerializerType Type)? CustomSerializer);
|
||||
|
||||
public enum CustomSerializerType
|
||||
{
|
||||
Copier,
|
||||
CopyCreator
|
||||
}
|
||||
564
Robust.Serialization.Generator/Generator.cs
Normal file
564
Robust.Serialization.Generator/Generator.cs
Normal file
@@ -0,0 +1,564 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Robust.Serialization.Generator.CustomSerializerType;
|
||||
using static Robust.Serialization.Generator.Types;
|
||||
|
||||
namespace Robust.Serialization.Generator;
|
||||
|
||||
[Generator]
|
||||
public class Generator : IIncrementalGenerator
|
||||
{
|
||||
private const string TypeCopierInterfaceNamespace = "Robust.Shared.Serialization.TypeSerializers.Interfaces.ITypeCopier";
|
||||
private const string TypeCopyCreatorInterfaceNamespace = "Robust.Shared.Serialization.TypeSerializers.Interfaces.ITypeCopyCreator";
|
||||
private const string SerializationHooksNamespace = "Robust.Shared.Serialization.ISerializationHooks";
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext initContext)
|
||||
{
|
||||
IncrementalValuesProvider<TypeDeclarationSyntax> dataDefinitions = initContext.SyntaxProvider.CreateSyntaxProvider(
|
||||
static (node, _) => node is TypeDeclarationSyntax,
|
||||
static (context, _) =>
|
||||
{
|
||||
var type = (TypeDeclarationSyntax) context.Node;
|
||||
var symbol = (ITypeSymbol) context.SemanticModel.GetDeclaredSymbol(type)!;
|
||||
return IsDataDefinition(symbol) ? type : null;
|
||||
}
|
||||
).Where(static type => type != null)!;
|
||||
|
||||
var comparer = new DataDefinitionComparer();
|
||||
initContext.RegisterSourceOutput(
|
||||
initContext.CompilationProvider.Combine(dataDefinitions.WithComparer(comparer).Collect()),
|
||||
static (sourceContext, source) =>
|
||||
{
|
||||
var (compilation, declarations) = source;
|
||||
var builder = new StringBuilder();
|
||||
var containingTypes = new Stack<INamedTypeSymbol>();
|
||||
|
||||
foreach (var declaration in declarations)
|
||||
{
|
||||
builder.Clear();
|
||||
containingTypes.Clear();
|
||||
|
||||
var type = compilation.GetSemanticModel(declaration.SyntaxTree).GetDeclaredSymbol(declaration)!;
|
||||
|
||||
var nonPartial = !IsPartial(declaration);
|
||||
|
||||
var namespaceString = type.ContainingNamespace.IsGlobalNamespace
|
||||
? string.Empty
|
||||
: $"namespace {type.ContainingNamespace.ToDisplayString()};";
|
||||
|
||||
var containingType = type.ContainingType;
|
||||
while (containingType != null)
|
||||
{
|
||||
containingTypes.Push(containingType);
|
||||
containingType = containingType.ContainingType;
|
||||
}
|
||||
|
||||
var containingTypesStart = new StringBuilder();
|
||||
var containingTypesEnd = new StringBuilder();
|
||||
foreach (var parent in containingTypes)
|
||||
{
|
||||
var syntax = (ClassDeclarationSyntax) parent.DeclaringSyntaxReferences[0].GetSyntax();
|
||||
if (!IsPartial(syntax))
|
||||
{
|
||||
nonPartial = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
containingTypesStart.AppendLine($"{GetPartialTypeDefinitionLine(parent)}\n{{");
|
||||
containingTypesEnd.AppendLine("}");
|
||||
}
|
||||
|
||||
var definition = GetDataDefinition(type);
|
||||
if (nonPartial || definition.InvalidFields)
|
||||
continue;
|
||||
|
||||
builder.AppendLine($$"""
|
||||
#nullable enable
|
||||
using System;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Exceptions;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
|
||||
{{namespaceString}}
|
||||
|
||||
{{containingTypesStart}}
|
||||
|
||||
[RobustAutoGenerated]
|
||||
{{GetPartialTypeDefinitionLine(type)}} : ISerializationGenerated<{{definition.GenericTypeName}}>
|
||||
{
|
||||
{{GetConstructor(definition)}}
|
||||
|
||||
{{GetCopyMethods(definition)}}
|
||||
|
||||
{{GetInstantiators(definition)}}
|
||||
}
|
||||
|
||||
{{containingTypesEnd}}
|
||||
""");
|
||||
|
||||
var symbolName = type
|
||||
.ToDisplayString()
|
||||
.Replace('<', '{')
|
||||
.Replace('>', '}');
|
||||
|
||||
var sourceText = CSharpSyntaxTree
|
||||
.ParseText(builder.ToString())
|
||||
.GetRoot()
|
||||
.NormalizeWhitespace()
|
||||
.ToFullString();
|
||||
|
||||
sourceContext.AddSource($"{symbolName}.g.cs", sourceText);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static DataDefinition GetDataDefinition(ITypeSymbol definition)
|
||||
{
|
||||
var fields = new List<DataField>();
|
||||
var invalidFields = false;
|
||||
|
||||
foreach (var member in definition.GetMembers())
|
||||
{
|
||||
if (member is not IFieldSymbol && member is not IPropertySymbol)
|
||||
continue;
|
||||
|
||||
if (member.IsStatic)
|
||||
continue;
|
||||
|
||||
if (IsDataField(member, out var type, out var attribute))
|
||||
{
|
||||
if (attribute.ConstructorArguments.FirstOrDefault(arg => arg.Kind == TypedConstantKind.Type).Value is INamedTypeSymbol customSerializer)
|
||||
{
|
||||
if (ImplementsInterface(customSerializer, TypeCopierInterfaceNamespace))
|
||||
{
|
||||
fields.Add(new DataField(member, type, (customSerializer, Copier)));
|
||||
continue;
|
||||
}
|
||||
else if (ImplementsInterface(customSerializer, TypeCopyCreatorInterfaceNamespace))
|
||||
{
|
||||
fields.Add(new DataField(member, type, (customSerializer, CopyCreator)));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
fields.Add(new DataField(member, type, null));
|
||||
|
||||
if (IsReadOnlyMember(definition, type))
|
||||
{
|
||||
invalidFields = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var typeName = GetGenericTypeName(definition);
|
||||
var hasHooks = ImplementsInterface(definition, SerializationHooksNamespace);
|
||||
|
||||
return new DataDefinition(definition, typeName, fields, hasHooks, invalidFields);
|
||||
}
|
||||
|
||||
private static string GetConstructor(DataDefinition definition)
|
||||
{
|
||||
if (definition.Type.TypeKind == TypeKind.Interface)
|
||||
return string.Empty;
|
||||
|
||||
var builder = new StringBuilder();
|
||||
|
||||
if (NeedsEmptyConstructor(definition.Type))
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
// Implicit constructor
|
||||
#pragma warning disable CS8618
|
||||
public {{definition.Type.Name}}()
|
||||
#pragma warning enable CS8618
|
||||
{
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static string GetCopyMethods(DataDefinition definition)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
var modifiers = IsVirtualClass(definition.Type) ? "virtual " : string.Empty;
|
||||
var baseCall = string.Empty;
|
||||
string baseCopy;
|
||||
var baseType = definition.Type.BaseType;
|
||||
|
||||
if (baseType != null && IsDataDefinition(definition.Type.BaseType))
|
||||
{
|
||||
var baseName = baseType.ToDisplayString();
|
||||
baseCall = $"""
|
||||
var definitionCast = ({baseName}) target;
|
||||
base.InternalCopy(ref definitionCast, serialization, hookCtx, context);
|
||||
target = ({definition.GenericTypeName}) definitionCast;
|
||||
""";
|
||||
|
||||
baseCopy = $$"""
|
||||
public override void Copy(ref {{baseName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
var cast = ({{definition.GenericTypeName}}) target;
|
||||
Copy(ref cast, serialization, hookCtx, context);
|
||||
target = cast!;
|
||||
}
|
||||
|
||||
public override void Copy(ref object target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
var cast = ({{definition.GenericTypeName}}) target;
|
||||
Copy(ref cast, serialization, hookCtx, context);
|
||||
target = cast!;
|
||||
}
|
||||
""";
|
||||
}
|
||||
else
|
||||
{
|
||||
baseCopy = $$"""
|
||||
public {{modifiers}} void Copy(ref object target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
var cast = ({{definition.GenericTypeName}}) target;
|
||||
Copy(ref cast, serialization, hookCtx, context);
|
||||
target = cast!;
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
builder.AppendLine($$"""
|
||||
public {{modifiers}} void InternalCopy(ref {{definition.GenericTypeName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
{{baseCall}}
|
||||
{{CopyDataFields(definition)}}
|
||||
}
|
||||
|
||||
public {{modifiers}} void Copy(ref {{definition.GenericTypeName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
InternalCopy(ref target, serialization, hookCtx, context);
|
||||
}
|
||||
|
||||
{{baseCopy}}
|
||||
""");
|
||||
|
||||
foreach (var @interface in GetImplicitDataDefinitionInterfaces(definition.Type, true))
|
||||
{
|
||||
var interfaceModifiers = baseType != null && baseType.AllInterfaces.Contains(@interface, SymbolEqualityComparer.Default)
|
||||
? "override "
|
||||
: modifiers;
|
||||
var interfaceName = @interface.ToDisplayString();
|
||||
|
||||
builder.AppendLine($$"""
|
||||
public {{interfaceModifiers}} void InternalCopy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
var def = ({{definition.GenericTypeName}}) target;
|
||||
Copy(ref def, serialization, hookCtx, context);
|
||||
target = def;
|
||||
}
|
||||
|
||||
public {{interfaceModifiers}} void Copy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
InternalCopy(ref target, serialization, hookCtx, context);
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static string GetInstantiators(DataDefinition definition)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
var modifiers = string.Empty;
|
||||
|
||||
if (definition.Type.BaseType is { } baseType && IsDataDefinition(baseType))
|
||||
modifiers = "override ";
|
||||
else if (IsVirtualClass(definition.Type))
|
||||
modifiers = "virtual ";
|
||||
|
||||
if (definition.Type.IsAbstract)
|
||||
{
|
||||
// TODO make abstract once data definitions are forced to be partial
|
||||
builder.AppendLine($$"""
|
||||
public {{modifiers}} {{definition.GenericTypeName}} Instantiate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
""");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
public {{modifiers}} {{definition.GenericTypeName}} Instantiate()
|
||||
{
|
||||
return new {{definition.GenericTypeName}}();
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
foreach (var @interface in GetImplicitDataDefinitionInterfaces(definition.Type, false))
|
||||
{
|
||||
var interfaceName = @interface.ToDisplayString();
|
||||
builder.AppendLine($$"""
|
||||
{{interfaceName}} {{interfaceName}}.Instantiate()
|
||||
{
|
||||
return Instantiate();
|
||||
}
|
||||
|
||||
{{interfaceName}} ISerializationGenerated<{{interfaceName}}>.Instantiate()
|
||||
{
|
||||
return Instantiate();
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
// TODO serveronly? do we care? who knows!!
|
||||
private static StringBuilder CopyDataFields(DataDefinition definition)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.AppendLine($"""
|
||||
if (serialization.TryCustomCopy(this, ref target, hookCtx, {definition.HasHooks.ToString().ToLower()}, context))
|
||||
return;
|
||||
""");
|
||||
|
||||
var structCopier = new StringBuilder();
|
||||
foreach (var field in definition.Fields)
|
||||
{
|
||||
var type = field.Type;
|
||||
var typeName = type.ToDisplayString();
|
||||
if (IsMultidimensionalArray(type))
|
||||
{
|
||||
typeName = typeName.Replace("*", "");
|
||||
}
|
||||
|
||||
var isNullableValueType = IsNullableValueType(type);
|
||||
var nonNullableTypeName = type.WithNullableAnnotation(NullableAnnotation.None).ToDisplayString();
|
||||
if (isNullableValueType)
|
||||
{
|
||||
nonNullableTypeName = typeName.Substring(0, typeName.Length - 1);
|
||||
}
|
||||
|
||||
var isClass = type.IsReferenceType || type.SpecialType == SpecialType.System_String;
|
||||
var isNullable = type.NullableAnnotation == NullableAnnotation.Annotated;
|
||||
var nullableOverride = isClass && !isNullable ? ", true" : string.Empty;
|
||||
var name = field.Symbol.Name;
|
||||
var tempVarName = $"{name}Temp";
|
||||
var nullableValue = isNullableValueType ? ".Value" : string.Empty;
|
||||
var nullNotAllowed = isClass && !isNullable;
|
||||
|
||||
if (field.CustomSerializer is { Serializer: var serializer, Type: var serializerType })
|
||||
{
|
||||
if (nullNotAllowed)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
if ({{name}} == null)
|
||||
{
|
||||
throw new NullNotAllowedException();
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
builder.AppendLine($$"""
|
||||
{{typeName}} {{tempVarName}} = default!;
|
||||
""");
|
||||
|
||||
if (isNullable || isNullableValueType)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
if ({{name}} == null)
|
||||
{
|
||||
{{tempVarName}} = null!;
|
||||
}
|
||||
else
|
||||
{
|
||||
""");
|
||||
}
|
||||
|
||||
var serializerName = serializer.ToDisplayString();
|
||||
switch (serializerType)
|
||||
{
|
||||
case Copier:
|
||||
CopyToCustom(
|
||||
builder,
|
||||
nonNullableTypeName,
|
||||
serializerName,
|
||||
tempVarName,
|
||||
name,
|
||||
isNullable,
|
||||
isClass,
|
||||
isNullableValueType
|
||||
);
|
||||
break;
|
||||
case CopyCreator:
|
||||
CreateCopyCustom(
|
||||
builder,
|
||||
name,
|
||||
tempVarName,
|
||||
nonNullableTypeName,
|
||||
serializerName,
|
||||
nullableValue,
|
||||
nullableOverride
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if (isNullable || isNullableValueType)
|
||||
{
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
|
||||
if (definition.Type.IsValueType)
|
||||
{
|
||||
structCopier.AppendLine($"{name} = {tempVarName}!,");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"target.{name} = {tempVarName}!;");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
{{typeName}} {{tempVarName}} = default!;
|
||||
""");
|
||||
|
||||
if (nullNotAllowed)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
if ({{name}} == null)
|
||||
{
|
||||
throw new NullNotAllowedException();
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
var instantiator = string.Empty;
|
||||
if (IsDataDefinition(type))
|
||||
{
|
||||
instantiator = $"{tempVarName} = {name}.Instantiate();";
|
||||
}
|
||||
else if (!type.IsAbstract &&
|
||||
HasEmptyPublicConstructor(type) &&
|
||||
(type.IsReferenceType || IsNullableType(type)))
|
||||
{
|
||||
instantiator = $"{tempVarName} = new();";
|
||||
}
|
||||
|
||||
var hasHooks = ImplementsInterface(type, SerializationHooksNamespace) || !type.IsSealed;
|
||||
builder.AppendLine($$"""
|
||||
if (!serialization.TryCustomCopy(this.{{name}}, ref {{tempVarName}}, hookCtx, {{hasHooks.ToString().ToLower()}}, context))
|
||||
{
|
||||
""");
|
||||
|
||||
if (CanBeCopiedByValue(field.Symbol, field.Type))
|
||||
{
|
||||
builder.AppendLine($"{tempVarName} = {name};");
|
||||
}
|
||||
else if (IsDataDefinition(type) && !type.IsAbstract &&
|
||||
type is not INamedTypeSymbol { TypeKind: TypeKind.Interface })
|
||||
{
|
||||
var nullability = type.IsValueType ? string.Empty : "?";
|
||||
var nullable = !type.IsValueType || IsNullableType(type);
|
||||
|
||||
if (nullable)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
if ({{name}} == null)
|
||||
{
|
||||
{{tempVarName}} = null!;
|
||||
}
|
||||
else
|
||||
{
|
||||
""");
|
||||
}
|
||||
|
||||
builder.AppendLine($$"""
|
||||
{{instantiator}}
|
||||
{{name}}{{nullability}}.Copy(ref {{tempVarName}}, serialization, hookCtx, context);
|
||||
""");
|
||||
|
||||
if (nullable)
|
||||
{
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"{tempVarName} = serialization.CreateCopy({name}, hookCtx, context);");
|
||||
}
|
||||
|
||||
builder.AppendLine("}");
|
||||
|
||||
if (definition.Type.IsValueType)
|
||||
{
|
||||
structCopier.AppendLine($"{name} = {tempVarName}!,");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"target.{name} = {tempVarName}!;");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.Type.IsValueType)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
target = target with
|
||||
{
|
||||
{{structCopier}}
|
||||
};
|
||||
""");
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static void CopyToCustom(
|
||||
StringBuilder builder,
|
||||
string typeName,
|
||||
string serializerName,
|
||||
string tempVarName,
|
||||
string varName,
|
||||
bool isNullable,
|
||||
bool isClass,
|
||||
bool isNullableValueType)
|
||||
{
|
||||
var newTemp = isNullable && isClass ? $"{tempVarName} ??= new();" : string.Empty;
|
||||
var nullableOverride = isClass ? ", true" : string.Empty;
|
||||
var nullableValue = isNullableValueType ? ".Value" : string.Empty;
|
||||
var nonNullableTypeName = typeName.EndsWith("?") ? typeName.Substring(0, typeName.Length - 1) : typeName;
|
||||
|
||||
builder.AppendLine($$"""
|
||||
{{nonNullableTypeName}} {{tempVarName}}CopyTo = default!;
|
||||
{{newTemp}}
|
||||
serialization.CopyTo<{{typeName}}, {{serializerName}}>(this.{{varName}}{{nullableValue}}, ref {{tempVarName}}CopyTo, hookCtx, context{{nullableOverride}});
|
||||
{{tempVarName}} = {{tempVarName}}CopyTo;
|
||||
""");
|
||||
}
|
||||
|
||||
private static void CreateCopyCustom(
|
||||
StringBuilder builder,
|
||||
string varName,
|
||||
string tempVarName,
|
||||
string nonNullableTypeName,
|
||||
string serializerName,
|
||||
string nullableValue,
|
||||
string nullableOverride)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
{{tempVarName}} = serialization.CreateCopy<{{nonNullableTypeName}}, {{serializerName}}>(this.{{varName}}{{nullableValue}}, hookCtx, context{{nullableOverride}});
|
||||
""");
|
||||
}
|
||||
}
|
||||
8
Robust.Serialization.Generator/IsExternalInit.cs
Normal file
8
Robust.Serialization.Generator/IsExternalInit.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System.Runtime.CompilerServices;
|
||||
|
||||
// Need this to be able to define records in the project
|
||||
internal static class IsExternalInit
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>11</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.4.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
331
Robust.Serialization.Generator/Types.cs
Normal file
331
Robust.Serialization.Generator/Types.cs
Normal file
@@ -0,0 +1,331 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Robust.Serialization.Generator;
|
||||
|
||||
internal static class Types
|
||||
{
|
||||
private const string DataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataDefinitionAttribute";
|
||||
private const string ImplicitDataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.ImplicitDataDefinitionForInheritorsAttribute";
|
||||
private const string DataFieldBaseNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataFieldBaseAttribute";
|
||||
private const string CopyByRefNamespace = "Robust.Shared.Serialization.Manager.Attributes.CopyByRefAttribute";
|
||||
|
||||
internal static bool IsPartial(TypeDeclarationSyntax type)
|
||||
{
|
||||
return type.Modifiers.IndexOf(SyntaxKind.PartialKeyword) != -1;
|
||||
}
|
||||
|
||||
internal static bool IsDataDefinition(ITypeSymbol? type)
|
||||
{
|
||||
if (type == null)
|
||||
return false;
|
||||
|
||||
return HasAttribute(type, DataDefinitionNamespace) ||
|
||||
IsImplicitDataDefinition(type);
|
||||
}
|
||||
|
||||
internal static bool IsDataField(ISymbol member, out ITypeSymbol type, out AttributeData attribute)
|
||||
{
|
||||
// TODO data records and other attributes
|
||||
if (member is IFieldSymbol field)
|
||||
{
|
||||
foreach (var attr in field.GetAttributes())
|
||||
{
|
||||
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
||||
{
|
||||
type = field.Type;
|
||||
attribute = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (member is IPropertySymbol property)
|
||||
{
|
||||
foreach (var attr in property.GetAttributes())
|
||||
{
|
||||
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
||||
{
|
||||
type = property.Type;
|
||||
attribute = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type = null!;
|
||||
attribute = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsImplicitDataDefinition(ITypeSymbol type)
|
||||
{
|
||||
if (HasAttribute(type, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
|
||||
foreach (var baseType in GetBaseTypes(type))
|
||||
{
|
||||
if (HasAttribute(baseType, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var @interface in type.AllInterfaces)
|
||||
{
|
||||
if (IsImplicitDataDefinitionInterface(@interface))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsImplicitDataDefinitionInterface(ITypeSymbol @interface)
|
||||
{
|
||||
if (HasAttribute(@interface, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
|
||||
foreach (var subInterface in @interface.AllInterfaces)
|
||||
{
|
||||
if (HasAttribute(subInterface, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static IEnumerable<ITypeSymbol> GetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all)
|
||||
{
|
||||
var interfaces = all ? type.AllInterfaces : type.Interfaces;
|
||||
foreach (var @interface in interfaces)
|
||||
{
|
||||
if (IsImplicitDataDefinitionInterface(@interface))
|
||||
yield return @interface;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsNullableType(ITypeSymbol type)
|
||||
{
|
||||
if (type.NullableAnnotation == NullableAnnotation.Annotated)
|
||||
return true;
|
||||
|
||||
if (type.OriginalDefinition.ToDisplayString() == "System.Nullable<T>")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsNullableValueType(ITypeSymbol type)
|
||||
{
|
||||
return type.IsValueType && IsNullableType(type);
|
||||
}
|
||||
|
||||
internal static bool IsMultidimensionalArray(ITypeSymbol type)
|
||||
{
|
||||
return type is IArrayTypeSymbol { Rank: > 1 };
|
||||
}
|
||||
|
||||
internal static bool CanBeCopiedByValue(ISymbol member, ITypeSymbol type)
|
||||
{
|
||||
if (type.OriginalDefinition.ToDisplayString() == "System.Nullable<T>")
|
||||
return CanBeCopiedByValue(member, ((INamedTypeSymbol) type).TypeArguments[0]);
|
||||
|
||||
if (type.TypeKind == TypeKind.Enum)
|
||||
return true;
|
||||
|
||||
switch (type.SpecialType)
|
||||
{
|
||||
case SpecialType.System_Enum:
|
||||
case SpecialType.System_Boolean:
|
||||
case SpecialType.System_Char:
|
||||
case SpecialType.System_SByte:
|
||||
case SpecialType.System_Byte:
|
||||
case SpecialType.System_Int16:
|
||||
case SpecialType.System_UInt16:
|
||||
case SpecialType.System_Int32:
|
||||
case SpecialType.System_UInt32:
|
||||
case SpecialType.System_Int64:
|
||||
case SpecialType.System_UInt64:
|
||||
case SpecialType.System_Decimal:
|
||||
case SpecialType.System_Single:
|
||||
case SpecialType.System_Double:
|
||||
case SpecialType.System_String:
|
||||
case SpecialType.System_DateTime:
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HasAttribute(member, CopyByRefNamespace))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static string GetGenericTypeName(ITypeSymbol symbol)
|
||||
{
|
||||
var name = symbol.Name;
|
||||
|
||||
if (symbol is INamedTypeSymbol { TypeParameters: { Length: > 0 } parameters })
|
||||
{
|
||||
name += "<";
|
||||
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
name += parameter.Name;
|
||||
|
||||
if (i < parameters.Length - 1)
|
||||
{
|
||||
name += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
name += ">";
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
internal static string GetPartialTypeDefinitionLine(ITypeSymbol symbol)
|
||||
{
|
||||
var access = symbol.DeclaredAccessibility switch
|
||||
{
|
||||
Accessibility.Private => "private",
|
||||
Accessibility.ProtectedAndInternal => "protected internal",
|
||||
Accessibility.Protected => "protected",
|
||||
Accessibility.Internal => "internal",
|
||||
Accessibility.Public => "public",
|
||||
_ => "public"
|
||||
};
|
||||
|
||||
var typeKeyword = "partial ";
|
||||
if (symbol.TypeKind == TypeKind.Interface)
|
||||
{
|
||||
typeKeyword += "interface";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (symbol.IsRecord)
|
||||
{
|
||||
typeKeyword += symbol.IsValueType ? "record struct" : "record";
|
||||
}
|
||||
else
|
||||
{
|
||||
typeKeyword += symbol.IsValueType ? "struct" : "class";
|
||||
}
|
||||
|
||||
if (symbol.IsAbstract)
|
||||
{
|
||||
typeKeyword = $"abstract {typeKeyword}";
|
||||
}
|
||||
}
|
||||
|
||||
var typeName = GetGenericTypeName(symbol);
|
||||
return $"{access} {typeKeyword} {typeName}";
|
||||
}
|
||||
|
||||
internal static bool Inherits(ITypeSymbol type, string parent)
|
||||
{
|
||||
foreach (var baseType in GetBaseTypes(type))
|
||||
{
|
||||
if (baseType.ToDisplayString() == parent)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool ImplementsInterface(ITypeSymbol type, string interfaceName)
|
||||
{
|
||||
foreach (var interfaceType in type.AllInterfaces)
|
||||
{
|
||||
if (interfaceType.ToDisplayString().Contains(interfaceName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsReadOnlyMember(ITypeSymbol type, ISymbol member)
|
||||
{
|
||||
if (member is IFieldSymbol field)
|
||||
{
|
||||
return field.IsReadOnly;
|
||||
}
|
||||
else if (member is IPropertySymbol property)
|
||||
{
|
||||
if (property.SetMethod == null)
|
||||
return true;
|
||||
|
||||
if (property.SetMethod.IsInitOnly)
|
||||
return type.IsReferenceType;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool NeedsEmptyConstructor(ITypeSymbol type)
|
||||
{
|
||||
if (type is not INamedTypeSymbol named)
|
||||
return false;
|
||||
|
||||
if (named.InstanceConstructors.Length == 0)
|
||||
return true;
|
||||
|
||||
foreach (var constructor in named.InstanceConstructors)
|
||||
{
|
||||
if (constructor.Parameters.Length == 0 &&
|
||||
!constructor.IsImplicitlyDeclared)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool HasEmptyPublicConstructor(ITypeSymbol type)
|
||||
{
|
||||
if (type is not INamedTypeSymbol named)
|
||||
return false;
|
||||
|
||||
foreach (var constructor in named.InstanceConstructors)
|
||||
{
|
||||
if (constructor.DeclaredAccessibility == Accessibility.Public &&
|
||||
constructor.Parameters.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsVirtualClass(ITypeSymbol type)
|
||||
{
|
||||
return type.IsReferenceType && !type.IsSealed && type.TypeKind != TypeKind.Interface;
|
||||
}
|
||||
|
||||
internal static bool HasAttribute(ISymbol symbol, string attributeName)
|
||||
{
|
||||
foreach (var attribute in symbol.GetAttributes())
|
||||
{
|
||||
if (attribute.AttributeClass?.ToDisplayString() == attributeName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static IEnumerable<ITypeSymbol> GetBaseTypes(ITypeSymbol type)
|
||||
{
|
||||
var baseType = type.BaseType;
|
||||
while (baseType != null)
|
||||
{
|
||||
yield return baseType;
|
||||
baseType = baseType.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,19 +59,20 @@ public sealed class ScaleCommand : LocalizedCommands
|
||||
|
||||
if (_entityManager.TryGetComponent(uid, out FixturesComponent? manager))
|
||||
{
|
||||
foreach (var fixture in manager.Fixtures.Values)
|
||||
foreach (var (id, fixture) in manager.Fixtures)
|
||||
{
|
||||
switch (fixture.Shape)
|
||||
{
|
||||
case EdgeShape edge:
|
||||
physics.SetVertices(uid, fixture, edge,
|
||||
physics.SetVertices(uid, id, fixture,
|
||||
edge,
|
||||
edge.Vertex0 * scale,
|
||||
edge.Vertex1 * scale,
|
||||
edge.Vertex2 * scale,
|
||||
edge.Vertex3 * scale, manager);
|
||||
break;
|
||||
case PhysShapeCircle circle:
|
||||
physics.SetPositionRadius(uid, fixture, circle, circle.Position * scale, circle.Radius * scale, manager);
|
||||
physics.SetPositionRadius(uid, id, fixture, circle, circle.Position * scale, circle.Radius * scale, manager);
|
||||
break;
|
||||
case PolygonShape poly:
|
||||
var verts = poly.Vertices;
|
||||
@@ -81,7 +82,7 @@ public sealed class ScaleCommand : LocalizedCommands
|
||||
verts[i] *= scale;
|
||||
}
|
||||
|
||||
physics.SetVertices(uid, fixture, poly, verts, manager);
|
||||
physics.SetVertices(uid, id, fixture, poly, verts, manager);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
|
||||
@@ -137,10 +137,10 @@ namespace Robust.Server.Console.Commands
|
||||
var ground = _ent.AddComponent<PhysicsComponent>(groundUid);
|
||||
|
||||
var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0));
|
||||
fixtures.CreateFixture(groundUid, new Fixture("fix1", horizontal, 2, 2, true), body: ground);
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
|
||||
|
||||
var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10));
|
||||
fixtures.CreateFixture(groundUid, new Fixture("fix2", vertical, 2, 2, true), body: ground);
|
||||
fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground);
|
||||
|
||||
var xs = new[]
|
||||
{
|
||||
@@ -166,7 +166,7 @@ namespace Robust.Server.Console.Commands
|
||||
shape = new PolygonShape();
|
||||
shape.SetAsBox(0.5f, 0.5f);
|
||||
physics.SetFixedRotation(boxUid, false, body: box);
|
||||
fixtures.CreateFixture(boxUid, new Fixture("fix1", shape, 2, 2, true), body: box);
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true), body: box);
|
||||
|
||||
physics.WakeBody(boxUid, body: box);
|
||||
}
|
||||
@@ -184,10 +184,10 @@ namespace Robust.Server.Console.Commands
|
||||
var ground = _ent.AddComponent<PhysicsComponent>(groundUid);
|
||||
|
||||
var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0));
|
||||
fixtures.CreateFixture(groundUid, new Fixture("fix1", horizontal, 2, 2, true), body: ground);
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
|
||||
|
||||
var vertical = new EdgeShape(new Vector2(20, 0), new Vector2(20, 20));
|
||||
fixtures.CreateFixture(groundUid, new Fixture("fix2", vertical, 2, 2, true), body: ground);
|
||||
fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground);
|
||||
|
||||
var xs = new[]
|
||||
{
|
||||
@@ -213,7 +213,7 @@ namespace Robust.Server.Console.Commands
|
||||
physics.SetFixedRotation(boxUid, false, body: box);
|
||||
// TODO: Need to detect shape and work out if we need to use fixedrotation
|
||||
|
||||
fixtures.CreateFixture(boxUid, new Fixture("fix1", shape, 2, 2, true, 5f));
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f));
|
||||
physics.WakeBody(boxUid, body: box);
|
||||
}
|
||||
}
|
||||
@@ -232,7 +232,7 @@ namespace Robust.Server.Console.Commands
|
||||
var ground = _ent.AddComponent<PhysicsComponent>(groundUid);
|
||||
|
||||
var horizontal = new EdgeShape(new Vector2(40, 0), new Vector2(-40, 0));
|
||||
fixtures.CreateFixture(groundUid, new Fixture("fix1", horizontal, 2, 2, true), body: ground);
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
|
||||
physics.WakeBody(groundUid, body: ground);
|
||||
|
||||
// Setup boxes
|
||||
@@ -255,7 +255,7 @@ namespace Robust.Server.Console.Commands
|
||||
var box = _ent.AddComponent<PhysicsComponent>(boxUid);
|
||||
physics.SetBodyType(boxUid, BodyType.Dynamic, body: box);
|
||||
|
||||
fixtures.CreateFixture(boxUid, new Fixture("fix1", shape, 2, 2, true, 5f), body: box);
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f), body: box);
|
||||
y += deltaY;
|
||||
|
||||
physics.WakeBody(boxUid, body: box);
|
||||
@@ -275,7 +275,7 @@ namespace Robust.Server.Console.Commands
|
||||
var ground = _ent.AddComponent<PhysicsComponent>(groundUid);
|
||||
// Due to lookup changes fixtureless bodies are invalid, so
|
||||
var cShape = new PhysShapeCircle(1f);
|
||||
fixtures.CreateFixture(groundUid, new Fixture("fix1", cShape, 0, 0, false));
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(cShape, 0, 0, false));
|
||||
|
||||
var bodyUid = _ent.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId));
|
||||
var body = _ent.AddComponent<PhysicsComponent>(bodyUid);
|
||||
@@ -288,19 +288,19 @@ namespace Robust.Server.Console.Commands
|
||||
// TODO: Box2D just deref, bleh shape structs someday
|
||||
var shape1 = new PolygonShape();
|
||||
shape1.SetAsBox(0.5f, 10.0f, new Vector2(10.0f, 0.0f), 0.0f);
|
||||
fixtures.CreateFixture(bodyUid, new Fixture("fix1", shape1, 2, 0, true, 20f));
|
||||
fixtures.CreateFixture(bodyUid, "fix1", new Fixture(shape1, 2, 0, true, 20f));
|
||||
|
||||
var shape2 = new PolygonShape();
|
||||
shape2.SetAsBox(0.5f, 10.0f, new Vector2(-10.0f, 0.0f), 0f);
|
||||
fixtures.CreateFixture(bodyUid, new Fixture("fix2", shape2, 2, 0, true, 20f));
|
||||
fixtures.CreateFixture(bodyUid, "fix2", new Fixture(shape2, 2, 0, true, 20f));
|
||||
|
||||
var shape3 = new PolygonShape();
|
||||
shape3.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, 10.0f), 0f);
|
||||
fixtures.CreateFixture(bodyUid, new Fixture("fix3", shape3, 2, 0, true, 20f));
|
||||
fixtures.CreateFixture(bodyUid, "fix3", new Fixture(shape3, 2, 0, true, 20f));
|
||||
|
||||
var shape4 = new PolygonShape();
|
||||
shape4.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, -10.0f), 0f);
|
||||
fixtures.CreateFixture(bodyUid, new Fixture("fix4", shape4, 2, 0, true, 20f));
|
||||
fixtures.CreateFixture(bodyUid, "fix4", new Fixture(shape4, 2, 0, true, 20f));
|
||||
|
||||
physics.WakeBody(groundUid, body: ground);
|
||||
physics.WakeBody(bodyUid, body: body);
|
||||
@@ -328,7 +328,7 @@ namespace Robust.Server.Console.Commands
|
||||
physics.SetFixedRotation(boxUid, false, body: box);
|
||||
var shape = new PolygonShape();
|
||||
shape.SetAsBox(0.125f, 0.125f);
|
||||
fixtures.CreateFixture(boxUid, new Fixture("fix1", shape, 2, 2, true, 0.0625f), body: box);
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 0.0625f), body: box);
|
||||
physics.WakeBody(boxUid, body: box);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class ActorComponent : Component
|
||||
public sealed partial class ActorComponent : Component
|
||||
{
|
||||
[ViewVariables]
|
||||
public IPlayerSession PlayerSession { get; internal set; } = default!;
|
||||
|
||||
@@ -11,7 +11,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
[RegisterComponent, ComponentReference(typeof(SharedEyeComponent))]
|
||||
public sealed class EyeComponent : SharedEyeComponent
|
||||
public sealed partial class EyeComponent : SharedEyeComponent
|
||||
{
|
||||
public const int DefaultVisibilityMask = 1;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ using Robust.Shared.GameObjects;
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
internal sealed class ViewSubscriberComponent : Component
|
||||
internal sealed partial class ViewSubscriberComponent : Component
|
||||
{
|
||||
internal readonly HashSet<IPlayerSession> SubscribedSessions = new();
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@ namespace Robust.Server.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedPointLightComponent))]
|
||||
public sealed class PointLightComponent : SharedPointLightComponent {}
|
||||
public sealed partial class PointLightComponent : SharedPointLightComponent {}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@ namespace Robust.Server.GameObjects;
|
||||
/// close the UI automatically.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class IgnoreUIRangeComponent : Component
|
||||
public sealed partial class IgnoreUIRangeComponent : Component
|
||||
{
|
||||
}
|
||||
|
||||
@@ -15,14 +15,14 @@ namespace Robust.Server.GameObjects
|
||||
/// <seealso cref="BoundUserInterface"/>
|
||||
[PublicAPI]
|
||||
[RegisterComponent, ComponentReference(typeof(SharedUserInterfaceComponent))]
|
||||
public sealed class ServerUserInterfaceComponent : SharedUserInterfaceComponent
|
||||
public sealed partial class ServerUserInterfaceComponent : SharedUserInterfaceComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
public readonly Dictionary<Enum, BoundUserInterface> Interfaces = new();
|
||||
}
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class ActiveUserInterfaceComponent : Component
|
||||
public sealed partial class ActiveUserInterfaceComponent : Component
|
||||
{
|
||||
public HashSet<BoundUserInterface> Interfaces = new();
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Robust.Server.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
[Access(typeof(VisibilitySystem))]
|
||||
public sealed class VisibilityComponent : Component
|
||||
public sealed partial class VisibilityComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The visibility layer for the entity.
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robust.Server.GameObjects
|
||||
/// This can then be used to re-serialize the entity with the same UID for the merge driver to recognize.
|
||||
/// </remarks>
|
||||
[RegisterComponent]
|
||||
public sealed class MapSaveIdComponent : Component
|
||||
public sealed partial class MapSaveIdComponent : Component
|
||||
{
|
||||
public int Uid { get; set; }
|
||||
}
|
||||
|
||||
@@ -81,30 +81,35 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, EntityUid uid = default, IEntityLoadContext? context = null)
|
||||
{
|
||||
var entity = base.CreateEntity(prototypeName, uid, context);
|
||||
if (prototypeName == null)
|
||||
return base.CreateEntity(prototypeName, uid, context);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(prototypeName))
|
||||
{
|
||||
var prototype = PrototypeManager.Index<EntityPrototype>(prototypeName);
|
||||
if (!PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype))
|
||||
throw new EntityCreationException($"Attempted to spawn an entity with an invalid prototype: {prototypeName}");
|
||||
|
||||
// At this point in time, all data configure on the entity *should* be purely from the prototype.
|
||||
// As such, we can reset the modified ticks to Zero,
|
||||
// which indicates "not different from client's own deserialization".
|
||||
// So the initial data for the component or even the creation doesn't have to be sent over the wire.
|
||||
foreach (var (netId, component) in GetNetComponents(entity))
|
||||
{
|
||||
// Make sure to ONLY get components that are defined in the prototype.
|
||||
// Others could be instantiated directly by AddComponent (e.g. ContainerManager).
|
||||
// And those aren't guaranteed to exist on the client, so don't clear them.
|
||||
var compName = ComponentFactory.GetComponentName(component.GetType());
|
||||
if (prototype.Components.ContainsKey(compName))
|
||||
component.ClearTicks();
|
||||
}
|
||||
}
|
||||
var entity = base.CreateEntity(prototype, uid, context);
|
||||
|
||||
// At this point in time, all data configure on the entity *should* be purely from the prototype.
|
||||
// As such, we can reset the modified ticks to Zero,
|
||||
// which indicates "not different from client's own deserialization".
|
||||
// So the initial data for the component or even the creation doesn't have to be sent over the wire.
|
||||
ClearTicks(entity, prototype);
|
||||
return entity;
|
||||
}
|
||||
|
||||
private void ClearTicks(EntityUid entity, EntityPrototype prototype)
|
||||
{
|
||||
foreach (var (netId, component) in GetNetComponents(entity))
|
||||
{
|
||||
// Make sure to ONLY get components that are defined in the prototype.
|
||||
// Others could be instantiated directly by AddComponent (e.g. ContainerManager).
|
||||
// And those aren't guaranteed to exist on the client, so don't clear them.
|
||||
var compName = ComponentFactory.GetComponentName(netId);
|
||||
if (prototype.Components.ContainsKey(compName))
|
||||
component.ClearTicks();
|
||||
}
|
||||
}
|
||||
|
||||
public override EntityStringRepresentation ToPrettyString(EntityUid uid)
|
||||
{
|
||||
TryGetComponent(uid, out ActorComponent? actor);
|
||||
|
||||
@@ -77,11 +77,21 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// </summary>
|
||||
private readonly HashSet<TIndex> _globalOverrides = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="TIndex"/> that should always get sent along with all of their children.
|
||||
/// </summary>
|
||||
private readonly HashSet<TIndex> _globalRecursiveOverrides = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="TIndex"/> that should always get sent.
|
||||
/// </summary>
|
||||
public HashSet<TIndex>.Enumerator GlobalOverridesEnumerator => _globalOverrides.GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="TIndex"/> that should always get sent along with all of their children.
|
||||
/// </summary>
|
||||
public HashSet<TIndex>.Enumerator GlobalRecursiveOverridesEnumerator => _globalRecursiveOverrides.GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="TIndex"/> that should always get sent to a certain <see cref="ICommonSession"/>.
|
||||
/// </summary>
|
||||
@@ -203,8 +213,11 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
{
|
||||
switch (location)
|
||||
{
|
||||
case GlobalOverride _:
|
||||
_globalOverrides.Add(index);
|
||||
case GlobalOverride global:
|
||||
if (global.Recursive)
|
||||
_globalRecursiveOverrides.Add(index);
|
||||
else
|
||||
_globalOverrides.Add(index);
|
||||
break;
|
||||
case GridChunkLocation gridChunkLocation:
|
||||
// might be gone due to grid-deletions
|
||||
@@ -239,8 +252,11 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
// since we can find the index, we can assume the dicts will be there too & dont need to do any checks. gaming.
|
||||
switch (location)
|
||||
{
|
||||
case GlobalOverride _:
|
||||
_globalOverrides.Remove(index);
|
||||
case GlobalOverride global:
|
||||
if (global.Recursive)
|
||||
_globalRecursiveOverrides.Remove(index);
|
||||
else
|
||||
_globalOverrides.Remove(index);
|
||||
break;
|
||||
case GridChunkLocation gridChunkLocation:
|
||||
_gridChunkContents[gridChunkLocation.GridId][gridChunkLocation.ChunkIndices].Remove(index);
|
||||
@@ -376,15 +392,10 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
|
||||
#region UpdateIndex
|
||||
|
||||
private bool IsOverride(TIndex index)
|
||||
private bool TryGetLocation(TIndex index, out IIndexLocation? location)
|
||||
{
|
||||
if (_locationChangeBuffer.TryGetValue(index, out var change) &&
|
||||
change is GlobalOverride or LocalOverride) return true;
|
||||
|
||||
if (_indexLocations.TryGetValue(index, out var indexLoc) &&
|
||||
indexLoc is GlobalOverride or LocalOverride) return true;
|
||||
|
||||
return false;
|
||||
return _locationChangeBuffer.TryGetValue(index, out location)
|
||||
|| _indexLocations.TryGetValue(index, out location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -392,15 +403,25 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// </summary>
|
||||
/// <param name="index">The <see cref="TIndex"/> to update.</param>
|
||||
/// <param name="removeFromOverride">An index at an override position will not be updated unless you set this flag.</param>
|
||||
public void UpdateIndex(TIndex index, bool removeFromOverride = false)
|
||||
/// <param name="recursive">If true, this will also recursively send any children of the given index.</param>
|
||||
public void AddGlobalOverride(TIndex index, bool removeFromOverride, bool recursive)
|
||||
{
|
||||
if(!removeFromOverride && IsOverride(index))
|
||||
if (!TryGetLocation(index, out var oldLocation))
|
||||
{
|
||||
RegisterUpdate(index, new GlobalOverride(recursive));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!removeFromOverride && oldLocation is LocalOverride)
|
||||
return;
|
||||
|
||||
if (_indexLocations.TryGetValue(index, out var oldLocation) &&
|
||||
oldLocation is GlobalOverride) return;
|
||||
if (oldLocation is GlobalOverride global &&
|
||||
(!removeFromOverride || global.Recursive == recursive))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterUpdate(index, new GlobalOverride());
|
||||
RegisterUpdate(index, new GlobalOverride(recursive));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -411,12 +432,20 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <param name="removeFromOverride">An index at an override position will not be updated unless you set this flag.</param>
|
||||
public void UpdateIndex(TIndex index, ICommonSession session, bool removeFromOverride = false)
|
||||
{
|
||||
if(!removeFromOverride && IsOverride(index))
|
||||
if (!TryGetLocation(index, out var oldLocation))
|
||||
{
|
||||
RegisterUpdate(index, new LocalOverride(session));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!removeFromOverride || oldLocation is GlobalOverride)
|
||||
return;
|
||||
|
||||
if (_indexLocations.TryGetValue(index, out var oldLocation) &&
|
||||
oldLocation is LocalOverride local &&
|
||||
local.Session == session) return;
|
||||
if (oldLocation is LocalOverride local &&
|
||||
(!removeFromOverride || local.Session == session))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterUpdate(index, new LocalOverride(session));
|
||||
}
|
||||
@@ -429,34 +458,42 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <param name="removeFromOverride">An index at an override position will not be updated unless you set this flag.</param>
|
||||
public void UpdateIndex(TIndex index, EntityCoordinates coordinates, bool removeFromOverride = false)
|
||||
{
|
||||
if(!removeFromOverride && IsOverride(index))
|
||||
if (!removeFromOverride
|
||||
&& TryGetLocation(index, out var oldLocation)
|
||||
&& oldLocation is GlobalOverride or LocalOverride)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entityManager.TryGetComponent(coordinates.EntityId, out TransformComponent? xform))
|
||||
return;
|
||||
|
||||
var gridIdOpt = coordinates.GetGridUid(_entityManager);
|
||||
if (gridIdOpt is EntityUid gridId && gridId.IsValid())
|
||||
if (xform.GridUid is { } gridId && gridId.IsValid())
|
||||
{
|
||||
var gridIndices = GetChunkIndices(coordinates.Position);
|
||||
UpdateIndex(index, gridId, gridIndices, true); //skip overridecheck bc we already did it (saves some dict lookups)
|
||||
return;
|
||||
}
|
||||
|
||||
var mapCoordinates = coordinates.ToMap(_entityManager, _transformSystem);
|
||||
var mapIndices = GetChunkIndices(coordinates.Position);
|
||||
UpdateIndex(index, mapCoordinates.MapId, mapIndices, true); //skip overridecheck bc we already did it (saves some dict lookups)
|
||||
var worldPos = _transformSystem.GetWorldMatrix(xform).Transform(coordinates.Position);
|
||||
var mapIndices = GetChunkIndices(worldPos);
|
||||
UpdateIndex(index, xform.MapID, mapIndices, true); //skip overridecheck bc we already did it (saves some dict lookups)
|
||||
}
|
||||
|
||||
public IChunkIndexLocation GetChunkIndex(EntityCoordinates coordinates)
|
||||
{
|
||||
var gridIdOpt = coordinates.GetGridUid(_entityManager);
|
||||
if (gridIdOpt is EntityUid gridId && gridId.IsValid())
|
||||
if (!_entityManager.TryGetComponent(coordinates.EntityId, out TransformComponent? xform))
|
||||
return new MapChunkLocation(default, default);
|
||||
|
||||
if (xform.GridUid is { } gridId && gridId.IsValid())
|
||||
{
|
||||
var gridIndices = GetChunkIndices(coordinates.Position);
|
||||
return new GridChunkLocation(gridId, gridIndices);
|
||||
}
|
||||
|
||||
var mapCoordinates = coordinates.ToMap(_entityManager, _transformSystem);
|
||||
var mapIndices = GetChunkIndices(coordinates.Position);
|
||||
return new MapChunkLocation(mapCoordinates.MapId, mapIndices);
|
||||
var worldPos = _transformSystem.GetWorldMatrix(xform).Transform(coordinates.Position);
|
||||
var mapIndices = GetChunkIndices(worldPos);
|
||||
return new MapChunkLocation(xform.MapID, mapIndices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -469,11 +506,14 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <param name="forceDirty">If true, this will mark the previous chunk as dirty even if the entity did not move from that chunk.</param>
|
||||
public void UpdateIndex(TIndex index, EntityUid gridId, Vector2i chunkIndices, bool removeFromOverride = false, bool forceDirty = false)
|
||||
{
|
||||
if(!removeFromOverride && IsOverride(index))
|
||||
_locationChangeBuffer.TryGetValue(index, out var bufferedLocation);
|
||||
_indexLocations.TryGetValue(index, out var oldLocation);
|
||||
|
||||
//removeFromOverride is false 99% of the time.
|
||||
if ((bufferedLocation ?? oldLocation) is GlobalOverride or LocalOverride && !removeFromOverride)
|
||||
return;
|
||||
|
||||
if (_indexLocations.TryGetValue(index, out var oldLocation) &&
|
||||
oldLocation is GridChunkLocation oldGrid &&
|
||||
if (oldLocation is GridChunkLocation oldGrid &&
|
||||
oldGrid.ChunkIndices == chunkIndices &&
|
||||
oldGrid.GridId == gridId)
|
||||
{
|
||||
@@ -497,22 +537,26 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <param name="forceDirty">If true, this will mark the previous chunk as dirty even if the entity did not move from that chunk.</param>
|
||||
public void UpdateIndex(TIndex index, MapId mapId, Vector2i chunkIndices, bool removeFromOverride = false, bool forceDirty = false)
|
||||
{
|
||||
if(!removeFromOverride && IsOverride(index))
|
||||
_locationChangeBuffer.TryGetValue(index, out var bufferedLocation);
|
||||
_indexLocations.TryGetValue(index, out var oldLocation);
|
||||
|
||||
//removeFromOverride is false 99% of the time.
|
||||
if ((bufferedLocation ?? oldLocation) is GlobalOverride or LocalOverride && !removeFromOverride)
|
||||
return;
|
||||
|
||||
if (_indexLocations.TryGetValue(index, out var oldLocation) &&
|
||||
oldLocation is MapChunkLocation oldMap &&
|
||||
// Is this entity just returning to its old location?
|
||||
if (oldLocation is MapChunkLocation oldMap &&
|
||||
oldMap.ChunkIndices == chunkIndices &&
|
||||
oldMap.MapId == mapId)
|
||||
{
|
||||
_locationChangeBuffer.Remove(index);
|
||||
if (bufferedLocation != null)
|
||||
_locationChangeBuffer.Remove(index);
|
||||
|
||||
if (forceDirty)
|
||||
_dirtyChunks.Add(oldMap);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
RegisterUpdate(index, new MapChunkLocation(mapId, chunkIndices));
|
||||
}
|
||||
|
||||
@@ -584,7 +628,18 @@ public struct GridChunkLocation : IIndexLocation, IChunkIndexLocation, IEquatabl
|
||||
}
|
||||
}
|
||||
|
||||
public struct GlobalOverride : IIndexLocation { }
|
||||
public struct GlobalOverride : IIndexLocation
|
||||
{
|
||||
/// <summary>
|
||||
/// If true, this will also send all children of the override.
|
||||
/// </summary>
|
||||
public readonly bool Recursive;
|
||||
|
||||
public GlobalOverride(bool recursive)
|
||||
{
|
||||
Recursive = recursive;
|
||||
}
|
||||
}
|
||||
|
||||
public struct LocalOverride : IIndexLocation
|
||||
{
|
||||
|
||||
@@ -12,20 +12,23 @@ public sealed class PvsOverrideSystem : EntitySystem
|
||||
[Shared.IoC.Dependency] private readonly PvsSystem _pvs = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Used to ensure that an entity is always sent to every client. Overrides any client-specific overrides.
|
||||
/// Used to ensure that an entity is always sent to every client. By default this overrides any client-specific overrides.
|
||||
/// </summary>
|
||||
public void AddGlobalOverride(EntityUid uid)
|
||||
/// <param name="removeExistingOverride">Whether or not to supersede existing overrides.</param>
|
||||
/// <param name="recursive">If true, this will also recursively send any children of the given index.</param>
|
||||
public void AddGlobalOverride(EntityUid uid, bool removeExistingOverride = true, bool recursive = false)
|
||||
{
|
||||
_pvs.EntityPVSCollection.UpdateIndex(uid, true);
|
||||
_pvs.EntityPVSCollection.AddGlobalOverride(uid, removeExistingOverride, recursive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to ensure that an entity is always sent to a specific client. Overrides any global or pre-existing
|
||||
/// client-specific overrides.
|
||||
/// </summary>
|
||||
public void AddSessionOverride(EntityUid uid, ICommonSession session)
|
||||
/// <param name="removeExistingOverride">Whether or not to supersede existing overrides.</param>
|
||||
public void AddSessionOverride(EntityUid uid, ICommonSession session,bool removeExistingOverride = true)
|
||||
{
|
||||
_pvs.EntityPVSCollection.UpdateIndex(uid, session, true);
|
||||
_pvs.EntityPVSCollection.UpdateIndex(uid, session, removeExistingOverride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Robust.Server.Configuration;
|
||||
@@ -13,11 +12,9 @@ using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Threading;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -107,6 +104,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
private EntityQuery<EyeComponent> _eyeQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -114,6 +112,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
_eyeQuery = GetEntityQuery<EyeComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
_entityPvsCollection = RegisterPVSCollection<EntityUid>();
|
||||
|
||||
@@ -387,7 +386,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
pvsCollection.AddGrid(gridId);
|
||||
}
|
||||
|
||||
_entityPvsCollection.UpdateIndex(gridId);
|
||||
_entityPvsCollection.AddGlobalOverride(gridId, true, false);
|
||||
}
|
||||
|
||||
private void OnMapDestroyed(MapChangedEvent e)
|
||||
@@ -407,7 +406,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
if(e.Map == MapId.Nullspace) return;
|
||||
var uid = _mapManager.GetMapEntityId(e.Map);
|
||||
_entityPvsCollection.UpdateIndex(uid);
|
||||
_entityPvsCollection.AddGlobalOverride(uid, true, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -749,6 +748,16 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
globalEnumerator.Dispose();
|
||||
|
||||
|
||||
var globalRecursiveEnumerator = _entityPvsCollection.GlobalRecursiveOverridesEnumerator;
|
||||
while (globalRecursiveEnumerator.MoveNext())
|
||||
{
|
||||
var uid = globalRecursiveEnumerator.Current;
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
}
|
||||
globalRecursiveEnumerator.Dispose();
|
||||
|
||||
var localEnumerator = _entityPvsCollection.GetElementsForSession(session);
|
||||
while (localEnumerator.MoveNext())
|
||||
{
|
||||
@@ -764,7 +773,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
var expandEvent = new ExpandPvsEvent(session, new List<EntityUid>());
|
||||
var expandEvent = new ExpandPvsEvent(session);
|
||||
RaiseLocalEvent(ref expandEvent);
|
||||
foreach (var entityUid in expandEvent.Entities)
|
||||
{
|
||||
@@ -772,6 +781,12 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
foreach (var entityUid in expandEvent.RecursiveEntities)
|
||||
{
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
}
|
||||
|
||||
var entityStates = new List<EntityState>(entStateCount);
|
||||
|
||||
foreach (var (uid, visiblity) in visibleEnts)
|
||||
@@ -898,8 +913,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
public bool RecursivelyAddOverride(
|
||||
in EntityUid uid,
|
||||
public bool RecursivelyAddOverride(in EntityUid uid,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<EntityUid, PvsEntityVisibility> toSend,
|
||||
@@ -911,34 +925,74 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
ref int enteredEntityCount,
|
||||
ref int entStateCount,
|
||||
in int newEntityBudget,
|
||||
in int enteredEntityBudget)
|
||||
in int enteredEntityBudget,
|
||||
bool addChildren = false)
|
||||
{
|
||||
//are we valid?
|
||||
//sometimes uids gets added without being valid YET (looking at you mapmanager) (mapcreate & gridcreated fire before the uids becomes valid)
|
||||
if (!uid.IsValid()) return false;
|
||||
if (!uid.IsValid())
|
||||
return false;
|
||||
|
||||
var parent = transQuery.GetComponent(uid).ParentUid;
|
||||
var xform = transQuery.GetComponent(uid);
|
||||
var parent = xform.ParentUid;
|
||||
if (parent.IsValid() && !RecursivelyAddOverride(in parent, lastAcked, lastSent, toSend, lastSeen, in metaQuery, in transQuery, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget))
|
||||
return false;
|
||||
|
||||
//did we already get added?
|
||||
if (toSend.ContainsKey(uid)) return true;
|
||||
// Note that we check this AFTER adding parents. This is because while this entity may already have been added
|
||||
// to the toSend set, it doesn't guarantee that its parents have been. E.g., if a player ghost just teleported
|
||||
// to follow a far away entity, the player's own entity is still being sent, but we need to ensure that we also
|
||||
// send the new parents, which may otherwise be delayed because of the PVS budget..
|
||||
if (!toSend.ContainsKey(uid))
|
||||
{
|
||||
// TODO PERFORMANCE.
|
||||
// ProcessEntry() unnecessarily checks lastSent.ContainsKey() and maybe lastSeen.Contains(). Given that at this
|
||||
// point the budgets are just ignored, this should just bypass those checks. But then again 99% of the time this
|
||||
// is just the player's own entity + maybe a singularity. So currently not all that performance intensive.
|
||||
var (entered, _) = ProcessEntry(in uid, lastAcked, lastSent, lastSeen, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
AddToSendSet(in uid, metaQuery.GetComponent(uid), toSend, fromTick, in entered, ref entStateCount);
|
||||
}
|
||||
|
||||
// TODO PERFORMANCE.
|
||||
// ProcessEntry() unnecessarily checks lastSent.ContainsKey() and maybe lastSeen.Contains(). Given that at this
|
||||
// point the budgets are just ignored, this should just bypass those checks. But then again 99% of the time this
|
||||
// is just the player's own entity + maybe a singularity. So currently not all that performance intensive.
|
||||
var (entered, _) = ProcessEntry(in uid, lastAcked, lastSent, lastSeen, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
if (addChildren)
|
||||
{
|
||||
RecursivelyAddChildren(xform, lastAcked, lastSent, toSend, lastSeen, fromTick, ref newEntityCount,
|
||||
ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
AddToSendSet(in uid, metaQuery.GetComponent(uid), toSend, fromTick, in entered, ref entStateCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RecursivelyAddChildren(TransformComponent xform,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<EntityUid, PvsEntityVisibility> toSend,
|
||||
Dictionary<EntityUid, GameTick> lastSeen,
|
||||
in GameTick fromTick,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
ref int entStateCount,
|
||||
in int newEntityBudget,
|
||||
in int enteredEntityBudget)
|
||||
{
|
||||
foreach (var child in xform.ChildEntities)
|
||||
{
|
||||
if (!_xformQuery.TryGetComponent(child, out var childXform))
|
||||
continue;
|
||||
|
||||
if (!toSend.ContainsKey(child))
|
||||
{
|
||||
var (entered, _) = ProcessEntry(in child, lastAcked, lastSent, lastSeen, ref newEntityCount,
|
||||
ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
|
||||
AddToSendSet(in child, _metaQuery.GetComponent(child), toSend, fromTick, in entered, ref entStateCount);
|
||||
}
|
||||
|
||||
RecursivelyAddChildren(childXform, lastAcked, lastSent, toSend, lastSeen, fromTick, ref newEntityCount,
|
||||
ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
}
|
||||
|
||||
private (bool Entered, bool ShouldAdd) ProcessEntry(in EntityUid uid,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
@@ -1034,26 +1088,20 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
while (query.MoveNext(out var uid, out var md))
|
||||
{
|
||||
DebugTools.Assert(md.EntityLifeStage >= EntityLifeStage.Initialized);
|
||||
DebugTools.Assert(md.EntityLifeStage < EntityLifeStage.Terminating);
|
||||
if (md.EntityLastModifiedTick <= fromTick)
|
||||
continue;
|
||||
|
||||
var state = GetEntityState(player, uid, fromTick, md);
|
||||
|
||||
// Temporary debugging code.
|
||||
// TODO REMOVE TEMPORARY CODE
|
||||
if (state.Empty)
|
||||
{
|
||||
var msg = $"{nameof(GetEntityState)} returned an empty state while enumerating all. Entity {ToPrettyString(uid)}. Net component Data:";
|
||||
foreach (var (_, cmp) in EntityManager.GetNetComponents(uid))
|
||||
{
|
||||
msg += $"\nName: {_factory.GetComponentName(cmp.GetType())}" +
|
||||
$"Enabled: {cmp.NetSyncEnabled}, " +
|
||||
$"Lifestage: {cmp.LifeStage}, " +
|
||||
$"OwnerOnly: {cmp.SendOnlyToOwner}, " +
|
||||
$"SessionSpecific: {cmp.SessionSpecific}, " +
|
||||
$"LastModified: {cmp.LastModifiedTick}";
|
||||
}
|
||||
Log.Error(msg);
|
||||
Log.Error($@"{nameof(GetEntityState)} returned an empty state while enumerating entities.
|
||||
Tick: {fromTick}--{_gameTiming.CurTick}
|
||||
Entity: {ToPrettyString(uid)}
|
||||
Last modified: {md.EntityLastModifiedTick}
|
||||
Metadata last modified: {md.LastModifiedTick}
|
||||
Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
}
|
||||
|
||||
stateEntities.Add(state);
|
||||
@@ -1077,26 +1125,21 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(md.EntityLifeStage >= EntityLifeStage.Initialized);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick >= md.CreationTick || md.EntityLastModifiedTick == GameTick.Zero);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick > fromTick || md.EntityLastModifiedTick == GameTick.Zero);
|
||||
DebugTools.Assert(md.EntityLifeStage < EntityLifeStage.Terminating);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick >= md.CreationTick);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick > fromTick);
|
||||
|
||||
var state = GetEntityState(player, uid, fromTick, md);
|
||||
|
||||
// Temporary debugging code.
|
||||
// TODO REMOVE TEMPORARY CODE
|
||||
if (state.Empty)
|
||||
{
|
||||
var msg = $"{nameof(GetEntityState)} returned an empty state for new entity {ToPrettyString(uid)}. Net component Data:";
|
||||
foreach (var (_, cmp) in EntityManager.GetNetComponents(uid))
|
||||
{
|
||||
msg += $"\nName: {_factory.GetComponentName(cmp.GetType())}" +
|
||||
$"Enabled: {cmp.NetSyncEnabled}, " +
|
||||
$"Lifestage: {cmp.LifeStage}, " +
|
||||
$"OwnerOnly: {cmp.SendOnlyToOwner}, " +
|
||||
$"SessionSpecific: {cmp.SessionSpecific}, " +
|
||||
$"LastModified: {cmp.LastModifiedTick}";
|
||||
}
|
||||
Log.Error(msg);
|
||||
Log.Error($@"{nameof(GetEntityState)} returned an empty state for a new entity.
|
||||
Tick: {fromTick}--{_gameTiming.CurTick}
|
||||
Entity: {ToPrettyString(uid)}
|
||||
Last modified: {md.EntityLastModifiedTick}
|
||||
Metadata last modified: {md.LastModifiedTick}
|
||||
Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
continue;
|
||||
}
|
||||
|
||||
stateEntities.Add(state);
|
||||
@@ -1109,8 +1152,9 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(md.EntityLifeStage >= EntityLifeStage.Initialized);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick >= md.CreationTick || md.EntityLastModifiedTick == GameTick.Zero);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick > fromTick || md.EntityLastModifiedTick == GameTick.Zero);
|
||||
DebugTools.Assert(md.EntityLifeStage < EntityLifeStage.Terminating);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick >= md.CreationTick);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick > fromTick);
|
||||
|
||||
var state = GetEntityState(player, uid, fromTick, md);
|
||||
if (!state.Empty)
|
||||
@@ -1330,11 +1374,20 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
public readonly struct ExpandPvsEvent
|
||||
{
|
||||
public readonly IPlayerSession Session;
|
||||
public readonly List<EntityUid> Entities;
|
||||
|
||||
public ExpandPvsEvent(IPlayerSession session, List<EntityUid> entities)
|
||||
/// <summary>
|
||||
/// List of entities that will get added to this session's PVS set.
|
||||
/// </summary>
|
||||
public readonly List<EntityUid> Entities = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of entities that will get added to this session's PVS set. Unlike <see cref="Entities"/> this will also
|
||||
/// recursively add all children of the given entity.
|
||||
/// </summary>
|
||||
public readonly List<EntityUid> RecursiveEntities = new();
|
||||
|
||||
public ExpandPvsEvent(IPlayerSession session)
|
||||
{
|
||||
Session = session;
|
||||
Entities = entities;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Server.Maps;
|
||||
/// Added to Maps that were loaded by MapLoaderSystem. If not present then this map was created externally.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class LoadedMapComponent : Component
|
||||
public sealed partial class LoadedMapComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Xml.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Utility;
|
||||
using SysVector3 = System.Numerics.Vector3;
|
||||
@@ -1638,6 +1637,11 @@ namespace Robust.Shared.Maths
|
||||
/// </summary>
|
||||
public static Color RoyalBlue => new(65, 105, 225, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (204, 71, 120, 255).
|
||||
/// </summary>
|
||||
public static Color Ruber => new(204, 71, 120, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (139, 69, 19, 255).
|
||||
/// </summary>
|
||||
@@ -1653,6 +1657,11 @@ namespace Robust.Shared.Maths
|
||||
/// </summary>
|
||||
public static Color SandyBrown => new(244, 164, 96, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (0, 66, 153, 255).
|
||||
/// </summary>
|
||||
public static Color SeaBlue => new(0, 66, 153, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (46, 139, 87, 255).
|
||||
/// </summary>
|
||||
@@ -1733,6 +1742,16 @@ namespace Robust.Shared.Maths
|
||||
/// </summary>
|
||||
public static Color Violet => new(238, 130, 238, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (126, 3, 168, 255).
|
||||
/// </summary>
|
||||
public static Color BetterViolet => new(126, 3, 168, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (255, 153, 0, 255).
|
||||
/// </summary>
|
||||
public static Color VividGamboge => new(255, 153, 0, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (245, 222, 179, 255).
|
||||
/// </summary>
|
||||
@@ -1767,6 +1786,7 @@ namespace Robust.Shared.Maths
|
||||
["aquamarine"] = Aquamarine,
|
||||
["azure"] = Azure,
|
||||
["beige"] = Beige,
|
||||
["betterviolet"] = BetterViolet,
|
||||
["bisque"] = Bisque,
|
||||
["black"] = Black,
|
||||
["blanchedalmond"] = BlanchedAlmond,
|
||||
@@ -1877,9 +1897,11 @@ namespace Robust.Shared.Maths
|
||||
["red"] = Red,
|
||||
["rosybrown"] = RosyBrown,
|
||||
["royalblue"] = RoyalBlue,
|
||||
["ruber"] = Ruber,
|
||||
["saddlebrown"] = SaddleBrown,
|
||||
["salmon"] = Salmon,
|
||||
["sandybrown"] = SandyBrown,
|
||||
["seablue"] = SeaBlue,
|
||||
["seagreen"] = SeaGreen,
|
||||
["seashell"] = SeaShell,
|
||||
["sienna"] = Sienna,
|
||||
@@ -1896,6 +1918,7 @@ namespace Robust.Shared.Maths
|
||||
["tomato"] = Tomato,
|
||||
["turquoise"] = Turquoise,
|
||||
["violet"] = Violet,
|
||||
["vividgamboge"] = VividGamboge,
|
||||
["wheat"] = Wheat,
|
||||
["white"] = White,
|
||||
["whitesmoke"] = WhiteSmoke,
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Shared.Analyzers;
|
||||
/// Placed on auto-generated classes to mark to certain robust analyzers that they are auto-generated
|
||||
/// and may need to be ignored (e.g. the access analyzer)
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
|
||||
public sealed class RobustAutoGeneratedAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Robust.Shared.Audio
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
[DataDefinition]
|
||||
public struct AudioParams
|
||||
public partial struct AudioParams
|
||||
{
|
||||
/// <summary>
|
||||
/// The DistanceModel to use for this specific source.
|
||||
|
||||
@@ -11,8 +11,8 @@ public sealed class SoundCollectionPrototype : IPrototype
|
||||
{
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
[DataField("files")]
|
||||
public List<ResPath> PickFiles { get; } = new();
|
||||
public List<ResPath> PickFiles { get; private set; } = new();
|
||||
}
|
||||
|
||||
@@ -13,22 +13,22 @@ using System;
|
||||
namespace Robust.Shared.Audio;
|
||||
|
||||
[ImplicitDataDefinitionForInheritors, Serializable, NetSerializable]
|
||||
public abstract class SoundSpecifier
|
||||
public abstract partial class SoundSpecifier
|
||||
{
|
||||
[DataField("params")]
|
||||
public AudioParams Params { get; init; } = AudioParams.Default;
|
||||
public AudioParams Params { get; set; } = AudioParams.Default;
|
||||
|
||||
[Obsolete("Use SharedAudioSystem.GetSound(), or just pass sound specifier directly into SharedAudioSystem.")]
|
||||
public abstract string GetSound(IRobustRandom? rand = null, IPrototypeManager? proto = null);
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SoundPathSpecifier : SoundSpecifier
|
||||
public sealed partial class SoundPathSpecifier : SoundSpecifier
|
||||
{
|
||||
public const string Node = "path";
|
||||
|
||||
[DataField(Node, customTypeSerializer: typeof(ResPathSerializer), required: true)]
|
||||
public ResPath Path { get; }
|
||||
public ResPath Path { get; private set; }
|
||||
|
||||
[UsedImplicitly]
|
||||
private SoundPathSpecifier()
|
||||
@@ -54,12 +54,12 @@ public sealed class SoundPathSpecifier : SoundSpecifier
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SoundCollectionSpecifier : SoundSpecifier
|
||||
public sealed partial class SoundCollectionSpecifier : SoundSpecifier
|
||||
{
|
||||
public const string Node = "collection";
|
||||
|
||||
[DataField(Node, customTypeSerializer: typeof(PrototypeIdSerializer<SoundCollectionPrototype>), required: true)]
|
||||
public string? Collection { get; }
|
||||
public string? Collection { get; private set; }
|
||||
|
||||
[UsedImplicitly]
|
||||
public SoundCollectionSpecifier() { }
|
||||
|
||||
@@ -283,6 +283,12 @@ namespace Robust.Shared
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> NetFakeDuplicates = CVarDef.Create("net.fakeduplicates", 0f, CVar.CHEAT);
|
||||
|
||||
/// <summary>
|
||||
/// When using Happy Eyeballs to try both IPv6 over IPv4, the delay that IPv4 gets to get less priority.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> NetHappyEyeballsDelay =
|
||||
CVarDef.Create("net.happy_eyeballs_delay", 0.025f, CVar.CLIENTONLY);
|
||||
|
||||
/**
|
||||
* SUS
|
||||
*/
|
||||
@@ -1189,10 +1195,10 @@ namespace Robust.Shared
|
||||
CVarDef.Create("discord.enabled", true, CVar.CLIENTONLY);
|
||||
|
||||
public static readonly CVarDef<string> DiscordRichPresenceMainIconId =
|
||||
CVarDef.Create("discord.rich_main_icon_id", "devstation", CVar.CLIENTONLY);
|
||||
CVarDef.Create("discord.rich_main_icon_id", "devstation", CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
public static readonly CVarDef<string> DiscordRichPresenceSecondIconId =
|
||||
CVarDef.Create("discord.rich_second_icon_id", "logo", CVar.CLIENTONLY);
|
||||
CVarDef.Create("discord.rich_second_icon_id", "logo", CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
/*
|
||||
* RES
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Robust.Shared.Containers
|
||||
/// <summary>
|
||||
/// Base container class that all container inherit from.
|
||||
/// </summary>
|
||||
public abstract class BaseContainer : IContainer
|
||||
public abstract partial class BaseContainer : IContainer
|
||||
{
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Robust.Shared.Containers
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[SerializedType(ClassName)]
|
||||
public sealed class Container : BaseContainer
|
||||
public sealed partial class Container : BaseContainer
|
||||
{
|
||||
private const string ClassName = "Container";
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Robust.Shared.Containers
|
||||
/// The generic container class uses a list of entities
|
||||
/// </summary>
|
||||
[DataField("ents")]
|
||||
private readonly List<EntityUid> _containerList = new();
|
||||
private List<EntityUid> _containerList = new();
|
||||
|
||||
private readonly List<EntityUid> _expectedEntities = new();
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Robust.Shared.Containers
|
||||
[ComponentReference(typeof(IContainerManager))]
|
||||
[NetworkedComponent]
|
||||
[RegisterComponent, ComponentProtoName("ContainerContainer")]
|
||||
public sealed class ContainerManagerComponent : Component, IContainerManager, ISerializationHooks
|
||||
public sealed partial class ContainerManagerComponent : Component, IContainerManager, ISerializationHooks
|
||||
{
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
@@ -184,7 +184,7 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
private struct ContainerPrototypeData
|
||||
private partial struct ContainerPrototypeData
|
||||
{
|
||||
[DataField("entities")] public List<EntityUid> Entities = new ();
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Robust.Shared.Containers
|
||||
{
|
||||
[UsedImplicitly]
|
||||
[SerializedType(ClassName)]
|
||||
public sealed class ContainerSlot : BaseContainer
|
||||
public sealed partial class ContainerSlot : BaseContainer
|
||||
{
|
||||
private const string ClassName = "ContainerSlot";
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Robust.Shared.Containers
|
||||
/// <seealso cref="IContainerManager" />
|
||||
[PublicAPI]
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public interface IContainer
|
||||
public partial interface IContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Readonly collection of all the entities contained within this specific container
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Shared.Containers
|
||||
/// Manages containers on an entity.
|
||||
/// </summary>
|
||||
/// <seealso cref="IContainer" />
|
||||
public interface IContainerManager : IComponent
|
||||
public partial interface IContainerManager : IComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes a new container of the specified type.
|
||||
|
||||
@@ -2,6 +2,8 @@ using System;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -12,7 +14,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
[Reflect(false)]
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract class Component : IComponent
|
||||
public abstract partial class Component : IComponent
|
||||
{
|
||||
[DataField("netsync")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -100,8 +102,14 @@ namespace Robust.Shared.GameObjects
|
||||
/// </remarks>
|
||||
internal void LifeShutdown(IEntityManager entManager)
|
||||
{
|
||||
// Starting allows a component to remove itself in it's own Startup function.
|
||||
DebugTools.Assert(LifeStage == ComponentLifeStage.Starting || LifeStage == ComponentLifeStage.Running);
|
||||
DebugTools.Assert(LifeStage is >= ComponentLifeStage.Initializing and < ComponentLifeStage.Stopping);
|
||||
|
||||
if (LifeStage <= ComponentLifeStage.Initialized)
|
||||
{
|
||||
// Component was never started, no shutdown logic necessary. Simply mark it as stopped.
|
||||
LifeStage = ComponentLifeStage.Stopped;
|
||||
return;
|
||||
}
|
||||
|
||||
LifeStage = ComponentLifeStage.Stopping;
|
||||
entManager.EventBus.RaiseComponentEvent(this, CompShutdownInstance);
|
||||
|
||||
@@ -328,6 +328,12 @@ namespace Robust.Shared.GameObjects
|
||||
return GetRegistration(componentType).Name;
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public string GetComponentName(ushort netID)
|
||||
{
|
||||
return GetRegistration(netID).Name;
|
||||
}
|
||||
|
||||
public ComponentRegistration GetRegistration(ushort netID)
|
||||
{
|
||||
if (_networkedComponents is null)
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Robust.Shared.GameObjects;
|
||||
/// Visualization works client side with derivatives of the <see cref="Robust.Client.GameObjects.VisualizerSystem">VisualizerSystem</see> class and corresponding components.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class AppearanceComponent : Component
|
||||
public sealed partial class AppearanceComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether or not the appearance needs to be updated.
|
||||
|
||||
@@ -3,4 +3,4 @@ using Robust.Shared.GameStates;
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class ScaleVisualsComponent : Component {}
|
||||
public sealed partial class ScaleVisualsComponent : Component {}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// A component that toggles collision on an entity being toggled.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class CollideOnAnchorComponent : Component
|
||||
public sealed partial class CollideOnAnchorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether we toggle collision on or off when anchoring (and vice versa when unanchoring).
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent()]
|
||||
[Access(typeof(CollisionWakeSystem))]
|
||||
public sealed class CollisionWakeComponent : Component
|
||||
public sealed partial class CollisionWakeComponent : Component
|
||||
{
|
||||
[DataField("enabled")]
|
||||
public bool Enabled = true;
|
||||
|
||||
@@ -10,17 +10,17 @@ namespace Robust.Shared.GameObjects
|
||||
/// Throws an exception in <see cref="OnAdd" />.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class DebugExceptionOnAddComponent : Component { }
|
||||
public sealed partial class DebugExceptionOnAddComponent : Component { }
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception in <see cref="Initialize" />.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class DebugExceptionInitializeComponent : Component { }
|
||||
public sealed partial class DebugExceptionInitializeComponent : Component { }
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception in <see cref="Startup" />.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class DebugExceptionStartupComponent : Component { }
|
||||
public sealed partial class DebugExceptionStartupComponent : Component { }
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[NetworkedComponent()]
|
||||
public abstract class SharedEyeComponent : Component
|
||||
public abstract partial class SharedEyeComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public virtual bool DrawFov { get; set; }
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Robust.Shared.GameObjects;
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent()]
|
||||
[Access(typeof(OccluderSystem))]
|
||||
public sealed class OccluderComponent : Component, IComponentTreeEntry<OccluderComponent>
|
||||
public sealed partial class OccluderComponent : Component, IComponentTreeEntry<OccluderComponent>
|
||||
{
|
||||
[DataField("enabled")]
|
||||
public bool Enabled = true;
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Robust.Shared.GameObjects;
|
||||
/// Stores the relevant occluder children of this entity.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class OccluderTreeComponent : Component, IComponentTreeComponent<OccluderComponent>
|
||||
public sealed partial class OccluderTreeComponent : Component, IComponentTreeComponent<OccluderComponent>
|
||||
{
|
||||
public DynamicTree<ComponentTreeEntry<OccluderComponent>> Tree { get; set; } = default!;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ using System.Numerics;
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[NetworkedComponent]
|
||||
public abstract class SharedPointLightComponent : Component
|
||||
public abstract partial class SharedPointLightComponent : Component
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ namespace Robust.Shared.GameObjects.Components.Localization
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent()]
|
||||
public sealed class GrammarComponent : Component
|
||||
public sealed partial class GrammarComponent : Component
|
||||
{
|
||||
[DataField("attributes")]
|
||||
public Dictionary<string, string> Attributes { get; } = new();
|
||||
public Dictionary<string, string> Attributes { get; private set; } = new();
|
||||
|
||||
[ViewVariables]
|
||||
public Gender? Gender
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// Contains meta data about this entity that isn't component specific.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class MetaDataComponent : Component
|
||||
public sealed partial class MetaDataComponent : Component
|
||||
{
|
||||
[DataField("name")] internal string? _entityName;
|
||||
[DataField("desc")] internal string? _entityDescription;
|
||||
@@ -71,7 +71,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// Every entity starts at tick 1, because they are conceptually created in the time between 0->1
|
||||
[ViewVariables]
|
||||
public GameTick EntityLastModifiedTick { get; internal set; } = GameTick.Zero;
|
||||
public GameTick EntityLastModifiedTick { get; internal set; } = GameTick.First;
|
||||
|
||||
/// <summary>
|
||||
/// This is the tick at which the client last applied state data received from the server.
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.Shared.Serialization.Manager.Attributes;
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
[Serializable, NetSerializable, DataDefinition]
|
||||
public sealed class PrototypeLayerData
|
||||
public sealed partial class PrototypeLayerData
|
||||
{
|
||||
/// <summary>
|
||||
/// The shader prototype to use for this layer.
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Robust.Shared.GameObjects;
|
||||
/// this is useful if you require multiple entities to have synchronised animations.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class SyncSpriteComponent : Component
|
||||
public sealed partial class SyncSpriteComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ using Timer = Robust.Shared.Timing.Timer;
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[RegisterComponent, Obsolete("Use a system update loop instead")]
|
||||
public sealed class TimerComponent : Component
|
||||
public sealed partial class TimerComponent : Component
|
||||
{
|
||||
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// Stores the position and orientation of the entity.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class TransformComponent : Component, IComponentDebug
|
||||
public sealed partial class TransformComponent : Component, IComponentDebug
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
@@ -8,19 +8,19 @@ using Robust.Shared.Serialization.Manager.Attributes;
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[NetworkedComponent]
|
||||
public abstract class SharedUserInterfaceComponent : Component
|
||||
public abstract partial class SharedUserInterfaceComponent : Component
|
||||
{
|
||||
[DataField("interfaces")]
|
||||
internal List<PrototypeData> _interfaceData = new();
|
||||
|
||||
[DataDefinition]
|
||||
public sealed class PrototypeData
|
||||
public sealed partial class PrototypeData
|
||||
{
|
||||
[DataField("key", required: true)]
|
||||
public Enum UiKey { get; } = default!;
|
||||
public Enum UiKey { get; private set; } = default!;
|
||||
|
||||
[DataField("type", required: true)]
|
||||
public string ClientType { get; } = default!;
|
||||
public string ClientType { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum range before a BUI auto-closes. A non-positive number means there is no limit.
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
foreach (var comp in comps)
|
||||
{
|
||||
if (comp is { LifeStage: < ComponentLifeStage.Initialized })
|
||||
if (comp is { LifeStage: ComponentLifeStage.Added })
|
||||
comp.LifeInitialize(this, CompIdx.Index(comp.GetType()));
|
||||
}
|
||||
|
||||
@@ -490,7 +490,7 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.Running)
|
||||
if (component.LifeStage >= ComponentLifeStage.Initialized && component.LifeStage <= ComponentLifeStage.Running)
|
||||
component.LifeShutdown(this);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
}
|
||||
|
||||
191
Robust.Shared/GameObjects/EntityManager.Spawn.cs
Normal file
191
Robust.Shared/GameObjects/EntityManager.Spawn.cs
Normal file
@@ -0,0 +1,191 @@
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public partial class EntityManager
|
||||
{
|
||||
// This method will soon be marked as obsolete.
|
||||
public EntityUid SpawnEntity(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> SpawnAttachedTo(protoName, coordinates, overrides);
|
||||
|
||||
// This method will soon be marked as obsolete.
|
||||
public EntityUid SpawnEntity(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> Spawn(protoName, coordinates, overrides);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, params string?[] protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Length];
|
||||
for (var i = 0; i < protoNames.Length; i++)
|
||||
{
|
||||
ents[i] = SpawnAttachedTo(protoNames[i], coordinates);
|
||||
}
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntities(MapCoordinates coordinates, params string?[] protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Length];
|
||||
for (var i = 0; i < protoNames.Length; i++)
|
||||
{
|
||||
ents[i] = Spawn(protoNames[i], coordinates);
|
||||
}
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, List<string?> protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Count];
|
||||
for (var i = 0; i < protoNames.Count; i++)
|
||||
{
|
||||
ents[i] = SpawnAttachedTo(protoNames[i], coordinates);
|
||||
}
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntities(MapCoordinates coordinates, List<string?> protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Count];
|
||||
for (var i = 0; i < protoNames.Count; i++)
|
||||
{
|
||||
ents[i] = Spawn(protoNames[i], coordinates);
|
||||
}
|
||||
return ents;
|
||||
}
|
||||
|
||||
public virtual EntityUid SpawnAttachedTo(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
if (!coordinates.IsValid(this))
|
||||
throw new InvalidOperationException($"Tried to spawn entity {protoName} on invalid coordinates {coordinates}.");
|
||||
|
||||
var entity = CreateEntityUninitialized(protoName, coordinates, overrides);
|
||||
InitializeAndStartEntity(entity, coordinates.GetMapId(this));
|
||||
return entity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid Spawn(string? protoName = null, ComponentRegistry? overrides = null)
|
||||
=> Spawn(protoName, MapCoordinates.Nullspace, overrides);
|
||||
|
||||
public virtual EntityUid Spawn(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var entity = CreateEntityUninitialized(protoName, coordinates, overrides);
|
||||
InitializeAndStartEntity(entity, coordinates.MapId);
|
||||
return entity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid SpawnAtPosition(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> Spawn(protoName, coordinates.ToMap(this, _xforms), overrides);
|
||||
|
||||
public bool TrySpawnNextTo(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
uid = null;
|
||||
if (!_xformQuery.Resolve(target, ref xform))
|
||||
return false;
|
||||
|
||||
if (!xform.ParentUid.IsValid())
|
||||
return false;
|
||||
|
||||
if (!_metaQuery.TryGetComponent(target, out var meta))
|
||||
return false;
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) == 0)
|
||||
{
|
||||
uid = SpawnAttachedTo(protoName, xform.Coordinates, overrides);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!TryGetComponent(xform.ParentUid, out ContainerManagerComponent? containerComp))
|
||||
return false;
|
||||
|
||||
foreach (var container in containerComp.Containers.Values)
|
||||
{
|
||||
if (!container.Contains(target))
|
||||
continue;
|
||||
|
||||
uid = Spawn(protoName, overrides);
|
||||
if (container.Insert(uid.Value, this))
|
||||
return true;
|
||||
|
||||
DeleteEntity(uid.Value);
|
||||
uid = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TrySpawnInContainer(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
uid = null;
|
||||
if (containerComp == null && !TryGetComponent(containerUid, out containerComp))
|
||||
return false;
|
||||
|
||||
if (!containerComp.Containers.TryGetValue(containerId, out var container))
|
||||
return false;
|
||||
|
||||
uid = Spawn(protoName, overrides);
|
||||
|
||||
if (container.Insert(uid.Value, this))
|
||||
return true;
|
||||
|
||||
DeleteEntity(uid.Value);
|
||||
uid = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public EntityUid SpawnNextToOrDrop(string? protoName, EntityUid target, TransformComponent? xform = null, ComponentRegistry? overrides = null)
|
||||
{
|
||||
xform ??= _xformQuery.GetComponent(target);
|
||||
if (!xform.ParentUid.IsValid())
|
||||
return Spawn(protoName);
|
||||
|
||||
var uid = Spawn(protoName, overrides);
|
||||
_xforms.PlaceNextToOrDrop(uid, target);
|
||||
return uid;
|
||||
}
|
||||
|
||||
public EntityUid SpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
var uid = Spawn(protoName, overrides);
|
||||
|
||||
if ((containerComp == null && !TryGetComponent(containerUid, out containerComp))
|
||||
|| !containerComp.Containers.TryGetValue(containerId, out var container)
|
||||
|| !container.Insert(uid, this))
|
||||
{
|
||||
|
||||
xform ??= _xformQuery.GetComponent(containerUid);
|
||||
if (xform.ParentUid.IsValid())
|
||||
_xforms.PlaceNextToOrDrop(uid, containerUid, targetXform: xform);
|
||||
}
|
||||
|
||||
return uid;
|
||||
}
|
||||
}
|
||||
@@ -318,12 +318,7 @@ namespace Robust.Shared.GameObjects
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, default, overrides);
|
||||
|
||||
if (coordinates.IsValid(this))
|
||||
{
|
||||
_xforms.SetCoordinates(newEntity, _xformQuery.GetComponent(newEntity), coordinates, unanchor: false);
|
||||
}
|
||||
|
||||
_xforms.SetCoordinates(newEntity, _xformQuery.GetComponent(newEntity), coordinates, unanchor: false);
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
@@ -360,77 +355,6 @@ namespace Robust.Shared.GameObjects
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntities(EntityCoordinates coordinates, params string?[] protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Length];
|
||||
|
||||
for (var i = 0; i < protoNames.Length; i++)
|
||||
{
|
||||
ents[i] = SpawnEntity(protoNames[i], coordinates);
|
||||
}
|
||||
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntities(MapCoordinates coordinates, params string?[] protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Length];
|
||||
|
||||
for (var i = 0; i < protoNames.Length; i++)
|
||||
{
|
||||
ents[i] = SpawnEntity(protoNames[i], coordinates);
|
||||
}
|
||||
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntities(EntityCoordinates coordinates, List<string?> protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Count];
|
||||
|
||||
for (var i = 0; i < protoNames.Count; i++)
|
||||
{
|
||||
ents[i] = SpawnEntity(protoNames[i], coordinates);
|
||||
}
|
||||
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntities(MapCoordinates coordinates, List<string?> protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Count];
|
||||
|
||||
for (var i = 0; i < protoNames.Count; i++)
|
||||
{
|
||||
ents[i] = SpawnEntity(protoNames[i], coordinates);
|
||||
}
|
||||
|
||||
return ents;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid SpawnEntity(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
if (!coordinates.IsValid(this))
|
||||
throw new InvalidOperationException($"Tried to spawn entity {protoName} on invalid coordinates {coordinates}.");
|
||||
|
||||
var entity = CreateEntityUninitialized(protoName, coordinates, overrides);
|
||||
InitializeAndStartEntity(entity, coordinates.GetMapId(this));
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid SpawnEntity(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var entity = CreateEntityUninitialized(protoName, coordinates, overrides);
|
||||
InitializeAndStartEntity(entity, coordinates.MapId);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int EntityCount => Entities.Count;
|
||||
|
||||
@@ -751,8 +675,17 @@ namespace Robust.Shared.GameObjects
|
||||
if (prototypeName == null)
|
||||
return AllocEntity(out _, uid);
|
||||
|
||||
PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype);
|
||||
if (!PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype))
|
||||
throw new EntityCreationException($"Attempted to spawn an entity with an invalid prototype: {prototypeName}");
|
||||
|
||||
return CreateEntity(prototype, uid, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates an entity and loads components but does not do initialization.
|
||||
/// </summary>
|
||||
private protected EntityUid CreateEntity(EntityPrototype prototype, EntityUid uid = default, IEntityLoadContext? context = null)
|
||||
{
|
||||
var entity = AllocEntity(prototype, out var metadata, uid);
|
||||
try
|
||||
{
|
||||
@@ -764,7 +697,7 @@ namespace Robust.Shared.GameObjects
|
||||
// Exception during entity loading.
|
||||
// Need to delete the entity to avoid corrupt state causing crashes later.
|
||||
DeleteEntity(entity);
|
||||
throw new EntityCreationException($"Exception inside CreateEntity with prototype {prototypeName}", e);
|
||||
throw new EntityCreationException($"Exception inside CreateEntity with prototype {prototype.ID}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -641,18 +642,80 @@ public partial class EntitySystem
|
||||
|
||||
#region Entity Spawning
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnEntity(string?, EntityCoordinates, ComponentRegistry?)" />
|
||||
// This method will be obsoleted soon.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid Spawn(string? prototype, EntityCoordinates coordinates)
|
||||
{
|
||||
return EntityManager.SpawnEntity(prototype, coordinates);
|
||||
return ((IEntityManager)EntityManager).SpawnEntity(prototype, coordinates);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnEntity(string?, MapCoordinates, ComponentRegistry?)" />
|
||||
/// <inheritdoc cref="IEntityManager.Spawn(string?, MapCoordinates, ComponentRegistry?)" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid Spawn(string? prototype, MapCoordinates coordinates)
|
||||
=> EntityManager.Spawn(prototype, coordinates);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.Spawn(string?, ComponentRegistry?)" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid Spawn(string? prototype)
|
||||
=> EntityManager.Spawn(prototype);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnAttachedTo" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid SpawnAttachedTo(string? prototype, EntityCoordinates coordinates)
|
||||
=> EntityManager.SpawnAttachedTo(prototype, coordinates);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnAtPosition" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid SpawnAtPosition(string? prototype, EntityCoordinates coordinates)
|
||||
=> EntityManager.SpawnAtPosition(prototype, coordinates);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.TrySpawnInContainer" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool TrySpawnInContainer(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return EntityManager.SpawnEntity(prototype, coordinates);
|
||||
return EntityManager.TrySpawnInContainer(protoName, containerUid, containerId, out uid, containerComp, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.TrySpawnNextTo" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool TrySpawnNextTo(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return EntityManager.TrySpawnNextTo(protoName, target, out uid, xform, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnNextToOrDrop" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid SpawnNextToOrDrop(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return EntityManager.SpawnNextToOrDrop(protoName, target, xform, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnInContainerOrDrop" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid SpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? container = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return EntityManager.SpawnInContainerOrDrop(protoName, containerUid, containerId, xform, container, overrides);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
@@ -8,7 +10,8 @@ namespace Robust.Shared.GameObjects
|
||||
/// All discoverable implementations of IComponent must override the <see cref="Name" />.
|
||||
/// Instances are dynamically instantiated by a <c>ComponentFactory</c>, and will have their IoC Dependencies resolved.
|
||||
/// </remarks>
|
||||
public interface IComponent
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public partial interface IComponent : ISerializationGenerated<IComponent>
|
||||
{
|
||||
/// <summary>
|
||||
/// The current lifetime stage of this component. You can use this to check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public interface IComponentDebug : IComponent
|
||||
public partial interface IComponentDebug : IComponent
|
||||
{
|
||||
string GetDebugString();
|
||||
}
|
||||
|
||||
@@ -156,6 +156,17 @@ namespace Robust.Shared.GameObjects
|
||||
/// </exception>
|
||||
[Pure]
|
||||
string GetComponentName(Type componentType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a component, throwing an exception if it does not exist.
|
||||
/// </summary>
|
||||
/// <param name="netID">The network ID corresponding to the component.</param>
|
||||
/// <returns>The registered name of the component</returns>
|
||||
/// <exception cref="UnknownComponentException">
|
||||
/// Thrown if no component with id <see cref="netID"/> exists.
|
||||
/// </exception>
|
||||
[Pure]
|
||||
string GetComponentName(ushort netID);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the registration belonging to a component, throwing an exception if it does not exist.
|
||||
|
||||
91
Robust.Shared/GameObjects/IEntityManager.Spawn.cs
Normal file
91
Robust.Shared/GameObjects/IEntityManager.Spawn.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public partial interface IEntityManager
|
||||
{
|
||||
// This method will soon be marked as obsolete.
|
||||
EntityUid[] SpawnEntities(EntityCoordinates coordinates, List<string?> protoNames)
|
||||
=> SpawnEntitiesAttachedTo(coordinates, protoNames);
|
||||
|
||||
// This method will soon be marked as obsolete.
|
||||
EntityUid SpawnEntity(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
// This method will soon be marked as obsolete.
|
||||
EntityUid SpawnEntity(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, params string?[] protoNames);
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, List<string?> protoNames);
|
||||
EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, List<string?> protoNames);
|
||||
EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, params string?[] protoNames);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an entity in nullspace.
|
||||
/// </summary>
|
||||
EntityUid Spawn(string? protoName = null, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an entity at a specific world position. The entity will either be parented to the map or a grid.
|
||||
/// </summary>
|
||||
EntityUid Spawn(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an entity and then parents it to the entity that the given entity coordinates are relative to.
|
||||
/// </summary>
|
||||
EntityUid SpawnAttachedTo(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the given entity coordinates into world coordinates and spawns an entity at that location. The
|
||||
/// entity will either be parented to the map or a grid.
|
||||
/// </summary>
|
||||
EntityUid SpawnAtPosition(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn an entity inside of a container.
|
||||
/// </summary>
|
||||
bool TrySpawnInContainer(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn an entity inside of a container. If it fails to insert into the container, it will
|
||||
/// instead attempt to spawn the entity next to the target.
|
||||
/// </summary>
|
||||
public EntityUid SpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn an entity adjacent to some other entity. If the other entity is in a container, this will
|
||||
/// attempt to insert the new entity into the same container.
|
||||
/// </summary>
|
||||
bool TrySpawnNextTo(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn an entity adjacent to some other entity. If the other entity is in a container, this will
|
||||
/// attempt to insert the new entity into the same container. If it fails to insert into the container, it will
|
||||
/// instead attempt to spawn the entity next to the target's parent.
|
||||
/// </summary>
|
||||
EntityUid SpawnNextToOrDrop(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
}
|
||||
@@ -71,32 +71,6 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
void StartEntity(EntityUid entity);
|
||||
|
||||
EntityUid[] SpawnEntities(EntityCoordinates coordinates, params string?[] protoNames);
|
||||
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, params string?[] protoNames);
|
||||
|
||||
EntityUid[] SpawnEntities(EntityCoordinates coordinates, List<string?> protoNames);
|
||||
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, List<string?> protoNames);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an initialized entity and sets its local coordinates to the given entity coordinates. Note that this
|
||||
/// means that if you specify coordinates relative to some entity, the newly spawned entity will be a child of
|
||||
/// that entity.
|
||||
/// </summary>
|
||||
/// <param name="protoName">The prototype to clone. If this is null, the entity won't have a prototype.</param>
|
||||
/// <param name="coordinates"></param>
|
||||
/// <returns>Newly created entity.</returns>
|
||||
EntityUid SpawnEntity(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an entity at a specific world position.
|
||||
/// </summary>
|
||||
/// <param name="protoName"></param>
|
||||
/// <param name="coordinates"></param>
|
||||
/// <returns></returns>
|
||||
EntityUid SpawnEntity(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// How many entities are currently active.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Robust.Shared.GameObjects;
|
||||
/// If an entity with this component is placed on top of another anchored entity with this component and the same key it will replace it.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class PlacementReplacementComponent : Component
|
||||
public sealed partial class PlacementReplacementComponent : Component
|
||||
{
|
||||
[DataField("key")]
|
||||
public string Key = "";
|
||||
|
||||
@@ -221,7 +221,8 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
return _transform.GetInvWorldMatrix(treeXform).TransformBox(GetWorldAABB(entity, xform));
|
||||
}
|
||||
|
||||
internal void CreateProxies(EntityUid uid, TransformComponent xform, Fixture fixture, PhysicsComponent body)
|
||||
internal void CreateProxies(EntityUid uid, string fixtureId, Fixture fixture, TransformComponent xform,
|
||||
PhysicsComponent body)
|
||||
{
|
||||
if (!TryGetCurrentBroadphase(xform, out var broadphase))
|
||||
return;
|
||||
@@ -237,10 +238,10 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
var tree = body.BodyType == BodyType.Static ? broadphase.StaticTree : broadphase.DynamicTree;
|
||||
DebugTools.Assert(fixture.ProxyCount == 0);
|
||||
|
||||
AddOrMoveProxies(uid, fixture, body, tree, broadphaseTransform, mapTransform, physMap.MoveBuffer);
|
||||
AddOrMoveProxies(uid, fixtureId, fixture, body, tree, broadphaseTransform, mapTransform, physMap.MoveBuffer);
|
||||
}
|
||||
|
||||
internal void DestroyProxies(EntityUid uid, Fixture fixture, TransformComponent xform, BroadphaseComponent broadphase, PhysicsMapComponent? physicsMap)
|
||||
internal void DestroyProxies(EntityUid uid, string fixtureId, Fixture fixture, TransformComponent xform, BroadphaseComponent broadphase, PhysicsMapComponent? physicsMap)
|
||||
{
|
||||
DebugTools.AssertNotNull(xform.Broadphase);
|
||||
DebugTools.Assert(xform.Broadphase!.Value.Uid == broadphase.Owner);
|
||||
@@ -250,7 +251,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
|
||||
if (fixture.ProxyCount == 0)
|
||||
{
|
||||
Log.Warning($"Tried to destroy fixture {fixture.ID} on {ToPrettyString(uid)} that already has no proxies?");
|
||||
Log.Warning($"Tried to destroy fixture {fixtureId} on {ToPrettyString(uid)} that already has no proxies?");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -381,14 +382,15 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
// TODO BROADPHASE PARENTING this just assumes local = world
|
||||
var broadphaseTransform = new Transform(broadphaseXform.InvLocalMatrix.Transform(mapTransform.Position), mapTransform.Quaternion2D.Angle - broadphaseXform.LocalRotation);
|
||||
|
||||
foreach (var fixture in manager.Fixtures.Values)
|
||||
foreach (var (id, fixture) in manager.Fixtures)
|
||||
{
|
||||
AddOrMoveProxies(uid, fixture, body, tree, broadphaseTransform, mapTransform, physicsMap.MoveBuffer);
|
||||
AddOrMoveProxies(uid, id, fixture, body, tree, broadphaseTransform, mapTransform, physicsMap.MoveBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddOrMoveProxies(
|
||||
EntityUid uid,
|
||||
string fixtureId,
|
||||
Fixture fixture,
|
||||
PhysicsComponent body,
|
||||
IBroadPhase tree,
|
||||
@@ -417,7 +419,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var bounds = fixture.Shape.ComputeAABB(broadphaseTransform, i);
|
||||
var proxy = new FixtureProxy(uid, body, bounds, fixture, i);
|
||||
var proxy = new FixtureProxy(uid, body, bounds, fixtureId, fixture, i);
|
||||
proxy.ProxyId = tree.AddProxy(ref proxy);
|
||||
proxy.AABB = bounds;
|
||||
proxies[i] = proxy;
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
@@ -95,12 +96,16 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
var fixtures = new List<Fixture>(mapChunks.Count);
|
||||
var fixtures = new Dictionary<string, Fixture>(mapChunks.Count);
|
||||
|
||||
foreach (var (chunk, rectangles) in mapChunks)
|
||||
{
|
||||
UpdateFixture(uid, chunk, rectangles, body, manager, xform);
|
||||
fixtures.AddRange(chunk.Fixtures);
|
||||
|
||||
foreach (var (id, fixture) in chunk.Fixtures)
|
||||
{
|
||||
fixtures[id] = fixture;
|
||||
}
|
||||
}
|
||||
|
||||
EntityManager.EventBus.RaiseLocalEvent(uid,new GridFixtureChangeEvent {NewFixtures = fixtures}, true);
|
||||
@@ -126,7 +131,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// Additionally, we need to handle map deserialization where content may have stored its own data
|
||||
// on the grid (e.g. mass) which we want to preserve.
|
||||
var newFixtures = new List<Fixture>();
|
||||
var newFixtures = new ValueList<(string Id, Fixture Fixture)>();
|
||||
|
||||
Span<Vector2> vertices = stackalloc Vector2[4];
|
||||
|
||||
@@ -144,21 +149,20 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
#pragma warning disable CS0618
|
||||
var newFixture = new Fixture(
|
||||
$"grid_chunk-{bounds.Left}-{bounds.Bottom}",
|
||||
poly,
|
||||
MapGridHelpers.CollisionGroup,
|
||||
MapGridHelpers.CollisionGroup,
|
||||
true) { Body = body};
|
||||
#pragma warning restore CS0618
|
||||
|
||||
newFixtures.Add(newFixture);
|
||||
newFixtures.Add(($"grid_chunk-{bounds.Left}-{bounds.Bottom}", newFixture));
|
||||
}
|
||||
|
||||
var toRemove = new RemQueue<Fixture>();
|
||||
var toRemove = new ValueList<(string Id, Fixture Fixture)>();
|
||||
// Check if we even need to issue an eventbus event
|
||||
var updated = false;
|
||||
|
||||
foreach (var oldFixture in chunk.Fixtures)
|
||||
foreach (var (oldId, oldFixture) in chunk.Fixtures)
|
||||
{
|
||||
var existing = false;
|
||||
|
||||
@@ -166,8 +170,10 @@ namespace Robust.Shared.GameObjects
|
||||
// (TODO: Check IDs and cross-reference for updates?)
|
||||
for (var i = newFixtures.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var fixture = newFixtures[i];
|
||||
if (!oldFixture.Equals(fixture)) continue;
|
||||
var fixture = newFixtures[i].Fixture;
|
||||
if (!oldFixture.Equals(fixture))
|
||||
continue;
|
||||
|
||||
existing = true;
|
||||
newFixtures.RemoveSwap(i);
|
||||
break;
|
||||
@@ -176,36 +182,36 @@ namespace Robust.Shared.GameObjects
|
||||
// Doesn't align with any new fixtures so delete
|
||||
if (existing) continue;
|
||||
|
||||
toRemove.Add(oldFixture);
|
||||
toRemove.Add((oldId, oldFixture));
|
||||
}
|
||||
|
||||
foreach (var fixture in toRemove)
|
||||
foreach (var (id, fixture) in toRemove)
|
||||
{
|
||||
// TODO add a DestroyFixture() override that takes in a list.
|
||||
// reduced broadphase lookups
|
||||
chunk.Fixtures.Remove(fixture);
|
||||
_fixtures.DestroyFixture(uid, fixture, false, body: body, manager: manager, xform: xform);
|
||||
chunk.Fixtures.Remove(id);
|
||||
_fixtures.DestroyFixture(uid, id, fixture, false, body: body, manager: manager, xform: xform);
|
||||
}
|
||||
|
||||
if (newFixtures.Count > 0 || toRemove.List?.Count > 0)
|
||||
if (newFixtures.Count > 0 || toRemove.Count > 0)
|
||||
{
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// Anything remaining is a new fixture (or at least, may have not serialized onto the chunk yet).
|
||||
foreach (var fixture in newFixtures)
|
||||
foreach (var (id, fixture) in newFixtures)
|
||||
{
|
||||
var existingFixture = _fixtures.GetFixtureOrNull(uid, fixture.ID, manager: manager);
|
||||
var existingFixture = _fixtures.GetFixtureOrNull(uid, id, manager: manager);
|
||||
// Check if it's the same (otherwise remove anyway).
|
||||
if (existingFixture?.Shape is PolygonShape poly &&
|
||||
poly.EqualsApprox((PolygonShape) fixture.Shape))
|
||||
{
|
||||
chunk.Fixtures.Add(existingFixture);
|
||||
chunk.Fixtures.Add(id, existingFixture);
|
||||
continue;
|
||||
}
|
||||
|
||||
chunk.Fixtures.Add(fixture);
|
||||
_fixtures.CreateFixture(uid, fixture, false, manager, body, xform);
|
||||
chunk.Fixtures.Add(id, fixture);
|
||||
_fixtures.CreateFixture(uid, id, fixture, false, manager, body, xform);
|
||||
}
|
||||
|
||||
return updated;
|
||||
@@ -218,7 +224,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public sealed class GridFixtureChangeEvent : EntityEventArgs
|
||||
{
|
||||
public List<Fixture> NewFixtures { get; init; } = default!;
|
||||
public Dictionary<string, Fixture> NewFixtures { get; init; } = default!;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user