CS2OpenDev.Sdk
1.0.1
dotnet add package CS2OpenDev.Sdk --version 1.0.1
NuGet\Install-Package CS2OpenDev.Sdk -Version 1.0.1
<PackageReference Include="CS2OpenDev.Sdk" Version="1.0.1" />
<PackageVersion Include="CS2OpenDev.Sdk" Version="1.0.1" />
<PackageReference Include="CS2OpenDev.Sdk" />
paket add CS2OpenDev.Sdk --version 1.0.1
#r "nuget: CS2OpenDev.Sdk, 1.0.1"
#:package CS2OpenDev.Sdk@1.0.1
#addin nuget:?package=CS2OpenDev.Sdk&version=1.0.1
#tool nuget:?package=CS2OpenDev.Sdk&version=1.0.1
CS2OpenDev.Sdk
A strongly-typed C# SDK for the Counter-Strike 2 schema system. Every class, struct, and enum that CS2 reflects through its schema runtime is exposed as a C# type, with the original C++ field names and byte offsets preserved as attributes for native interop work.
The SDK is built from the community-enriched CS2OpenDev-Docs mirror of the DumpSource2 schema, pulled in via the upstream/ git submodule. The committed source under src/CS2OpenDev.Sdk/ is the canonical artifact — every file is auto-generated, but the regeneration pipeline lives in this repo so contributors can review what changed when CS2 patches the schema.
Using the SDK
Add the NuGet package to your project:
<PackageReference Include="CS2OpenDev.Sdk" Version="1.0.0" />
Then reference types out of the per-module namespaces. The root namespace is CS2OpenSchema; each schema module gets a child namespace (CS2OpenSchema.Client, CS2OpenSchema.Server, CS2OpenSchema.Common, etc.).
using CS2OpenSchema.Client;
CCSPlayerPawn pawn = new()
{
Health = 100,
ArmorValue = 50,
};
Native-interop attributes
Every property carries metadata that lets you bridge from the managed projection back to the native layout:
public partial class CCSPlayerPawn : CCSPlayerPawnBase
{
[NativeOffset(0x83C)]
[NativeName("m_iArmor")]
public int ArmorValue { get; set; }
// …
}
| Attribute | Where | What it carries |
|---|---|---|
[NativeName("…")] |
classes, properties, enum members | The original C++ identifier as it appears in cs2_schema.json. |
[NativeOffset(0x…)] |
properties | Byte offset of the field within its native C++ type. |
[NativeSize(N)] |
classes | Informational size in bytes of the native type. Not a P/Invoke marshalling contract — the managed layout isn't required to match. |
[NativeMetadata("Key", "Value")] |
properties, enum members | Round-trips schema markers (MPropertyFriendlyName, MNotSaved, MNetworkVar, …) so downstream tooling can read them without re-parsing cs2_schema.json. |
The reverse lookup — given a generated C# property name, recover the raw C++ field name without reflection — is available via the static SchemaNames table:
string nativeName = SchemaNames.CCSPlayerPawn.ArmorValue; // "m_iArmor"
Game events
Every .gameevents entry in the upstream registry is emitted as a public sealed record under CS2OpenSchema.Events. Each property's KV1 type tag (string, short, ehandle, player_controller_and_pawn, …) is preserved via [GameEventFieldType("...")] so demo parsers and dispatchers can recover the original wire shape; the C# property type is the SDK's projection of that tag.
using CS2OpenSchema.Events;
PlayerDeathEvent death = new()
{
Userid = 7, // KV1 tag: player_controller_and_pawn — raw userid
Attacker = 12,
Weapon = "ak47",
Headshot = true,
// …every field is `required init`, so the compiler enforces completeness
};
string nativeFieldName = SchemaEvents.PlayerDeathEvent.Weapon; // "weapon"
string nativeEventName = SchemaEvents.PlayerDeathEvent.EventName; // "player_death"
Cross-file duplicates (15 events that appear in more than one source .gameevents file with different field shapes) follow source priority mod > game > core: the mod variant gets the unsuffixed type, others carry a source-name suffix (e.g. PlayerDeathEvent for the CS2 shape, PlayerDeathCoreEvent for the Source-2-base shape).
Extending generated types
Every class is emitted as public partial class. To add your own methods or properties without losing them on regeneration, declare a partial extension in a different file that doesn't start with // <auto-generated/>:
// MyProject/CCSPlayerPawn.Extensions.cs
namespace CS2OpenSchema.Client;
public partial class CCSPlayerPawn
{
public bool IsLowHealth => Health < 25;
}
The regeneration pipeline's stale-file sweep uses the // <auto-generated/> marker as the discriminator. Files without it are never touched.
Repo layout
src/
CS2OpenDev.Sdk/ — the generated SDK (4k+ files; what the NuGet ships)
Client/, Server/, Common/, … — one file per reflected class/enum, grouped by module
Events/ — one record per `.gameevents` entry (288 events)
SchemaNames.cs — reverse-lookup table: C# property → native C++ field
SchemaEvents.cs — reverse-lookup table: C# event property → native KV1 name
CS2OpenDev.Sdk.Generator/ — emitter library (consumes cs2_schema.json + gameevents_schema.json)
CS2OpenDev.Sdk.Exporter/ — CLI that drives the emitters and writes the SDK to disk
test/
CS2OpenDev.Sdk.Generator.Tests/
upstream/ — git submodule → CS2OpenDev-Docs (refreshed every 4h upstream)
docs/generated/downstream-codegen-schemas/
cs2_schema.json — entity classes/enums/fields (DumpSource2 mirror)
gameevents_schema.json — `.gameevents` registry (KV1)
The SDK targets net8.0 for broad consumer compatibility. The Generator and Exporter target net10.0.
Regenerating the SDK
Initialise the upstream submodule once after cloning:
git submodule update --init upstream
Refresh it to the latest CS2OpenDev-Docs revision (upstream updates every 4 hours) whenever you want a newer schema:
git submodule update --remote upstream
Then regenerate:
dotnet run --project src/CS2OpenDev.Sdk.Exporter
That single command builds the Generator and Exporter as needed, parses the schema, and writes the per-class layout into src/CS2OpenDev.Sdk/. Idempotent — running it twice produces no change.
Optional arguments for non-default paths (e.g. a custom dump or vendored copy):
dotnet run --project src/CS2OpenDev.Sdk.Exporter -- <schema-path> <output-dir>
The Exporter also prunes orphan generated files — classes that disappeared from the schema since the last regen — using the // <auto-generated/> first-line marker to discriminate emitter output from any hand-written partial-class extensions.
Versioning
Package versions follow SemVer 2 with build metadata identifying the upstream schema:
{Generator-MAJOR}.{Generator-MINOR}.{git-height}+cs.{cs2-revision}.dump.{yyyy-MM-dd}
| Segment | Where it comes from | Who bumps it |
|---|---|---|
MAJOR |
version.json |
Human — reserved for breaking SDK API changes |
MINOR |
version.json |
Human — new emitter features |
PATCH |
Git commit height since the last MAJOR.MINOR bump |
Automatic via Nerdbank.GitVersioning |
| Build metadata | cs2_schema.json |
Automatic from CI at pack-time |
pathFilters in version.json scopes the patch-bumping commits to src/CS2OpenDev.Sdk/ only, so every regen produces a monotonically newer version, while contributions to tests / docs / generator code that don't change the SDK content don't churn the version. Build metadata is informational — NuGet shows it in the package details, but ordering is determined by MAJOR.MINOR.PATCH alone, which is exactly what we want (every regen advances the SortKey).
Continuous integration
| Workflow | Trigger | What it does |
|---|---|---|
ci.yml |
PRs + pushes to main | Build, test, regenerate, fail if the regen output diverges from committed SDK, verify pack succeeds |
check-upstream.yml |
Cron (every 4h) + manual | Bump the upstream submodule, regenerate, push to main if anything changed, then invoke the reusable pack-and-publish flow in the same run |
release.yml |
Pushes to main that touch src/CS2OpenDev.Sdk/** or version.json + manual |
Invokes the reusable pack-and-publish flow (for human-driven version bumps and the post-merge case) |
_pack-and-publish.yml |
workflow_call from the two above |
Packs the SDK with CS2 build metadata, uploads the .nupkg as a workflow artifact, and pushes to NuGet.org (gated on NUGET_API_KEY) |
The bot identity used for automated pushes is CS2OpenDev-bot <bot@CS2OpenDev.invalid>. check-upstream.yml calls the publish workflow as a dependent job in the same run (via workflow_call) rather than relying on its push to fire release.yml — that's because GitHub blocks workflow-triggered pushes authenticated with the default GITHUB_TOKEN from triggering downstream workflows, and we'd rather skip the PAT-rotation burden.
To enable NuGet.org publishing, add a NUGET_API_KEY secret in Settings → Secrets and variables → Actions. Without it, the package is still built on every run and uploaded as a workflow artifact (downloadable from the run page) — useful for forks and during initial setup.
What ends up in the published artifact
NuGet's package filename uses only the SemVer 2 MAJOR.MINOR.PATCH segment — so 1.0.42+cs.10673343.dump.2026-05-20 is filed as CS2OpenDev.Sdk.1.0.42.nupkg. The build metadata is preserved inside the package's AssemblyInformationalVersion (visible via reflection or dotnet --info-style inspection), so consumers can recover which CS2 build their installed SDK was generated from without unpacking the nupkg.
Build commands
| Task | Command |
|---|---|
| Restore + build everything | dotnet build |
| Build the generated SDK only | dotnet build src/CS2OpenDev.Sdk |
| Run the test suite | dotnet run --project test/CS2OpenDev.Sdk.Generator.Tests/ |
| Run one test | dotnet run --project test/CS2OpenDev.Sdk.Generator.Tests/ -- --treenode-filter "/*/*/*/TestName" |
| Regenerate the SDK | dotnet run --project src/CS2OpenDev.Sdk.Exporter |
The repo uses TUnit on the Microsoft Testing Platform; invoke tests with dotnet run, not dotnet test.
Diagnostics emitted by the regen pipeline
| ID | Severity | Meaning |
|---|---|---|
CS2_GEN_001 |
Error | cs2_schema.json failed to parse. The Exporter exits with status 1. |
CS2_GEN_002 |
Warning | Both output/schemas.json and a repo-root schemas.json exist (legacy local-cache paths); the resolver chose the dump-layout copy. The new upstream submodule path is preferred when present. |
CS2_GEN_003 |
Info | A schema atomic type fell through every classification branch in TypeMapper. It was emitted as an empty stub class; adding it to the right set in TypeMapper.cs gives it a real C# projection. |
Working on the generator
If you're changing how types are named, mapped, or emitted, the architecture lives in src/CS2OpenDev.Sdk.Generator/:
Emitters/ModuleEmitter.cs— the choke point.EmitAll(IGeneratorSink, SchemaRoot, ns)orchestrates classification (per-module vsCommon), conflict propagation, per-class file emission, stub collection, andSchemaNamesemission.Emitters/ClassEmitter.cs/EnumEmitter.cs/SyntheticTypes.cs— produce one type's source. They append to aStringBuilderand return it;ModuleEmitterhands the result to the sink.Emitters/TypeMapper.cs— C++ atomic type → C# type mapping. Adding a new atomic classification branch inMapAtomicCoremust be mirrored inIsKnownAtomicNameand (if it surfaces an inner type)AtomicProjectionUsesInner/AtomicProjectionUsesInner2.Emitters/NameHelpers.cs— single source of truth for identifier/filename normalisation (Hungarian-prefix stripping, suffix handling, sanitisation).IGeneratorSink.cs— output abstraction. The Exporter'sDiskSinkwrites to the source tree; tests use aCapturingSinkthat retains output in memory.
Workflow: edit the relevant emitter, run the test suite, regenerate, review the SDK diff. The Exporter doesn't carry a parallel naming path — there's exactly one source of truth.
License
GPL-3.0 — see LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.1 | 31 | 5/22/2026 |