Skip to content

Latest commit

 

History

History
554 lines (458 loc) · 31.2 KB

File metadata and controls

554 lines (458 loc) · 31.2 KB

CLAUDE.md — GLMod

IMPORTANT — Living document

This file is the source of truth for the technical and functional specification of the GLMod project. It MUST be consulted before any modification of the project (new feature, refactor, bug fix, dependency bump, etc.). It MUST be kept up to date whenever the project evolves:

  • Addition / removal / rename of a service, entity, enum, or constant.
  • Change of the targeted Among Us version, .NET, BepInEx, or any dependency.
  • Change of the Good Loss API contract (endpoints, payloads, RPC codes).
  • Change of the folder structure or naming conventions.
  • Addition or modification of a Harmony patch.
  • Any change to the game flow (GameStep, validation steps, RPC).

If a PR introduces a change that is not reflected here, the PR MUST be considered incomplete.


0. Language rule (MANDATORY)

The entire project is written and maintained in English. No exceptions.

This rule applies to:

  • Source code: identifiers (classes, methods, fields, properties, variables, parameters), file names, namespaces.
  • Comments: single-line (//), multi-line (/* */), and XML doc comments (///).
  • Log messages sent via GLMod.log(...), _logger.LogInfo(...), or any logging facility.
  • String literals used for diagnostics, errors, and developer-facing messages.
  • Commit messages, branch names, PR titles, and PR descriptions.
  • Documentation: this CLAUDE.md, README.md, docs/dev.MD, and any other repository documentation.
  • Configuration keys in BepInEx config files.

Exceptions

  • Auto-generated files: GLMod/Properties/Resources.Designer.cs is regenerated by Visual Studio in the developer's UI culture. Do not hand-edit; if it must be regenerated, do so on a machine configured with English UI.

Enforcement

Any code review MUST reject contributions that introduce non-English identifiers, comments, or developer-facing strings. When updating existing code, opportunistically translate any leftover non-English content you encounter.


1. Project overview

1.1 Identity

1.2 Purpose

GLMod is an open source mod for Among Us that collects match data and uploads it to the Good Loss platform in order to:

  • Provide a complete match history per player.
  • Compute and display detailed statistics (kills, exiles, votes, tasks, etc.).
  • Maintain a ranking system (ranks) per mod.
  • Enable third-party integration: other Among Us mods can override GLMod to record their custom roles and actions.

1.3 Supported platforms

  • Game OS: Windows and Linux (via Proton/Wine).
  • Platform: Steam only (identification is based on SteamID). No other platform (Epic, mobile, console) is supported.
  • Targeted Among Us version: see <GameVersion> in GLMod/GLMod.csproj (currently 2026.3.31). The README documents the downloadable release version (must be synchronized with every release).
  • Runtime: .NET 6.0 (net6.0).

1.4 Main runtime dependencies

  • BepInEx (IL2CPP) — Plugin injection framework for Unity IL2CPP.
  • HarmonyLib — Runtime patching of Among Us methods.
  • Il2CppInterop.Runtime — Managed C# ↔ IL2CPP bridge.
  • Steamworks.NET — Reads the local SteamID.
  • Hazel — Among Us internal network RPC system.
  • UnityEngine — Coroutines, GameObjects, MonoBehaviour.
  • System.Text.Json — Serialization / deserialization of API payloads.

The required DLLs are referenced via wildcard in the csproj:

<Reference Include="..\Among Us\BepInEx\core\*.dll" />
<Reference Include="..\Among Us\BepInEx\interop\*.dll" />

2. Architecture

2.1 Architectural pattern

GLMod follows a service-oriented architecture with:

  • Interfaces in GLMod/Services/Interfaces/ (contracts).
  • Implementations in GLMod/Services/Implementations/ (logic).
  • Manual dependency injection in GLMod.Load() (GLMod/GLMod.cs).
  • Static access from the outside via the static properties of the GLMod class (e.g. GLMod.AuthService, GLMod.GameStateManager).

2.2 Lifecycle

  1. BasePlugin.Load() is invoked by BepInEx when the game starts.
  2. InitializeConfiguration() — binds the BepInEx ConfigEntry<string> instances (BepInEx/config/glmod.cfg).
  3. InitializeServices() — instantiates the services (order matters, see dependencies below).
  4. VerifyStartupServices() — logs the status of each service.
  5. ConfigureDefaultSettings() — enables the default services (see §3.3).
  6. CoroutineRunner.Init() — creates a persistent DontDestroyOnLoad GameObject to run coroutines from outside MonoBehaviours.
  7. Harmony.PatchAll() — applies all Harmony patches from the GLMod.EventPatch namespace.

2.3 Service dependency graph

AuthenticationService     ← ManualLogSource, ConfigEntry<string> connectionState
ConfigurationService      ← ManualLogSource, configPath
GameStateManager          ← Logger, AuthService, ConfigService, apiEndpoint, stepRpc
ServiceManager            ← (none)
ItemService               ← Logger, AuthService, apiEndpoint
RankService               ← Logger, AuthService, ConfigService, apiEndpoint
IntegrityService          ← Logger, apiEndpoint
MapService                ← Logger

2.4 Coroutines & async

  • API calls use Unity coroutines (IEnumerator) to remain compatible with the Unity main thread.
  • The bridge between Task-based async work and coroutines is centralized in GLMod/Class/CoroutineHelpers.cs (RunAsync<T> / RunAsync). It encapsulates the Task.Run + volatile done flag + yield return null pattern. Do not re-implement this pattern elsewhere.
  • See GLMod/Class/ApiService.cs (PostFormAsync, PostFormWithErrorHandlingAsync) for typical usage.
  • IIntegrityService additionally exposes async/await variants (*Async) for consumers running outside a Unity context.
  • CoroutineRunner (GLMod/Class/CoroutineRunner.cs) is a MonoBehaviour singleton registered in IL2CPP via ClassInjector.RegisterTypeInIl2Cpp.

3. Services

Every service is exposed statically via the main GLMod class and has a dedicated interface.

3.1 IAuthenticationServiceInterfaces/IAuthenticationService.cs

  • Role: handle Steam → Good Loss authentication.
  • Endpoint: POST {API}/user/login with steamId.
  • HTTP codes:
    • 200 → token returned in response.Content.
    • 403 + body prefixed "Banned: " → user is banned, reason follows the prefix.
    • Others → silent failure.
  • Exposed state: Token, IsLoggedIn, IsBanned, BanReason.

3.2 IServiceManagerInterfaces/IServiceManager.cs

  • Role: enable / disable data collectors (see enum ServiceType).
  • Services enabled by default at startup (ConfigureDefaultSettings):
    • StartGame, EndGame, Tasks, TasksMax, Exiled, Kills,
    • BodyReported, Emergencies, Turns, Votes, Roles, Shield.
  • A third-party mod can disable a service to override its behaviour (see docs/dev.MD).

3.3 IGameStateManagerInterfaces/IGameStateManager.cs

  • Role: state machine of the match lifecycle + uploads game data to the API.
  • Properties: CurrentGame (GLGame), Step (GameStep), GameCode, GameMap.
  • Key methods:
    • StartGame(code, map, ranked) — initializes a GLGame.
    • SetRanked(ranked) — updates the ranked flag on the current game (bool setter; stored as "0"/"1").
    • AddPlayer(name, role, team, color) — adds a player to the current match.
    • SendGame(onComplete) — uploads the match to the API (coroutine).
    • SyncGameId(onComplete) — broadcasts the match ID to non-hosts via RPC.
    • AddMyPlayer(onComplete) — registers the local player on the server.
    • GetShieldPlayer(onComplete, onError) — returns the player who would have benefited from a T1 shield (retries on HTTP 400).
    • SetWinnerTeams(winners) / AddWinnerPlayer(name) — sets the outcome.
    • EndGame() — closes the match server-side.
    • AddAction(source, target, action) — records an action (kill, vote, sabotage, custom…).
    • ResetGame() — resets the state (Step = Initial).

3.4 IItemServiceInterfaces/IItemService.cs

  • Role: fetch unlocked items (achievements) and owned Steam DLCs.
  • Methods: ReloadItems(), IsUnlocked(id), ReloadDlcOwnerships(), HasDlc(appId).

3.5 IRankServiceInterfaces/IRankService.cs

  • Role: fetch a player rank for a given mod.
  • Method: GetRank(modName, onComplete)modName = null ⇒ uses the current mod.

3.6 IIntegrityServiceInterfaces/IIntegrityService.cs

  • Role: verify DLL integrity via remote checksum.
  • Endpoints: POST {API}/data, POST {API}/checksum.
  • Methods:
    • Coroutines: GetApiData, GetChecksum, VerifyDll, VerifyGLMod.
    • Async: GetApiDataAsync, GetChecksumAsync, VerifyDllAsync, VerifyGLModAsync.

3.7 IConfigurationServiceInterfaces/IConfigurationService.cs

  • Role: determine the host mod name.
  • Logic: FindModName() scans BepInEx/config/*.glmod. If no .glmod file is found, ModName = "Vanilla".
  • Manual override: SetModName(modName) (used by third-party mods in their Load()).

3.8 IMapServiceInterfaces/IMapService.cs

  • Role: resolve the current map name.
  • Logic: reads GameOptionsManager.Instance.currentGameOptions.MapId, maps it via GameMapType + special case Dleks → "dlekSehT".

4. Domain entities (GLMod/GLEntities/)

Class Role
GLGame Match model: id, code, map, modName, ranked, winner, turns, players, actions.
GLPlayer Player model: login, playerName, role, team, tasks, tasksDead, tasksMax, win, positions, color.
GLAction Collected action: turn, source, target, action, triggerTimeMs.
GLPosition Spatial position (x, y) at a given time for a player, indexed by turn.
GLItem Unlocked item: id, name.
GLRank Rank: id, name, link, percent, error.
GLData / GLDataList Generic key/value wrappers for API responses.
GLJson Static helpers Serialize<T> / Deserialize<T> (System.Text.Json) with try/catch + log.

Note: every numeric property transiting through the API is encoded as string (tasks = "0", ranked = "1", etc.). This convention matches the Good Loss API protocol and must not be changed without coordination with the API team.


5. Enums (GLMod/Enums/)

ServiceTypeEnums/ServiceType.cs

StartGame, EndGame, Tasks, TasksMax, Exiled, Kills,
BodyReported, Emergencies, Turns, Votes, Roles, Shield

GameStepEnums/GameStep.cs

Initial (0) → PlayersAdded (1) → GameSent (2) → GameIdSynced (3)
            → PlayersRecorded (4) → WinnerSet (5)

GameMapTypeEnums/GameMapType.cs

Unknown, TheSkeld (0), MiraHQ (1), Polus (2), Airship (4), TheFungle (5)

Special case: Dleks → display "dlekSehT".

SabotageTypeEnums/SabotageType.cs

Reactor, Coms, Lights, O2

6. Constants (GLMod/Constants/GameConstants.cs)

Constant Value
API_ENDPOINT https://goodloss.fr/api
SUPPORT_ID_CHARS A-Za-z1-9 (no 0)
SUPPORT_ID_LENGTH 10
RPC_SYNC_TIMEOUT 5.0f seconds
BACKGROUND_POLLING_INTERVAL 0.5f seconds
RPC_POLLING_INTERVAL 0.1f seconds
DEFAULT_GAME_CODE XXXXXX
DEFAULT_MAP_NAME Unknown
ACTION_PREFIX_DC_PROCESS DC_PROCESS_ (background-detected DC)
ACTION_PREFIX_DC_INTERNAL DC_INTERNAL_ (RPC DisconnectInternal)
ACTION_PREFIX_DC_HANDLE DC_HANDLE_ (RPC HandleDisconnect)
ACTION_PREFIX_DC_ON DC_ON_ (RPC OnDisconnect)
ACTION_PREFIX_SAB_START SAB_START_ (suffixed by sabotage type)
ACTION_PREFIX_SAB_END SAB_END_ (suffixed by sabotage type)

7. Harmony patches (GLMod/EventPatch/)

Patches are applied automatically via Harmony.PatchAll() at the end of Load(). Each file contains classes decorated with [HarmonyPatch(...)].

File Main targets
AmongUsClientPatch.cs Hooks on the Among Us network client (host/non-host).
InnerNetClientPatch.cs RPC reception.
MainMenuManagerPatch.cs Post-main-menu initialization.
PlayerControlPatch.cs Player actions (kill, report, vote, tasks…).
MeetingHudPatch.cs Meeting cycle (vote, exile).
ExileControllerPatch.cs Exile animation and turn rollover.
RPC.cs Manages CustomRPC 240 (HandleRpc). Contains GLRPCProcedure.makeRpcCall and handleRpc.

7.1 Custom RPC

  • Reserved ID: 240 (CustomRPC.HandleRpc).
  • Payload format: int id, int count, then count × string value.
  • Known RPC cases (see RPC.cs):
    • 1: non-hosts receive the match GameId.
    • 2: DisconnectInternal → builds reason DC_INTERNAL_<value>.
    • 3: HandleDisconnect → builds reason DC_HANDLE_<value>.
    • 4: OnDisconnect → builds reason DC_ON_<value>.
    • (additional cases must be documented as they are added — update this table).

Any new RPC ID must be reserved here before implementation to prevent collisions.


8. Utility classes (GLMod/Class/)

File Role
HttpHelper.cs Shared static HttpClient, proxy disabled.
ApiService.cs PostFormAsync, PostFormWithErrorHandlingAsync, ApiResponse type.
CoroutineHelpers.cs RunAsync / RunAsync<T> — async-task ↔ coroutine bridge. Use these instead of re-implementing the Task.Run + volatile done flag pattern.
CoroutineRunner.cs MonoBehaviour IL2CPP singleton used to run coroutines from static classes.
ServiceLogger.cs Log(logger, serviceName, message) — shared log formatter producing [GLMod][Service] PlayerName: message. Every service must delegate to it.
BackgroundEvents.cs Background loop: watches DCs, hidden sabotages, etc.
VanillaEvents.cs Default implementations (startGameVanilla, endGameVanilla…) used when the matching ServiceType is enabled.

9. BepInEx configuration (BepInEx/config/glmod.cfg)

Section Key Description
GoodLoss Connected Persisted connection state.
GoodLoss Enabled "Yes" / "No" — global mod toggle.
GoodLoss Support Id 10-character support id, generated once.
Validation steps Must be "YES" after a complete match.
Validation RPC Must be "YES" (with history) after a complete match.

A file BepInEx/config/<MODNAME>.glmod (empty) at the root of the config folder serves as a host-mod marker: its name (without extension) becomes ConfigService.ModName.


10. Good Loss API — Endpoints used

Base: https://goodloss.fr/api

Endpoint Method Service Usage
/user/login POST AuthService Steam login.
/user/steamownerships POST ItemService Steam DLC ownerships.
/data POST IntegrityService Generic data lookup by id.
/checksum POST IntegrityService Checksum retrieval.
/game/start POST GameStateManager Sends a new match.
/game/addMyPlayer POST GameStateManager Registers the local player.
/game/getShieldPlayer POST GameStateManager Returns the T1 shield candidate.
/game/end POST GameStateManager Finalizes a match.
/player/challengerItems POST ItemService Player's unlocked items.
/player/rank POST RankService Player rank by mod.

Keep up to date: every endpoint added to or removed from the project must be reflected in this table.


11. Build & CI

11.1 Local build

  • Recommended IDE: Visual Studio 2022.
  • SDK: .NET 6.0.
  • dotnet build --configuration Debug at the repo root.
  • dotnet test GLMod.Tests/GLMod.Tests.csproj --configuration Debug to run the unit-test suite.
  • An AfterTargets="Build" step copies GLMod.dll to Among Us/BepInEx/plugins/ (Debug only) — see GLMod/GLMod.csproj.

11.2 CI

  • GitHub Actions: .github/workflows/main.yml.
  • Trigger: push & PR on main.
  • Single job: dotnet restore + dotnet build --configuration Debug.
  • Unit tests (xUnit, in GLMod.Tests/) are runnable locally; the CI workflow does not invoke dotnet test yet.

11.4 Testing

  • Test framework: xUnit (GLMod.Tests/GLMod.Tests.csproj).
  • Scope: pure logic — entities (GLGame, GLPlayer, GLPosition), enums (SabotageType, GameMapType), ServiceManager, and GameConstants invariants. Anything that touches IL2CPP / Among Us types stays untestable until further decoupling.
  • Add new tests for any new pure-logic surface. Refactor toward injectable abstractions before adding tests that would otherwise require IL2CPP mocking.

11.3 Release

  • Build driven by build.cake (Cake).
  • Version is derived from the Git tag (refs/tags/X.Y.Z) or a CI suffix.
  • Iterates over GamePlatform = Steam | Itch (multi-build).

12. Project structure (must be followed strictly)

GLMod/                                # Repo root
├── .editorconfig                     # Style rules (IDE0002 disabled).
├── .github/workflows/main.yml        # .NET 6 CI pipeline.
├── .gitignore                        # The "Among Us/" folder is ignored.
├── build.cake                        # Multi-platform release build script.
├── CLAUDE.md                         # ← THIS FILE. Living project spec.
├── GLMod.sln                         # Visual Studio solution.
├── LICENSE                           # GPLv3.
├── README.md                         # End-user documentation.
├── docs/
│   └── dev.MD                        # Integration guide for third-party modders.
├── Among Us/                         # (gitignored) Local install used for debugging.
│   └── BepInEx/                      # Core + interop DLLs (referenced by csproj).
├── GLMod.Tests/                      # xUnit test project for pure-logic types.
│   ├── GLMod.Tests.csproj
│   ├── EntityTests.cs
│   ├── EnumTests.cs
│   ├── GameConstantsTests.cs
│   └── ServiceManagerTests.cs
└── GLMod/                            # C# project.
    ├── GLMod.csproj                  # Project definition, mod version, targeted Among Us version.
    ├── GLMod.cs                      # Plugin entry point + static facade.
    ├── Class/                        # Cross-cutting helpers (non-services).
    │   ├── ApiService.cs
    │   ├── BackgroundEvents.cs
    │   ├── CoroutineHelpers.cs       # Async-task ↔ coroutine bridge.
    │   ├── CoroutineRunner.cs
    │   ├── HttpHelper.cs
    │   ├── ServiceLogger.cs          # Shared [GLMod][Service] log formatter.
    │   └── VanillaEvents.cs
    ├── Constants/
    │   └── GameConstants.cs          # Global constants (API, timeouts, defaults).
    ├── Enums/
    │   ├── GameMapType.cs            # + GameMapTypeExtensions.
    │   ├── GameStep.cs
    │   ├── SabotageType.cs           # + SabotageTypeExtensions.
    │   └── ServiceType.cs
    ├── EventPatch/                   # Harmony patches — one file per patched Among Us target.
    │   ├── AmongUsClientPatch.cs
    │   ├── ExileControllerPatch.cs
    │   ├── InnerNetClientPatch.cs
    │   ├── MainMenuManagerPatch.cs
    │   ├── MeetingHudPatch.cs
    │   ├── PlayerControlPatch.cs
    │   └── RPC.cs                    # CustomRPC + handleRpc dispatcher.
    ├── GLEntities/                   # Domain entities (API DTOs + models).
    │   ├── GLAction.cs
    │   ├── GLData.cs
    │   ├── GLDataList.cs
    │   ├── GLGame.cs
    │   ├── GLItem.cs
    │   ├── GLJson.cs                 # Serialize / Deserialize helpers.
    │   ├── GLPlayer.cs
    │   ├── GLPosition.cs
    │   └── GLRank.cs
    ├── Properties/
    │   ├── Resources.Designer.cs     # Auto-generated by Visual Studio — DO NOT hand-edit.
    │   └── Resources.resx
    └── Services/                     # Service-oriented architecture.
        ├── Interfaces/               # ONE file per interface, prefixed with "I".
        │   ├── IAuthenticationService.cs
        │   ├── IConfigurationService.cs
        │   ├── IGameStateManager.cs
        │   ├── IIntegrityService.cs
        │   ├── IItemService.cs
        │   ├── IMapService.cs
        │   ├── IRankService.cs
        │   └── IServiceManager.cs
        └── Implementations/          # ONE file per implementation, same name without "I".
            ├── AuthenticationService.cs
            ├── ConfigurationService.cs
            ├── GameStateManager.cs
            ├── IntegrityService.cs
            ├── ItemService.cs
            ├── MapService.cs
            ├── RankService.cs
            └── ServiceManager.cs

12.1 Structure rules (NON-NEGOTIABLE)

  1. One service = one interface + one implementation, in their respective folders.
  2. Naming:
    • Interfaces: I<Name>Service or I<Name>Manager.
    • Implementations: <Name>Service / <Name>Manager (no I).
    • File = public class name (PascalCase.cs).
  3. Namespaces:
    • GLMod — static facade.
    • GLMod.Class — utilities.
    • GLMod.Constants — constants.
    • GLMod.Enums — enumerations (+ extensions in the same file).
    • GLMod.EventPatch — Harmony patches.
    • GLMod.GLEntities — DTOs / models.
    • GLMod.Services.Interfaces — contracts.
    • GLMod.Services.Implementations — logic.
  4. No business logic in GLMod.cs — only bootstrap + a static facade over the services. Legacy methods (StartGame, AddPlayer, etc.) are nothing more than wrappers delegating to the services.
  5. No direct access to HttpClient from a service — go through ApiService or HttpHelper.Client.
  6. No new HttpClient instance — reuse the static HttpHelper instance.
  7. Every coroutine must be launched via CoroutineRunner.Run(...) unless we are already inside an IL2CPP MonoBehaviour.
  8. Every new Harmony patch goes into GLMod/EventPatch/ with one file per patched Among Us class.
  9. Every new enum goes into GLMod/Enums/, with its <Enum>Extensions extensions class in the same file when applicable.
  10. Every constant goes into GLMod/Constants/GameConstants.cs — no magic strings/numbers scattered around.
  11. Every entity serialized to/from the API goes into GLMod/GLEntities/, with string properties (see §4).

12.2 Code style

  • Indentation: 4 spaces (default C# / VS).
  • Braces: Allman style (brace on its own line).
  • var: allowed for obvious types.
  • XML doc comments (/// <summary>): mandatory on every public method and property of a service interface.
  • Logging: inside a service, expose private void Log(string message) => ServiceLogger.Log(_logger, nameof(<Service>), message); and call Log(...) everywhere. The shared formatter (GLMod/Class/ServiceLogger.cs) produces [GLMod][ServiceName] PlayerName: message. Outside of services, use GLMod.log(...).
  • Exception handling: try/catch around every call into the game / network / parser, log the exception, never silently rethrow in a way that would crash the game.
  • Language: all identifiers, comments, and developer-facing strings MUST be in English (see §0).

13. Third-party integration (summary — details in docs/dev.MD)

A third-party mod consumes GLMod by:

  1. Adding GLMod.dll to BepInEx/plugins/.
  2. Creating an empty file BepInEx/config/<MOD_NAME>.glmod.
  3. Calling GLMod.ConfigService.SetModName("MOD_NAME") in its Load().
  4. (Optional) Disabling the default services it wants to override: GLMod.ServiceManager.DisableService(ServiceType.StartGame) etc.
  5. (Optional) Overriding the match lifecycle:
    • GLMod.StartGame(code, map, ranked)
    • GLMod.AddPlayer(name, role, team) for each player
    • GLMod.SendGame() + GLMod.AddMyPlayer() to validate
    • GLMod.SetWinnerTeams(...) + GLMod.AddWinnerPlayer(...) + GLMod.EndGame() at the end
  6. Adding custom actions: GLMod.addAction(source, target, action).

RPC 240 is reserved for GLMod. No third-party mod must use it.


14. Collected data

Any change in the collection surface must be reflected both in the README AND in this section.

14.1 Match data

  • State (Started / Finished), Code (disabled in transmission), Map, Start date, Duration, Mod used, Player count.

14.2 Player data

  • Name (PseudoInGame), Good Loss account (if connected), Role, Team (Crewmate / Impostor / Neutral / other), Tasks alive, Tasks dead, Tasks max, Win (0/1), Color, Positions.

14.3 Actions

  • source, target, action, turn, triggerTimeMs.
  • Default vanilla actions: kills, exiles, reports, emergencies, votes, shapeshifts, unshapeshifts, tracks, untracks, shields.
  • Any new default action must be listed here.

15. Security & privacy

  • Identifier: SteamID (only transmitted to goodloss.fr for authentication).
  • No password: login = SteamID → Good Loss token.
  • token.txt, output, server are gitignored (see .gitignore).
  • Ban: handled server-side, HTTP 403 response + "Banned: <reason>".
  • Integrity: IntegrityService can verify a DLL's SHA against a remote checksum to detect mod tampering.

16. Modification workflow (must be followed)

For every change to the project:

  1. Read CLAUDE.md first. Identify the affected section.
  2. Apply the change following the structure in §12.
  3. Update CLAUDE.md:
    • New service → §3 + §12.
    • New entity → §4.
    • New enum → §5.
    • New constant → §6.
    • New patch / new RPC → §7 (and table §7.1 for RPCs).
    • New API endpoint → §10.
    • Version bump (GLMod, GameVersion, .NET, BepInEx) → §1.1 and §1.3.
    • New ServiceType enabled by default → §3.2 and §14.
  4. Update README.md if the user-facing surface changes.
  5. Update docs/dev.MD if the third-party integration surface changes.
  6. Update the version in three places that MUST stay in sync:
  7. Make sure the change respects the English-only rule (§0) — code, comments, logs, docs.
  8. Commit + PR.

17. Known pitfalls / Points of attention

  • GLMod.Version vs csproj <Version> vs README badge: the three must be identical. Any drift breaks display on the Good Loss side or misleads users about the installed version. The README badge URL is https://img.shields.io/badge/GLMod-vX.Y.Z-blue — update the X.Y.Z segment on every version bump.
  • csproj <GameVersion> vs README: must point to the same Among Us version — a build against the wrong Among Us version will crash because of incompatible IL2CPP signatures.
  • PlayerControl.LocalPlayer may be null at startup (pre-menu). Always use ?. (see the pattern in every Log(...)).
  • IL2CPP: any new MonoBehaviour must be registered with ClassInjector.RegisterTypeInIl2Cpp<T>() before instantiation.
  • CurrentGame may be null between EndGame() and the next StartGame(). Always check before access.
  • Coroutines & threads: never touch the Unity API from a Task.Run — only from the main thread (the done flag + yield return null pattern is there for that).
  • The Among Us/ folder is gitignored — every developer installs their own copy. Never commit this folder.
  • Auto-generated Resources.Designer.cs: regenerated by Visual Studio in the developer's UI culture and is the only file allowed to contain non-English content. Do not hand-edit; regenerate from a machine with an English UI if needed.

Last updated: 2026-05-28 — GLMod version 5.4.0. Every change to the project must be accompanied by an update of this file.