player dbs...

This commit is contained in:
rfi
2024-02-20 18:35:48 +07:00
parent f33a60d33c
commit 3dc95a1044
22 changed files with 134833 additions and 16 deletions

View File

@@ -4,21 +4,21 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateRuntimeConfigurationFiles>True</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
<PackageReference Include="protobuf-net" Version="3.2.30" />
</ItemGroup>
<ItemGroup>
<None Update="Resources\sharecfgdata\chapter_template.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\sharecfgdata\ship_data_statistics.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\sharecfgdata\task_data_template.json">
<None Update="Resources\sharecfgdata\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

View File

@@ -6,14 +6,16 @@ public static class Data
{
static readonly Logger c = new(nameof(Data), ConsoleColor.Yellow);
public static Dictionary<int, ChapterTemplate> ChapterTemplate;
public static Dictionary<int, ShipDataStatistics> ShipDataStatistics;
public static Dictionary<int, TaskDateTemplate> TaskDataTemplate;
public static Dictionary<int, ChapterTemplate> ChapterTemplate = [];
public static Dictionary<int, ShipDataStatistics> ShipDataStatistics = [];
public static Dictionary<int, ShipDataTemplate> ShipDataTemplate = [];
public static Dictionary<int, TaskDateTemplate> TaskDataTemplate = [];
public static void Load()
{
LoadData(ref ChapterTemplate, "chapter_template.json", nameof(ChapterTemplate));
LoadData(ref ShipDataStatistics, "ship_data_statistics.json", nameof(ShipDataStatistics));
LoadData(ref ShipDataTemplate, "ship_data_template.json", nameof(ShipDataTemplate));
LoadData(ref TaskDataTemplate, "task_data_template.json", nameof(TaskDataTemplate));
c.Log("All data tables loaded");

View File

@@ -0,0 +1,80 @@
using System.Text.Json.Serialization;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace BLHX.Server.Common.Data
{
public partial class ShipDataTemplate : Model
{
[JsonPropertyName("airassist_time")]
public List<uint> AirassistTime { get; set; }
[JsonPropertyName("buff_list")]
public List<uint> BuffList { get; set; }
[JsonPropertyName("buff_list_display")]
public List<uint> BuffListDisplay { get; set; }
[JsonPropertyName("can_get_proficency")]
public uint CanGetProficency { get; set; }
[JsonPropertyName("energy")]
public uint Energy { get; set; }
[JsonPropertyName("equip_1")]
public List<uint> Equip1 { get; set; }
[JsonPropertyName("equip_2")]
public List<uint> Equip2 { get; set; }
[JsonPropertyName("equip_3")]
public List<uint> Equip3 { get; set; }
[JsonPropertyName("equip_4")]
public List<uint> Equip4 { get; set; }
[JsonPropertyName("equip_5")]
public List<uint> Equip5 { get; set; }
[JsonPropertyName("equip_id_1")]
public uint EquipId1 { get; set; }
[JsonPropertyName("equip_id_2")]
public uint EquipId2 { get; set; }
[JsonPropertyName("equip_id_3")]
public uint EquipId3 { get; set; }
[JsonPropertyName("group_type")]
public uint GroupType { get; set; }
[JsonPropertyName("hide_buff_list")]
public List<uint> HideBuffList { get; set; }
[JsonPropertyName("id")]
public uint Id { get; set; }
[JsonPropertyName("max_level")]
public uint MaxLevel { get; set; }
[JsonPropertyName("oil_at_end")]
public uint OilAtEnd { get; set; }
[JsonPropertyName("oil_at_start")]
public uint OilAtStart { get; set; }
[JsonPropertyName("specific_type")]
public List<string> SpecificType { get; set; }
[JsonPropertyName("star")]
public uint Star { get; set; }
[JsonPropertyName("star_max")]
public uint StarMax { get; set; }
[JsonPropertyName("strengthen_id")]
public uint StrengthenId { get; set; }
[JsonPropertyName("type")]
public uint Type { get; set; }
}
}

View File

@@ -10,7 +10,8 @@ namespace BLHX.Server.Common.Database
public AccountContext()
{
Database.EnsureCreated();
if (Database.GetPendingMigrations().Any())
Database.Migrate();
}
protected override void OnConfiguring(DbContextOptionsBuilder options)

View File

@@ -1,6 +1,9 @@
using BLHX.Server.Common.Utils;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using System.Reflection;
using System.Text.Json;
namespace BLHX.Server.Common.Database
{
@@ -8,6 +11,7 @@ namespace BLHX.Server.Common.Database
{
public static readonly Logger c = new(nameof(DBManager), ConsoleColor.DarkCyan);
public static AccountContext AccountContext { get; }
public static PlayerContext PlayerContext { get; }
static DBManager()
{
@@ -20,6 +24,7 @@ namespace BLHX.Server.Common.Database
}
AccountContext = new AccountContext();
PlayerContext = new PlayerContext();
}
}
@@ -33,4 +38,22 @@ namespace BLHX.Server.Common.Database
{
string IBLHXDBContext.GetFullDbPath() => Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, (string)typeof(TSelf).GetProperty(nameof(DbPath))!.GetValue(null)!);
}
public static class PropertyBuilderExtensions
{
public static PropertyBuilder<T> HasJsonConversion<T>(this PropertyBuilder<T> propertyBuilder) where T : class, new()
{
ValueConverter<T, string> converter = new
(
v => JsonSerializer.Serialize(v, JsonSerializerOptions.Default),
v => JsonSerializer.Deserialize<T>(v, JsonSerializerOptions.Default) ?? new T()
);
propertyBuilder.HasConversion(converter);
propertyBuilder.Metadata.SetValueConverter(converter);
propertyBuilder.HasColumnType("jsonb");
return propertyBuilder;
}
}
}

View File

@@ -0,0 +1,235 @@
using BLHX.Server.Common.Proto.common;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BLHX.Server.Common.Database
{
public sealed class PlayerContext : DbContext, IBLHXDBContext<PlayerContext>
{
public static string DbPath => "Databases/players.db";
public DbSet<Player> Players { get; set; }
public DbSet<PlayerResource> Resources { get; set; }
public DbSet<PlayerShip> Ships { get; set; }
public PlayerContext()
{
if (Database.GetPendingMigrations().Any())
Database.Migrate();
}
public Player Init(string token, uint shipId, string name)
{
var player = new Player(token, new Displayinfo() { Icon = shipId }, name);
Players.Add(player);
SaveChanges();
// Initial resousrces
player.DoResource(8, 4000);
player.DoResource(7, 5);
player.DoResource(5, 5);
player.DoResource(2, 500);
player.DoResource(1, 3000);
// Selected ship and Long Island as default...
player.AddShip(shipId);
player.AddShip(106011);
SaveChanges();
return player;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Player>(e =>
{
e.Property(b => b.DisplayInfo)
.HasJsonConversion();
e.HasMany(b => b.Resources)
.WithOne(e => e.Player)
.HasForeignKey(e => e.PlayerUid)
.IsRequired();
e.HasMany(b => b.Ships)
.WithOne(e => e.Player)
.HasForeignKey(e => e.PlayerUid)
.IsRequired();
});
modelBuilder.Entity<PlayerShip>(e =>
{
e.Property(b => b.Id)
.ValueGeneratedOnAdd();
e.Property(b => b.State)
.HasJsonConversion();
e.Property(b => b.EquipInfoLists)
.HasJsonConversion();
e.Property(b => b.TransformLists)
.HasJsonConversion();
e.Property(b => b.SkillIdLists)
.HasJsonConversion();
e.Property(b => b.StrengthLists)
.HasJsonConversion();
e.Property(b => b.MetaRepairLists)
.HasJsonConversion();
e.Property(b => b.CoreLists)
.HasJsonConversion();
});
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseLazyLoadingProxies()
.EnableSensitiveDataLogging()
.UseSqlite($"Data Source={((IBLHXDBContext)this).GetFullDbPath()}");
}
[PrimaryKey(nameof(Uid))]
[Index(nameof(Token), IsUnique = true)]
public class Player
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public uint Uid { get; set; }
public string Token { get; set; }
public string Name { get; set; }
public uint Level { get; set; }
// TODO: Exp add setter to recalculate cap and set level
public uint Exp { get; set; }
public Displayinfo DisplayInfo { get; set; }
public DateTime CreatedAt { get; set; }
public virtual ICollection<PlayerResource> Resources { get; set; } = [];
public virtual ICollection<PlayerShip> Ships { get; set; } = [];
public Player(string token, Displayinfo displayInfo, string name)
{
DisplayInfo = displayInfo;
Token = token;
Name = name;
Level = 1;
CreatedAt = DateTime.Now;
}
public void DoResource(uint id, int num)
{
var res = Resources.SingleOrDefault(x => x.Id == id);
if (res is null)
{
res = new() { Id = id, PlayerUid = Uid };
DBManager.PlayerContext.Resources.Add(res);
}
if (num < 0)
res.Num = (uint)Math.Max(res.Num - num, 0);
else
res.Num = Math.Max(res.Num + (uint)num, uint.MaxValue);
}
public void AddShip(uint shipTemplateId)
{
if (!Data.Data.ShipDataTemplate.TryGetValue((int)shipTemplateId, out var shipTemplate))
throw new InvalidDataException($"Ship template {shipTemplateId} not found!");
var ship = new PlayerShip()
{
TemplateId = shipTemplateId,
Level = 1,
EquipInfoLists = [
new EquipskinInfo() { Id = shipTemplate.EquipId1 },
new EquipskinInfo() { Id = shipTemplate.EquipId2 },
new EquipskinInfo() { Id = shipTemplate.EquipId3 },
new EquipskinInfo(),
new EquipskinInfo(),
],
Energy = shipTemplate.Energy,
// I'm not sure if buff_list in template refer to this thing
SkillIdLists = shipTemplate.BuffList.Select(x => new Shipskill() { SkillId = x, SkillLv = 1 }).ToList(),
//Idk if this is really the default
Intimacy = 5000,
PlayerUid = Uid
};
DBManager.PlayerContext.Ships.Add(ship);
}
}
[PrimaryKey(nameof(Id), nameof(PlayerUid))]
public class PlayerResource
{
[Key]
public uint Id { get; set; }
public uint Num { get; set; }
[Key]
public uint PlayerUid { get; set; }
public virtual Player Player { get; set; } = null!;
}
[PrimaryKey(nameof(Id))]
public class PlayerShip
{
[Key]
public uint Id { get; set; }
public uint TemplateId { get; set; }
public uint Level { get; set; }
// TODO: Exp add setter to recalculate cap and set level
public uint Exp { get; set; }
public List<EquipskinInfo> EquipInfoLists { get; set; } = [];
public uint Energy { get; set; }
public Shipstate State { get; set; } = new Shipstate() { State = 1, StateInfo1 = 1 };
public bool IsLocked { get; set; }
public List<TransformInfo> TransformLists { get; set; } = [];
public List<Shipskill> SkillIdLists { get; set; } = [];
public uint Intimacy { get; set; }
public uint Proficiency { get; set; }
public List<StrengthInfo> StrengthLists { get; set; } = [];
public uint SkinId { get; set; }
public uint Propose { get; set; }
public string? Name { get; set; }
public uint CommanderId { get; set; }
public uint BluePrintFlag { get; set; }
public uint? CommonFlag { get; set; }
public uint ActivityNpc { get; set; }
public List<uint> MetaRepairLists { get; set; } = [];
public List<Shipcoreinfo> CoreLists { get; set; } = [];
public uint PlayerUid { get; set; }
public virtual Player Player { get; set; } = null!;
public DateTime CreatedAt { get; set; } = DateTime.Now;
public DateTime? LastChangeName { get; set; }
public Shipinfo ToProto()
{
return new()
{
Id = Id,
TemplateId = TemplateId,
Level = Level,
Exp = Exp,
EquipInfoLists = EquipInfoLists,
Energy = Energy,
State = State,
IsLocked = Convert.ToUInt32(IsLocked),
TransformLists = TransformLists,
SkillIdLists = SkillIdLists,
Intimacy = Intimacy,
Proficiency = Proficiency,
StrengthLists = StrengthLists,
CreateTime = (uint)new DateTimeOffset(CreatedAt).ToUnixTimeSeconds(),
SkinId = SkinId,
Propose = Propose,
Name = Name,
ChangeNameTimestamp = LastChangeName is null ? 0 : (uint)new DateTimeOffset((DateTime)LastChangeName).ToUnixTimeSeconds(),
Commanderid = CommanderId,
MaxLevel = Data.Data.ShipDataTemplate[(int)TemplateId].MaxLevel,
BluePrintFlag = BluePrintFlag,
CommonFlag = CommonFlag ?? default,
ActivityNpc = ActivityNpc,
MetaRepairLists = MetaRepairLists,
CoreLists = CoreLists
};
}
}
}

View File

@@ -0,0 +1,43 @@
// <auto-generated />
using BLHX.Server.Common.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BLHX.Server.Common.Migrations
{
[DbContext(typeof(AccountContext))]
[Migration("20240220103725_Init_AccountContext")]
partial class Init_AccountContext
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.2");
modelBuilder.Entity("BLHX.Server.Common.Database.Account", b =>
{
b.Property<uint>("Uid")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("DeviceId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Token")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Uid");
b.ToTable("Accounts");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BLHX.Server.Common.Migrations
{
/// <inheritdoc />
public partial class Init_AccountContext : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Accounts",
columns: table => new
{
Uid = table.Column<uint>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DeviceId = table.Column<string>(type: "TEXT", nullable: false),
Token = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Accounts", x => x.Uid);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Accounts");
}
}
}

View File

@@ -0,0 +1,40 @@
// <auto-generated />
using BLHX.Server.Common.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BLHX.Server.Common.Migrations
{
[DbContext(typeof(AccountContext))]
partial class AccountContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.2");
modelBuilder.Entity("BLHX.Server.Common.Database.Account", b =>
{
b.Property<uint>("Uid")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("DeviceId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Token")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Uid");
b.ToTable("Accounts");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,203 @@
// <auto-generated />
using System;
using BLHX.Server.Common.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BLHX.Server.Common.Migrations.Player
{
[DbContext(typeof(PlayerContext))]
[Migration("20240220103529_Init_PlayerContext")]
partial class Init_PlayerContext
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.2")
.HasAnnotation("Proxies:ChangeTracking", false)
.HasAnnotation("Proxies:CheckEquality", false)
.HasAnnotation("Proxies:LazyLoading", true);
modelBuilder.Entity("BLHX.Server.Common.Database.Player", b =>
{
b.Property<uint>("Uid")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("DisplayInfo")
.IsRequired()
.HasColumnType("jsonb");
b.Property<uint>("Exp")
.HasColumnType("INTEGER");
b.Property<uint>("Level")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Token")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Uid");
b.HasIndex("Token")
.IsUnique();
b.ToTable("Players");
});
modelBuilder.Entity("BLHX.Server.Common.Database.PlayerResource", b =>
{
b.Property<uint>("Id")
.HasColumnType("INTEGER");
b.Property<uint>("PlayerUid")
.HasColumnType("INTEGER");
b.Property<uint>("Num")
.HasColumnType("INTEGER");
b.HasKey("Id", "PlayerUid");
b.HasIndex("PlayerUid");
b.ToTable("Resources");
});
modelBuilder.Entity("BLHX.Server.Common.Database.PlayerShip", b =>
{
b.Property<uint>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<uint>("ActivityNpc")
.HasColumnType("INTEGER");
b.Property<uint>("BluePrintFlag")
.HasColumnType("INTEGER");
b.Property<uint>("CommanderId")
.HasColumnType("INTEGER");
b.Property<uint?>("CommonFlag")
.HasColumnType("INTEGER");
b.Property<string>("CoreLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<uint>("Energy")
.HasColumnType("INTEGER");
b.Property<string>("EquipInfoLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<uint>("Exp")
.HasColumnType("INTEGER");
b.Property<uint>("Intimacy")
.HasColumnType("INTEGER");
b.Property<bool>("IsLocked")
.HasColumnType("INTEGER");
b.Property<DateTime>("LastChangeName")
.HasColumnType("TEXT");
b.Property<uint>("Level")
.HasColumnType("INTEGER");
b.Property<string>("MetaRepairLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<uint>("PlayerUid")
.HasColumnType("INTEGER");
b.Property<uint>("Proficiency")
.HasColumnType("INTEGER");
b.Property<uint>("Propose")
.HasColumnType("INTEGER");
b.Property<string>("SkillIdLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<uint>("SkinId")
.HasColumnType("INTEGER");
b.Property<string>("State")
.IsRequired()
.HasColumnType("jsonb");
b.Property<string>("StrengthLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<uint>("TemplateId")
.HasColumnType("INTEGER");
b.Property<string>("TransformLists")
.IsRequired()
.HasColumnType("jsonb");
b.HasKey("Id");
b.HasIndex("PlayerUid");
b.ToTable("Ships");
});
modelBuilder.Entity("BLHX.Server.Common.Database.PlayerResource", b =>
{
b.HasOne("BLHX.Server.Common.Database.Player", "Player")
.WithMany("Resources")
.HasForeignKey("PlayerUid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Player");
});
modelBuilder.Entity("BLHX.Server.Common.Database.PlayerShip", b =>
{
b.HasOne("BLHX.Server.Common.Database.Player", "Player")
.WithMany("Ships")
.HasForeignKey("PlayerUid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Player");
});
modelBuilder.Entity("BLHX.Server.Common.Database.Player", b =>
{
b.Navigation("Resources");
b.Navigation("Ships");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,123 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BLHX.Server.Common.Migrations.Player
{
/// <inheritdoc />
public partial class Init_PlayerContext : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Players",
columns: table => new
{
Uid = table.Column<uint>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Token = table.Column<string>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: false),
Level = table.Column<uint>(type: "INTEGER", nullable: false),
Exp = table.Column<uint>(type: "INTEGER", nullable: false),
DisplayInfo = table.Column<string>(type: "jsonb", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Players", x => x.Uid);
});
migrationBuilder.CreateTable(
name: "Resources",
columns: table => new
{
Id = table.Column<uint>(type: "INTEGER", nullable: false),
PlayerUid = table.Column<uint>(type: "INTEGER", nullable: false),
Num = table.Column<uint>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Resources", x => new { x.Id, x.PlayerUid });
table.ForeignKey(
name: "FK_Resources_Players_PlayerUid",
column: x => x.PlayerUid,
principalTable: "Players",
principalColumn: "Uid",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Ships",
columns: table => new
{
Id = table.Column<uint>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
TemplateId = table.Column<uint>(type: "INTEGER", nullable: false),
Level = table.Column<uint>(type: "INTEGER", nullable: false),
Exp = table.Column<uint>(type: "INTEGER", nullable: false),
EquipInfoLists = table.Column<string>(type: "jsonb", nullable: false),
Energy = table.Column<uint>(type: "INTEGER", nullable: false),
State = table.Column<string>(type: "jsonb", nullable: false),
IsLocked = table.Column<bool>(type: "INTEGER", nullable: false),
TransformLists = table.Column<string>(type: "jsonb", nullable: false),
SkillIdLists = table.Column<string>(type: "jsonb", nullable: false),
Intimacy = table.Column<uint>(type: "INTEGER", nullable: false),
Proficiency = table.Column<uint>(type: "INTEGER", nullable: false),
StrengthLists = table.Column<string>(type: "jsonb", nullable: false),
SkinId = table.Column<uint>(type: "INTEGER", nullable: false),
Propose = table.Column<uint>(type: "INTEGER", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: true),
CommanderId = table.Column<uint>(type: "INTEGER", nullable: false),
BluePrintFlag = table.Column<uint>(type: "INTEGER", nullable: false),
CommonFlag = table.Column<uint>(type: "INTEGER", nullable: true),
ActivityNpc = table.Column<uint>(type: "INTEGER", nullable: false),
MetaRepairLists = table.Column<string>(type: "jsonb", nullable: false),
CoreLists = table.Column<string>(type: "jsonb", nullable: false),
PlayerUid = table.Column<uint>(type: "INTEGER", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
LastChangeName = table.Column<DateTime>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Ships", x => x.Id);
table.ForeignKey(
name: "FK_Ships_Players_PlayerUid",
column: x => x.PlayerUid,
principalTable: "Players",
principalColumn: "Uid",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Players_Token",
table: "Players",
column: "Token",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Resources_PlayerUid",
table: "Resources",
column: "PlayerUid");
migrationBuilder.CreateIndex(
name: "IX_Ships_PlayerUid",
table: "Ships",
column: "PlayerUid");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Resources");
migrationBuilder.DropTable(
name: "Ships");
migrationBuilder.DropTable(
name: "Players");
}
}
}

View File

@@ -0,0 +1,203 @@
// <auto-generated />
using System;
using BLHX.Server.Common.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BLHX.Server.Common.Migrations.Player
{
[DbContext(typeof(PlayerContext))]
[Migration("20240220111732_NullableLastChangeName")]
partial class NullableLastChangeName
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.2")
.HasAnnotation("Proxies:ChangeTracking", false)
.HasAnnotation("Proxies:CheckEquality", false)
.HasAnnotation("Proxies:LazyLoading", true);
modelBuilder.Entity("BLHX.Server.Common.Database.Player", b =>
{
b.Property<uint>("Uid")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("DisplayInfo")
.IsRequired()
.HasColumnType("jsonb");
b.Property<uint>("Exp")
.HasColumnType("INTEGER");
b.Property<uint>("Level")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Token")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Uid");
b.HasIndex("Token")
.IsUnique();
b.ToTable("Players");
});
modelBuilder.Entity("BLHX.Server.Common.Database.PlayerResource", b =>
{
b.Property<uint>("Id")
.HasColumnType("INTEGER");
b.Property<uint>("PlayerUid")
.HasColumnType("INTEGER");
b.Property<uint>("Num")
.HasColumnType("INTEGER");
b.HasKey("Id", "PlayerUid");
b.HasIndex("PlayerUid");
b.ToTable("Resources");
});
modelBuilder.Entity("BLHX.Server.Common.Database.PlayerShip", b =>
{
b.Property<uint>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<uint>("ActivityNpc")
.HasColumnType("INTEGER");
b.Property<uint>("BluePrintFlag")
.HasColumnType("INTEGER");
b.Property<uint>("CommanderId")
.HasColumnType("INTEGER");
b.Property<uint?>("CommonFlag")
.HasColumnType("INTEGER");
b.Property<string>("CoreLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<uint>("Energy")
.HasColumnType("INTEGER");
b.Property<string>("EquipInfoLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<uint>("Exp")
.HasColumnType("INTEGER");
b.Property<uint>("Intimacy")
.HasColumnType("INTEGER");
b.Property<bool>("IsLocked")
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastChangeName")
.HasColumnType("TEXT");
b.Property<uint>("Level")
.HasColumnType("INTEGER");
b.Property<string>("MetaRepairLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<uint>("PlayerUid")
.HasColumnType("INTEGER");
b.Property<uint>("Proficiency")
.HasColumnType("INTEGER");
b.Property<uint>("Propose")
.HasColumnType("INTEGER");
b.Property<string>("SkillIdLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<uint>("SkinId")
.HasColumnType("INTEGER");
b.Property<string>("State")
.IsRequired()
.HasColumnType("jsonb");
b.Property<string>("StrengthLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<uint>("TemplateId")
.HasColumnType("INTEGER");
b.Property<string>("TransformLists")
.IsRequired()
.HasColumnType("jsonb");
b.HasKey("Id");
b.HasIndex("PlayerUid");
b.ToTable("Ships");
});
modelBuilder.Entity("BLHX.Server.Common.Database.PlayerResource", b =>
{
b.HasOne("BLHX.Server.Common.Database.Player", "Player")
.WithMany("Resources")
.HasForeignKey("PlayerUid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Player");
});
modelBuilder.Entity("BLHX.Server.Common.Database.PlayerShip", b =>
{
b.HasOne("BLHX.Server.Common.Database.Player", "Player")
.WithMany("Ships")
.HasForeignKey("PlayerUid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Player");
});
modelBuilder.Entity("BLHX.Server.Common.Database.Player", b =>
{
b.Navigation("Resources");
b.Navigation("Ships");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BLHX.Server.Common.Migrations.Player
{
/// <inheritdoc />
public partial class NullableLastChangeName : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "LastChangeName",
table: "Ships",
type: "TEXT",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "TEXT");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "LastChangeName",
table: "Ships",
type: "TEXT",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
oldClrType: typeof(DateTime),
oldType: "TEXT",
oldNullable: true);
}
}
}

View File

@@ -0,0 +1,200 @@
// <auto-generated />
using System;
using BLHX.Server.Common.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BLHX.Server.Common.Migrations.Player
{
[DbContext(typeof(PlayerContext))]
partial class PlayerContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.2")
.HasAnnotation("Proxies:ChangeTracking", false)
.HasAnnotation("Proxies:CheckEquality", false)
.HasAnnotation("Proxies:LazyLoading", true);
modelBuilder.Entity("BLHX.Server.Common.Database.Player", b =>
{
b.Property<uint>("Uid")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("DisplayInfo")
.IsRequired()
.HasColumnType("jsonb");
b.Property<uint>("Exp")
.HasColumnType("INTEGER");
b.Property<uint>("Level")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Token")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Uid");
b.HasIndex("Token")
.IsUnique();
b.ToTable("Players");
});
modelBuilder.Entity("BLHX.Server.Common.Database.PlayerResource", b =>
{
b.Property<uint>("Id")
.HasColumnType("INTEGER");
b.Property<uint>("PlayerUid")
.HasColumnType("INTEGER");
b.Property<uint>("Num")
.HasColumnType("INTEGER");
b.HasKey("Id", "PlayerUid");
b.HasIndex("PlayerUid");
b.ToTable("Resources");
});
modelBuilder.Entity("BLHX.Server.Common.Database.PlayerShip", b =>
{
b.Property<uint>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<uint>("ActivityNpc")
.HasColumnType("INTEGER");
b.Property<uint>("BluePrintFlag")
.HasColumnType("INTEGER");
b.Property<uint>("CommanderId")
.HasColumnType("INTEGER");
b.Property<uint?>("CommonFlag")
.HasColumnType("INTEGER");
b.Property<string>("CoreLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<uint>("Energy")
.HasColumnType("INTEGER");
b.Property<string>("EquipInfoLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<uint>("Exp")
.HasColumnType("INTEGER");
b.Property<uint>("Intimacy")
.HasColumnType("INTEGER");
b.Property<bool>("IsLocked")
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastChangeName")
.HasColumnType("TEXT");
b.Property<uint>("Level")
.HasColumnType("INTEGER");
b.Property<string>("MetaRepairLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<uint>("PlayerUid")
.HasColumnType("INTEGER");
b.Property<uint>("Proficiency")
.HasColumnType("INTEGER");
b.Property<uint>("Propose")
.HasColumnType("INTEGER");
b.Property<string>("SkillIdLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<uint>("SkinId")
.HasColumnType("INTEGER");
b.Property<string>("State")
.IsRequired()
.HasColumnType("jsonb");
b.Property<string>("StrengthLists")
.IsRequired()
.HasColumnType("jsonb");
b.Property<uint>("TemplateId")
.HasColumnType("INTEGER");
b.Property<string>("TransformLists")
.IsRequired()
.HasColumnType("jsonb");
b.HasKey("Id");
b.HasIndex("PlayerUid");
b.ToTable("Ships");
});
modelBuilder.Entity("BLHX.Server.Common.Database.PlayerResource", b =>
{
b.HasOne("BLHX.Server.Common.Database.Player", "Player")
.WithMany("Resources")
.HasForeignKey("PlayerUid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Player");
});
modelBuilder.Entity("BLHX.Server.Common.Database.PlayerShip", b =>
{
b.HasOne("BLHX.Server.Common.Database.Player", "Player")
.WithMany("Ships")
.HasForeignKey("PlayerUid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Player");
});
modelBuilder.Entity("BLHX.Server.Common.Database.Player", b =>
{
b.Navigation("Resources");
b.Navigation("Ships");
});
#pragma warning restore 612, 618
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,6 @@
using BLHX.Server.Common.Proto;
using BLHX.Server.Common.Database;
using BLHX.Server.Common.Proto;
using BLHX.Server.Common.Proto.p11;
using BLHX.Server.Common.Utils;
using ProtoBuf;
using System.Buffers.Binary;
@@ -10,12 +12,15 @@ namespace BLHX.Server.Game
{
public class Connection
{
public const uint Monday0oclockTimestamp = 1606114800;
public readonly Logger c;
public Account account = null!;
public Player player = null!;
readonly TcpClient tcpClient;
readonly CancellationTokenSource cts = new();
readonly Task loopTask;
ushort packetIdx = 0;
private ushort NextPacketIdx => packetIdx++;
private ushort NextPacketIdx => packetIdx;
public IPEndPoint EndPoint => (IPEndPoint)tcpClient.Client.RemoteEndPoint!;
public Connection(TcpClient tcpClient)
@@ -51,7 +56,7 @@ namespace BLHX.Server.Game
string svrList = @"[{""id"":1,""name"":""BLHX.Server"",""state"":0,""flag"":0,""sort"":0}]";
SendHttpResponse(svrList, "application/json");
readLen = len;
cts.Cancel();
EndProtocol();
break;
}
@@ -63,13 +68,18 @@ namespace BLHX.Server.Game
var handler = PacketFactory.GetPacketHandler(packet.command);
if (handler is not null)
{
handler(this, packet);
packetIdx++;
}
else
{
c.Warn($"{packet.command} unhandled!"
#if DEBUG
, Enum.IsDefined(packet.command) ? BitConverter.ToString(packet.bytes).Replace("-", "") : BitConverter.ToString(buf[readLen..]).Replace("-", "")
#endif
);
}
readLen += packet.length + Packet.LENGTH_SIZE;
}

View File

@@ -31,7 +31,7 @@ namespace BLHX.Server.Game.Handlers
"dTag-1"
],
Timestamp = (uint)DateTimeOffset.Now.ToUnixTimeSeconds(),
Monday0oclockTimestamp = 1606114800
Monday0oclockTimestamp = Connection.Monday0oclockTimestamp
});
connection.EndProtocol();
}
@@ -75,10 +75,40 @@ namespace BLHX.Server.Game.Handlers
{
rsp.Result = 1;
connection.Send(rsp);
connection.EndProtocol();
return;
}
// TODO: Create/access player data!
connection.account = account;
rsp.ServerTicket = req.ServerTicket;
var player = DBManager.PlayerContext.Players.SingleOrDefault(x => x.Token == req.ServerTicket);
if (player is null)
{
connection.Send(rsp);
return;
}
connection.player = player;
rsp.UserId = player.Uid;
connection.Send(rsp);
}
[PacketHandler(Command.Cs10024)]
static void CreateNewPlayerHandler(Connection connection, Packet packet)
{
var req = packet.Decode<Cs10024>();
var rsp = new Sc10025();
if (connection.player is not null)
{
rsp.Result = 1011;
connection.Send(rsp);
return;
}
var player = DBManager.PlayerContext.Init(connection.account.Token, req.ShipId, req.NickName);
connection.player = player;
rsp.UserId = connection.player.Uid;
connection.Send(rsp);
}

View File

@@ -0,0 +1,61 @@
using BLHX.Server.Common.Proto;
using BLHX.Server.Common.Proto.p11;
namespace BLHX.Server.Game.Handlers
{
internal static class P11
{
[PacketHandler(Command.Cs11001)]
static void ServerTimeHandler(Connection connection, Packet packet)
{
connection.NotifyPlayerData();
connection.NotifyStatisticsInit();
connection.NotifyShipData();
connection.Send(new Sc11002()
{
Timestamp = (uint)DateTimeOffset.Now.ToUnixTimeSeconds(),
Monday0oclockTimestamp = Connection.Monday0oclockTimestamp,
ShipCount = connection.player is null ? 0 : (uint)connection.player.Ships.Count
});
}
[PacketHandler(Command.Cs11401)]
static void ChangeChatRoomHandler(Connection connection, Packet packet)
{
var req = packet.Decode<Cs11401>();
connection.Send(new Sc11402()
{
Result = 0,
RoomId = req.RoomId
});
}
}
static class P11ConnectionNotifyExtensions
{
public static void NotifyPlayerData(this Connection connection)
{
if (connection.player is not null)
{
connection.Send(new Sc11003()
{
Id = connection.player.Uid,
Name = connection.player.Name,
Level = connection.player.Level,
Exp = connection.player.Exp,
ResourceLists = connection.player.Resources.Select(x => new Resource() { Num = x.Num, Type = x.Id }).ToList(),
Characters = [1],
ShipBagMax = 150,
EquipBagMax = 350,
GmFlag = 1,
Rank = 1,
GuideIndex = 1,
RegisterTime = (uint)new DateTimeOffset(connection.player.CreatedAt).ToUnixTimeSeconds(),
Display = connection.player.DisplayInfo,
Appreciation = new()
});
}
}
}
}

View File

@@ -0,0 +1,22 @@
using BLHX.Server.Common.Proto.p12;
namespace BLHX.Server.Game.Handlers
{
internal static class P12
{
}
static class P12ConnectionNotifyExtensions
{
public static void NotifyShipData(this Connection connection)
{
if (connection.player is not null)
{
connection.Send(new Sc12001()
{
Shiplists = connection.player.Ships.Select(x => x.ToProto()).ToList()
});
}
}
}
}

View File

@@ -0,0 +1,34 @@
using BLHX.Server.Common.Data;
using BLHX.Server.Common.Proto.p17;
namespace BLHX.Server.Game.Handlers
{
internal class P17
{
}
static class P17ConnectionNotifyExtensions
{
public static void NotifyStatisticsInit(this Connection connection)
{
if (connection.player is not null)
{
connection.Send(new Sc17001()
{
ShipInfoLists = connection.player.Ships.OrderByDescending(x => x.Level).DistinctBy(x => x.TemplateId).Select(x =>
{
var template = Data.ShipDataTemplate[(int)x.TemplateId];
return new ShipStatisticsInfo()
{
Id = template.GroupType,
Star = template.Star,
LvMax = x.Level,
IntimacyMax = x.Intimacy
};
}).ToList()
});
}
}
}
}

View File

@@ -6,6 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<InvariantGlobalization>true</InvariantGlobalization>
<IncludeSourceRevisionInInformationalVersion>true</IncludeSourceRevisionInInformationalVersion>
</PropertyGroup>
<ItemGroup>

View File

@@ -3,6 +3,7 @@ using BLHX.Server.Common.Utils;
using BLHX.Server.Game;
using BLHX.Server.Sdk;
using System.Net.NetworkInformation;
using System.Reflection;
namespace BLHX.Server;
@@ -10,6 +11,7 @@ internal class Program
{
static void Main(string[] args)
{
Logger.c.Log($"Version {Assembly.GetEntryAssembly()?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion}");
Logger.c.Log("Starting...");
Config.Load();