TableReaderV2 finally, caching pending, please migrate carefully
This commit is contained in:
parent
989e02aeaf
commit
cf82daad00
|
@ -361,3 +361,6 @@ MigrationBackup/
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# Generator logs
|
||||||
|
generator_fails*
|
|
@ -1,4 +1,5 @@
|
||||||
using AscNet.Logging;
|
using AscNet.Logging;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace AscNet.Common.Util
|
namespace AscNet.Common.Util
|
||||||
{
|
{
|
||||||
|
@ -28,5 +29,97 @@ namespace AscNet.Common.Util
|
||||||
|
|
||||||
public abstract void Load();
|
public abstract void Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface ITable
|
||||||
|
{
|
||||||
|
abstract static string File { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TableReaderV2
|
||||||
|
{
|
||||||
|
private static readonly Logger c = new(typeof(TableReaderV2), nameof(TableReaderV2), LogLevel.DEBUG, LogLevel.DEBUG);
|
||||||
|
|
||||||
|
public static List<T> Parse<T>() where T : ITable
|
||||||
|
{
|
||||||
|
List<T> result = new();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var reader = new StreamReader(T.File))
|
||||||
|
{
|
||||||
|
// Read the header line to get column names
|
||||||
|
string headerLine = reader.ReadLine()!;
|
||||||
|
string[] columnNames = headerLine.Split('\t');
|
||||||
|
|
||||||
|
// Read data lines and parse them into objects
|
||||||
|
while (!reader.EndOfStream)
|
||||||
|
{
|
||||||
|
string dataLine = reader.ReadLine()!;
|
||||||
|
if (string.IsNullOrEmpty(dataLine))
|
||||||
|
break;
|
||||||
|
|
||||||
|
string[] values = dataLine.Split('\t');
|
||||||
|
|
||||||
|
T obj = MapToObject<T>(columnNames, values);
|
||||||
|
result.Add(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
c.Error($"An error occurred: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static T MapToObject<T>(string[] columnNames, string[] values) where T : ITable
|
||||||
|
{
|
||||||
|
T obj = Activator.CreateInstance<T>();
|
||||||
|
|
||||||
|
for (int i = 0; i < Math.Min(columnNames.Length, values.Length); i++)
|
||||||
|
{
|
||||||
|
PropertyInfo? prop = typeof(T).GetProperty(columnNames[i].Split('[').First());
|
||||||
|
if (prop != null)
|
||||||
|
{
|
||||||
|
if (prop.PropertyType == typeof(List<int>))
|
||||||
|
{
|
||||||
|
if (prop.GetValue(obj) is null)
|
||||||
|
{
|
||||||
|
prop.SetValue(obj, new List<int>());
|
||||||
|
}
|
||||||
|
string value = values[i];
|
||||||
|
if (!string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
prop.PropertyType.GetMethod("Add").Invoke(prop.GetValue(obj), new object[] { int.Parse(value) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (prop.PropertyType == typeof(List<string>))
|
||||||
|
{
|
||||||
|
if (prop.GetValue(obj) is null)
|
||||||
|
{
|
||||||
|
prop.SetValue(obj, new List<string>());
|
||||||
|
}
|
||||||
|
string value = values[i];
|
||||||
|
if (!string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
prop.PropertyType.GetMethod("Add").Invoke(prop.GetValue(obj), new object[] { value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (prop.PropertyType == typeof(int))
|
||||||
|
{
|
||||||
|
// For int properties like GroupId
|
||||||
|
prop.SetValue(obj, int.Parse(values[i]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prop.SetValue(obj, values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" PrivateAssets="all" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" GeneratePathProperty="true" PrivateAssets="all" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" GeneratePathProperty="true" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||||
|
|
||||||
<!-- Package the Newtonsoft.Json dependency alongside the generator assembly -->
|
<!-- Package the Newtonsoft.Json dependency alongside the generator assembly -->
|
||||||
<None Include="$(PkgNewtonsoft_Json)\lib\netstandard2.0\*.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
<None Include="$(PkgNewtonsoft_Json)\lib\netstandard2.0\*.dll" Pack="true" PackagePath="/lib/netstandard2.0/" Visible="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -43,7 +43,7 @@ namespace AscNet.Table
|
||||||
}
|
}
|
||||||
types.Add("global::System.String");
|
types.Add("global::System.String");
|
||||||
}
|
}
|
||||||
resolvedTypes.Add(types.ToArray());
|
resolvedTypes.Add([.. types]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Dictionary<string, string> properties = new();
|
Dictionary<string, string> properties = new();
|
||||||
|
@ -152,11 +152,13 @@ namespace AscNet.Table{ns}
|
||||||
{
|
{
|
||||||
fails.Add(new
|
fails.Add(new
|
||||||
{
|
{
|
||||||
msg = ex.Message,
|
msg = ex.ToString(),
|
||||||
file = table.Path
|
file = table.Path
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// File.WriteAllText("./generator_fails.json", JsonConvert.SerializeObject(fails, Formatting.Indented));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(GeneratorInitializationContext context) { }
|
public void Initialize(GeneratorInitializationContext context) { }
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
// using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace AscNet.Table
|
||||||
|
{
|
||||||
|
[Generator]
|
||||||
|
public class TableGeneratorV2 : ISourceGenerator
|
||||||
|
{
|
||||||
|
public void Execute(GeneratorExecutionContext context)
|
||||||
|
{
|
||||||
|
List<object> fails = new();
|
||||||
|
foreach (var table in context.AdditionalFiles.Where(x => x.Path.EndsWith(".tsv")))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string ns = string.Join("", Path.GetDirectoryName(table.Path)?.Split(new string[] { "table" }, StringSplitOptions.None).Skip(1)!)
|
||||||
|
.Replace('\\', '/').Replace('/', '.');
|
||||||
|
|
||||||
|
string content = (table.GetText()?.ToString()) ?? throw new FormatException("File is empty!");
|
||||||
|
List<string> lines = content.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||||
|
|
||||||
|
List<MemberType> members = new();
|
||||||
|
List<MemberType> membersRealCount = new();
|
||||||
|
string headLine = lines.First();
|
||||||
|
lines.RemoveAt(0);
|
||||||
|
|
||||||
|
foreach (var head in headLine.Split('\t'))
|
||||||
|
{
|
||||||
|
if (head.Contains('['))
|
||||||
|
{
|
||||||
|
if (members.Any(x => x.Name == head.Split('[').First()))
|
||||||
|
{
|
||||||
|
MemberType memberType = members.First(x => x.Name == head.Split('[').First());
|
||||||
|
memberType.Span++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
members.Add(new MemberType
|
||||||
|
{
|
||||||
|
Name = head.Split('[').First(),
|
||||||
|
Nullable = false,
|
||||||
|
Span = 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
members.Add(new MemberType
|
||||||
|
{
|
||||||
|
Name = head,
|
||||||
|
Nullable = false,
|
||||||
|
Span = 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
membersRealCount.Add(members.First(x => x.Name == head.Split('[').First()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int x = 0; x < lines.Count; x++)
|
||||||
|
{
|
||||||
|
string[] values = lines[x].Split('\t');
|
||||||
|
|
||||||
|
for (int y = 0; y < values.Length; y++)
|
||||||
|
{
|
||||||
|
string value = values[y];
|
||||||
|
MemberType memberType = membersRealCount[y];
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
memberType.Nullable = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (int.TryParse(value, out int intVal))
|
||||||
|
{
|
||||||
|
if (memberType.Type != typeof(string).FullName)
|
||||||
|
memberType.Type = typeof(int).FullName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memberType.Type = typeof(string).FullName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
membersRealCount[y] = memberType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string propDefs = string.Empty;
|
||||||
|
foreach (MemberType memberType in membersRealCount.GroupBy(x => x.Name).Select(g => g.First()))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(memberType.Type) || string.IsNullOrEmpty(memberType.Name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (membersRealCount.Where(x => x.Name == memberType.Name).Count() > 1)
|
||||||
|
{
|
||||||
|
propDefs += $"public List<{memberType.Type}> {memberType.Name} {{ get; set; }}\r\n\t";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
propDefs += $"public {memberType.Type + (memberType.Nullable ? "?" : string.Empty)} {memberType.Name} {{ get; set; }}\r\n\t";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string file = $@"// <auto-generated/>
|
||||||
|
namespace AscNet.Table.V2{ns}
|
||||||
|
{{
|
||||||
|
#nullable enable
|
||||||
|
#pragma warning disable CS8618, CS8602 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||||
|
public class {Path.GetFileNameWithoutExtension(table.Path)}Table : global::AscNet.Common.Util.ITable
|
||||||
|
{{
|
||||||
|
public static string File => ""{string.Join("", table.Path.Replace("\\", "/").Split(new string[] { "/Resources/" }, StringSplitOptions.None).Skip(1))}"";
|
||||||
|
|
||||||
|
{propDefs}}}
|
||||||
|
}}";
|
||||||
|
|
||||||
|
context.AddSource($"V2{ns}.{Path.GetFileNameWithoutExtension(table.Path)}V2.g.cs", file);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
fails.Add(new
|
||||||
|
{
|
||||||
|
msg = ex.ToString(),
|
||||||
|
file = table.Path
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// File.WriteAllText("./generator_fails_new.json", JsonConvert.SerializeObject(fails, Formatting.Indented));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize(GeneratorInitializationContext context) { }
|
||||||
|
|
||||||
|
struct MemberType
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public bool Nullable { get; set; }
|
||||||
|
public int Span { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,8 @@
|
||||||
using AscNet.GameServer.Handlers;
|
using AscNet.GameServer.Handlers;
|
||||||
using AscNet.GameServer.Commands;
|
using AscNet.GameServer.Commands;
|
||||||
using AscNet.Logging;
|
using AscNet.Logging;
|
||||||
|
using AscNet.Common.Util;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace AscNet
|
namespace AscNet
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue