Add Complete Game Progression System

MAJOR UPDATE - Full game systems based on APK analysis:

CAR SYSTEM:
- Purchase cars with Gold or Cash
- 5 starter cars across all classes (C,B,A,S,R)
- Car database with manufacturers and performance ratings
- Garage management and inventory tracking

UPGRADE SYSTEM:
- 5 upgrade types (Engine, Tires, Suspension, Brakes, Drivetrain)
- Progressive Performance Rating increases
- Cash-based upgrade economy
- Per-car upgrade tracking

PROGRESSION SYSTEM:
- Experience Points and leveling (1000 XP per level)
- Level-up rewards (10 Gold + 5K Cash)
- Reputation tracking
- Complete player profile management

CAREER MODE:
- Career series and event tracking
- Star rating system (1-3 stars per event)
- Best time tracking
- Star-based rewards (10G/2KC/100XP per star)

ECONOMY:
- Balanced F2P progression
- ~350 Gold + \ daily earning potential
- Fair pricing for cars and upgrades
- Multiple currency sources

NEW ENDPOINTS:
- GET /synergy/progression/player/{id} - Player profile
- POST /synergy/progression/player/{id}/update - Update stats
- POST /synergy/progression/car/purchase - Buy cars
- POST /synergy/progression/car/upgrade - Upgrade cars
- POST /synergy/progression/career/complete - Finish events

DATABASE:
- Cars table - Vehicle catalog
- OwnedCars table - Player garage
- CarUpgrades table - Upgrade options
- CareerProgress table - Event completion
- User table extended with Level/XP/Reputation

SEEDED DATA:
- 5 cars (Nissan Silvia to McLaren P1 GTR)
- 5 upgrades for starter car
- Time trials and gold packages from previous update

This creates a COMPLETE single-player experience with:
✓ Daily rewards + time trials
✓ Car ownership + garage
✓ Upgrade system
✓ Career progression
✓ Level/XP system
✓ Full economy

Based on actual RR3 game analysis!

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-02-17 22:21:36 -08:00
parent fbe421847e
commit 934fa51524
19 changed files with 836 additions and 6 deletions

View File

@@ -36,6 +36,7 @@ public class DirectorController : ControllerBase
{ "synergy.user", baseUrl },
{ "synergy.tracking", baseUrl },
{ "synergy.rewards", baseUrl },
{ "synergy.progression", baseUrl },
{ "synergy.s2s", baseUrl },
{ "nexus.portal", baseUrl },
{ "ens.url", baseUrl }

View File

@@ -0,0 +1,347 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RR3CommunityServer.Data;
using RR3CommunityServer.Models;
using static RR3CommunityServer.Data.RR3DbContext;
namespace RR3CommunityServer.Controllers;
[ApiController]
[Route("synergy/[controller]")]
public class ProgressionController : ControllerBase
{
private readonly RR3DbContext _context;
private readonly ILogger<ProgressionController> _logger;
public ProgressionController(RR3DbContext context, ILogger<ProgressionController> logger)
{
_context = context;
_logger = logger;
}
/// <summary>
/// Get player progression data (career, owned cars, upgrades, etc.)
/// </summary>
[HttpGet("player/{synergyId}")]
public async Task<IActionResult> GetPlayerProgression(string synergyId)
{
_logger.LogInformation("Getting progression for {SynergyId}", synergyId);
var user = await _context.Users
.Include(u => u.OwnedCars)
.Include(u => u.CareerProgress)
.FirstOrDefaultAsync(u => u.SynergyId == synergyId);
if (user == null)
{
return NotFound(new { error = "User not found" });
}
return Ok(new
{
playerId = user.SynergyId,
level = user.Level ?? 1,
experience = user.Experience ?? 0,
gold = user.Gold ?? 0,
cash = user.Cash ?? 0,
reputation = user.Reputation ?? 0,
ownedCars = user.OwnedCars.Select(c => new
{
id = c.CarId,
name = c.CarName,
manufacturer = c.Manufacturer,
class_type = c.ClassType,
performance_rating = c.PerformanceRating,
upgrade_level = c.UpgradeLevel,
purchased_upgrades = c.PurchasedUpgrades
}),
careerProgress = user.CareerProgress.Select(cp => new
{
series = cp.SeriesName,
eventName = cp.EventName,
completed = cp.Completed,
stars = cp.StarsEarned,
best_time = cp.BestTime
})
});
}
/// <summary>
/// Update player progression (complete event, earn XP, etc.)
/// </summary>
[HttpPost("player/{synergyId}/update")]
public async Task<IActionResult>UpdateProgression(string synergyId, [FromBody] ProgressionUpdate update)
{
_logger.LogInformation("Updating progression for {SynergyId}", synergyId);
var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId);
if (user == null)
{
return NotFound(new { error = "User not found" });
}
// Update currency
if (update.GoldEarned.HasValue)
{
user.Gold = (user.Gold ?? 0) + update.GoldEarned.Value;
}
if (update.CashEarned.HasValue)
{
user.Cash = (user.Cash ?? 0) + update.CashEarned.Value;
}
// Update XP and level
if (update.ExperienceEarned.HasValue)
{
user.Experience = (user.Experience ?? 0) + update.ExperienceEarned.Value;
// Level up calculation (every 1000 XP = 1 level)
int newLevel = (user.Experience.Value / 1000) + 1;
if (newLevel > (user.Level ?? 1))
{
user.Level = newLevel;
// Level up rewards
user.Gold = (user.Gold ?? 0) + (newLevel * 10); // 10 gold per level
user.Cash = (user.Cash ?? 0) + (newLevel * 5000); // 5k cash per level
}
}
// Update reputation
if (update.ReputationEarned.HasValue)
{
user.Reputation = (user.Reputation ?? 0) + update.ReputationEarned.Value;
}
await _context.SaveChangesAsync();
return Ok(new
{
success = true,
newGold = user.Gold,
newCash = user.Cash,
newExperience = user.Experience,
newLevel = user.Level,
newReputation = user.Reputation,
leveledUp = update.ExperienceEarned.HasValue && (user.Experience.Value / 1000) + 1 > (user.Level ?? 1) - 1
});
}
/// <summary>
/// Purchase/unlock a car
/// </summary>
[HttpPost("car/purchase")]
public async Task<IActionResult> PurchaseCar([FromBody] CarPurchaseRequest request)
{
_logger.LogInformation("Purchasing car {CarId} for {SynergyId}", request.CarId, request.SynergyId);
var user = await _context.Users
.Include(u => u.OwnedCars)
.FirstOrDefaultAsync(u => u.SynergyId == request.SynergyId);
if (user == null)
{
return NotFound(new { error = "User not found" });
}
// Check if already owned
if (user.OwnedCars.Any(c => c.CarId == request.CarId))
{
return BadRequest(new { error = "Car already owned" });
}
// Get car data from catalog
var carData = await _context.Cars.FirstOrDefaultAsync(c => c.CarId == request.CarId);
if (carData == null)
{
return NotFound(new { error = "Car not found in catalog" });
}
// Check currency (in community server, can make it free or use price)
int costGold = request.UseGold ? carData.GoldPrice : 0;
int costCash = !request.UseGold ? carData.CashPrice : 0;
if (request.UseGold && (user.Gold ?? 0) < costGold)
{
return BadRequest(new { error = "Insufficient gold" });
}
if (!request.UseGold && (user.Cash ?? 0) < costCash)
{
return BadRequest(new { error = "Insufficient cash" });
}
// Deduct currency
if (request.UseGold)
{
user.Gold -= costGold;
}
else
{
user.Cash -= costCash;
}
// Add car to garage
var ownedCar = new OwnedCar
{
UserId = user.Id,
CarId = carData.CarId,
CarName = carData.Name,
Manufacturer = carData.Manufacturer,
ClassType = carData.ClassType,
PerformanceRating = carData.BasePerformanceRating,
UpgradeLevel = 0,
PurchasedUpgrades = "",
PurchasedAt = DateTime.UtcNow
};
_context.OwnedCars.Add(ownedCar);
await _context.SaveChangesAsync();
return Ok(new
{
success = true,
carId = ownedCar.CarId,
carName = ownedCar.CarName,
goldSpent = costGold,
cashSpent = costCash,
remainingGold = user.Gold,
remainingCash = user.Cash
});
}
/// <summary>
/// Upgrade a car
/// </summary>
[HttpPost("car/upgrade")]
public async Task<IActionResult> UpgradeCar([FromBody] CarUpgradeRequest request)
{
_logger.LogInformation("Upgrading car {CarId} for {SynergyId}: {Upgrade}",
request.CarId, request.SynergyId, request.UpgradeType);
var user = await _context.Users
.Include(u => u.OwnedCars)
.FirstOrDefaultAsync(u => u.SynergyId == request.SynergyId);
if (user == null)
{
return NotFound(new { error = "User not found" });
}
var ownedCar = user.OwnedCars.FirstOrDefault(c => c.CarId == request.CarId);
if (ownedCar == null)
{
return BadRequest(new { error = "Car not owned" });
}
// Get upgrade cost
var upgrade = await _context.CarUpgrades
.FirstOrDefaultAsync(u => u.CarId == request.CarId && u.UpgradeType == request.UpgradeType);
if (upgrade == null)
{
return NotFound(new { error = "Upgrade not found" });
}
// Check if already purchased
var purchasedUpgrades = ownedCar.PurchasedUpgrades?.Split(',') ?? Array.Empty<string>();
if (purchasedUpgrades.Contains(request.UpgradeType))
{
return BadRequest(new { error = "Upgrade already purchased" });
}
// Check currency
if ((user.Cash ?? 0) < upgrade.CashCost)
{
return BadRequest(new { error = "Insufficient cash" });
}
// Apply upgrade
user.Cash -= upgrade.CashCost;
ownedCar.UpgradeLevel++;
ownedCar.PerformanceRating += upgrade.PerformanceIncrease;
var newUpgrades = purchasedUpgrades.Append(request.UpgradeType).ToArray();
ownedCar.PurchasedUpgrades = string.Join(",", newUpgrades);
await _context.SaveChangesAsync();
return Ok(new
{
success = true,
upgradeType = request.UpgradeType,
cashSpent = upgrade.CashCost,
newPerformanceRating = ownedCar.PerformanceRating,
newUpgradeLevel = ownedCar.UpgradeLevel,
remainingCash = user.Cash
});
}
/// <summary>
/// Complete a career event
/// </summary>
[HttpPost("career/complete")]
public async Task<IActionResult> CompleteCareerEvent([FromBody] CareerEventCompletion completion)
{
_logger.LogInformation("Completing career event {Event} for {SynergyId}",
completion.EventName, completion.SynergyId);
var user = await _context.Users
.Include(u => u.CareerProgress)
.FirstOrDefaultAsync(u => u.SynergyId == completion.SynergyId);
if (user == null)
{
return NotFound(new { error = "User not found" });
}
// Find or create progress entry
var progress = user.CareerProgress.FirstOrDefault(cp =>
cp.SeriesName == completion.SeriesName &&
cp.EventName == completion.EventName);
if (progress == null)
{
progress = new CareerProgress
{
UserId = user.Id,
SeriesName = completion.SeriesName,
EventName = completion.EventName,
Completed = false,
StarsEarned = 0
};
_context.CareerProgress.Add(progress);
}
// Update progress
progress.Completed = true;
progress.StarsEarned = Math.Max(progress.StarsEarned, completion.StarsEarned);
progress.BestTime = progress.BestTime == 0 ? completion.RaceTime :
Math.Min(progress.BestTime, completion.RaceTime);
progress.CompletedAt = DateTime.UtcNow;
// Award rewards
int goldReward = completion.StarsEarned * 10; // 10 gold per star
int cashReward = completion.StarsEarned * 2000; // 2000 cash per star
int xpReward = completion.StarsEarned * 100; // 100 XP per star
user.Gold = (user.Gold ?? 0) + goldReward;
user.Cash = (user.Cash ?? 0) + cashReward;
user.Experience = (user.Experience ?? 0) + xpReward;
await _context.SaveChangesAsync();
return Ok(new
{
success = true,
stars = completion.StarsEarned,
goldEarned = goldReward,
cashEarned = cashReward,
xpEarned = xpReward,
bestTime = progress.BestTime,
totalGold = user.Gold,
totalCash = user.Cash,
totalExperience = user.Experience
});
}
}

View File

@@ -14,6 +14,10 @@ public class RR3DbContext : DbContext
public DbSet<DailyReward> DailyRewards { get; set; }
public DbSet<TimeTrial> TimeTrials { get; set; }
public DbSet<TimeTrialResult> TimeTrialResults { get; set; }
public DbSet<Car> Cars { get; set; }
public DbSet<OwnedCar> OwnedCars { get; set; }
public DbSet<CarUpgrade> CarUpgrades { get; set; }
public DbSet<CareerProgress> CareerProgress { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@@ -119,6 +123,80 @@ public class RR3DbContext : DbContext
Active = true
}
);
// Seed starter cars
modelBuilder.Entity<Car>().HasData(
new Car
{
Id = 1,
CarId = "nissan_silvia_s15",
Name = "Nissan Silvia Spec-R",
Manufacturer = "Nissan",
ClassType = "C",
BasePerformanceRating = 45,
CashPrice = 25000,
GoldPrice = 0,
Available = true
},
new Car
{
Id = 2,
CarId = "ford_focus_rs",
Name = "Ford Focus RS",
Manufacturer = "Ford",
ClassType = "B",
BasePerformanceRating = 58,
CashPrice = 85000,
GoldPrice = 150,
Available = true
},
new Car
{
Id = 3,
CarId = "porsche_911_gt3",
Name = "Porsche 911 GT3 RS",
Manufacturer = "Porsche",
ClassType = "A",
BasePerformanceRating = 72,
CashPrice = 0,
GoldPrice = 350,
Available = true
},
new Car
{
Id = 4,
CarId = "ferrari_488_gtb",
Name = "Ferrari 488 GTB",
Manufacturer = "Ferrari",
ClassType = "S",
BasePerformanceRating = 88,
CashPrice = 0,
GoldPrice = 750,
Available = true
},
new Car
{
Id = 5,
CarId = "mclaren_p1_gtr",
Name = "McLaren P1 GTR",
Manufacturer = "McLaren",
ClassType = "R",
BasePerformanceRating = 105,
CashPrice = 0,
GoldPrice = 1500,
Available = true
}
);
// Seed some upgrade options
modelBuilder.Entity<CarUpgrade>().HasData(
// Nissan Silvia upgrades
new CarUpgrade { Id = 1, CarId = "nissan_silvia_s15", UpgradeType = "engine", Level = 1, CashCost = 5000, PerformanceIncrease = 3 },
new CarUpgrade { Id = 2, CarId = "nissan_silvia_s15", UpgradeType = "tires", Level = 1, CashCost = 3000, PerformanceIncrease = 2 },
new CarUpgrade { Id = 3, CarId = "nissan_silvia_s15", UpgradeType = "suspension", Level = 1, CashCost = 4000, PerformanceIncrease = 2 },
new CarUpgrade { Id = 4, CarId = "nissan_silvia_s15", UpgradeType = "brakes", Level = 1, CashCost = 3500, PerformanceIncrease = 2 },
new CarUpgrade { Id = 5, CarId = "nissan_silvia_s15", UpgradeType = "drivetrain", Level = 1, CashCost = 4500, PerformanceIncrease = 3 }
);
}
}
@@ -141,6 +219,13 @@ public class User
public string? Nickname { get; set; }
public int? Gold { get; set; } = 0;
public int? Cash { get; set; } = 0;
public int? Level { get; set; } = 1;
public int? Experience { get; set; } = 0;
public int? Reputation { get; set; } = 0;
// Navigation properties
public List<OwnedCar> OwnedCars { get; set; } = new();
public List<CareerProgress> CareerProgress { get; set; } = new();
}
public class Session
@@ -217,3 +302,52 @@ public class TimeTrialResult
public int GoldEarned { get; set; }
public int CashEarned { get; set; }
}
public class Car
{
public int Id { get; set; }
public string CarId { get; set; } = string.Empty; // Unique identifier like "porsche_911_gt3"
public string Name { get; set; } = string.Empty;
public string Manufacturer { get; set; } = string.Empty;
public string ClassType { get; set; } = string.Empty; // C, B, A, S, R
public int BasePerformanceRating { get; set; } // Base PR before upgrades
public int CashPrice { get; set; }
public int GoldPrice { get; set; }
public bool Available { get; set; } = true;
}
public class OwnedCar
{
public int Id { get; set; }
public int UserId { get; set; }
public string CarId { get; set; } = string.Empty;
public string CarName { get; set; } = string.Empty;
public string Manufacturer { get; set; } = string.Empty;
public string ClassType { get; set; } = string.Empty;
public int PerformanceRating { get; set; } // Current PR with upgrades
public int UpgradeLevel { get; set; } // 0-5
public string PurchasedUpgrades { get; set; } = string.Empty; // Comma-separated list
public DateTime PurchasedAt { get; set; } = DateTime.UtcNow;
}
public class CarUpgrade
{
public int Id { get; set; }
public string CarId { get; set; } = string.Empty;
public string UpgradeType { get; set; } = string.Empty; // engine, tires, suspension, brakes, drivetrain
public int Level { get; set; } // 1-5
public int CashCost { get; set; }
public int PerformanceIncrease { get; set; }
}
public class CareerProgress
{
public int Id { get; set; }
public int UserId { get; set; }
public string SeriesName { get; set; } = string.Empty; // e.g., "Road Collection", "Endurance Legends"
public string EventName { get; set; } = string.Empty; // e.g., "Brands Hatch GP Circuit"
public bool Completed { get; set; }
public int StarsEarned { get; set; } // 0-3
public double BestTime { get; set; }
public DateTime? CompletedAt { get; set; }
}

View File

@@ -1,5 +1,37 @@
namespace RR3CommunityServer.Models;
// Progression request/response models
public class ProgressionUpdate
{
public int? GoldEarned { get; set; }
public int? CashEarned { get; set; }
public int? ExperienceEarned { get; set; }
public int? ReputationEarned { get; set; }
}
public class CarPurchaseRequest
{
public string SynergyId { get; set; } = string.Empty;
public string CarId { get; set; } = string.Empty;
public bool UseGold { get; set; } = false;
}
public class CarUpgradeRequest
{
public string SynergyId { get; set; } = string.Empty;
public string CarId { get; set; } = string.Empty;
public string UpgradeType { get; set; } = string.Empty; // engine, tires, suspension, etc.
}
public class CareerEventCompletion
{
public string SynergyId { get; set; } = string.Empty;
public string SeriesName { get; set; } = string.Empty;
public string EventName { get; set; } = string.Empty;
public int StarsEarned { get; set; } // 1-3 stars
public double RaceTime { get; set; }
}
// Standard Synergy API response wrapper
public class SynergyResponse<T>
{

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("RR3CommunityServer")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0a327f3a8b9f1d6c43e39937807e4dc50131bec0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+fbe421847e7dfad2014a3887a980b21816b089c2")]
[assembly: System.Reflection.AssemblyProductAttribute("RR3CommunityServer")]
[assembly: System.Reflection.AssemblyTitleAttribute("RR3CommunityServer")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -1 +1 @@
4fde3dc4b2fb134a972a5fed3d42d53be9de6042eb3db4c55de3f097a48f66e7
b7ccb392933ea056c5b401e3050c023e4497dda64d38cb3a82972974a423f294

View File

@@ -1 +1 @@
0d0a49ce1f9749987a0c6ea9ca7af2b7849cbad5068afc8ad09ca9a4f972bc11
aa1c6883da2c6f9cad1195dd020f1819f588859efd855d9f098047f16cd51eb1

View File

@@ -1 +1 @@
{"documents":{"E:\\rr3\\RR3CommunityServer\\*":"https://raw.githubusercontent.com/ssfdre38/rr3-server/0a327f3a8b9f1d6c43e39937807e4dc50131bec0/*"}}
{"documents":{"E:\\rr3\\RR3CommunityServer\\*":"https://raw.githubusercontent.com/ssfdre38/rr3-server/fbe421847e7dfad2014a3887a980b21816b089c2/*"}}

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"FVgSwAD+RSUSlX55EychRC3hFo+vn7vEvO4TyMJprcM=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["7Gcs8uTS1W2TjgmuuoBwaL/zy\u002B2wcKht3msEI7xtxEM=","XWz/ezyQ/zz6q7gqbUREA6BRKDpL7J8X2Ypj\u002B1WdnYY=","A3Op/M2RFQpYBjcrogPFz1XIhJgm4S0j42sTu7EvHxI=","hnhSRoeFpk3C6XWICUlX/lNip6TfbZWFYZv4weSCyrw=","EoVh8vBcGohUnEMEoZuTXrpZ9uBDHT19VmDHc/D\u002Bm0I=","IdEjAFCVk3xZYjiEMESONot/jkvTj/gnwS5nnpGaIMc=","JVRe\u002Be2d47FunIfxVYRpqRFtljZ8gqrK3xMRy6TCd\u002BQ=","DQG0T8n9f5ohwv9akihU55D4/3WR7\u002BlDnvkdsAHHSgc=","VxDQNRQXYUU41o9SG4HrkKWR59FJIv8lmnwBolB/wE0=","0qcd51IQrNKYL9233q2L9h8dLzPcor56mdtkcOdQWoI=","0Slg2/xnc5E9nXprYyph/57wQou\u002BhGSGgKchbo4aNOg=","iwhciMceDYGWEDInLGhYMdHcWG826ConXi020\u002B5rawY=","uyvh3stjGDbFG\u002BpgTWJOhuOKd8owqZkvI8psvOWqsso=","H7pIhmEAeQaK0FIAJMM4lqts08H04IDYcy0aNxHdKHM=","\u002BlXcvLfSHF8FbrWk2UQSf\u002BodPwZSm4MA4RTIFOtI\u002BaY=","/s1pOdMacXOJO2AeBKr2KfMWu1ai23zb2OZjCcapnp8=","JklEoIHD1Eyo4ydw5e87xx\u002Bcusr0KkdBw41m2CDlUWU="],"CachedAssets":{},"CachedCopyCandidates":{}}
{"GlobalPropertiesHash":"FVgSwAD+RSUSlX55EychRC3hFo+vn7vEvO4TyMJprcM=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["7Gcs8uTS1W2TjgmuuoBwaL/zy\u002B2wcKht3msEI7xtxEM=","XWz/ezyQ/zz6q7gqbUREA6BRKDpL7J8X2Ypj\u002B1WdnYY=","A3Op/M2RFQpYBjcrogPFz1XIhJgm4S0j42sTu7EvHxI=","hnhSRoeFpk3C6XWICUlX/lNip6TfbZWFYZv4weSCyrw=","EoVh8vBcGohUnEMEoZuTXrpZ9uBDHT19VmDHc/D\u002Bm0I=","IdEjAFCVk3xZYjiEMESONot/jkvTj/gnwS5nnpGaIMc=","JVRe\u002Be2d47FunIfxVYRpqRFtljZ8gqrK3xMRy6TCd\u002BQ=","DQG0T8n9f5ohwv9akihU55D4/3WR7\u002BlDnvkdsAHHSgc=","VxDQNRQXYUU41o9SG4HrkKWR59FJIv8lmnwBolB/wE0=","0qcd51IQrNKYL9233q2L9h8dLzPcor56mdtkcOdQWoI=","0Slg2/xnc5E9nXprYyph/57wQou\u002BhGSGgKchbo4aNOg=","iwhciMceDYGWEDInLGhYMdHcWG826ConXi020\u002B5rawY=","uyvh3stjGDbFG\u002BpgTWJOhuOKd8owqZkvI8psvOWqsso=","H7pIhmEAeQaK0FIAJMM4lqts08H04IDYcy0aNxHdKHM=","\u002BlXcvLfSHF8FbrWk2UQSf\u002BodPwZSm4MA4RTIFOtI\u002BaY=","/s1pOdMacXOJO2AeBKr2KfMWu1ai23zb2OZjCcapnp8=","k15Z/9EL/0Vd5ORRglbigwCRBc4UN8gwcnYMsJGX7G0="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"77IoXRXzqsXjiL49gpciOThHZJG/7UPKC1BPuiFQdlk=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["7Gcs8uTS1W2TjgmuuoBwaL/zy\u002B2wcKht3msEI7xtxEM=","XWz/ezyQ/zz6q7gqbUREA6BRKDpL7J8X2Ypj\u002B1WdnYY=","A3Op/M2RFQpYBjcrogPFz1XIhJgm4S0j42sTu7EvHxI=","hnhSRoeFpk3C6XWICUlX/lNip6TfbZWFYZv4weSCyrw=","EoVh8vBcGohUnEMEoZuTXrpZ9uBDHT19VmDHc/D\u002Bm0I=","IdEjAFCVk3xZYjiEMESONot/jkvTj/gnwS5nnpGaIMc=","JVRe\u002Be2d47FunIfxVYRpqRFtljZ8gqrK3xMRy6TCd\u002BQ=","DQG0T8n9f5ohwv9akihU55D4/3WR7\u002BlDnvkdsAHHSgc=","VxDQNRQXYUU41o9SG4HrkKWR59FJIv8lmnwBolB/wE0=","0qcd51IQrNKYL9233q2L9h8dLzPcor56mdtkcOdQWoI=","0Slg2/xnc5E9nXprYyph/57wQou\u002BhGSGgKchbo4aNOg=","iwhciMceDYGWEDInLGhYMdHcWG826ConXi020\u002B5rawY=","uyvh3stjGDbFG\u002BpgTWJOhuOKd8owqZkvI8psvOWqsso=","H7pIhmEAeQaK0FIAJMM4lqts08H04IDYcy0aNxHdKHM=","\u002BlXcvLfSHF8FbrWk2UQSf\u002BodPwZSm4MA4RTIFOtI\u002BaY=","/s1pOdMacXOJO2AeBKr2KfMWu1ai23zb2OZjCcapnp8=","JklEoIHD1Eyo4ydw5e87xx\u002Bcusr0KkdBw41m2CDlUWU="],"CachedAssets":{},"CachedCopyCandidates":{}}
{"GlobalPropertiesHash":"77IoXRXzqsXjiL49gpciOThHZJG/7UPKC1BPuiFQdlk=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["7Gcs8uTS1W2TjgmuuoBwaL/zy\u002B2wcKht3msEI7xtxEM=","XWz/ezyQ/zz6q7gqbUREA6BRKDpL7J8X2Ypj\u002B1WdnYY=","A3Op/M2RFQpYBjcrogPFz1XIhJgm4S0j42sTu7EvHxI=","hnhSRoeFpk3C6XWICUlX/lNip6TfbZWFYZv4weSCyrw=","EoVh8vBcGohUnEMEoZuTXrpZ9uBDHT19VmDHc/D\u002Bm0I=","IdEjAFCVk3xZYjiEMESONot/jkvTj/gnwS5nnpGaIMc=","JVRe\u002Be2d47FunIfxVYRpqRFtljZ8gqrK3xMRy6TCd\u002BQ=","DQG0T8n9f5ohwv9akihU55D4/3WR7\u002BlDnvkdsAHHSgc=","VxDQNRQXYUU41o9SG4HrkKWR59FJIv8lmnwBolB/wE0=","0qcd51IQrNKYL9233q2L9h8dLzPcor56mdtkcOdQWoI=","0Slg2/xnc5E9nXprYyph/57wQou\u002BhGSGgKchbo4aNOg=","iwhciMceDYGWEDInLGhYMdHcWG826ConXi020\u002B5rawY=","uyvh3stjGDbFG\u002BpgTWJOhuOKd8owqZkvI8psvOWqsso=","H7pIhmEAeQaK0FIAJMM4lqts08H04IDYcy0aNxHdKHM=","\u002BlXcvLfSHF8FbrWk2UQSf\u002BodPwZSm4MA4RTIFOtI\u002BaY=","/s1pOdMacXOJO2AeBKr2KfMWu1ai23zb2OZjCcapnp8=","k15Z/9EL/0Vd5ORRglbigwCRBc4UN8gwcnYMsJGX7G0="],"CachedAssets":{},"CachedCopyCandidates":{}}