From fdbe66f990522e9cd5cde9cfd118c9504125b53a Mon Sep 17 00:00:00 2001 From: isMADAO <1024codemadao@gmail.com> Date: Sun, 26 Apr 2026 06:14:46 +0800 Subject: [PATCH 1/2] feat(codegen): GameEventsGenerator add FieldOverrides --- src/CS2/Generators/GameEventsGenerator.cs | 55 ++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/src/CS2/Generators/GameEventsGenerator.cs b/src/CS2/Generators/GameEventsGenerator.cs index 75c11c4..40ae89a 100644 --- a/src/CS2/Generators/GameEventsGenerator.cs +++ b/src/CS2/Generators/GameEventsGenerator.cs @@ -29,6 +29,22 @@ public class GameEvents : BaseGenerator { "ehandle", ("nint", "Ptr", true, null) } }; + /// + /// Force a specific C# type for a given event field, overriding the type declared in the .gameevents file. + /// + private static readonly Dictionary<(string Event, string Field), (string CsType, string Accessor, bool CanSet, string? CastKind)> FieldOverrides = new() + { + { ("player_hurt", "health"), ("int", "Int", true, null) }, + { ("player_hurt", "armor"), ("int", "Int", true, null) }, + { ("player_hurt", "dmg_health"), ("int", "Int", true, null) }, + { ("player_hurt", "dmg_armor"), ("int", "Int", true, null) }, + { ("player_hurt", "hitgroup"), (CS2SchemaType.HitGroup_t, "Int", true, CS2SchemaType.HitGroup_t) }, + + { ("player_death", "dmg_health"), ("int", "Int", true, null) }, + { ("player_death", "dmg_armor"), ("int", "Int", true, null) }, + { ("player_death", "hitgroup"), (CS2SchemaType.HitGroup_t, "Int", true, CS2SchemaType.HitGroup_t) }, + }; + private static readonly HashSet SkipTypes = new() { "none", "local" }; private static readonly Regex EventNameRegex = new(@"^\s*""([^""]+)"""); @@ -362,7 +378,7 @@ private void GenerateInterface(GameEventDef ev, string outputDir) }); var filePath = Path.Combine(outputDir, $"{typeName}.cs"); - File.WriteAllText(filePath, writer.GetCode() + "\n"); + File.WriteAllText(filePath, writer.GetCode("\r\n") + "\r\n"); } private void GenerateClass(GameEventDef ev, string outputDir) @@ -393,7 +409,7 @@ private void GenerateClass(GameEventDef ev, string outputDir) }); var filePath = Path.Combine(outputDir, $"{typeName}Impl.cs"); - File.WriteAllText(filePath, writer.GetCode() + "\n"); + File.WriteAllText(filePath, writer.GetCode("\r\n") + "\r\n"); } private void RenderHeaderComment(CodeWriter writer, GameEventDef ev) @@ -421,8 +437,21 @@ private void RenderInterfaceFields(CodeWriter writer, GameEventDef ev) continue; } - if (!TypeMap.TryGetValue(ftype, out var typeInfo)) + (string CsType, string Accessor, bool CanSet, string? CastKind) typeInfo; + var overridden = false; + if (FieldOverrides.TryGetValue((ev.Name, fname), out var ov)) + { + typeInfo = ov; + overridden = true; + } + else if (TypeMap.TryGetValue(ftype, out var defaultInfo)) + { + typeInfo = defaultInfo; + } + else + { continue; + } var (csType, _, canSet, _) = typeInfo; var propName = GetUniquePropName(ToPropertyName(fname), usedPropNames); @@ -434,7 +463,7 @@ private void RenderInterfaceFields(CodeWriter writer, GameEventDef ev) writer.AddLine($"/// {fdef.Comment}"); writer.AddLine("///
"); } - writer.AddLine($"/// type: {ftype}"); + writer.AddLine(overridden ? $"/// type: {ftype} (overridden to {csType})" : $"/// type: {ftype}"); writer.AddLine("/// "); writer.AddLine($"{csType} {propName} {{ get; {(canSet ? "set; " : "")}}}"); } @@ -500,8 +529,19 @@ private void RenderClassFields(CodeWriter writer, GameEventDef ev) continue; } - if (!TypeMap.TryGetValue(ftype, out var typeInfo)) + (string CsType, string Accessor, bool CanSet, string? CastKind) typeInfo; + if (FieldOverrides.TryGetValue((ev.Name, fname), out var ov)) + { + typeInfo = ov; + } + else if (TypeMap.TryGetValue(ftype, out var defaultInfo)) + { + typeInfo = defaultInfo; + } + else + { continue; + } var (csType, accessor, canSet, castKind) = typeInfo; var propName = GetUniquePropName(ToPropertyName(fname), usedPropNames); @@ -564,6 +604,7 @@ private void RenderClassPlayerField(CodeWriter writer, string fname, EventField "Bool" => ($"Accessor.GetBool(\"{fname}\")", $"Accessor.SetBool(\"{fname}\", value)"), "Int" when castKind == "byte" => ($"(byte)Accessor.GetInt32(\"{fname}\")", $"Accessor.SetInt32(\"{fname}\", value)"), "Int" when castKind == "short" => ($"(short)Accessor.GetInt32(\"{fname}\")", $"Accessor.SetInt32(\"{fname}\", value)"), + "Int" when castKind != null => ($"({castKind})Accessor.GetInt32(\"{fname}\")", $"Accessor.SetInt32(\"{fname}\", (int)value)"), "Int" => ($"Accessor.GetInt32(\"{fname}\")", $"Accessor.SetInt32(\"{fname}\", value)"), "Uint64" => ($"Accessor.GetUInt64(\"{fname}\")", $"Accessor.SetUInt64(\"{fname}\", value)"), "Float" => ($"Accessor.GetFloat(\"{fname}\")", $"Accessor.SetFloat(\"{fname}\", value)"), @@ -674,6 +715,10 @@ public EventField(string name, string typeName, string comment) } } +internal static class CS2SchemaType { + public const string HitGroup_t = "HitGroup_t"; +} + internal class GameEventDef { public string Name { get; } From f8a26ce39565d6e5d92ae0da098b4ccd97a340c2 Mon Sep 17 00:00:00 2001 From: isMADAO <1024codemadao@gmail.com> Date: Sun, 26 Apr 2026 19:32:37 +0800 Subject: [PATCH 2/2] update(codegen): keep original property as obsolete, add Actual* for overrides --- src/CS2/Generators/GameEventsGenerator.cs | 77 ++++++++++++++--------- 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/src/CS2/Generators/GameEventsGenerator.cs b/src/CS2/Generators/GameEventsGenerator.cs index 40ae89a..2f34131 100644 --- a/src/CS2/Generators/GameEventsGenerator.cs +++ b/src/CS2/Generators/GameEventsGenerator.cs @@ -437,24 +437,12 @@ private void RenderInterfaceFields(CodeWriter writer, GameEventDef ev) continue; } - (string CsType, string Accessor, bool CanSet, string? CastKind) typeInfo; - var overridden = false; - if (FieldOverrides.TryGetValue((ev.Name, fname), out var ov)) - { - typeInfo = ov; - overridden = true; - } - else if (TypeMap.TryGetValue(ftype, out var defaultInfo)) - { - typeInfo = defaultInfo; - } - else - { + if (!TypeMap.TryGetValue(ftype, out var defaultInfo)) continue; - } - var (csType, _, canSet, _) = typeInfo; + var (csType, _, canSet, _) = defaultInfo; var propName = GetUniquePropName(ToPropertyName(fname), usedPropNames); + var hasOverride = FieldOverrides.TryGetValue((ev.Name, fname), out var ov); writer.AddLine(); writer.AddLine("/// "); @@ -463,9 +451,33 @@ private void RenderInterfaceFields(CodeWriter writer, GameEventDef ev) writer.AddLine($"/// {fdef.Comment}"); writer.AddLine("///
"); } - writer.AddLine(overridden ? $"/// type: {ftype} (overridden to {csType})" : $"/// type: {ftype}"); + writer.AddLine($"/// type: {ftype}"); + if (hasOverride) + { + writer.AddLine("///
"); + writer.AddLine($"/// See ."); + } writer.AddLine("///
"); + if (hasOverride) + writer.AddLine($"[Obsolete(\"The declared type may not match the actual value. Use Actual{propName} instead.\")]"); writer.AddLine($"{csType} {propName} {{ get; {(canSet ? "set; " : "")}}}"); + + if (hasOverride) + { + var (ovCsType, _, ovCanSet, _) = ov; + var actualName = GetUniquePropName($"Actual{propName}", usedPropNames); + + writer.AddLine(); + writer.AddLine("/// "); + if (!string.IsNullOrEmpty(fdef.Comment)) + { + writer.AddLine($"/// {fdef.Comment}"); + writer.AddLine("///
"); + } + writer.AddLine($"/// type: {ovCsType}"); + writer.AddLine("///
"); + writer.AddLine($"{ovCsType} {actualName} {{ get; {(ovCanSet ? "set; " : "")}}}"); + } } } @@ -529,24 +541,13 @@ private void RenderClassFields(CodeWriter writer, GameEventDef ev) continue; } - (string CsType, string Accessor, bool CanSet, string? CastKind) typeInfo; - if (FieldOverrides.TryGetValue((ev.Name, fname), out var ov)) - { - typeInfo = ov; - } - else if (TypeMap.TryGetValue(ftype, out var defaultInfo)) - { - typeInfo = defaultInfo; - } - else - { + if (!TypeMap.TryGetValue(ftype, out var defaultInfo)) continue; - } - var (csType, accessor, canSet, castKind) = typeInfo; + var (csType, accessor, canSet, castKind) = defaultInfo; var propName = GetUniquePropName(ToPropertyName(fname), usedPropNames); - var (getter, setter) = BuildAccessors(fname, accessor, castKind); + var hasOverride = FieldOverrides.TryGetValue((ev.Name, fname), out var ov); writer.AddLine(); if (!string.IsNullOrEmpty(fdef.Comment)) @@ -556,6 +557,22 @@ private void RenderClassFields(CodeWriter writer, GameEventDef ev) writer.AddLine($"{{ get => {getter}; set => {setter}; }}"); else writer.AddLine($"{{ get => {getter}; }}"); + + if (hasOverride) + { + var (ovCsType, ovAccessor, ovCanSet, ovCastKind) = ov; + var actualName = GetUniquePropName($"Actual{propName}", usedPropNames); + var (ovGetter, ovSetter) = BuildAccessors(fname, ovAccessor, ovCastKind); + + writer.AddLine(); + if (!string.IsNullOrEmpty(fdef.Comment)) + writer.AddLine($"// {fdef.Comment}"); + writer.AddLine($"public {ovCsType} {actualName}"); + if (ovCanSet && ovSetter != null) + writer.AddLine($"{{ get => {ovGetter}; set => {ovSetter}; }}"); + else + writer.AddLine($"{{ get => {ovGetter}; }}"); + } } }