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}; }}");
+ }
}
}