Skip to content

Commit 22684c8

Browse files
committed
将升级器同步到全局,现在可以继承 BackwardCompatible{?}MetadataJsonConverter 来获取统一的升级器功能
1 parent c79585c commit 22684c8

8 files changed

Lines changed: 314 additions & 255 deletions

File tree

README.zh-cn.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ RhythmBase.Rizline ← Rizline 适配
4242

4343
**支持的关卡格式**
4444

45-
| 游戏 | 单文件 | 多文件目录 | 压缩包 | JSON 读写 |
46-
| ---------- | ---------- | -------------------------------------- | --------------- | ------------------ |
47-
| 节奏医生 | `.rdlevel` | - | `.rdzip` `.zip` | :white_check_mark: |
48-
| 冰与火之舞 | `.adofai` | - | `.zip` | :white_check_mark: |
49-
| BeatBlock | - | `manifest.json` + `level.json` + chart | `.bbz` `.zip` | - |
50-
| Rizline | - | `metadata.json` + chart | `.rlz` `.zip` | - |
45+
| 游戏 | 单文件 | 多文件目录 | 压缩包 | JSON 读写 | 时间解析 | 历史版本适配 |
46+
| ---------- | ---------- | -------------------------------------- | --------------- | ------------------ | ------------------ | ------------------ |
47+
| 节奏医生 | `.rdlevel` | - | `.rdzip` `.zip` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
48+
| 冰与火之舞 | `.adofai` | - | `.zip` | :white_check_mark: | | |
49+
| BeatBlock | - | `manifest.json` + `level.json` + chart | `.zip` | :white_check_mark: | | |
50+
| Rizline | - | `metadata.json` + chart | `.zip` | - | | |
5151

5252
## 特别感谢
5353

RhythmBase.BeatBlock/BeatBlock/Components/Metadata.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public class Chart :
142142
/// <summary>
143143
/// Gets or sets the difficulty of the chart.
144144
/// </summary>
145-
public float Difficulty { get; set; }
145+
public int Difficulty { get; set; }
146146
/// <summary>
147147
/// Gets or sets the display name of the chart.
148148
/// </summary>

RhythmBase.BeatBlock/BeatBlock/Converters/ManifestConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ internal class ManifestConverter : MetadataJsonConverter<Level>
127127
if (reader.ValueTextEquals("charter"u8) && reader.Read())
128128
variant.Charter = reader.GetString() ?? "";
129129
else if (reader.ValueTextEquals("difficulty"u8) && reader.Read())
130-
variant.Difficulty = reader.GetSingle();
130+
variant.Difficulty = (int)reader.GetSingle();
131131
else if (reader.ValueTextEquals("display"u8) && reader.Read())
132132
variant.Display = reader.GetString() ?? "";
133133
else if (reader.ValueTextEquals("extra"u8) && reader.Read())

RhythmBase.BeatBlock/BeatBlock/Converters/MemberConverter.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@
44

55
namespace RhythmBase.BeatBlock.Converters;
66

7-
internal class BaseEventConverter : MetadataJsonConverter<IBaseEvent>
7+
internal class BaseEventConverter : BackwardCompatibleMetadataJsonConverter
88
{
9+
protected override void InitializeUpgraters()
10+
{
11+
}
912
public override bool CanConvert(Type typeToConvert)
1013
{
1114
return Type.IsAssignableFrom(typeToConvert);
1215
}
1316
public override IBaseEvent? Read(ref Utf8JsonReader reader, Type typeToConvert, MetadataJsonSerializerOptions options)
1417
{
1518
JsonException.ThrowIfNotMatch(ref reader, JsonTokenType.StartObject);
16-
string? type = null;
1719

1820
Utf8JsonReader checkpoint = reader;
1921
while (reader.Read())
@@ -25,19 +27,19 @@ public override bool CanConvert(Type typeToConvert)
2527
if (reader.ValueTextEquals("type"u8))
2628
{
2729
reader.Read();
28-
type = reader.GetString();
2930
break;
3031
}
3132
else
3233
reader.Skip();
3334
}
3435
}
35-
reader = checkpoint; IBaseEvent e;
36-
if (!EnumConverter.TryParse(type, out EventType typeEnum))
37-
e = ReadForwardEvent(ref reader) ?? new ForwardEvent() { ActualType = type ?? "" };
36+
IBaseEvent e;
37+
if (!EnumConverter.TryParse(ref reader, out EventType typeEnum))
38+
e = ReadForwardEvent(ref checkpoint) ?? new ForwardEvent() { ActualType = reader.GetString() ?? "" };
3839
else
39-
e = EventConverterMap.GetConverter(typeEnum).ReadProperties(ref reader, options);
40-
JsonException.ThrowIfNotMatch(ref reader, JsonTokenType.EndObject);
40+
e = EventConverterMap.GetConverter(typeEnum).ReadProperties(ref checkpoint, options);
41+
JsonException.ThrowIfNotMatch(ref checkpoint, JsonTokenType.EndObject);
42+
reader = checkpoint;
4143
return e;
4244
}
4345
public static Events.IForwardEvent? ReadForwardEvent(ref Utf8JsonReader reader)

RhythmBase.Generator/ConverterGenerator.Helpers.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,7 +1066,7 @@ protected override void Write(Utf8JsonWriter writer, ref {{t.ToDisplayString()}}
10661066
}
10671067
private class EventTypeRegistryGenerationInfo
10681068
{
1069-
public INamedTypeSymbol ClassType { get; set; }
1069+
public INamedTypeSymbol RootClassType { get; set; }
10701070
public INamedTypeSymbol ClassTypeEnum { get; set; }
10711071
public IEnumerable<INamedTypeSymbol> Classes { get; set; }
10721072
public Dictionary<INamedTypeSymbol, HashSet<ISymbol>> EventTypeRegistry { get; set; }
@@ -1096,9 +1096,9 @@ public static partial class EventTypeRegistry
10961096
""");
10971097
for (int i = 0; i < infos.Length; i++)
10981098
{
1099-
string indexPostfix = infos.Length > 1 ? $"_{i}" : "";
1100-
string enumPostfix = infos.Length > 1 ? $"_{infos[i].ClassTypeEnum.Name}" : "";
1101-
string classPostfix = infos.Length > 1 ? $"_{infos[i].ClassType.Name}" : "";
1099+
string indexPostfix = infos.Length > 1 ? $"{i}" : "";
1100+
string enumPostfix = infos.Length > 1 ? $"{infos[i].ClassTypeEnum.Name}" : "";
1101+
string classPostfix = infos.Length > 1 ? $"{infos[i].RootClassType.Name}" : "";
11021102
EventTypeRegistryGenerationInfo? info = infos[i];
11031103
int maxTypeStrLength = 100;
11041104
int maxEnumStrLength = 100;
@@ -1179,7 +1179,7 @@ public static partial class EventTypeRegistry
11791179
/// </summary>
11801180
/// <typeparam name="TEvent">The generic event type to convert.</typeparam>
11811181
/// <returns>The corresponding <see cref="{{info.ClassTypeEnum.ToDisplayString()}}" /> enumeration.</returns>
1182-
public static {{info.ClassTypeEnum.ToDisplayString()}} ToEnum{{enumPostfix}}<TEvent>() where TEvent : {{info.ClassType.ToDisplayString()}}, new() => ToEnum{{enumPostfix}}(typeof(TEvent));
1182+
public static {{info.ClassTypeEnum.ToDisplayString()}} ToEnum{{enumPostfix}}<TEvent>() where TEvent : {{info.RootClassType.ToDisplayString()}}, new() => ToEnum{{enumPostfix}}(typeof(TEvent));
11831183
/// <summary>
11841184
/// Converts a type to an array of corresponding <see cref="{{info.ClassTypeEnum.ToDisplayString()}}" /> enumerations.
11851185
/// </summary>
@@ -1195,7 +1195,7 @@ public static partial class EventTypeRegistry
11951195
/// </summary>
11961196
/// <typeparam name="TEvent">The generic event type to convert.</typeparam>
11971197
/// <returns>An array of corresponding <see cref="{{info.ClassTypeEnum.ToDisplayString()}}" /> enumerations.</returns>
1198-
public static ReadOnlyEnumCollection<{{info.ClassTypeEnum.ToDisplayString()}}> ToEnums{{enumPostfix}}<TEvent>() where TEvent : {{info.ClassType.ToDisplayString()}} => ToEnums{{enumPostfix}}(typeof(TEvent));
1198+
public static ReadOnlyEnumCollection<{{info.ClassTypeEnum.ToDisplayString()}}> ToEnums{{enumPostfix}}<TEvent>() where TEvent : {{info.RootClassType.ToDisplayString()}} => ToEnums{{enumPostfix}}(typeof(TEvent));
11991199
/// <summary>
12001200
/// Converts an <see cref="{{info.ClassTypeEnum.ToDisplayString()}}" /> enumeration to its corresponding Type.
12011201
/// </summary>
@@ -1205,7 +1205,7 @@ public static partial class EventTypeRegistry
12051205
public static Type ToType(this {{info.ClassTypeEnum.ToDisplayString()}} type)
12061206
{
12071207
if (_enum2type{{indexPostfix}} == null)
1208-
return Type.GetType($"{{info.ClassType.ContainingNamespace.ToDisplayString()}}.{type}") ?? throw new InvalidOperationException(
1208+
return Type.GetType($"{{info.RootClassType.ContainingNamespace.ToDisplayString()}}.{type}") ?? throw new InvalidOperationException(
12091209
$"Illegal Type: {type}.");
12101210
if (_enum2type{{indexPostfix}}.TryGetValue(type, out Type t))
12111211
return t;
@@ -1234,7 +1234,7 @@ public static Type ToType(string type)
12341234
""");
12351235
}
12361236
sb.AppendLine($$"""
1237-
return typeof({{(infos.Length == 1 ? (infos[0].FallbackClassType?.ToDisplayString() ?? infos[0].ClassType.ToDisplayString()) : "object")}});
1237+
return typeof({{(infos.Length == 1 ? (infos[0].FallbackClassType?.ToDisplayString() ?? infos[0].RootClassType.ToDisplayString()) : "object")}});
12381238
}
12391239
""");
12401240
}
@@ -1261,7 +1261,7 @@ internal static void Initialize()
12611261
""");
12621262
for (int i = 0; i < infos.Length; i++)
12631263
{
1264-
string enumPostfix = infos.Length > 1 ? $"_{infos[i].ClassTypeEnum.Name}" : "";
1264+
string enumPostfix = infos.Length > 1 ? $"{infos[i].ClassTypeEnum.Name}" : "";
12651265
string enumType = infos[i].ClassTypeEnum.ToDisplayString();
12661266
sb.AppendLine($$"""
12671267
try

RhythmBase.Generator/ConverterGenerator.cs

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.CodeAnalysis;
22
using Microsoft.CodeAnalysis.CSharp;
33
using Microsoft.CodeAnalysis.CSharp.Syntax;
4+
using System.Text;
45

56
// 这坨写得太史了
67

@@ -289,7 +290,7 @@ var errors
289290
property?.Name ?? subClass.Name,
290291
property?.ContainingType?.ToDisplayString() ?? subClass.ContainingType?.ToDisplayString()));
291292
}
292-
if(fallbackType is not null)
293+
if (fallbackType is not null)
293294
{
294295
var property = fallbackType.GetMembers().FirstOrDefault(m => m.Kind == SymbolKind.Property && m.Name == enumPropertyName);
295296
if (property is IPropertySymbol propSymbol)
@@ -322,7 +323,7 @@ var errors
322323
}
323324
typesToGenerate.Add(new()
324325
{
325-
ClassType = classGen.RootType,
326+
RootClassType = classGen.RootType,
326327
ClassTypeEnum = enumType,
327328
FallbackClassType = fallbackType,
328329
FallbackClassTypeEnum = fallbackEnumMember,
@@ -537,6 +538,7 @@ static IEnumerable<INamedTypeSymbol> GetAllTypes(INamespaceSymbol namespaceSymbo
537538
GenerateEventTypeRegistry(context, registryInfo.Combine(context.CompilationProvider).Combine(EventTypeRegistryInfo), errors);
538539
GenerateEnumConverter(context, registryInfo);
539540
GenerateOtherFiles(context, registryInfo);
541+
GenerateUpgrater(context, registryInfo.Combine(EventTypeRegistryInfo));
540542

541543
}
542544
private const string CoreNs = "Global";
@@ -625,6 +627,90 @@ public static void SerializeMainEntry<T>(T mainEntry, Stream stream, RhythmBase.
625627
context.AddSource($"FileMainEntryConverter.{registryId}.g.cs", src);
626628
});
627629
}
630+
private static void GenerateUpgrater(IncrementalGeneratorInitializationContext cxt, IncrementalValueProvider<(string? Left, EventTypeRegistryGenerationInfo[] Right)> incrementalValueProvider)
631+
{
632+
cxt.RegisterSourceOutput(incrementalValueProvider, (source, value) =>
633+
{
634+
(string? registryId, EventTypeRegistryGenerationInfo[]? gens) = value;
635+
if (string.IsNullOrEmpty(registryId))
636+
return;
637+
bool multiple = gens?.Length > 1;
638+
StringBuilder sb = new();
639+
sb.AppendLine($"namespace RhythmBase.{registryId}.Converters;");
640+
foreach (var info in gens ?? [])
641+
{
642+
string mtpName = multiple ? $"{info.RootClassType.Name}" : "";
643+
string src = $$"""
644+
/// <summary>
645+
/// A JSON converter for <see cref="{{info.RootClassType.ToDisplayString()}}"/> that uses metadata-aware serializer options.
646+
/// </summary>
647+
internal abstract class BackwardCompatible{{mtpName}}MetadataJsonConverter : RhythmBase.Global.Converters.MetadataJsonConverter<{{info.RootClassType.ToDisplayString()}}>
648+
{
649+
protected class Upgrater
650+
{
651+
internal int MaxVersion { get; init; }
652+
internal required Action<{{info.RootClassType.ToDisplayString()}}> UpgrateFunc { get; init; }
653+
internal required {{info.ClassTypeEnum.ToDisplayString()}} Type { get; init; }
654+
}
655+
private readonly List<Upgrater> _upgraters = [];
656+
private readonly EnumCollection<{{info.ClassTypeEnum.ToDisplayString()}}> _typeHasUpgrater = [];
657+
private int _maxVersion;
658+
/// <summary>
659+
/// The maximum version that this converter can upgrade.
660+
/// </summary>
661+
internal int MaxVersion => _maxVersion;
662+
/// <summary>
663+
/// The types of events that this converter can upgrade.
664+
/// </summary>
665+
internal EnumCollection<{{info.ClassTypeEnum.ToDisplayString()}}> TypeHasUpgrater => _typeHasUpgrater;
666+
/// <summary>
667+
/// Registers an upgrader for a specific event type and version.
668+
/// </summary>
669+
/// <typeparam name="T">The type of the event to upgrade.</typeparam>
670+
/// <param name="version">
671+
/// The version for which to register the upgrader.
672+
/// Versions <b>equal to or lower than</b> this will be affected by this upgrader.
673+
/// </param>
674+
/// <param name="upgrateAction">The action to perform when upgrading the event.</param>
675+
protected void Register<T>(int version, Action<{{info.RootClassType.ToDisplayString()}}> upgrateAction) where T : {{info.RootClassType.ToDisplayString()}}, new()
676+
{
677+
var type = EventTypeRegistry.ToEnum{{(multiple ? info.ClassTypeEnum.Name : "")}}<T>();
678+
_maxVersion = int.Max(_maxVersion, version);
679+
_typeHasUpgrater.Add(type);
680+
_upgraters.Add(new Upgrater()
681+
{
682+
MaxVersion = version,
683+
Type = type,
684+
UpgrateFunc = upgrateAction
685+
});
686+
}
687+
/// <summary>
688+
/// Upgrades the specified event to the latest version if an upgrader is registered for its type and version.
689+
/// </summary>
690+
/// <param name="version">The version of the event to upgrade.</param>
691+
/// <param name="type">The type of the event to upgrade.</param>
692+
/// <returns>An enumerable of upgraders that can upgrade the event.</returns>
693+
protected IEnumerable<Upgrater> GetUpgraters(int version, {{info.ClassTypeEnum.ToDisplayString()}} type)
694+
{
695+
foreach (Upgrater upgrater in _upgraters)
696+
if (upgrater.Type == type && upgrater.MaxVersion >= version)
697+
yield return upgrater;
698+
}
699+
internal BackwardCompatible{{mtpName}}MetadataJsonConverter()
700+
{
701+
InitializeUpgraters();
702+
}
703+
/// <summary>
704+
/// Initializes the upgraders for this converter. This method is called once when the converter is first used.
705+
/// </summary>
706+
protected abstract void InitializeUpgraters();
707+
}
708+
""";
709+
sb.AppendLine(src);
710+
}
711+
source.AddSource($"Upgrader.{registryId}.g.cs", sb.ToString());
712+
});
713+
}
628714

629715
private static ClassGenCvtrInfo GetClassGenCvtrInfo(Compilation compilation, INamedTypeSymbol? enumType, string enumPropertyName, INamedTypeSymbol? symbol)
630716
{

0 commit comments

Comments
 (0)