Skip to content

Commit 9f8861b

Browse files
committed
chore: add netpacket global id
1 parent 65b5aa2 commit 9f8861b

File tree

6 files changed

+157
-39
lines changed

6 files changed

+157
-39
lines changed

src/TrProtocol.SerializerGenerator/Internal/Models/ProtocolTypeData.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ namespace TrProtocol.SerializerGenerator.Internal.Models
66
{
77
public class ProtocolTypeData
88
{
9-
public ProtocolTypeData(TypeDeclarationSyntax classDeclaration, INamedTypeSymbol symbol, string name, string @namespace, string[] imports, SerializationExpandContext[] members) {
9+
public ProtocolTypeData(TypeDeclarationSyntax classDeclaration, INamedTypeSymbol symbol, string name, string nameSpace, string[] imports, SerializationExpandContext[] members) {
1010
DefSyntax = classDeclaration;
1111
DefSymbol = symbol;
1212
TypeName = name;
1313
Members = members;
14-
Namespace = @namespace;
14+
Namespace = nameSpace;
1515
Imports = new HashSet<string>(imports);
1616
}
1717
public readonly INamedTypeSymbol DefSymbol;
@@ -29,7 +29,7 @@ public ProtocolTypeData(TypeDeclarationSyntax classDeclaration, INamedTypeSymbol
2929
public bool IsNetPacket;
3030
public bool IsPolymorphic;
3131
[MemberNotNullWhen(true, nameof(ConcreteImplData))]
32-
public bool IsConcreteType { get; set; }
32+
public bool IsConcreteImpl { get; set; }
3333
public ConcreteImplData? ConcreteImplData;
3434

3535
public bool HasExtraData;
@@ -44,5 +44,9 @@ public ProtocolTypeData(TypeDeclarationSyntax classDeclaration, INamedTypeSymbol
4444

4545
public readonly string TypeName;
4646
public readonly IReadOnlyList<SerializationExpandContext> Members;
47+
48+
public int GlobalID = -1;
49+
public int AllocatedGlobalIDCount = -1;
50+
public bool IsGlobalIDRoot => AllocatedGlobalIDCount >= 0;
4751
}
4852
}

src/TrProtocol.SerializerGenerator/Internal/Serialization/ProtocolModelValidator.cs

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ public static Dictionary<string, PolymorphicImplsData> ValidatePolymorphic(Dicti
208208
polymorphicModels.Add(inheritFrom.discriminatorEnum.Name, polymorphicData = new(models.First(m => m.TypeName == inheritFrom.type.Name), inheritFrom));
209209
}
210210
polymorphicData.Implementations.Add(enumMemberAcess.Name.Identifier.Text, model);
211-
model.IsConcreteType = true;
211+
model.IsConcreteImpl = true;
212212
model.ConcreteImplData = new ConcreteImplData(model, polymorphicData, enumMemberAcess);
213213
}
214214

@@ -307,7 +307,104 @@ public static Dictionary<string, PolymorphicImplsData> ValidatePolymorphic(Dicti
307307
foreach (var polymorphicModel in polymorphicModels.Values) {
308308
polymorphicModel.PolymorphicBaseType.IsPolymorphic = true;
309309
}
310+
311+
var globalIDRootTypes = VaidateGlobalTypeID(polymorphicTypes, polymorphicModels, models);
312+
AllocateGlobalTypeID(polymorphicTypes, polymorphicModels, globalIDRootTypes);
313+
310314
return polymorphicModels;
311315
}
316+
static void AllocateGlobalTypeID(
317+
Dictionary<string, PolymorphicImplsInfo> polymorphicInfos,
318+
Dictionary<string, PolymorphicImplsData> polymorphicImpls,
319+
ProtocolTypeData[] globalIDRootTypes) {
320+
321+
foreach (var globalIDRootType in globalIDRootTypes) {
322+
int id = 0;
323+
foreach (var impl in EnumerateConcreteImpl(polymorphicInfos, polymorphicImpls, globalIDRootType)) {
324+
impl.GlobalID = id++;
325+
}
326+
globalIDRootType.AllocatedGlobalIDCount = id;
327+
}
328+
}
329+
static IEnumerable<ProtocolTypeData> EnumerateConcreteImpl(
330+
Dictionary<string, PolymorphicImplsInfo> polymorphicInfos,
331+
Dictionary<string, PolymorphicImplsData> polymorphicImpls,
332+
ProtocolTypeData type) {
333+
if (!type.IsPolymorphic) {
334+
yield return type;
335+
yield break;
336+
}
337+
else {
338+
var info = polymorphicInfos[type.DefSymbol.GetFullName()];
339+
var data = polymorphicImpls[info.discriminatorEnum.Name];
340+
int index = 0;
341+
Dictionary<string, int> enumOrder = [];
342+
foreach (var enumMember in info.discriminatorEnum.GetMembers()) {
343+
enumOrder[enumMember.Name] = index++;
344+
}
345+
foreach (var impl in data.Implementations.OrderBy(x => enumOrder[x.Key]).Select(x => x.Value)) {
346+
foreach (var child in EnumerateConcreteImpl(polymorphicInfos, polymorphicImpls, impl)) {
347+
yield return child;
348+
}
349+
}
350+
}
351+
}
352+
static ProtocolTypeData[] VaidateGlobalTypeID(
353+
Dictionary<string, PolymorphicImplsInfo> polymorphicInfos,
354+
Dictionary<string, PolymorphicImplsData> polymorphicImpls,
355+
ProtocolTypeData[] models) {
356+
var rootTypes = models.Where(m => m.IsPolymorphic && !m.IsConcreteImpl);
357+
List<ProtocolTypeData> globalIDRootTypes = [];
358+
foreach (var type in rootTypes) {
359+
VaidateGlobalTypeID(globalIDRootTypes, polymorphicInfos, polymorphicImpls, type, false);
360+
}
361+
return [.. globalIDRootTypes];
362+
}
363+
static void VaidateGlobalTypeID(
364+
List<ProtocolTypeData> globalIDRootTypes,
365+
Dictionary<string, PolymorphicImplsInfo> polymorphicInfos,
366+
Dictionary<string, PolymorphicImplsData> polymorphicImpls,
367+
ProtocolTypeData type,
368+
bool hasAttribute) {
369+
370+
if (type.DefSyntax.AttributeMatch<GenerateGlobalIDAttribute>(out var match)) {
371+
if (hasAttribute) {
372+
throw new DiagnosticException(
373+
Diagnostic.Create(
374+
new DiagnosticDescriptor(
375+
"SCG10",
376+
$"Redundant [GenerateGlobalId] attribute on derived type",
377+
"GenerateGlobalIDAttribute should not be applied to type '{0}' derived from another type already marked with GenerateGlobalIDAttribute.",
378+
"PolymorphicGlobalId",
379+
DiagnosticSeverity.Error,
380+
true),
381+
match.GetLocation(),
382+
type.TypeName));
383+
}
384+
hasAttribute = true;
385+
globalIDRootTypes.Add(type);
386+
}
387+
388+
if (hasAttribute && type.IsPolymorphic && type.IsAbstract) {
389+
throw new DiagnosticException(
390+
Diagnostic.Create(
391+
new DiagnosticDescriptor(
392+
"SCG10",
393+
$"invaild model declaration",
394+
"The interface marked with GenerateGlobalIDAttribute cannot be implemented by an abstract class.",
395+
"",
396+
DiagnosticSeverity.Error,
397+
true),
398+
type.DefSyntax.BaseList!.GetLocation(),
399+
type.TypeName));
400+
}
401+
402+
if (polymorphicInfos.TryGetValue(type.DefSymbol.GetFullName(), out var polymorphicInfo)) {
403+
var impls = polymorphicImpls[polymorphicInfo.discriminatorEnum.Name];
404+
foreach (var impl in impls.Implementations) {
405+
VaidateGlobalTypeID(globalIDRootTypes, polymorphicInfos, polymorphicImpls, impl.Value, hasAttribute);
406+
}
407+
}
408+
}
312409
}
313410
}

src/TrProtocol.SerializerGenerator/SerializeGenerator.cs

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Microsoft.CodeAnalysis.CSharp.Syntax;
33
using Microsoft.CodeAnalysis.Text;
44
using System.Collections.Immutable;
5+
using System.Diagnostics;
56
using System.Text;
67
using Terraria;
78
using TrProtocol.Attributes;
@@ -21,7 +22,7 @@ namespace TrProtocol.SerializerGenerator
2122
public partial class SerializeGenerator : IIncrementalGenerator
2223
{
2324
private static bool FilterTypes(SyntaxNode syntaxNode, CancellationToken token) {
24-
if (syntaxNode is TypeDeclarationSyntax td /*&& td.Keyword.ToString() is not "interface"*/ && td.Keyword.ToString() is not "record" && td.BaseList is not null) {
25+
if (syntaxNode is TypeDeclarationSyntax td/* && td.Keyword.ToString() is not "interface" && td.Keyword.ToString() is not "record" && td.BaseList is not null*/) {
2526
return true;
2627
}
2728
return false;
@@ -65,15 +66,15 @@ private static ProtocolTypeInfo Transform(TypeDeclarationSyntax typeDeclaration)
6566
}
6667
#endregion
6768

68-
static readonly string[] NeccessaryUsings = new string[] {
69+
static readonly string[] NeccessaryUsings = [
6970
"System.Runtime.CompilerServices",
7071
"System.Runtime.InteropServices",
7172
"System.Diagnostics.CodeAnalysis",
7273
"TrProtocol.Attributes",
7374
"TrProtocol.Interfaces",
7475
"TrProtocol.Exceptions",
7576
"TrProtocol.Models",
76-
};
77+
];
7778

7879
// TODO: Split the serialization code generation process into more smaller SyntaxTemplates
7980
private static void Execute(SourceProductionContext context, (Compilation compilation, ImmutableArray<ProtocolTypeInfo> infos) data) {
@@ -173,25 +174,31 @@ static void CheckMemberSymbol(INamedTypeSymbol typeSym, SerializationExpandConte
173174
var classNode = namespaceBlock.WriteTypeDefinition(model);
174175
classNode.WriteAutoDiscriminator(model);
175176

176-
#region Manage type constructors and special behavior
177+
#region Add Global ID
178+
if (model.GlobalID >= 0) {
179+
classNode.WriteLine($"public static int GlobalID => {model.GlobalID};");
180+
}
181+
#endregion
182+
183+
#region Add type constructors and special behavior
177184

178185
List<(string memberName, string memberType)> externalMembers = model.DefSyntax.Members
179-
.Where(m => m.AttributeLists
180-
.SelectMany(a => a.Attributes)
181-
.Any(a => a.AttributeMatch<ExternalMemberAttribute>()))
182-
.Select((m) => {
183-
if (m is PropertyDeclarationSyntax prop) {
184-
return new List<(string, string)>() { (prop.Identifier.ToString(), prop.Type.ToString()) };
185-
}
186-
else {
187-
var field = (FieldDeclarationSyntax)m;
188-
var list = new List<(string memberName, string memberType)>();
189-
foreach (var variable in field.Declaration.Variables) {
190-
list.Add((variable.Identifier.ToString(), field.Declaration.Type.ToString()));
186+
.Where(m => m.AttributeLists
187+
.SelectMany(a => a.Attributes)
188+
.Any(a => a.AttributeMatch<ExternalMemberAttribute>()))
189+
.Select((m) => {
190+
if (m is PropertyDeclarationSyntax prop) {
191+
return new List<(string, string)>() { (prop.Identifier.ToString(), prop.Type.ToString()) };
191192
}
192-
return list;
193-
}
194-
}).SelectMany(m => m).ToList();
193+
else {
194+
var field = (FieldDeclarationSyntax)m;
195+
var list = new List<(string memberName, string memberType)>();
196+
foreach (var variable in field.Declaration.Variables) {
197+
list.Add((variable.Identifier.ToString(), field.Declaration.Type.ToString()));
198+
}
199+
return list;
200+
}
201+
}).SelectMany(m => m).ToList();
195202

196203
string externalMemberParams;
197204
if (externalMembers.Count == 0) {
@@ -1489,7 +1496,7 @@ object GetArraySize(ExpressionSyntax indexExp, int i) {
14891496

14901497
writeNode.WriteLine("var ptr_current = ptr;");
14911498
writeNode.WriteLine();
1492-
if (model.IsConcreteType) {
1499+
if (model.IsConcreteImpl) {
14931500
INamedTypeSymbol[] ExtractAbstractModelInheritance(INamedTypeSymbol type) {
14941501
var abstractModelAncestors = type.GetFullInheritanceTree()
14951502
.Where(t => t.HasAbstractModelAttribute())
@@ -1603,7 +1610,7 @@ param[0].Type is PointerTypeSyntax pointerType &&
16031610
}
16041611
return false;
16051612
})) {
1606-
classNode.Write($"public unsafe {((model.IsConcreteType && !model.IsValueType) ? "override " : "")}void WriteContent(ref void* ptr) ");
1613+
classNode.Write($"public unsafe {((model.IsConcreteImpl && !model.IsValueType) ? "override " : "")}void WriteContent(ref void* ptr) ");
16071614
classNode.Sources.Add(writeNode);
16081615
}
16091616
#endregion
@@ -1630,7 +1637,7 @@ param[0].Type is PointerTypeSyntax pointerType &&
16301637
classNode.WriteLine("/// This operation is not supported and always throws a System.NotSupportedException.");
16311638
classNode.WriteLine("/// </summary>");
16321639
classNode.WriteLine($"[Obsolete]");
1633-
if ((model.IsConcreteType && !model.IsValueType)) {
1640+
if ((model.IsConcreteImpl && !model.IsValueType)) {
16341641
classNode.WriteLine($"public override void ReadContent(ref void* ptr) => throw new {nameof(NotSupportedException)}();");
16351642
}
16361643
else {
@@ -1641,7 +1648,7 @@ param[0].Type is PointerTypeSyntax pointerType &&
16411648
}
16421649
else {
16431650
classNode.WriteLine($"[MemberNotNull({string.Join(", ", memberNullables.Select(m => $"nameof({m})"))})]");
1644-
classNode.Write($"public unsafe {((model.IsConcreteType && !model.IsValueType) ? "override " : "")}void ReadContent(ref void* ptr) ");
1651+
classNode.Write($"public unsafe {((model.IsConcreteImpl && !model.IsValueType) ? "override " : "")}void ReadContent(ref void* ptr) ");
16451652
}
16461653
classNode.Sources.Add(readNode);
16471654
}
@@ -1683,16 +1690,15 @@ param[0].Type is PointerTypeSyntax pointerType &&
16831690
l.DefSyntax.GetNamespace(out _, out var ns, out _);
16841691
return ns;
16851692
})).Distinct()) {
1686-
16871693
source.WriteLine($"using {us};");
16881694
}
16891695

16901696

1691-
List<(string memberName, string memberType)> externalMembers = polymorphicBase.DefSyntax.Members
1697+
List<(string memberName, string memberType)> externalMembers = [.. polymorphicBase.DefSyntax.Members
16921698
.Where(m => m.AttributeLists
16931699
.SelectMany(a => a.Attributes)
16941700
.Any(a => a.AttributeMatch<ExternalMemberAttribute>()))
1695-
.Select((m) => {
1701+
.SelectMany((m) => {
16961702
if (m is PropertyDeclarationSyntax prop) {
16971703
return new List<(string, string)>() { (prop.Identifier.ToString(), prop.Type.ToString()) };
16981704
}
@@ -1704,7 +1710,7 @@ param[0].Type is PointerTypeSyntax pointerType &&
17041710
}
17051711
return list;
17061712
}
1707-
}).SelectMany(m => m).ToList();
1713+
})];
17081714
string externalMemberParams;
17091715
string externalMemberParamsCall;
17101716
if (externalMembers.Count == 0) {
@@ -1721,6 +1727,12 @@ param[0].Type is PointerTypeSyntax pointerType &&
17211727
var typeKind = polymorphicBase.IsInterface ? "interface" : polymorphicBase.IsValueType ? "struct" : "class";
17221728
source.Write($"public unsafe partial {typeKind} {polymorphicBase.TypeName} ");
17231729
source.BlockWrite((source) => {
1730+
1731+
if (polymorphicBase.IsGlobalIDRoot) {
1732+
source.WriteLine($"public const int GlobalIDCount = {polymorphicBase.AllocatedGlobalIDCount};");
1733+
source.WriteLine("public static abstract int GlobalID { get; }");
1734+
}
1735+
17241736
source.Write($"public unsafe static {polymorphicBase.TypeName} Read{polymorphicBase.TypeName}(ref void* ptr{(polymorphicBase.IsNetPacket ? ", void* ptr_end" : "")}{(polymorphicBase.IsNetPacket ? ", bool isServerSide" : "")}{externalMemberParams}) ");
17251737
source.BlockWrite((source) => {
17261738
source.WriteLine($"{enumType.Name} identity = ({enumType.Name})Unsafe.Read<{enumType.EnumUnderlyingType}>(ptr);");
@@ -1753,13 +1765,6 @@ param[0].Type is PointerTypeSyntax pointerType &&
17531765
static CompilationContext Compilation = new CompilationContext();
17541766

17551767
public void Initialize(IncrementalGeneratorInitializationContext initContext) {
1756-
1757-
#if DEBUG
1758-
//if (!Debugger.IsAttached) {
1759-
// Debugger.Launch();
1760-
//}
1761-
#endif
1762-
17631768
initContext.RegisterSourceOutput(initContext.CompilationProvider.WithComparer(Compilation), Compilation.LoadCompilation);
17641769

17651770

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace TrProtocol.Attributes
6+
{
7+
[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false)]
8+
public class GenerateGlobalIDAttribute : Attribute
9+
{
10+
}
11+
}

src/TrProtocol.Shared/TrProtocol.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<Compile Include="$(MSBuildThisFileDirectory)Attributes\ExplicitImportTypeAttribute.cs" />
2424
<Compile Include="$(MSBuildThisFileDirectory)Attributes\ExternalMemberAttribute.cs" />
2525
<Compile Include="$(MSBuildThisFileDirectory)Attributes\ExternalMemberValueSetterAttribute.cs" />
26+
<Compile Include="$(MSBuildThisFileDirectory)Attributes\GenerateGlobalIDAttribute.cs" />
2627
<Compile Include="$(MSBuildThisFileDirectory)Attributes\IgnoreSerializeAttribute.cs" />
2728
<Compile Include="$(MSBuildThisFileDirectory)Attributes\ImplementationClaimAttribute.cs" />
2829
<Compile Include="$(MSBuildThisFileDirectory)Attributes\InitDefaultValueAttribute.cs" />
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace TrProtocol
55
{
6-
6+
[GenerateGlobalID]
77
[PolymorphicBase(typeof(MessageID), nameof(Type))]
88
public partial interface INetPacket : IAutoSerializable
99
{

0 commit comments

Comments
 (0)