Files
rr3-apk/ASSET_DOWNLOAD_SYSTEM.md

16 KiB

🎨 RR3 Asset Download System - Community Server Support

Overview

Real Racing 3 downloads game assets (cars, tracks, textures, etc.) from EA's CDN after installation. We can redirect these downloads to community servers for game preservation.

🔍 How RR3 Asset Downloads Work

Director Pattern

When the game starts, it calls the /director endpoint which returns URLs for various services:

{
  "serverUrls": {
    "synergy.account": "http://your-server:5001/synergy/account",
    "synergy.commerce": "http://your-server:5001/synergy/commerce",
    "synergy.content": "http://your-server:5001/synergy/content",  // <-- ASSETS!
    "synergy.rewards": "http://your-server:5001/synergy/rewards",
    "synergy.progression": "http://your-server:5001/synergy/progression"
  }
}

Key Service: synergy.content

This service handles all asset downloads:

  • Car models (.pak files)
  • Track data
  • Textures
  • Audio files
  • Updates

URL Structure

From SynergyEnvironmentImpl.java, we can see:

// Line 109-116
public String getServerUrlWithKey(String str) {
    // Returns URL for service key like "synergy.content"
    return environmentDataContainer.getServerUrlWithKey(str);
}

The game asks for URLs by key, and the Director response tells it where to download content.

🎯 Solution: Community Asset Server

Phase 1: Intercept Asset Requests

Update DirectorController.cs to include content service:

[HttpGet("director")]
public IActionResult GetDirector()
{
    var baseUrl = $"{Request.Scheme}://{Request.Host}";
    
    return Ok(new
    {
        serverUrls = new Dictionary<string, string>
        {
            ["synergy.account"] = $"{baseUrl}/synergy/account",
            ["synergy.commerce"] = $"{baseUrl}/synergy/commerce",
            ["synergy.rewards"] = $"{baseUrl}/synergy/rewards",
            ["synergy.progression"] = $"{baseUrl}/synergy/progression",
            ["synergy.content"] = $"{baseUrl}/synergy/content"  // NEW!
        },
        // ... rest of response
    });
}

Phase 2: Create Content Controller

Handle asset download requests:

[ApiController]
[Route("synergy/content")]
public class ContentController : ControllerBase
{
    private readonly IWebHostEnvironment _env;
    private readonly string _assetsPath;
    
    public ContentController(IWebHostEnvironment env)
    {
        _env = env;
        _assetsPath = Path.Combine(_env.ContentRootPath, "Assets");
        
        // Create assets directory if not exists
        Directory.CreateDirectory(_assetsPath);
    }
    
    // Get asset manifest (list of available content)
    [HttpGet("manifest")]
    public IActionResult GetManifest()
    {
        return Ok(new
        {
            version = "1.0.0",
            assets = GetAssetList()
        });
    }
    
    // Download specific asset
    [HttpGet("download/{assetType}/{assetId}")]
    public IActionResult DownloadAsset(string assetType, string assetId)
    {
        var assetPath = Path.Combine(_assetsPath, assetType, $"{assetId}.pak");
        
        if (!System.IO.File.Exists(assetPath))
        {
            return NotFound(new { error = $"Asset not found: {assetType}/{assetId}" });
        }
        
        var fileBytes = System.IO.File.ReadAllBytes(assetPath);
        return File(fileBytes, "application/octet-stream", $"{assetId}.pak");
    }
    
    // Get asset metadata
    [HttpGet("info/{assetType}/{assetId}")]
    public IActionResult GetAssetInfo(string assetType, string assetId)
    {
        var assetPath = Path.Combine(_assetsPath, assetType, $"{assetId}.pak");
        
        if (!System.IO.File.Exists(assetPath))
        {
            return NotFound();
        }
        
        var fileInfo = new FileInfo(assetPath);
        
        return Ok(new
        {
            assetId = assetId,
            assetType = assetType,
            size = fileInfo.Length,
            checksum = CalculateMD5(assetPath),
            version = "1.0.0"
        });
    }
    
    private List<object> GetAssetList()
    {
        var assets = new List<object>();
        
        if (Directory.Exists(_assetsPath))
        {
            foreach (var typeDir in Directory.GetDirectories(_assetsPath))
            {
                var assetType = Path.GetFileName(typeDir);
                
                foreach (var file in Directory.GetFiles(typeDir, "*.pak"))
                {
                    var assetId = Path.GetFileNameWithoutExtension(file);
                    var fileInfo = new FileInfo(file);
                    
                    assets.Add(new
                    {
                        id = assetId,
                        type = assetType,
                        size = fileInfo.Length,
                        url = $"/synergy/content/download/{assetType}/{assetId}"
                    });
                }
            }
        }
        
        return assets;
    }
    
    private string CalculateMD5(string filePath)
    {
        using var md5 = System.Security.Cryptography.MD5.Create();
        using var stream = System.IO.File.OpenRead(filePath);
        var hash = md5.ComputeHash(stream);
        return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
    }
}

📁 Asset Directory Structure

Create this structure on your community server:

RR3CommunityServer/
└── Assets/
    ├── cars/
    │   ├── nissan_silvia_s15.pak
    │   ├── ford_focus_rs.pak
    │   └── porsche_911_gt3.pak
    ├── tracks/
    │   ├── silverstone_national.pak
    │   ├── dubai_autodrome.pak
    │   └── brands_hatch.pak
    ├── textures/
    │   ├── ui_textures.pak
    │   └── car_textures.pak
    └── audio/
        ├── engine_sounds.pak
        └── music.pak

🔧 How to Get Original Assets

# Connect Android device
adb shell

# Find RR3 data directory
cd /data/data/com.ea.games.r3_row/

# List downloaded assets
ls -la files/

# Pull assets to PC (for backup/preservation)
adb pull /data/data/com.ea.games.r3_row/files/ ./rr3-assets/

Method 2: Intercept Downloads (Monitor what gets downloaded)

# Monitor network traffic while game downloads assets
adb shell tcpdump -i any -w /sdcard/rr3_traffic.pcap

# Or use Charles Proxy / Fiddler to see requests
# Game will request: https://cdn.ea.com/rr3/assets/cars/xxx.pak

Method 3: APK Assets (Some are bundled)

# Decompile APK
apktool d realracing3.apk -o rr3-decompiled

# Check assets folder
cd rr3-decompiled/assets/
ls -la

# Look for .pak, .unity3d, or compressed files

🌐 Admin Panel for Asset Management

Add to Pages/Assets.cshtml:

@page
@model AssetsModel
@{
    ViewData["Title"] = "Asset Management";
}

<div class="container-fluid">
    <div class="row">
        <div class="col-12">
            <h2>🎨 Asset Management</h2>
            <p>Manage game assets (cars, tracks, textures)</p>
        </div>
    </div>

    <div class="row mb-3">
        <div class="col-md-3">
            <div class="card">
                <div class="card-body">
                    <h5>Cars</h5>
                    <h3>@Model.CarAssetCount</h3>
                </div>
            </div>
        </div>
        <div class="col-md-3">
            <div class="card">
                <div class="card-body">
                    <h5>Tracks</h5>
                    <h3>@Model.TrackAssetCount</h3>
                </div>
            </div>
        </div>
        <div class="col-md-3">
            <div class="card">
                <div class="card-body">
                    <h5>Textures</h5>
                    <h3>@Model.TextureAssetCount</h3>
                </div>
            </div>
        </div>
        <div class="col-md-3">
            <div class="card">
                <div class="card-body">
                    <h5>Total Size</h5>
                    <h3>@Model.TotalSizeMB MB</h3>
                </div>
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-12">
            <div class="card">
                <div class="card-header">
                    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadModal">
                        📤 Upload Asset
                    </button>
                </div>
                <div class="card-body">
                    <table class="table">
                        <thead>
                            <tr>
                                <th>Type</th>
                                <th>Asset ID</th>
                                <th>Size</th>
                                <th>Checksum</th>
                                <th>Actions</th>
                            </tr>
                        </thead>
                        <tbody>
                            @foreach (var asset in Model.Assets)
                            {
                                <tr>
                                    <td><span class="badge bg-info">@asset.Type</span></td>
                                    <td>@asset.Id</td>
                                    <td>@asset.SizeMB MB</td>
                                    <td><code>@asset.Checksum</code></td>
                                    <td>
                                        <a href="/synergy/content/download/@asset.Type/@asset.Id" class="btn btn-sm btn-success">Download</a>
                                        <button class="btn btn-sm btn-danger" onclick="deleteAsset('@asset.Type', '@asset.Id')">Delete</button>
                                    </td>
                                </tr>
                            }
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- Upload Modal -->
<div class="modal fade" id="uploadModal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Upload Asset</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
            </div>
            <form method="post" enctype="multipart/form-data">
                <div class="modal-body">
                    <div class="mb-3">
                        <label class="form-label">Asset Type</label>
                        <select class="form-select" name="AssetType" required>
                            <option value="cars">Cars</option>
                            <option value="tracks">Tracks</option>
                            <option value="textures">Textures</option>
                            <option value="audio">Audio</option>
                        </select>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Asset ID</label>
                        <input type="text" class="form-control" name="AssetId" placeholder="e.g., nissan_silvia_s15" required />
                    </div>
                    <div class="mb-3">
                        <label class="form-label">File (.pak)</label>
                        <input type="file" class="form-control" name="AssetFile" accept=".pak" required />
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                    <button type="submit" class="btn btn-primary">Upload</button>
                </div>
            </form>
        </div>
    </div>
</div>

🎮 Testing Asset Downloads

1. Start Server with Assets

cd RR3CommunityServer/RR3CommunityServer
dotnet run

2. Check Asset Manifest

curl http://localhost:5001/synergy/content/manifest

3. Download Test Asset

curl http://localhost:5001/synergy/content/download/cars/nissan_silvia_s15 -o test.pak

4. Verify in Game

  • Connect APK to community server
  • Start game
  • Game should request assets from your server
  • Monitor logs:
adb logcat | grep -E "(Download|Asset|Content)"

📝 Asset Metadata Format

Each asset should have a companion .json metadata file:

{
  "id": "nissan_silvia_s15",
  "type": "car",
  "name": "Nissan Silvia Spec-R",
  "version": "1.0.0",
  "size": 15728640,
  "checksum": "5d41402abc4b2a76b9719d911017c592",
  "dependencies": [
    "car_textures",
    "engine_sounds"
  ],
  "tags": ["class_c", "nissan", "drift"]
}

🔐 Security Considerations

Allowed:

  • Backing up assets from games you own
  • Running private servers for personal use
  • Preserving games after shutdown
  • Using with legally obtained APK

Not Allowed:

  • Distributing EA's copyrighted assets
  • Pirating the game
  • Selling assets
  • Public redistribution without permission

Asset Checksums

Always verify integrity:

private bool VerifyAssetChecksum(string filePath, string expectedChecksum)
{
    var actualChecksum = CalculateMD5(filePath);
    return actualChecksum.Equals(expectedChecksum, StringComparison.OrdinalIgnoreCase);
}

🚀 Implementation Steps

Server Side (rr3-server)

  1. Add ContentController.cs
  2. Create Assets/ directory structure
  3. Update DirectorController to include synergy.content
  4. Add admin page for asset management
  5. Implement upload/download endpoints

APK Side (rr3-apk)

No changes needed! 🎉

The APK already uses the Director pattern. Once your community server returns the synergy.content URL, the game will automatically use it!

Asset Extraction

  1. Install original RR3 from Play Store
  2. Let it download all assets
  3. Use adb pull to extract assets
  4. Upload to your community server
  5. Share with community (if legally allowed)

📊 Asset Types in RR3

Cars (~10-20 MB each)

  • 3D models
  • Physics data
  • Paint variants
  • Upgrade visual changes

Tracks (~50-100 MB each)

  • 3D environment
  • AI pathfinding data
  • Weather variants
  • Time of day variants

Textures (~5-10 MB per pack)

  • UI elements
  • Car liveries
  • Environmental textures
  • Effects

Audio (~1-5 MB per pack)

  • Engine sounds per car
  • Music tracks
  • UI sounds
  • Ambient audio

💡 Future Enhancements

  • Asset CDN: Distribute assets via CDN for faster downloads
  • Compression: Serve compressed .pak files
  • Versioning: Support multiple asset versions
  • Differential Updates: Only download changed files
  • P2P Distribution: BitTorrent for large assets
  • Asset Workshop: Community-created content
  • Mod Support: Custom cars/tracks

🎯 Expected Behavior

Without Community Assets

  • Game connects to community server
  • Gameplay works (with existing assets)
  • New cars might not load properly
  • Missing tracks won't be available

With Community Assets

  • Game connects to community server
  • Downloads assets from community server
  • All content available
  • Full offline gameplay

📚 Resources

  • Unity .pak Format: Research Unity asset bundle format
  • EA Nimble SDK: Understanding the download system
  • Asset Extraction Tools: QuickBMS, Unity Asset Bundle Extractor
  • Network Analysis: Charles Proxy, Wireshark

This enables TRUE game preservation! 🎮

Players can download all game content from community servers, making RR3 fully playable even if EA shuts down their servers!

Legal Note: Only use assets you legally own. This is for preservation, not piracy.