Add Phase 1 critical endpoints: Config & Save/Load system
- Added ConfigController with 4 endpoints:
- getGameConfig: Server config, feature flags, URLs
- getServerTime: UTC timestamps
- getFeatureFlags: Feature toggles
- getServerStatus: Health check
- Added save/load system to ProgressionController:
- POST /save/{synergyId}: Save JSON blob
- GET /save/{synergyId}/load: Load JSON blob
- Version tracking and timestamps
- Added PlayerSave entity to database:
- Stores arbitrary JSON game state
- Version tracking (increments on save)
- LastModified timestamps
- Updated appsettings.json:
- ServerSettings section (version, URLs, MOTD)
- FeatureFlags section (7 feature toggles)
- Created migration: AddPlayerSavesAndConfig
- Updated ApiModels with new DTOs
- All endpoints tested and working
Phase 1 objectives complete:
✅ Synergy ID generation (already existed)
✅ Configuration endpoints (new)
✅ Save/load system (new)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
143
RR3CommunityServer/Controllers/ConfigController.cs
Normal file
143
RR3CommunityServer/Controllers/ConfigController.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using RR3CommunityServer.Models;
|
||||
|
||||
namespace RR3CommunityServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("config/api/android")]
|
||||
public class ConfigController : ControllerBase
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<ConfigController> _logger;
|
||||
|
||||
public ConfigController(IConfiguration configuration, ILogger<ConfigController> logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get game configuration - server time, feature flags, version info
|
||||
/// </summary>
|
||||
[HttpGet("getGameConfig")]
|
||||
public ActionResult<SynergyResponse<GameConfig>> GetGameConfig()
|
||||
{
|
||||
_logger.LogInformation("GetGameConfig request");
|
||||
|
||||
var config = new GameConfig
|
||||
{
|
||||
ServerTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
ServerVersion = _configuration["ServerSettings:Version"] ?? "1.0.0",
|
||||
GameVersion = _configuration["ServerSettings:GameVersion"] ?? "14.0.1",
|
||||
MaintenanceMode = bool.Parse(_configuration["ServerSettings:MaintenanceMode"] ?? "false"),
|
||||
MessageOfTheDay = _configuration["ServerSettings:MessageOfTheDay"] ?? "Welcome to RR3 Community Server!",
|
||||
FeatureFlags = new FeatureFlags
|
||||
{
|
||||
MultiplayerEnabled = bool.Parse(_configuration["FeatureFlags:MultiplayerEnabled"] ?? "false"),
|
||||
LeaderboardsEnabled = bool.Parse(_configuration["FeatureFlags:LeaderboardsEnabled"] ?? "true"),
|
||||
DailyRewardsEnabled = bool.Parse(_configuration["FeatureFlags:DailyRewardsEnabled"] ?? "true"),
|
||||
TimeTrialsEnabled = bool.Parse(_configuration["FeatureFlags:TimeTrialsEnabled"] ?? "true"),
|
||||
CustomContentEnabled = bool.Parse(_configuration["FeatureFlags:CustomContentEnabled"] ?? "true"),
|
||||
SpecialEventsEnabled = bool.Parse(_configuration["FeatureFlags:SpecialEventsEnabled"] ?? "true"),
|
||||
AllItemsFree = bool.Parse(_configuration["FeatureFlags:AllItemsFree"] ?? "true")
|
||||
},
|
||||
Urls = new ServerUrls
|
||||
{
|
||||
BaseUrl = _configuration["ServerSettings:BaseUrl"] ?? "http://localhost:5001",
|
||||
AssetsUrl = _configuration["ServerSettings:AssetsUrl"] ?? "http://localhost:5001/content/api",
|
||||
LeaderboardsUrl = _configuration["ServerSettings:LeaderboardsUrl"] ?? "http://localhost:5001/leaderboards/api",
|
||||
MultiplayerUrl = _configuration["ServerSettings:MultiplayerUrl"] ?? "http://localhost:5001/multiplayer/api"
|
||||
}
|
||||
};
|
||||
|
||||
var response = new SynergyResponse<GameConfig>
|
||||
{
|
||||
resultCode = 0,
|
||||
message = "Success",
|
||||
data = config
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get server time (Unix timestamp)
|
||||
/// </summary>
|
||||
[HttpGet("getServerTime")]
|
||||
public ActionResult<SynergyResponse<ServerTime>> GetServerTime()
|
||||
{
|
||||
_logger.LogInformation("GetServerTime request");
|
||||
|
||||
var response = new SynergyResponse<ServerTime>
|
||||
{
|
||||
resultCode = 0,
|
||||
message = "Success",
|
||||
data = new ServerTime
|
||||
{
|
||||
ServerTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
ServerTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
Timezone = "UTC",
|
||||
IsDST = false
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get feature flags
|
||||
/// </summary>
|
||||
[HttpGet("getFeatureFlags")]
|
||||
public ActionResult<SynergyResponse<FeatureFlags>> GetFeatureFlags()
|
||||
{
|
||||
_logger.LogInformation("GetFeatureFlags request");
|
||||
|
||||
var flags = new FeatureFlags
|
||||
{
|
||||
MultiplayerEnabled = bool.Parse(_configuration["FeatureFlags:MultiplayerEnabled"] ?? "false"),
|
||||
LeaderboardsEnabled = bool.Parse(_configuration["FeatureFlags:LeaderboardsEnabled"] ?? "true"),
|
||||
DailyRewardsEnabled = bool.Parse(_configuration["FeatureFlags:DailyRewardsEnabled"] ?? "true"),
|
||||
TimeTrialsEnabled = bool.Parse(_configuration["FeatureFlags:TimeTrialsEnabled"] ?? "true"),
|
||||
CustomContentEnabled = bool.Parse(_configuration["FeatureFlags:CustomContentEnabled"] ?? "true"),
|
||||
SpecialEventsEnabled = bool.Parse(_configuration["FeatureFlags:SpecialEventsEnabled"] ?? "true"),
|
||||
AllItemsFree = bool.Parse(_configuration["FeatureFlags:AllItemsFree"] ?? "true")
|
||||
};
|
||||
|
||||
var response = new SynergyResponse<FeatureFlags>
|
||||
{
|
||||
resultCode = 0,
|
||||
message = "Success",
|
||||
data = flags
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check server status and health
|
||||
/// </summary>
|
||||
[HttpGet("getServerStatus")]
|
||||
public ActionResult<SynergyResponse<ServerStatus>> GetServerStatus()
|
||||
{
|
||||
_logger.LogInformation("GetServerStatus request");
|
||||
|
||||
var status = new ServerStatus
|
||||
{
|
||||
Status = "online",
|
||||
Version = _configuration["ServerSettings:Version"] ?? "1.0.0",
|
||||
MaintenanceMode = bool.Parse(_configuration["ServerSettings:MaintenanceMode"] ?? "false"),
|
||||
PlayerCount = 0, // TODO: Implement player counting
|
||||
Uptime = Environment.TickCount64 / 1000, // Seconds since server start
|
||||
Message = _configuration["ServerSettings:MessageOfTheDay"] ?? string.Empty
|
||||
};
|
||||
|
||||
var response = new SynergyResponse<ServerStatus>
|
||||
{
|
||||
resultCode = 0,
|
||||
message = "Success",
|
||||
data = status
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
||||
@@ -344,4 +344,107 @@ public class ProgressionController : ControllerBase
|
||||
totalExperience = user.Experience
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save player game state as JSON blob
|
||||
/// </summary>
|
||||
[HttpPost("save/{synergyId}")]
|
||||
public async Task<IActionResult> SavePlayerData(string synergyId, [FromBody] SaveDataRequest request)
|
||||
{
|
||||
_logger.LogInformation("Saving data for {SynergyId} ({Bytes} bytes)",
|
||||
synergyId, request.SaveData?.Length ?? 0);
|
||||
|
||||
if (string.IsNullOrEmpty(request.SaveData))
|
||||
{
|
||||
return BadRequest(new { error = "Save data is empty" });
|
||||
}
|
||||
|
||||
// Find or create save record
|
||||
var save = await _context.PlayerSaves
|
||||
.FirstOrDefaultAsync(s => s.SynergyId == synergyId);
|
||||
|
||||
if (save == null)
|
||||
{
|
||||
save = new PlayerSave
|
||||
{
|
||||
SynergyId = synergyId,
|
||||
SaveDataJson = request.SaveData,
|
||||
Version = 1,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
LastModified = DateTime.UtcNow
|
||||
};
|
||||
_context.PlayerSaves.Add(save);
|
||||
}
|
||||
else
|
||||
{
|
||||
save.SaveDataJson = request.SaveData;
|
||||
save.Version++;
|
||||
save.LastModified = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var response = new SynergyResponse<SaveDataResponse>
|
||||
{
|
||||
resultCode = 0,
|
||||
message = "Save successful",
|
||||
data = new SaveDataResponse
|
||||
{
|
||||
Success = true,
|
||||
Version = save.Version,
|
||||
LastModified = new DateTimeOffset(save.LastModified).ToUnixTimeSeconds(),
|
||||
SaveData = string.Empty // Don't echo back the full data
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load player game state JSON blob
|
||||
/// </summary>
|
||||
[HttpGet("save/{synergyId}/load")]
|
||||
public async Task<IActionResult> LoadPlayerData(string synergyId)
|
||||
{
|
||||
_logger.LogInformation("Loading save data for {SynergyId}", synergyId);
|
||||
|
||||
var save = await _context.PlayerSaves
|
||||
.FirstOrDefaultAsync(s => s.SynergyId == synergyId);
|
||||
|
||||
if (save == null)
|
||||
{
|
||||
// Return empty save for new players
|
||||
var emptySave = new SaveDataResponse
|
||||
{
|
||||
SaveData = "{}",
|
||||
Version = 0,
|
||||
LastModified = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Success = true
|
||||
};
|
||||
|
||||
var emptyResponse = new SynergyResponse<SaveDataResponse>
|
||||
{
|
||||
resultCode = 0,
|
||||
message = "No save found - new player",
|
||||
data = emptySave
|
||||
};
|
||||
|
||||
return Ok(emptyResponse);
|
||||
}
|
||||
|
||||
var response = new SynergyResponse<SaveDataResponse>
|
||||
{
|
||||
resultCode = 0,
|
||||
message = "Save loaded successfully",
|
||||
data = new SaveDataResponse
|
||||
{
|
||||
SaveData = save.SaveDataJson,
|
||||
Version = save.Version,
|
||||
LastModified = new DateTimeOffset(save.LastModified).ToUnixTimeSeconds(),
|
||||
Success = true
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ public class RR3DbContext : DbContext
|
||||
public DbSet<GameAsset> GameAssets { get; set; }
|
||||
public DbSet<ModPack> ModPacks { get; set; }
|
||||
public DbSet<UserSettings> UserSettings { get; set; }
|
||||
public DbSet<PlayerSave> PlayerSaves { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@@ -407,6 +408,16 @@ public class GameAsset
|
||||
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;
|
||||
}
|
||||
|
||||
// Mod Pack entity - bundles of custom content
|
||||
public class ModPack
|
||||
{
|
||||
|
||||
994
RR3CommunityServer/Migrations/20260222074748_AddPlayerSavesAndConfig.Designer.cs
generated
Normal file
994
RR3CommunityServer/Migrations/20260222074748_AddPlayerSavesAndConfig.Designer.cs
generated
Normal file
@@ -0,0 +1,994 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using RR3CommunityServer.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace RR3CommunityServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(RR3DbContext))]
|
||||
[Migration("20260222074748_AddPlayerSavesAndConfig")]
|
||||
partial class AddPlayerSavesAndConfig
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.11");
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.Car", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Available")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("BasePerformanceRating")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CarId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("CashPrice")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClassType")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomAuthor")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomVersion")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("GoldPrice")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsCustom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Manufacturer")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Year")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Cars");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Available = true,
|
||||
BasePerformanceRating = 45,
|
||||
CarId = "nissan_silvia_s15",
|
||||
CashPrice = 25000,
|
||||
ClassType = "C",
|
||||
GoldPrice = 0,
|
||||
IsCustom = false,
|
||||
Manufacturer = "Nissan",
|
||||
Name = "Nissan Silvia Spec-R",
|
||||
Year = 0
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Available = true,
|
||||
BasePerformanceRating = 58,
|
||||
CarId = "ford_focus_rs",
|
||||
CashPrice = 85000,
|
||||
ClassType = "B",
|
||||
GoldPrice = 150,
|
||||
IsCustom = false,
|
||||
Manufacturer = "Ford",
|
||||
Name = "Ford Focus RS",
|
||||
Year = 0
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
Available = true,
|
||||
BasePerformanceRating = 72,
|
||||
CarId = "porsche_911_gt3",
|
||||
CashPrice = 0,
|
||||
ClassType = "A",
|
||||
GoldPrice = 350,
|
||||
IsCustom = false,
|
||||
Manufacturer = "Porsche",
|
||||
Name = "Porsche 911 GT3 RS",
|
||||
Year = 0
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
Available = true,
|
||||
BasePerformanceRating = 88,
|
||||
CarId = "ferrari_488_gtb",
|
||||
CashPrice = 0,
|
||||
ClassType = "S",
|
||||
GoldPrice = 750,
|
||||
IsCustom = false,
|
||||
Manufacturer = "Ferrari",
|
||||
Name = "Ferrari 488 GTB",
|
||||
Year = 0
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 5,
|
||||
Available = true,
|
||||
BasePerformanceRating = 105,
|
||||
CarId = "mclaren_p1_gtr",
|
||||
CashPrice = 0,
|
||||
ClassType = "R",
|
||||
GoldPrice = 1500,
|
||||
IsCustom = false,
|
||||
Manufacturer = "McLaren",
|
||||
Name = "McLaren P1 GTR",
|
||||
Year = 0
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.CarUpgrade", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CarId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("CashCost")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("PerformanceIncrease")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UpgradeType")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CarUpgrades");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
CarId = "nissan_silvia_s15",
|
||||
CashCost = 5000,
|
||||
Level = 1,
|
||||
PerformanceIncrease = 3,
|
||||
UpgradeType = "engine"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
CarId = "nissan_silvia_s15",
|
||||
CashCost = 3000,
|
||||
Level = 1,
|
||||
PerformanceIncrease = 2,
|
||||
UpgradeType = "tires"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
CarId = "nissan_silvia_s15",
|
||||
CashCost = 4000,
|
||||
Level = 1,
|
||||
PerformanceIncrease = 2,
|
||||
UpgradeType = "suspension"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
CarId = "nissan_silvia_s15",
|
||||
CashCost = 3500,
|
||||
Level = 1,
|
||||
PerformanceIncrease = 2,
|
||||
UpgradeType = "brakes"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 5,
|
||||
CarId = "nissan_silvia_s15",
|
||||
CashCost = 4500,
|
||||
Level = 1,
|
||||
PerformanceIncrease = 3,
|
||||
UpgradeType = "drivetrain"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.CareerProgress", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("BestTime")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<bool>("Completed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("CompletedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EventName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SeriesName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("StarsEarned")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("CareerProgress");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.CatalogItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Available")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Sku")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CatalogItems");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Available = true,
|
||||
Name = "1000 Gold",
|
||||
Price = 0.99m,
|
||||
Sku = "com.ea.rr3.gold_1000",
|
||||
Type = "currency"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Available = true,
|
||||
Name = "Starter Car",
|
||||
Price = 0m,
|
||||
Sku = "com.ea.rr3.car_tier1",
|
||||
Type = "car"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
Available = true,
|
||||
Name = "Engine Upgrade",
|
||||
Price = 4.99m,
|
||||
Sku = "com.ea.rr3.upgrade_engine",
|
||||
Type = "upgrade"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
Available = true,
|
||||
Name = "100 Gold",
|
||||
Price = 0m,
|
||||
Sku = "com.ea.rr3.gold_100",
|
||||
Type = "currency"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 5,
|
||||
Available = true,
|
||||
Name = "500 Gold",
|
||||
Price = 0m,
|
||||
Sku = "com.ea.rr3.gold_500",
|
||||
Type = "currency"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 6,
|
||||
Available = true,
|
||||
Name = "1000 Gold",
|
||||
Price = 0m,
|
||||
Sku = "com.ea.rr3.gold_1000",
|
||||
Type = "currency"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 7,
|
||||
Available = true,
|
||||
Name = "5000 Gold",
|
||||
Price = 0m,
|
||||
Sku = "com.ea.rr3.gold_5000",
|
||||
Type = "currency"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.DailyReward", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("CashAmount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Claimed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("ClaimedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("GoldAmount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("RewardDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Streak")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("DailyRewards");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.Device", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DeviceId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HardwareId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastSeenAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Devices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.GameAsset", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AccessCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AssetId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AssetType")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CarId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Category")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long?>("CompressedSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ContentType")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomAuthor")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("DownloadedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EaCdnPath")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FileName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FileSha256")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("FileSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsAvailable")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsCustomContent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsRequired")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("LastAccessedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LocalPath")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Md5Hash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalUrl")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TrackId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UploadedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Version")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("GameAssets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.ModPack", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Author")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CarIds")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("DownloadCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PackId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<double>("Rating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<string>("TrackIds")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Version")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ModPacks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.OwnedCar", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CarId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CarName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClassType")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Manufacturer")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("PerformanceRating")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("PurchasedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PurchasedUpgrades")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UpgradeLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("OwnedCars");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.PlayerSave", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastModified")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SaveDataJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SynergyId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("Version")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlayerSaves");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.Purchase", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ItemId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OrderId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("PurchaseTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Sku")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SynergyId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Purchases");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.Session", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DeviceId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SessionId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SynergyId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Sessions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.TimeTrial", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CarName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("CashReward")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("EndDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("GoldReward")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("StartDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<double>("TargetTime")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<string>("TrackName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("TimeTrials");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Active = true,
|
||||
CarName = "Any Car",
|
||||
CashReward = 10000,
|
||||
EndDate = new DateTime(2026, 3, 1, 7, 47, 46, 836, DateTimeKind.Utc).AddTicks(7182),
|
||||
GoldReward = 50,
|
||||
Name = "Daily Sprint Challenge",
|
||||
StartDate = new DateTime(2026, 2, 22, 7, 47, 46, 836, DateTimeKind.Utc).AddTicks(7180),
|
||||
TargetTime = 90.5,
|
||||
TrackName = "Silverstone National"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Active = true,
|
||||
CarName = "Any Car",
|
||||
CashReward = 25000,
|
||||
EndDate = new DateTime(2026, 3, 1, 7, 47, 46, 836, DateTimeKind.Utc).AddTicks(7192),
|
||||
GoldReward = 100,
|
||||
Name = "Speed Demon Trial",
|
||||
StartDate = new DateTime(2026, 2, 22, 7, 47, 46, 836, DateTimeKind.Utc).AddTicks(7191),
|
||||
TargetTime = 120.0,
|
||||
TrackName = "Dubai Autodrome"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.TimeTrialResult", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("BeatTarget")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("CashEarned")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("GoldEarned")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("SubmittedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<double>("TimeSeconds")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("TimeTrialId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("TimeTrialResults");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("Cash")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DeviceId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("Experience")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("Gold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Nickname")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("Reputation")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SynergyId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Models.Account", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EmailVerificationToken")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("EmailVerified")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("LastLoginAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("PasswordResetExpiry")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordResetToken")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Models.DeviceAccount", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AccountId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DeviceId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DeviceName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastUsedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LinkedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AccountId");
|
||||
|
||||
b.ToTable("DeviceAccounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Models.UserSettings", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DeviceId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Mode")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ServerUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.CareerProgress", b =>
|
||||
{
|
||||
b.HasOne("RR3CommunityServer.Data.User", null)
|
||||
.WithMany("CareerProgress")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.OwnedCar", b =>
|
||||
{
|
||||
b.HasOne("RR3CommunityServer.Data.User", null)
|
||||
.WithMany("OwnedCars")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Models.Account", b =>
|
||||
{
|
||||
b.HasOne("RR3CommunityServer.Data.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Models.DeviceAccount", b =>
|
||||
{
|
||||
b.HasOne("RR3CommunityServer.Models.Account", "Account")
|
||||
.WithMany("LinkedDevices")
|
||||
.HasForeignKey("AccountId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Account");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.User", b =>
|
||||
{
|
||||
b.Navigation("CareerProgress");
|
||||
|
||||
b.Navigation("OwnedCars");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Models.Account", b =>
|
||||
{
|
||||
b.Navigation("LinkedDevices");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace RR3CommunityServer.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPlayerSavesAndConfig : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PlayerSaves",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
SynergyId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
SaveDataJson = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Version = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
LastModified = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PlayerSaves", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "TimeTrials",
|
||||
keyColumn: "Id",
|
||||
keyValue: 1,
|
||||
columns: new[] { "EndDate", "StartDate" },
|
||||
values: new object[] { new DateTime(2026, 3, 1, 7, 47, 46, 836, DateTimeKind.Utc).AddTicks(7182), new DateTime(2026, 2, 22, 7, 47, 46, 836, DateTimeKind.Utc).AddTicks(7180) });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "TimeTrials",
|
||||
keyColumn: "Id",
|
||||
keyValue: 2,
|
||||
columns: new[] { "EndDate", "StartDate" },
|
||||
values: new object[] { new DateTime(2026, 3, 1, 7, 47, 46, 836, DateTimeKind.Utc).AddTicks(7192), new DateTime(2026, 2, 22, 7, 47, 46, 836, DateTimeKind.Utc).AddTicks(7191) });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "PlayerSaves");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "TimeTrials",
|
||||
keyColumn: "Id",
|
||||
keyValue: 1,
|
||||
columns: new[] { "EndDate", "StartDate" },
|
||||
values: new object[] { new DateTime(2026, 2, 27, 17, 54, 50, 315, DateTimeKind.Utc).AddTicks(3387), new DateTime(2026, 2, 20, 17, 54, 50, 315, DateTimeKind.Utc).AddTicks(3384) });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "TimeTrials",
|
||||
keyColumn: "Id",
|
||||
keyValue: 2,
|
||||
columns: new[] { "EndDate", "StartDate" },
|
||||
values: new object[] { new DateTime(2026, 2, 27, 17, 54, 50, 315, DateTimeKind.Utc).AddTicks(3395), new DateTime(2026, 2, 20, 17, 54, 50, 315, DateTimeKind.Utc).AddTicks(3395) });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -589,6 +589,34 @@ namespace RR3CommunityServer.Migrations
|
||||
b.ToTable("OwnedCars");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.PlayerSave", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastModified")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SaveDataJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SynergyId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("Version")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlayerSaves");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RR3CommunityServer.Data.Purchase", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -711,10 +739,10 @@ namespace RR3CommunityServer.Migrations
|
||||
Active = true,
|
||||
CarName = "Any Car",
|
||||
CashReward = 10000,
|
||||
EndDate = new DateTime(2026, 2, 27, 17, 54, 50, 315, DateTimeKind.Utc).AddTicks(3387),
|
||||
EndDate = new DateTime(2026, 3, 1, 7, 47, 46, 836, DateTimeKind.Utc).AddTicks(7182),
|
||||
GoldReward = 50,
|
||||
Name = "Daily Sprint Challenge",
|
||||
StartDate = new DateTime(2026, 2, 20, 17, 54, 50, 315, DateTimeKind.Utc).AddTicks(3384),
|
||||
StartDate = new DateTime(2026, 2, 22, 7, 47, 46, 836, DateTimeKind.Utc).AddTicks(7180),
|
||||
TargetTime = 90.5,
|
||||
TrackName = "Silverstone National"
|
||||
},
|
||||
@@ -724,10 +752,10 @@ namespace RR3CommunityServer.Migrations
|
||||
Active = true,
|
||||
CarName = "Any Car",
|
||||
CashReward = 25000,
|
||||
EndDate = new DateTime(2026, 2, 27, 17, 54, 50, 315, DateTimeKind.Utc).AddTicks(3395),
|
||||
EndDate = new DateTime(2026, 3, 1, 7, 47, 46, 836, DateTimeKind.Utc).AddTicks(7192),
|
||||
GoldReward = 100,
|
||||
Name = "Speed Demon Trial",
|
||||
StartDate = new DateTime(2026, 2, 20, 17, 54, 50, 315, DateTimeKind.Utc).AddTicks(3395),
|
||||
StartDate = new DateTime(2026, 2, 22, 7, 47, 46, 836, DateTimeKind.Utc).AddTicks(7191),
|
||||
TargetTime = 120.0,
|
||||
TrackName = "Dubai Autodrome"
|
||||
});
|
||||
|
||||
@@ -130,3 +130,75 @@ public class DirectorResponse
|
||||
public string environment { get; set; } = "COMMUNITY";
|
||||
public string version { get; set; } = "1.0.0";
|
||||
}
|
||||
|
||||
// Configuration models
|
||||
public class GameConfig
|
||||
{
|
||||
public long ServerTime { get; set; }
|
||||
public string ServerVersion { get; set; } = string.Empty;
|
||||
public string GameVersion { get; set; } = string.Empty;
|
||||
public bool MaintenanceMode { get; set; }
|
||||
public string MessageOfTheDay { get; set; } = string.Empty;
|
||||
public FeatureFlags FeatureFlags { get; set; } = new();
|
||||
public ServerUrls Urls { get; set; } = new();
|
||||
}
|
||||
|
||||
public class FeatureFlags
|
||||
{
|
||||
public bool MultiplayerEnabled { get; set; }
|
||||
public bool LeaderboardsEnabled { get; set; }
|
||||
public bool DailyRewardsEnabled { get; set; }
|
||||
public bool TimeTrialsEnabled { get; set; }
|
||||
public bool CustomContentEnabled { get; set; }
|
||||
public bool SpecialEventsEnabled { get; set; }
|
||||
public bool AllItemsFree { get; set; }
|
||||
}
|
||||
|
||||
public class ServerUrls
|
||||
{
|
||||
public string BaseUrl { get; set; } = string.Empty;
|
||||
public string AssetsUrl { get; set; } = string.Empty;
|
||||
public string LeaderboardsUrl { get; set; } = string.Empty;
|
||||
public string MultiplayerUrl { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class ServerTime
|
||||
{
|
||||
public long ServerTimestamp { get; set; }
|
||||
public long ServerTimeMs { get; set; }
|
||||
public string Timezone { get; set; } = "UTC";
|
||||
public bool IsDST { get; set; }
|
||||
}
|
||||
|
||||
public class ServerStatus
|
||||
{
|
||||
public string Status { get; set; } = "online";
|
||||
public string Version { get; set; } = string.Empty;
|
||||
public bool MaintenanceMode { get; set; }
|
||||
public int PlayerCount { get; set; }
|
||||
public long Uptime { get; set; }
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
// Save/Load models
|
||||
public class PlayerSaveData
|
||||
{
|
||||
public string SynergyId { get; set; } = string.Empty;
|
||||
public string SaveDataJson { get; set; } = string.Empty;
|
||||
public long Version { get; set; } = 1;
|
||||
public long LastModified { get; set; }
|
||||
}
|
||||
|
||||
public class SaveDataRequest
|
||||
{
|
||||
public string SynergyId { get; set; } = string.Empty;
|
||||
public string SaveData { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class SaveDataResponse
|
||||
{
|
||||
public string SaveData { get; set; } = string.Empty;
|
||||
public long Version { get; set; }
|
||||
public long LastModified { get; set; }
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
|
||||
@@ -23,6 +23,24 @@
|
||||
"UnlimitedCurrency": false,
|
||||
"EnableModding": true,
|
||||
"MaxCustomCarUploadSizeMB": 100,
|
||||
"MaxCustomTrackUploadSizeMB": 200
|
||||
"MaxCustomTrackUploadSizeMB": 200,
|
||||
"Version": "1.0.0",
|
||||
"GameVersion": "14.0.1",
|
||||
"MaintenanceMode": false,
|
||||
"MessageOfTheDay": "Welcome to RR3 Community Server! 🏁",
|
||||
"BaseUrl": "http://localhost:5001",
|
||||
"AssetsUrl": "http://localhost:5001/content/api",
|
||||
"LeaderboardsUrl": "http://localhost:5001/leaderboards/api",
|
||||
"MultiplayerUrl": "http://localhost:5001/multiplayer/api"
|
||||
},
|
||||
"FeatureFlags": {
|
||||
"MultiplayerEnabled": false,
|
||||
"LeaderboardsEnabled": true,
|
||||
"DailyRewardsEnabled": true,
|
||||
"TimeTrialsEnabled": true,
|
||||
"CustomContentEnabled": true,
|
||||
"SpecialEventsEnabled": true,
|
||||
"AllItemsFree": true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -23,6 +23,24 @@
|
||||
"UnlimitedCurrency": false,
|
||||
"EnableModding": true,
|
||||
"MaxCustomCarUploadSizeMB": 100,
|
||||
"MaxCustomTrackUploadSizeMB": 200
|
||||
"MaxCustomTrackUploadSizeMB": 200,
|
||||
"Version": "1.0.0",
|
||||
"GameVersion": "14.0.1",
|
||||
"MaintenanceMode": false,
|
||||
"MessageOfTheDay": "Welcome to RR3 Community Server! 🏁",
|
||||
"BaseUrl": "http://localhost:5001",
|
||||
"AssetsUrl": "http://localhost:5001/content/api",
|
||||
"LeaderboardsUrl": "http://localhost:5001/leaderboards/api",
|
||||
"MultiplayerUrl": "http://localhost:5001/multiplayer/api"
|
||||
},
|
||||
"FeatureFlags": {
|
||||
"MultiplayerEnabled": false,
|
||||
"LeaderboardsEnabled": true,
|
||||
"DailyRewardsEnabled": true,
|
||||
"TimeTrialsEnabled": true,
|
||||
"CustomContentEnabled": true,
|
||||
"SpecialEventsEnabled": true,
|
||||
"AllItemsFree": true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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+ac897cd1e9516bfff7c70ab4e2b39a2cfcbecf73")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+c0ddf3aa6fc17b0ad43a33dd4cd956176206e9da")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("RR3CommunityServer")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("RR3CommunityServer")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -1 +1 @@
|
||||
31559bf5ce7cdcd372f0b84358aabb338ec0e3580fe46eb91f589e374e96ec75
|
||||
edd2ae211230cce6ca5372fe176eebd7993497a552197d92affb247d42049965
|
||||
|
||||
@@ -1 +1 @@
|
||||
195a568585dab8f5d82f39f46c64854a79a780f3d0523447b1101938669c634e
|
||||
35adc4026547cea5d97701a77d5dcedeee3f7c5cf662ea648e68db92bb66fd73
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
{"documents":{"E:\\rr3\\RR3CommunityServer\\*":"https://raw.githubusercontent.com/ssfdre38/rr3-server/ac897cd1e9516bfff7c70ab4e2b39a2cfcbecf73/*"}}
|
||||
{"documents":{"E:\\rr3\\RR3CommunityServer\\*":"https://raw.githubusercontent.com/ssfdre38/rr3-server/c0ddf3aa6fc17b0ad43a33dd4cd956176206e9da/*"}}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"GlobalPropertiesHash":"gdYA/PLOQysRMD9wt3+IrqBqQw0g/GZFOcojepf8P6w=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["7Gcs8uTS1W2TjgmuuoBwaL/zy\u002B2wcKht3msEI7xtxEM=","UWedSjPPgrw4tts2Bk2ce0nYJfnBV9zMYOAjYg0PED8=","GecKXPxV0EAagvAtrRNTytwMtFCxZmgKm9sjLyEe8oI=","ORNyAfx/wyfOaBHn1RQCvVUhfXN9r\u002BeVJxHg2zLwBug=","hnhSRoeFpk3C6XWICUlX/lNip6TfbZWFYZv4weSCyrw=","fVR30KYkDSf6Wvsw9TujzlqruhwIMbw1wHxa1z/mksA=","bGtvAdvcs6Zz1qOTjdKz5gd/5jOpXDLvMjTZye3i/QI=","EoVh8vBcGohUnEMEoZuTXrpZ9uBDHT19VmDHc/D\u002Bm0I=","7gMXO5\u002Bhli7od21x4gC/qf3G6ddyyMyoSF6YFX9IaKg=","IdEjAFCVk3xZYjiEMESONot/jkvTj/gnwS5nnpGaIMc=","JVRe\u002Be2d47FunIfxVYRpqRFtljZ8gqrK3xMRy6TCd\u002BQ=","DQG0T8n9f5ohwv9akihU55D4/3WR7\u002BlDnvkdsAHHSgc=","VxDQNRQXYUU41o9SG4HrkKWR59FJIv8lmnwBolB/wE0=","x88k5Bg2fv\u002Bie1eIqFd4doOTQY0lwCNPv/5eJfhIK\u002Bw=","0Slg2/xnc5E9nXprYyph/57wQou\u002BhGSGgKchbo4aNOg="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
{"GlobalPropertiesHash":"gdYA/PLOQysRMD9wt3+IrqBqQw0g/GZFOcojepf8P6w=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["7Gcs8uTS1W2TjgmuuoBwaL/zy\u002B2wcKht3msEI7xtxEM=","Jy8pE0GG8Zj6SAaEr78BdqtQoC22L/QdUV2ukTtEYMY=","GecKXPxV0EAagvAtrRNTytwMtFCxZmgKm9sjLyEe8oI=","ORNyAfx/wyfOaBHn1RQCvVUhfXN9r\u002BeVJxHg2zLwBug=","hnhSRoeFpk3C6XWICUlX/lNip6TfbZWFYZv4weSCyrw=","fVR30KYkDSf6Wvsw9TujzlqruhwIMbw1wHxa1z/mksA=","bGtvAdvcs6Zz1qOTjdKz5gd/5jOpXDLvMjTZye3i/QI=","EoVh8vBcGohUnEMEoZuTXrpZ9uBDHT19VmDHc/D\u002Bm0I=","7gMXO5\u002Bhli7od21x4gC/qf3G6ddyyMyoSF6YFX9IaKg=","IdEjAFCVk3xZYjiEMESONot/jkvTj/gnwS5nnpGaIMc=","JVRe\u002Be2d47FunIfxVYRpqRFtljZ8gqrK3xMRy6TCd\u002BQ=","DQG0T8n9f5ohwv9akihU55D4/3WR7\u002BlDnvkdsAHHSgc=","VxDQNRQXYUU41o9SG4HrkKWR59FJIv8lmnwBolB/wE0=","x88k5Bg2fv\u002Bie1eIqFd4doOTQY0lwCNPv/5eJfhIK\u002Bw=","0Slg2/xnc5E9nXprYyph/57wQou\u002BhGSGgKchbo4aNOg="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
Binary file not shown.
BIN
RR3CommunityServer/rr3community.db-shm
Normal file
BIN
RR3CommunityServer/rr3community.db-shm
Normal file
Binary file not shown.
BIN
RR3CommunityServer/rr3community.db-wal
Normal file
BIN
RR3CommunityServer/rr3community.db-wal
Normal file
Binary file not shown.
Reference in New Issue
Block a user