Bugs Fixed: - Fixed SQLite missing column errors (Cash, Gold, Level, etc.) - Fixed LINQ translation error in AssetsController (File.Exists) - Fixed type mismatch in ModdingController (null coalescing) - Applied database migrations for complete schema Database Changes: - Added User currency columns (Gold, Cash, Level, XP, Reputation) - Added Car custom content fields (IsCustom, CustomAuthor, CustomVersion) - Added GameAsset metadata fields (Md5Hash, CompressedSize) - Added ModPacks table for mod bundling Testing: - Comprehensive test report: COMPREHENSIVE_TEST_REPORT.md - 9/9 critical endpoints passing - All APK-required functionality verified - Database operations validated - Response format compatibility confirmed Status: ✅ Server is production-ready (pending assets) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
388 lines
20 KiB
C#
388 lines
20 KiB
C#
using System;
|
|
using Microsoft.EntityFrameworkCore.Migrations;
|
|
|
|
#nullable disable
|
|
|
|
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
|
|
|
|
namespace RR3CommunityServer.Migrations
|
|
{
|
|
/// <inheritdoc />
|
|
public partial class AddUserCurrencyColumns : Migration
|
|
{
|
|
/// <inheritdoc />
|
|
protected override void Up(MigrationBuilder migrationBuilder)
|
|
{
|
|
migrationBuilder.CreateTable(
|
|
name: "Cars",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
CarId = table.Column<string>(type: "TEXT", nullable: false),
|
|
Name = table.Column<string>(type: "TEXT", nullable: false),
|
|
Manufacturer = table.Column<string>(type: "TEXT", nullable: false),
|
|
ClassType = table.Column<string>(type: "TEXT", nullable: false),
|
|
BasePerformanceRating = table.Column<int>(type: "INTEGER", nullable: false),
|
|
CashPrice = table.Column<int>(type: "INTEGER", nullable: false),
|
|
GoldPrice = table.Column<int>(type: "INTEGER", nullable: false),
|
|
Available = table.Column<bool>(type: "INTEGER", nullable: false)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_Cars", x => x.Id);
|
|
});
|
|
|
|
migrationBuilder.CreateTable(
|
|
name: "CarUpgrades",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
CarId = table.Column<string>(type: "TEXT", nullable: false),
|
|
UpgradeType = table.Column<string>(type: "TEXT", nullable: false),
|
|
Level = table.Column<int>(type: "INTEGER", nullable: false),
|
|
CashCost = table.Column<int>(type: "INTEGER", nullable: false),
|
|
PerformanceIncrease = table.Column<int>(type: "INTEGER", nullable: false)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_CarUpgrades", x => x.Id);
|
|
});
|
|
|
|
migrationBuilder.CreateTable(
|
|
name: "CatalogItems",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
Sku = table.Column<string>(type: "TEXT", nullable: false),
|
|
Name = table.Column<string>(type: "TEXT", nullable: false),
|
|
Type = table.Column<string>(type: "TEXT", nullable: false),
|
|
Price = table.Column<decimal>(type: "TEXT", nullable: false),
|
|
Available = table.Column<bool>(type: "INTEGER", nullable: false)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_CatalogItems", x => x.Id);
|
|
});
|
|
|
|
migrationBuilder.CreateTable(
|
|
name: "DailyRewards",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
UserId = table.Column<int>(type: "INTEGER", nullable: false),
|
|
RewardDate = table.Column<DateTime>(type: "TEXT", nullable: false),
|
|
GoldAmount = table.Column<int>(type: "INTEGER", nullable: false),
|
|
CashAmount = table.Column<int>(type: "INTEGER", nullable: false),
|
|
Claimed = table.Column<bool>(type: "INTEGER", nullable: false),
|
|
ClaimedAt = table.Column<DateTime>(type: "TEXT", nullable: true),
|
|
Streak = table.Column<int>(type: "INTEGER", nullable: false)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_DailyRewards", x => x.Id);
|
|
});
|
|
|
|
migrationBuilder.CreateTable(
|
|
name: "Devices",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
DeviceId = table.Column<string>(type: "TEXT", nullable: false),
|
|
HardwareId = table.Column<string>(type: "TEXT", nullable: false),
|
|
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
|
LastSeenAt = table.Column<DateTime>(type: "TEXT", nullable: false)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_Devices", x => x.Id);
|
|
});
|
|
|
|
migrationBuilder.CreateTable(
|
|
name: "GameAssets",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
AssetId = table.Column<string>(type: "TEXT", nullable: false),
|
|
AssetType = table.Column<string>(type: "TEXT", nullable: false),
|
|
FileName = table.Column<string>(type: "TEXT", nullable: false),
|
|
ContentType = table.Column<string>(type: "TEXT", nullable: false),
|
|
OriginalUrl = table.Column<string>(type: "TEXT", nullable: true),
|
|
EaCdnPath = table.Column<string>(type: "TEXT", nullable: true),
|
|
LocalPath = table.Column<string>(type: "TEXT", nullable: false),
|
|
FileSize = table.Column<long>(type: "INTEGER", nullable: false),
|
|
FileSha256 = table.Column<string>(type: "TEXT", nullable: false),
|
|
Version = table.Column<string>(type: "TEXT", nullable: true),
|
|
DownloadedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
|
LastAccessedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
|
AccessCount = table.Column<int>(type: "INTEGER", nullable: false),
|
|
IsAvailable = table.Column<bool>(type: "INTEGER", nullable: false),
|
|
CarId = table.Column<string>(type: "TEXT", nullable: true),
|
|
TrackId = table.Column<string>(type: "TEXT", nullable: true),
|
|
Category = table.Column<string>(type: "TEXT", nullable: false)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_GameAssets", x => x.Id);
|
|
});
|
|
|
|
migrationBuilder.CreateTable(
|
|
name: "Purchases",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
SynergyId = table.Column<string>(type: "TEXT", nullable: false),
|
|
ItemId = table.Column<string>(type: "TEXT", nullable: false),
|
|
Sku = table.Column<string>(type: "TEXT", nullable: false),
|
|
OrderId = table.Column<string>(type: "TEXT", nullable: false),
|
|
PurchaseTime = table.Column<DateTime>(type: "TEXT", nullable: false),
|
|
Token = table.Column<string>(type: "TEXT", nullable: false),
|
|
Price = table.Column<decimal>(type: "TEXT", nullable: false),
|
|
Status = table.Column<string>(type: "TEXT", nullable: false),
|
|
UserId = table.Column<int>(type: "INTEGER", nullable: true)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_Purchases", x => x.Id);
|
|
});
|
|
|
|
migrationBuilder.CreateTable(
|
|
name: "Sessions",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
SessionId = table.Column<string>(type: "TEXT", nullable: false),
|
|
SynergyId = table.Column<string>(type: "TEXT", nullable: true),
|
|
DeviceId = table.Column<string>(type: "TEXT", nullable: false),
|
|
UserId = table.Column<int>(type: "INTEGER", nullable: true),
|
|
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
|
ExpiresAt = table.Column<DateTime>(type: "TEXT", nullable: false)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_Sessions", x => x.Id);
|
|
});
|
|
|
|
migrationBuilder.CreateTable(
|
|
name: "TimeTrialResults",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
UserId = table.Column<int>(type: "INTEGER", nullable: false),
|
|
TimeTrialId = table.Column<int>(type: "INTEGER", nullable: false),
|
|
TimeSeconds = table.Column<double>(type: "REAL", nullable: false),
|
|
SubmittedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
|
BeatTarget = table.Column<bool>(type: "INTEGER", nullable: false),
|
|
GoldEarned = table.Column<int>(type: "INTEGER", nullable: false),
|
|
CashEarned = table.Column<int>(type: "INTEGER", nullable: false)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_TimeTrialResults", x => x.Id);
|
|
});
|
|
|
|
migrationBuilder.CreateTable(
|
|
name: "TimeTrials",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
Name = table.Column<string>(type: "TEXT", nullable: false),
|
|
TrackName = table.Column<string>(type: "TEXT", nullable: false),
|
|
CarName = table.Column<string>(type: "TEXT", nullable: false),
|
|
StartDate = table.Column<DateTime>(type: "TEXT", nullable: false),
|
|
EndDate = table.Column<DateTime>(type: "TEXT", nullable: false),
|
|
TargetTime = table.Column<double>(type: "REAL", nullable: false),
|
|
GoldReward = table.Column<int>(type: "INTEGER", nullable: false),
|
|
CashReward = table.Column<int>(type: "INTEGER", nullable: false),
|
|
Active = table.Column<bool>(type: "INTEGER", nullable: false)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_TimeTrials", x => x.Id);
|
|
});
|
|
|
|
migrationBuilder.CreateTable(
|
|
name: "Users",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
SynergyId = table.Column<string>(type: "TEXT", nullable: false),
|
|
DeviceId = table.Column<string>(type: "TEXT", nullable: true),
|
|
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
|
Nickname = table.Column<string>(type: "TEXT", nullable: true),
|
|
Gold = table.Column<int>(type: "INTEGER", nullable: true),
|
|
Cash = table.Column<int>(type: "INTEGER", nullable: true),
|
|
Level = table.Column<int>(type: "INTEGER", nullable: true),
|
|
Experience = table.Column<int>(type: "INTEGER", nullable: true),
|
|
Reputation = table.Column<int>(type: "INTEGER", nullable: true)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_Users", x => x.Id);
|
|
});
|
|
|
|
migrationBuilder.CreateTable(
|
|
name: "CareerProgress",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
UserId = table.Column<int>(type: "INTEGER", nullable: false),
|
|
SeriesName = table.Column<string>(type: "TEXT", nullable: false),
|
|
EventName = table.Column<string>(type: "TEXT", nullable: false),
|
|
Completed = table.Column<bool>(type: "INTEGER", nullable: false),
|
|
StarsEarned = table.Column<int>(type: "INTEGER", nullable: false),
|
|
BestTime = table.Column<double>(type: "REAL", nullable: false),
|
|
CompletedAt = table.Column<DateTime>(type: "TEXT", nullable: true)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_CareerProgress", x => x.Id);
|
|
table.ForeignKey(
|
|
name: "FK_CareerProgress_Users_UserId",
|
|
column: x => x.UserId,
|
|
principalTable: "Users",
|
|
principalColumn: "Id",
|
|
onDelete: ReferentialAction.Cascade);
|
|
});
|
|
|
|
migrationBuilder.CreateTable(
|
|
name: "OwnedCars",
|
|
columns: table => new
|
|
{
|
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
|
.Annotation("Sqlite:Autoincrement", true),
|
|
UserId = table.Column<int>(type: "INTEGER", nullable: false),
|
|
CarId = table.Column<string>(type: "TEXT", nullable: false),
|
|
CarName = table.Column<string>(type: "TEXT", nullable: false),
|
|
Manufacturer = table.Column<string>(type: "TEXT", nullable: false),
|
|
ClassType = table.Column<string>(type: "TEXT", nullable: false),
|
|
PerformanceRating = table.Column<int>(type: "INTEGER", nullable: false),
|
|
UpgradeLevel = table.Column<int>(type: "INTEGER", nullable: false),
|
|
PurchasedUpgrades = table.Column<string>(type: "TEXT", nullable: false),
|
|
PurchasedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
|
|
},
|
|
constraints: table =>
|
|
{
|
|
table.PrimaryKey("PK_OwnedCars", x => x.Id);
|
|
table.ForeignKey(
|
|
name: "FK_OwnedCars_Users_UserId",
|
|
column: x => x.UserId,
|
|
principalTable: "Users",
|
|
principalColumn: "Id",
|
|
onDelete: ReferentialAction.Cascade);
|
|
});
|
|
|
|
migrationBuilder.InsertData(
|
|
table: "CarUpgrades",
|
|
columns: new[] { "Id", "CarId", "CashCost", "Level", "PerformanceIncrease", "UpgradeType" },
|
|
values: new object[,]
|
|
{
|
|
{ 1, "nissan_silvia_s15", 5000, 1, 3, "engine" },
|
|
{ 2, "nissan_silvia_s15", 3000, 1, 2, "tires" },
|
|
{ 3, "nissan_silvia_s15", 4000, 1, 2, "suspension" },
|
|
{ 4, "nissan_silvia_s15", 3500, 1, 2, "brakes" },
|
|
{ 5, "nissan_silvia_s15", 4500, 1, 3, "drivetrain" }
|
|
});
|
|
|
|
migrationBuilder.InsertData(
|
|
table: "Cars",
|
|
columns: new[] { "Id", "Available", "BasePerformanceRating", "CarId", "CashPrice", "ClassType", "GoldPrice", "Manufacturer", "Name" },
|
|
values: new object[,]
|
|
{
|
|
{ 1, true, 45, "nissan_silvia_s15", 25000, "C", 0, "Nissan", "Nissan Silvia Spec-R" },
|
|
{ 2, true, 58, "ford_focus_rs", 85000, "B", 150, "Ford", "Ford Focus RS" },
|
|
{ 3, true, 72, "porsche_911_gt3", 0, "A", 350, "Porsche", "Porsche 911 GT3 RS" },
|
|
{ 4, true, 88, "ferrari_488_gtb", 0, "S", 750, "Ferrari", "Ferrari 488 GTB" },
|
|
{ 5, true, 105, "mclaren_p1_gtr", 0, "R", 1500, "McLaren", "McLaren P1 GTR" }
|
|
});
|
|
|
|
migrationBuilder.InsertData(
|
|
table: "CatalogItems",
|
|
columns: new[] { "Id", "Available", "Name", "Price", "Sku", "Type" },
|
|
values: new object[,]
|
|
{
|
|
{ 1, true, "1000 Gold", 0.99m, "com.ea.rr3.gold_1000", "currency" },
|
|
{ 2, true, "Starter Car", 0m, "com.ea.rr3.car_tier1", "car" },
|
|
{ 3, true, "Engine Upgrade", 4.99m, "com.ea.rr3.upgrade_engine", "upgrade" },
|
|
{ 4, true, "100 Gold", 0m, "com.ea.rr3.gold_100", "currency" },
|
|
{ 5, true, "500 Gold", 0m, "com.ea.rr3.gold_500", "currency" },
|
|
{ 6, true, "1000 Gold", 0m, "com.ea.rr3.gold_1000", "currency" },
|
|
{ 7, true, "5000 Gold", 0m, "com.ea.rr3.gold_5000", "currency" }
|
|
});
|
|
|
|
migrationBuilder.InsertData(
|
|
table: "TimeTrials",
|
|
columns: new[] { "Id", "Active", "CarName", "CashReward", "EndDate", "GoldReward", "Name", "StartDate", "TargetTime", "TrackName" },
|
|
values: new object[,]
|
|
{
|
|
{ 1, true, "Any Car", 10000, new DateTime(2026, 2, 25, 9, 44, 15, 715, DateTimeKind.Utc).AddTicks(5651), 50, "Daily Sprint Challenge", new DateTime(2026, 2, 18, 9, 44, 15, 715, DateTimeKind.Utc).AddTicks(5648), 90.5, "Silverstone National" },
|
|
{ 2, true, "Any Car", 25000, new DateTime(2026, 2, 25, 9, 44, 15, 715, DateTimeKind.Utc).AddTicks(5658), 100, "Speed Demon Trial", new DateTime(2026, 2, 18, 9, 44, 15, 715, DateTimeKind.Utc).AddTicks(5658), 120.0, "Dubai Autodrome" }
|
|
});
|
|
|
|
migrationBuilder.CreateIndex(
|
|
name: "IX_CareerProgress_UserId",
|
|
table: "CareerProgress",
|
|
column: "UserId");
|
|
|
|
migrationBuilder.CreateIndex(
|
|
name: "IX_OwnedCars_UserId",
|
|
table: "OwnedCars",
|
|
column: "UserId");
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void Down(MigrationBuilder migrationBuilder)
|
|
{
|
|
migrationBuilder.DropTable(
|
|
name: "CareerProgress");
|
|
|
|
migrationBuilder.DropTable(
|
|
name: "Cars");
|
|
|
|
migrationBuilder.DropTable(
|
|
name: "CarUpgrades");
|
|
|
|
migrationBuilder.DropTable(
|
|
name: "CatalogItems");
|
|
|
|
migrationBuilder.DropTable(
|
|
name: "DailyRewards");
|
|
|
|
migrationBuilder.DropTable(
|
|
name: "Devices");
|
|
|
|
migrationBuilder.DropTable(
|
|
name: "GameAssets");
|
|
|
|
migrationBuilder.DropTable(
|
|
name: "OwnedCars");
|
|
|
|
migrationBuilder.DropTable(
|
|
name: "Purchases");
|
|
|
|
migrationBuilder.DropTable(
|
|
name: "Sessions");
|
|
|
|
migrationBuilder.DropTable(
|
|
name: "TimeTrialResults");
|
|
|
|
migrationBuilder.DropTable(
|
|
name: "TimeTrials");
|
|
|
|
migrationBuilder.DropTable(
|
|
name: "Users");
|
|
}
|
|
}
|
|
}
|