240 lines
6.4 KiB
Markdown
240 lines
6.4 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```ini
|
|
# 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:
|
|
|
|
```json
|
|
{
|
|
"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
|