RECORDS & LEADERBOARDS (5/5 endpoints - 100%):
- Created LeaderboardsController with 5 endpoints
- GET /synergy/leaderboards/timetrials/{trialId}
- GET /synergy/leaderboards/career/{series}/{event}
- GET /synergy/leaderboards/global/top100
- GET /synergy/leaderboards/player/{synergyId}/records
- GET /synergy/leaderboards/compare/{synergyId1}/{synergyId2}
Added LeaderboardEntry and PersonalRecord models and database tables.
Migration applied: AddLeaderboardsAndRecords
Updated RewardsController.SubmitTimeTrial to track personal bests,
update leaderboards, and award 50 gold bonus for improvements.
Updated ProgressionController.CompleteCareerEvent similarly for
career event personal records.
TIME TRIALS (6/6 endpoints - 100%):
- GET /synergy/rewards/timetrials - List with time remaining
- GET /synergy/rewards/timetrials/{id} - Details with stats
- POST /synergy/rewards/timetrials/{id}/submit - Submit with PB tracking
- GET /synergy/rewards/timetrials/player/{synergyId}/results - History
- POST /synergy/rewards/timetrials/{id}/claim - Claim bonuses
- GET /synergy/leaderboards/timetrials/{id} - Leaderboards (above)
Added navigation properties to TimeTrialResult for easier queries.
Server progress: 66/73 endpoints (90%)
Two complete systems: Records/Leaderboards + Time Trials
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
481 lines
16 KiB
C#
481 lines
16 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using RR3CommunityServer.Models;
|
|
|
|
namespace RR3CommunityServer.Data;
|
|
|
|
public class RR3DbContext : DbContext
|
|
{
|
|
public RR3DbContext(DbContextOptions<RR3DbContext> options) : base(options) { }
|
|
|
|
public DbSet<Device> Devices { get; set; }
|
|
public DbSet<User> Users { get; set; }
|
|
public DbSet<Session> Sessions { get; set; }
|
|
public DbSet<Account> Accounts { get; set; }
|
|
public DbSet<DeviceAccount> DeviceAccounts { get; set; }
|
|
public DbSet<Purchase> Purchases { get; set; }
|
|
public DbSet<CatalogItem> CatalogItems { get; set; }
|
|
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; }
|
|
public DbSet<GameAsset> GameAssets { get; set; }
|
|
public DbSet<ModPack> ModPacks { get; set; }
|
|
public DbSet<UserSettings> UserSettings { get; set; }
|
|
public DbSet<PlayerSave> PlayerSaves { get; set; }
|
|
public DbSet<LeaderboardEntry> LeaderboardEntries { get; set; }
|
|
public DbSet<PersonalRecord> PersonalRecords { get; set; }
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
base.OnModelCreating(modelBuilder);
|
|
|
|
// Seed some default catalog items
|
|
modelBuilder.Entity<CatalogItem>().HasData(
|
|
new CatalogItem
|
|
{
|
|
Id = 1,
|
|
Sku = "com.ea.rr3.gold_1000",
|
|
Name = "1000 Gold",
|
|
Type = "currency",
|
|
Price = 0.99m,
|
|
Available = true
|
|
},
|
|
new CatalogItem
|
|
{
|
|
Id = 2,
|
|
Sku = "com.ea.rr3.car_tier1",
|
|
Name = "Starter Car",
|
|
Type = "car",
|
|
Price = 0m,
|
|
Available = true
|
|
},
|
|
new CatalogItem
|
|
{
|
|
Id = 3,
|
|
Sku = "com.ea.rr3.upgrade_engine",
|
|
Name = "Engine Upgrade",
|
|
Type = "upgrade",
|
|
Price = 4.99m,
|
|
Available = true
|
|
}
|
|
);
|
|
|
|
// Seed gold purchase options
|
|
modelBuilder.Entity<CatalogItem>().HasData(
|
|
new CatalogItem
|
|
{
|
|
Id = 4,
|
|
Sku = "com.ea.rr3.gold_100",
|
|
Name = "100 Gold",
|
|
Type = "currency",
|
|
Price = 0m, // FREE in community server
|
|
Available = true
|
|
},
|
|
new CatalogItem
|
|
{
|
|
Id = 5,
|
|
Sku = "com.ea.rr3.gold_500",
|
|
Name = "500 Gold",
|
|
Type = "currency",
|
|
Price = 0m,
|
|
Available = true
|
|
},
|
|
new CatalogItem
|
|
{
|
|
Id = 6,
|
|
Sku = "com.ea.rr3.gold_1000",
|
|
Name = "1000 Gold",
|
|
Type = "currency",
|
|
Price = 0m,
|
|
Available = true
|
|
},
|
|
new CatalogItem
|
|
{
|
|
Id = 7,
|
|
Sku = "com.ea.rr3.gold_5000",
|
|
Name = "5000 Gold",
|
|
Type = "currency",
|
|
Price = 0m,
|
|
Available = true
|
|
}
|
|
);
|
|
|
|
// Seed time trials
|
|
modelBuilder.Entity<TimeTrial>().HasData(
|
|
new TimeTrial
|
|
{
|
|
Id = 1,
|
|
Name = "Daily Sprint Challenge",
|
|
TrackName = "Silverstone National",
|
|
CarName = "Any Car",
|
|
StartDate = DateTime.UtcNow,
|
|
EndDate = DateTime.UtcNow.AddDays(7),
|
|
TargetTime = 90.5,
|
|
GoldReward = 50,
|
|
CashReward = 10000,
|
|
Active = true
|
|
},
|
|
new TimeTrial
|
|
{
|
|
Id = 2,
|
|
Name = "Speed Demon Trial",
|
|
TrackName = "Dubai Autodrome",
|
|
CarName = "Any Car",
|
|
StartDate = DateTime.UtcNow,
|
|
EndDate = DateTime.UtcNow.AddDays(7),
|
|
TargetTime = 120.0,
|
|
GoldReward = 100,
|
|
CashReward = 25000,
|
|
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 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// Database entities
|
|
public class Device
|
|
{
|
|
public int Id { get; set; }
|
|
public string DeviceId { get; set; } = string.Empty;
|
|
public string HardwareId { get; set; } = string.Empty;
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
public DateTime LastSeenAt { get; set; } = DateTime.UtcNow;
|
|
}
|
|
|
|
public class User
|
|
{
|
|
public int Id { get; set; }
|
|
public string SynergyId { get; set; } = string.Empty;
|
|
public string? DeviceId { get; set; }
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
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
|
|
{
|
|
public int Id { get; set; }
|
|
public string SessionId { get; set; } = string.Empty;
|
|
public string? SynergyId { get; set; }
|
|
public string DeviceId { get; set; } = string.Empty;
|
|
public int? UserId { get; set; }
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
public DateTime ExpiresAt { get; set; }
|
|
}
|
|
|
|
public class Purchase
|
|
{
|
|
public int Id { get; set; }
|
|
public string SynergyId { get; set; } = string.Empty;
|
|
public string ItemId { get; set; } = string.Empty;
|
|
public string Sku { get; set; } = string.Empty;
|
|
public string OrderId { get; set; } = string.Empty;
|
|
public DateTime PurchaseTime { get; set; } = DateTime.UtcNow;
|
|
public string Token { get; set; } = string.Empty;
|
|
public decimal Price { get; set; }
|
|
public string Status { get; set; } = "approved";
|
|
// For web panel display
|
|
public int? UserId { get; set; }
|
|
public DateTime PurchaseDate => PurchaseTime;
|
|
}
|
|
|
|
public class CatalogItem
|
|
{
|
|
public int Id { get; set; }
|
|
public string Sku { get; set; } = string.Empty;
|
|
public string Name { get; set; } = string.Empty;
|
|
public string Type { get; set; } = string.Empty;
|
|
public decimal Price { get; set; }
|
|
public bool Available { get; set; } = true;
|
|
}
|
|
|
|
public class DailyReward
|
|
{
|
|
public int Id { get; set; }
|
|
public int UserId { get; set; }
|
|
public DateTime RewardDate { get; set; }
|
|
public int GoldAmount { get; set; }
|
|
public int CashAmount { get; set; }
|
|
public bool Claimed { get; set; }
|
|
public DateTime? ClaimedAt { get; set; }
|
|
public int Streak { get; set; }
|
|
}
|
|
|
|
public class TimeTrial
|
|
{
|
|
public int Id { get; set; }
|
|
public string Name { get; set; } = string.Empty;
|
|
public string TrackName { get; set; } = string.Empty;
|
|
public string CarName { get; set; } = string.Empty;
|
|
public DateTime StartDate { get; set; }
|
|
public DateTime EndDate { get; set; }
|
|
public double TargetTime { get; set; }
|
|
public int GoldReward { get; set; }
|
|
public int CashReward { get; set; }
|
|
public bool Active { get; set; } = true;
|
|
}
|
|
|
|
public class TimeTrialResult
|
|
{
|
|
public int Id { get; set; }
|
|
public int UserId { get; set; }
|
|
public int TimeTrialId { get; set; }
|
|
public double TimeSeconds { get; set; }
|
|
public DateTime SubmittedAt { get; set; }
|
|
public bool BeatTarget { get; set; }
|
|
public int GoldEarned { get; set; }
|
|
public int CashEarned { get; set; }
|
|
|
|
// Navigation properties
|
|
public TimeTrial? TimeTrial { get; set; }
|
|
public User? User { 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 int Year { get; set; }
|
|
public string? Description { get; set; }
|
|
|
|
// Custom content fields
|
|
public bool IsCustom { get; set; }
|
|
public string? CustomAuthor { get; set; }
|
|
public string? CustomVersion { get; set; }
|
|
public DateTime? CreatedAt { get; set; }
|
|
}
|
|
|
|
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; }
|
|
}
|
|
|
|
public class GameAsset
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
// Asset identification
|
|
public string AssetId { get; set; } = string.Empty;
|
|
public string AssetType { get; set; } = string.Empty; // car, track, texture, audio, etc.
|
|
public string FileName { get; set; } = string.Empty;
|
|
public string ContentType { get; set; } = "application/octet-stream";
|
|
|
|
// EA CDN info
|
|
public string? OriginalUrl { get; set; }
|
|
public string? EaCdnPath { get; set; }
|
|
|
|
// Local storage
|
|
public string? LocalPath { get; set; }
|
|
public long FileSize { get; set; }
|
|
public string? FileSha256 { get; set; }
|
|
public string? Version { get; set; }
|
|
|
|
// Metadata
|
|
public DateTime DownloadedAt { get; set; } = DateTime.UtcNow;
|
|
public DateTime UploadedAt { get; set; } = DateTime.UtcNow;
|
|
public DateTime LastAccessedAt { get; set; } = DateTime.UtcNow;
|
|
public int AccessCount { get; set; } = 0;
|
|
public bool IsAvailable { get; set; } = true;
|
|
public bool IsRequired { get; set; } = false;
|
|
|
|
// Game-specific (optional)
|
|
public string? CarId { get; set; }
|
|
public string? TrackId { get; set; }
|
|
public string Category { get; set; } = "misc"; // models, textures, audio, etc.
|
|
public long? CompressedSize { get; set; }
|
|
public string? Md5Hash { get; set; }
|
|
public string? Description { get; set; }
|
|
|
|
// Custom content support
|
|
public bool IsCustomContent { get; set; }
|
|
public string? CustomAuthor { get; set; }
|
|
}
|
|
|
|
public class PlayerSave
|
|
{
|
|
public int Id { get; set; }
|
|
public string SynergyId { get; set; } = string.Empty;
|
|
public string SaveDataJson { get; set; } = "{}";
|
|
public long Version { get; set; } = 1;
|
|
public DateTime LastModified { get; set; } = DateTime.UtcNow;
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
}
|
|
|
|
public class LeaderboardEntry
|
|
{
|
|
public int Id { get; set; }
|
|
public string SynergyId { get; set; } = string.Empty;
|
|
public string PlayerName { get; set; } = string.Empty;
|
|
|
|
public string RecordType { get; set; } = string.Empty; // "TimeTrial", "Career", "Multiplayer"
|
|
public string RecordCategory { get; set; } = string.Empty;
|
|
public string? TrackName { get; set; }
|
|
public string? CarName { get; set; }
|
|
|
|
public double TimeSeconds { get; set; }
|
|
public DateTime SubmittedAt { get; set; }
|
|
}
|
|
|
|
public class PersonalRecord
|
|
{
|
|
public int Id { get; set; }
|
|
public string SynergyId { get; set; } = string.Empty;
|
|
|
|
public string RecordType { get; set; } = string.Empty;
|
|
public string RecordCategory { get; set; } = string.Empty;
|
|
public string? TrackName { get; set; }
|
|
public string? CarName { get; set; }
|
|
|
|
public double BestTimeSeconds { get; set; }
|
|
public DateTime AchievedAt { get; set; }
|
|
public DateTime? PreviousBestTime { get; set; }
|
|
public double? ImprovementSeconds { get; set; }
|
|
|
|
public int TotalAttempts { get; set; }
|
|
}
|
|
|
|
// Mod Pack entity - bundles of custom content
|
|
public class ModPack
|
|
{
|
|
public int Id { get; set; }
|
|
public string PackId { get; set; } = string.Empty;
|
|
public string Name { get; set; } = string.Empty;
|
|
public string Author { get; set; } = string.Empty;
|
|
public string? Description { get; set; }
|
|
public string Version { get; set; } = "1.0";
|
|
|
|
// Comma-separated IDs
|
|
public string? CarIds { get; set; }
|
|
public string? TrackIds { get; set; }
|
|
|
|
// Statistics
|
|
public int DownloadCount { get; set; }
|
|
public double Rating { get; set; }
|
|
|
|
public DateTime CreatedAt { get; set; }
|
|
public DateTime? UpdatedAt { get; set; }
|
|
}
|