Reviewed-on: https://git.boracik.pl/boracik/MiraExporter/pulls/1
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
- Download
TownOfUsStatsExporter.dll - Copy to
Among Us/BepInEx/plugins/ - Start the game once to generate
ApiSet.ini - Edit the configuration file (see Configuration section)
- 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):
- Game directory (where the DLL is located)
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
- Plugin Loading: The plugin initializes when BepInEx loads all mods
- Reflection Bridge: Caches metadata for TOU Mira's public classes
- Harmony Patch: Patches
EndGameManager.Startwith low priority (runs AFTER TOU Mira) - Data Collection: Uses reflection to access:
EndGamePatches.EndGameData.PlayerRecordsGameHistory.RoleHistoryGameHistory.PlayerStatsGameHistory.KilledPlayersGameHistory.WinningFaction
- Data Transformation: Converts reflected data to export format
- 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=trueinApiSet.ini- Game mode (Hide & Seek is skipped)
- Check BepInEx logs for errors
API Errors
Check:
ApiTokenis correctApiEndpointURL is correct and accessibleSecretmatches 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