Bartosz Gradzik 03d4673c00 Fix data export formatting and implement robust modifier detection
Major improvements to data cleaning, modifier extraction, and role name handling:

## Data Cleaning & Normalization
- Enhanced StripColorTags() to remove ALL HTML/Unity tags using universal regex
- Added removal of modifier symbols (♥, #, ★, etc.) from player names
- Fixed winningTeam containing HTML tags by applying StripColorTags()
- Result: Clean player names without tags or symbols in exported JSON

## Modifier Detection System
- Implemented three-tier modifier detection:
  1. Symbol extraction from <size=60%>SYMBOL</size> tags (primary)
  2. Reflection-based GetModifiers<GameModifier>() (secondary)
  3. RoleString parsing fallback (tertiary)
- Fixed false positive Underdog detection from friend codes (username#1234)
- Symbols now extracted ONLY from size tags, not entire player name
- Fixed Dictionary duplicate key error (removed \u2665 duplicate of ♥)
- Supported symbols: ♥❤♡ (Lovers), # (Underdog), ★☆ (VIP), @ (Sleuth), etc.

## Role Name Handling
- Added fallback for roles without GetRoleName() method
- Extracts role names from type names: "GrenadierRole" → "Grenadier"
- Removes "Tou" suffix: "EngineerTou" → "Engineer"
- Skips generic "RoleBehaviour" type (not a real role)
- Result: Full role history now populated correctly

## Code Quality
- Added comprehensive logging for debugging modifier/role extraction
- Enhanced error handling with per-player try-catch blocks
- Improved code documentation with inline comments

## Documentation
- Added CLAUDE.md with complete architecture guide
- Documented modifier detection system
- Added data cleaning & normalization section
- Included troubleshooting guide and development workflows

Fixes: #1 (HTML tags in export), #2 (missing role history), #3 (false modifier detection)

🎮 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-08 21:42:55 +02:00
2025-10-08 01:39:13 +02:00
2025-10-08 01:39:13 +02:00
2025-10-08 01:39:13 +02:00
2025-10-08 01:39:13 +02:00
2025-10-08 01:39:13 +02:00
2025-10-07 23:35:49 +00:00
2025-10-08 01:39:13 +02:00
2025-10-08 01:39:13 +02:00
2025-10-08 01:39:13 +02:00
2025-10-08 01:39:13 +02:00

TownOfUs Stats Exporter

A standalone BepInEx plugin that exports Town of Us Mira game statistics to a cloud API.

Overview

This plugin is 100% standalone and works alongside Town of Us Mira without requiring any modifications to the main mod. It uses reflection to access public classes and Harmony patches to hook into the game's event flow.

Features

  • Zero modifications to TOU Mira code
  • Reflection-based access to public game data
  • Harmony patches for event integration
  • Completely optional for users
  • Can be installed/removed without rebuilding TOU Mira
  • Exports comprehensive game statistics including:
    • Player roles and role changes (e.g., Amnesiac remembering)
    • Player modifiers
    • Kill statistics (correct/incorrect kills, assassin kills)
    • Task completion data
    • Game results and winning team
    • Player platforms and friend codes

Installation

For Users

  1. Download TownOfUsStatsExporter.dll
  2. Copy to Among Us/BepInEx/plugins/
  3. Start the game once to generate ApiSet.ini
  4. Edit the configuration file (see Configuration section)
  5. Restart the game

For Developers

Building the project:

cd TownOfUsStatsExporter
dotnet build -c Release

The compiled DLL will be in bin/Release/TownOfUsStatsExporter.dll

Configuration

The plugin looks for ApiSet.ini in two locations (in order):

  1. Game directory (where the DLL is located)
  2. Documents/TownOfUs/ApiSet.ini

Configuration File Format

# TownOfUs Stats Exporter Configuration

# Whether to enable API export (true/false)
EnableApiExport=true

# API Authentication Token
ApiToken=your_secret_token_here

# API Endpoint URL
ApiEndpoint=https://api.example.com/api/among-data

# Whether to save local backup copies (true/false)
SaveLocalBackup=true

# Additional secret/password for API authentication
Secret=your_secret_key_here

Local Backups

When SaveLocalBackup=true, game statistics are saved to:

Documents/TownOfUs/GameLogs/Game_YYYYMMDD_HHMMSS_<gameId>.json

Exported Data Format

The plugin exports data in JSON format with the following structure:

{
  "token": "your_api_token",
  "secret": "your_secret",
  "gameInfo": {
    "gameId": "unique-game-id",
    "timestamp": "2025-10-07T20:30:00Z",
    "lobbyCode": "ABCDEF",
    "gameMode": "Normal",
    "duration": 450.5,
    "map": "The Skeld"
  },
  "players": [
    {
      "playerId": 0,
      "playerName": "PlayerName",
      "playerTag": "PlayerName#1234",
      "platform": "Steam",
      "role": "Sheriff",
      "roles": ["Sheriff"],
      "modifiers": ["Giant"],
      "isWinner": true,
      "stats": {
        "totalTasks": 5,
        "tasksCompleted": 5,
        "kills": 2,
        "correctKills": 2,
        "incorrectKills": 0,
        "correctAssassinKills": 0,
        "incorrectAssassinKills": 0
      }
    }
  ],
  "gameResult": {
    "winningTeam": "Crewmates"
  }
}

Version Compatibility

Tested Versions

  • TOU Mira 1.2.1
  • TOU Mira 1.2.0

Probably Compatible

  • ⚠️ TOU Mira 1.3.x (same major version)

The plugin will log compatibility warnings if used with an untested version.

Architecture

TownOfUsStatsExporter.dll
├── TownOfUsStatsPlugin.cs          # BepInEx plugin entry point
├── Patches/
│   └── EndGameExportPatch.cs       # Harmony patch (low priority)
├── Reflection/
│   ├── TouMiraReflectionBridge.cs  # Main reflection interface
│   ├── ReflectionCache.cs          # Cached reflection metadata
│   ├── VersionCompatibility.cs     # Version checking
│   └── IL2CPPHelper.cs             # IL2CPP type conversions
├── Export/
│   ├── StatsExporter.cs            # Main export orchestrator
│   ├── DataTransformer.cs          # Transform TOU data to export format
│   └── ApiClient.cs                # HTTP client for API
├── Config/
│   ├── ApiConfigManager.cs         # INI file reader
│   └── ApiConfig.cs                # Config model
└── Models/
    ├── GameStatsData.cs            # Export data models
    └── ReflectedData.cs            # DTOs for reflected data

How It Works

  1. Plugin Loading: The plugin initializes when BepInEx loads all mods
  2. Reflection Bridge: Caches metadata for TOU Mira's public classes
  3. Harmony Patch: Patches EndGameManager.Start with low priority (runs AFTER TOU Mira)
  4. Data Collection: Uses reflection to access:
    • EndGamePatches.EndGameData.PlayerRecords
    • GameHistory.RoleHistory
    • GameHistory.PlayerStats
    • GameHistory.KilledPlayers
    • GameHistory.WinningFaction
  5. Data Transformation: Converts reflected data to export format
  6. Export: Sends to API and/or saves local backup (async, doesn't block UI)

Performance Impact

Operation Time Impact
Data Collection (reflection) ~22ms Negligible
Export (async) ~1500ms 0ms UI block

The export runs asynchronously and doesn't block the game UI.

Troubleshooting

Plugin Not Loading

Check:

  • DLL is in Among Us/BepInEx/plugins/
  • BepInEx is installed correctly
  • Check BepInEx console (F10) for error messages

"Failed to initialize reflection bridge"

Solutions:

  • Update to a compatible TOU Mira version
  • Check that TOU Mira is actually loaded
  • Update stats exporter to latest version

No Data Exported

Check:

  • EnableApiExport=true in ApiSet.ini
  • Game mode (Hide & Seek is skipped)
  • Check BepInEx logs for errors

API Errors

Check:

  • ApiToken is correct
  • ApiEndpoint URL is correct and accessible
  • Secret matches server configuration

Limitations

What Works

  • Access to public classes and properties
  • Role history tracking (including role changes)
  • Player modifiers
  • Kill statistics
  • Task completion data
  • JSON export and API transmission

What Doesn't Work

  • Access to internal/private members
  • Direct type safety (uses reflection)
  • Compile-time type checking
  • Guaranteed compatibility across major version changes

Development Notes

The plugin uses C# reflection extensively, which is ~100x slower than direct access. However:

  • Reflection metadata is cached for performance
  • Export runs asynchronously (no UI blocking)
  • Total overhead per game is <2 seconds

License

Same as Town of Us Mira

Credits

Built for the Town of Us Mira community

Description
No description provided
Readme MIT 169 KiB
2025-10-10 21:00:48 +00:00
Languages
C# 100%