Skip to content

Commit

Permalink
wip sync
Browse files Browse the repository at this point in the history
  • Loading branch information
teinarss committed Aug 31, 2023
1 parent cc807d2 commit 388a331
Show file tree
Hide file tree
Showing 11 changed files with 262 additions and 4 deletions.
2 changes: 2 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
<Nullable>disable</Nullable>
<Product>OpenRA</Product>
<Copyright>Copyright (c) The OpenRA Developers and Contributors</Copyright>

<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

<PropertyGroup>
Expand Down
3 changes: 3 additions & 0 deletions OpenRA.Game/OpenRA.Game.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Threading.Channels" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.SourceGenerators\OpenRA.SourceGenerators.csproj" ReferenceOutputAssembly="false" OutputItemType="Analyzer"/>
</ItemGroup>
</Project>
10 changes: 10 additions & 0 deletions OpenRA.Game/Sync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ public sealed class SyncAttribute : Attribute { }
// Marker interface
public interface ISync { }

public interface ISync2
{
int GetSyncHash();
}

public static class Sync
{
static readonly ConcurrentCache<Type, Func<object, int>> HashFunctions =
Expand Down Expand Up @@ -134,6 +139,11 @@ public static int HashPlayer(Player p)
return 0;
}

public static int HashBool(bool b)
{
return b ? 0xaaa : 0x555;
}

public static int HashTarget(Target t)
{
switch (t.Type)
Expand Down
10 changes: 10 additions & 0 deletions OpenRA.Game/SyncGenerationAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace OpenRA
{
[AttributeUsage(AttributeTargets.Class)]
public class SyncGenerationAttribute : Attribute
{
}

}
3 changes: 2 additions & 1 deletion OpenRA.Game/Traits/Player/Shroud.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ IEnumerable<LobbyOption> ILobbyOptions.LobbyOptions(MapPreview map)
public override object Create(ActorInitializer init) { return new Shroud(init.Self, this); }
}

public class Shroud : ISync, INotifyCreated, ITick
[SyncGeneration]
public partial class Shroud : ISync, INotifyCreated, ITick, ISync2
{
public enum SourceType : byte { PassiveVisibility, Shroud, Visibility }
public event Action<PPos> OnShroudChanged;
Expand Down
3 changes: 3 additions & 0 deletions OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
<PackageReference Include="rix0rrr.BeaconLib" Version="1.0.2" />
<PackageReference Include="Pfim" Version="0.11.2" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.SourceGenerators\OpenRA.SourceGenerators.csproj" ReferenceOutputAssembly="false" OutputItemType="Analyzer"/>
</ItemGroup>
</Project>
3 changes: 2 additions & 1 deletion OpenRA.Mods.Common/Projectiles/AreaBeam.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ public IProjectile Create(ProjectileArgs args)
}
}

public class AreaBeam : IProjectile, ISync
[SyncGeneration]
public partial class AreaBeam : IProjectile, ISync
{
readonly AreaBeamInfo info;
readonly ProjectileArgs args;
Expand Down
5 changes: 3 additions & 2 deletions OpenRA.Mods.Common/Traits/Mobile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,9 @@ IEnumerable<EditorActorOption> IEditorActorOptions.ActorOptions(ActorInfo ai, Wo
}
}

public class Mobile : PausableConditionalTrait<MobileInfo>, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, ITick, ICreationActivity,
IFacing, IDeathActorInitModifier, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove, IActorPreviewInitModifier, INotifyBecomingIdle
[SyncGeneration]
public partial class Mobile : PausableConditionalTrait<MobileInfo>, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, ITick, ICreationActivity,
IFacing, IDeathActorInitModifier, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove, IActorPreviewInitModifier, INotifyBecomingIdle, ISync2
{
readonly Actor self;
readonly Lazy<IEnumerable<int>> speedModifiers;
Expand Down
86 changes: 86 additions & 0 deletions OpenRA.SourceGenerators/Sync/SyncClassInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis;

namespace OpenRA.SourceGenerators.Sync
{
public sealed class SyncClassInfo : IEquatable<SyncClassInfo>
{
public string? Namespace { get; }

Check failure on line 11 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check failure on line 11 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
public string Name { get; }
public IReadOnlyList<SyncMember> SyncMembers { get; }

public SyncClassInfo(ITypeSymbol type)
{
Namespace = type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToString();
Name = type.Name;
//Debugger.Launch();

Check failure on line 19 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

Single-line comments should not be followed by blank line (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1512.md)

Check failure on line 19 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

Check failure on line 19 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

Check failure on line 19 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

Single-line comments should not be followed by blank line (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1512.md)

Check failure on line 19 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

Check failure on line 19 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)


var result = new List<SyncMember>();
var members = type.GetMembers().Where(m => m.GetAttributes().Any(a => a.AttributeClass.Name == "SyncAttribute"))
.ToList();

foreach (var member in members)
{
if (member is IFieldSymbol field)
{
result.Add(new SyncMember
{
Name = field.Name,
Type = field.Type.Name
});
}

if (member is IPropertySymbol property)
{
result.Add(new SyncMember
{
Name = property.Name,
Type = property.Type.Name
});
}
}

Check failure on line 44 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

Check failure on line 44 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

//HasNameProperty = type.GetMembers().Any(m => m.Name == "Name"

Check failure on line 45 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

Check failure on line 45 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

// && m is IPropertySymbol property
// && property.Type.SpecialType == SpecialType.System_String);

Check failure on line 47 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

Single-line comments should not be followed by blank line (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1512.md)

Check failure on line 47 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

Single-line comments should not be followed by blank line (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1512.md)

SyncMembers = result;
}

public override bool Equals(object? obj)

Check failure on line 52 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check failure on line 52 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
return obj is SyncClassInfo other && Equals(other);
}

public bool Equals(SyncClassInfo? other)

Check failure on line 57 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check failure on line 57 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;

return Namespace == other.Namespace
&& Name == other.Name;
}

public override int GetHashCode()
{
unchecked
{
var hashCode = (Namespace != null ? Namespace.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ Name.GetHashCode();

return hashCode;
}
}
}

public class SyncMember
{
public string Name { get; set; }
public string Type { get; set; }

}

Check failure on line 85 in OpenRA.SourceGenerators/Sync/SyncClassInfo.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

}
76 changes: 76 additions & 0 deletions OpenRA.SourceGenerators/Sync/SyncCodeGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace OpenRA.SourceGenerators.Sync
{
public static class SyncCodeGenerator
{
public static string Generate(SyncClassInfo syncClassInfo)
{
var ns = syncClassInfo.Namespace;
var name = syncClassInfo.Name;

var sb = new StringBuilder(@"// <auto-generated />
#nullable enable");


sb.Append($@"
using OpenRA;
{(ns is null ? null : $@"namespace {ns}
{{")}
partial class {name}
{{
public int GetSyncHash()
{{
return ");

GenerateHashCode(sb, syncClassInfo.SyncMembers);

sb.Append($@"
}}
}}
{(ns is null ? null : @"}
")}");

return sb.ToString();
}

private static void GenerateHashCode(StringBuilder sb, IReadOnlyList<SyncMember> itemNames)
{
var delimiter = "";

//Debugger.Launch();

foreach (var item in itemNames)
{
sb.Append(delimiter);
if (item.Type == "Boolean")
{
sb.Append($"Sync.HashBool({item.Name})");
}
else if(item.Type == "WPos")
{
sb.Append($"{item.Name}.GetHashCode()");
}
else if (item.Type == "CPos")
{
sb.Append($"{item.Name}.Bits");
}
else if (item.Type == "WAngle")
{
sb.Append($"{item.Name}.GetHashCode()");
}

delimiter = " ^ ";
}

sb.Append(";");
}
}
}
65 changes: 65 additions & 0 deletions OpenRA.SourceGenerators/Sync/SyncGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace OpenRA.SourceGenerators.Sync
{
[Generator]
public class SyncGenerator : IIncrementalGenerator
{
const string SyncGenerationAttribute = "OpenRA.SyncGenerationAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{

var enumTypes = context.SyntaxProvider
.ForAttributeWithMetadataName(SyncGenerationAttribute,
CouldBeEnumerationAsync,
GetSyncClassInfo)
.Collect()
.SelectMany((classInfos, _) => classInfos.Distinct());

context.RegisterSourceOutput(enumTypes, GenerateCode);
}


private static bool CouldBeEnumerationAsync(SyntaxNode syntaxNode, CancellationToken cancellationToken)
{
return syntaxNode is ClassDeclarationSyntax classDeclaration && IsPartial(classDeclaration);
}

private SyncClassInfo GetSyncClassInfo(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken)
{
var type = (INamedTypeSymbol)context.TargetSymbol;
var enumInfo = new SyncClassInfo(type);



return enumInfo;
}

public static bool IsPartial(ClassDeclarationSyntax classDeclaration)
{
return classDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword));
}

private static void GenerateCode(SourceProductionContext context, SyncClassInfo classInfo)
{

var ns = classInfo.Namespace is null ? null : $"{classInfo.Namespace}.";
var code = SyncCodeGenerator.Generate(classInfo);

if (!String.IsNullOrWhiteSpace(code))
context.AddSource($"{ns}{classInfo.Name}.g.cs", code);

}

}

}

Check failure on line 65 in OpenRA.SourceGenerators/Sync/SyncGenerator.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

0 comments on commit 388a331

Please sign in to comment.