Files
rr3-server/RR3CommunityServer/Data/RR3DbContext.cs
Daniel Elliott a934f57b52 Add Friends/Social & Multiplayer systems - 95 total endpoints
- Implemented Friends/Social Service (11 endpoints)
  * Friend management (list, add, accept, remove)
  * User search and invitations
  * Gift sending and claiming
  * Clubs/Teams system

- Implemented Multiplayer Service (12 endpoints)
  * Matchmaking (queue, status, leave)
  * Race sessions (create, join, ready, details)
  * Ghost data (upload, download)
  * Race results (submit, view)
  * Competitive rankings (rating, leaderboard)

- Added database entities:
  * Friends, FriendInvitations, Gifts
  * Clubs, ClubMembers
  * MatchmakingQueues, RaceSessions, RaceParticipants
  * GhostData, CompetitiveRatings

- Created migrations:
  * AddFriendsSocialSystem (5 tables)
  * AddMultiplayerSystem (5 tables)

Total: 95 endpoints - 100% EA server replacement ready

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-23 16:55:33 -08:00

739 lines
25 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; }
public DbSet<Event> Events { get; set; }
public DbSet<EventCompletion> EventCompletions { get; set; }
public DbSet<EventAttempt> EventAttempts { get; set; }
public DbSet<Notification> Notifications { get; set; }
public DbSet<Friend> Friends { get; set; }
public DbSet<FriendInvitation> FriendInvitations { get; set; }
public DbSet<Gift> Gifts { get; set; }
public DbSet<Club> Clubs { get; set; }
public DbSet<ClubMember> ClubMembers { get; set; }
public DbSet<MatchmakingQueue> MatchmakingQueues { get; set; }
public DbSet<RaceSession> RaceSessions { get; set; }
public DbSet<RaceParticipant> RaceParticipants { get; set; }
public DbSet<GhostData> GhostData { get; set; }
public DbSet<CompetitiveRating> CompetitiveRatings { 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; }
}
// Career Events entity
public class Event
{
public int Id { get; set; }
public string EventCode { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string SeriesName { get; set; } = string.Empty;
public int SeriesOrder { get; set; }
public int EventOrder { get; set; }
public string Track { get; set; } = string.Empty;
public string EventType { get; set; } = "race"; // race, duel, elimination, etc
public int Laps { get; set; }
public int RequiredPR { get; set; }
public string? RequiredCarClass { get; set; }
public double? TargetTime { get; set; }
public int GoldReward { get; set; }
public int CashReward { get; set; }
public int XPReward { get; set; }
public bool Active { get; set; } = true;
}
// Tracks player's completion of events
public class EventCompletion
{
public int Id { get; set; }
public int UserId { get; set; }
public int EventId { get; set; }
public double BestTime { get; set; }
public int CompletionCount { get; set; }
public DateTime FirstCompletedAt { get; set; }
public DateTime LastCompletedAt { get; set; }
// Navigation properties
public User? User { get; set; }
public Event? Event { get; set; }
}
// Tracks active event attempts
public class EventAttempt
{
public int Id { get; set; }
public int UserId { get; set; }
public int EventId { get; set; }
public DateTime StartedAt { get; set; }
public bool Completed { get; set; }
public DateTime? CompletedAt { get; set; }
public double? TimeSeconds { get; set; }
// Navigation properties
public User? User { get; set; }
public Event? Event { get; set; }
}
// In-game notifications
public class Notification
{
public int Id { get; set; }
public int UserId { get; set; }
public string Type { get; set; } = string.Empty; // "reward", "event", "system", "friend"
public string Title { get; set; } = string.Empty;
public string Message { get; set; } = string.Empty;
public bool IsRead { get; set; } = false;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? ExpiresAt { get; set; }
// Navigation property
public User? User { get; set; }
}
// Friend relationships
public class Friend
{
public int Id { get; set; }
public int User1Id { get; set; }
public int User2Id { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
// Navigation properties
public User? User1 { get; set; }
public User? User2 { get; set; }
}
// Friend invitations (pending requests)
public class FriendInvitation
{
public int Id { get; set; }
public int SenderId { get; set; }
public int ReceiverId { get; set; }
public string Status { get; set; } = "pending"; // "pending", "accepted", "declined", "expired"
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? RespondedAt { get; set; }
public DateTime ExpiresAt { get; set; } = DateTime.UtcNow.AddDays(7);
// Navigation properties
public User? Sender { get; set; }
public User? Receiver { get; set; }
}
// Gifts between friends
public class Gift
{
public int Id { get; set; }
public int SenderId { get; set; }
public int ReceiverId { get; set; }
public string GiftType { get; set; } = string.Empty; // "gold", "cash", "boost"
public int Amount { get; set; }
public string? Message { get; set; }
public bool Claimed { get; set; } = false;
public DateTime SentAt { get; set; } = DateTime.UtcNow;
public DateTime? ClaimedAt { get; set; }
public DateTime ExpiresAt { get; set; } = DateTime.UtcNow.AddDays(7);
// Navigation properties
public User? Sender { get; set; }
public User? Receiver { get; set; }
}
// Clubs/Teams
public class Club
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Tag { get; set; } = string.Empty; // 3-5 letter club tag
public int OwnerId { get; set; }
public int MaxMembers { get; set; } = 50;
public bool IsPublic { get; set; } = true;
public bool IsRecruiting { get; set; } = true;
public int TotalPoints { get; set; } = 0;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
// Navigation properties
public User? Owner { get; set; }
public ICollection<ClubMember> Members { get; set; } = new List<ClubMember>();
}
// Club memberships
public class ClubMember
{
public int Id { get; set; }
public int ClubId { get; set; }
public int UserId { get; set; }
public string Role { get; set; } = "member"; // "owner", "admin", "member"
public int ContributedPoints { get; set; } = 0;
public DateTime JoinedAt { get; set; } = DateTime.UtcNow;
// Navigation properties
public Club? Club { get; set; }
public User? User { get; set; }
}
// ===== MULTIPLAYER SYSTEM ENTITIES =====
// Matchmaking queue entries
public class MatchmakingQueue
{
public int Id { get; set; }
public int UserId { get; set; }
public string CarClass { get; set; } = string.Empty;
public string Track { get; set; } = string.Empty;
public string GameMode { get; set; } = string.Empty; // "ranked", "casual", "private"
public string Status { get; set; } = "queued"; // "queued", "matched", "cancelled"
public DateTime QueuedAt { get; set; } = DateTime.UtcNow;
public DateTime? MatchedAt { get; set; }
public int? SessionId { get; set; }
// Navigation properties
public User? User { get; set; }
public RaceSession? Session { get; set; }
}
// Race sessions (lobbies)
public class RaceSession
{
public int Id { get; set; }
public string SessionCode { get; set; } = string.Empty; // 6-digit join code
public string Track { get; set; } = string.Empty;
public string CarClass { get; set; } = string.Empty;
public int HostUserId { get; set; }
public int MaxPlayers { get; set; } = 8;
public string Status { get; set; } = "lobby"; // "lobby", "countdown", "racing", "finished"
public bool IsPrivate { get; set; } = false;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? StartedAt { get; set; }
public DateTime? FinishedAt { get; set; }
// Navigation properties
public User? Host { get; set; }
public ICollection<RaceParticipant> Participants { get; set; } = new List<RaceParticipant>();
}
// Race session participants
public class RaceParticipant
{
public int Id { get; set; }
public int SessionId { get; set; }
public int UserId { get; set; }
public string CarId { get; set; } = string.Empty;
public bool IsReady { get; set; } = false;
public int? FinishPosition { get; set; }
public double? RaceTime { get; set; }
public int? RewardGold { get; set; }
public int? RewardCash { get; set; }
public int? RewardXP { get; set; }
public DateTime JoinedAt { get; set; } = DateTime.UtcNow;
// Navigation properties
public RaceSession? Session { get; set; }
public User? User { get; set; }
}
// Ghost race data
public class GhostData
{
public int Id { get; set; }
public int UserId { get; set; }
public string Track { get; set; } = string.Empty;
public string CarId { get; set; } = string.Empty;
public double RaceTime { get; set; }
public string TelemetryData { get; set; } = string.Empty; // JSON compressed telemetry
public DateTime UploadedAt { get; set; } = DateTime.UtcNow;
public int Downloads { get; set; } = 0;
// Navigation properties
public User? User { get; set; }
}
// Competitive ratings (ranked mode)
public class CompetitiveRating
{
public int Id { get; set; }
public int UserId { get; set; }
public int Rating { get; set; } = 1000; // ELO-style rating
public int Wins { get; set; } = 0;
public int Losses { get; set; } = 0;
public int Draws { get; set; } = 0;
public string Division { get; set; } = "Bronze"; // Bronze, Silver, Gold, Platinum, Diamond
public int DivisionRank { get; set; } = 0;
public DateTime LastMatchAt { get; set; } = DateTime.UtcNow;
// Navigation properties
public User? User { get; set; }
}