using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity; using System.IO; using System.Runtime.InteropServices; using MySql.Data.EntityFramework; using PSO2SERVER.Models; using PSO2SERVER.Protocol; using static PSO2SERVER.Models.CharacterStruct; namespace PSO2SERVER.Database { public class Account { [Key] public int AccountId { get; set; } public string Username { get; set; } public string Password { get; set; } public string Nickname { get; set; } public string SettingsIni { get; set; } public int TextLang { get; set; } public int VoiceLang { get; set; } public int TextLang2 { get; set; } public int LangLang { get; set; } public string LanguageCode { get; set; } public int IsGM { get; set; } public int PacketType { get; set; } public string PSNID { get; set; } } public class AccountNetInterFace { [Key] public int id { get; set; } public int AccountId { get; set; } public string Username { get; set; } public int State { get; set; } public string Mac { get; set; } } public class AccountSystemInfo { [Key] public int id { get; set; } public int AccountId { get; set; } public string Username { get; set; } public string CpuInfo { get; set; } public string VideoInfo { get; set; } public long Vram { get; set; } public long TotalRam { get; set; } public int Unk1 { get; set; } public int Unk2 { get; set; } public string WindowsVersion { get; set; } public string WindowSize { get; set; } public string AudioDevices { get; set; } public string Unk4 { get; set; } public string VideoDriver { get; set; } public long TotalDiskSpace { get; set; } public long FreeDiskSpace { get; set; } } public class Character { // Probably more info than this [Key, Column(Order = 1)] public int CharacterID { get; set; } [Key, Column(Order = 2)] public int AccountID { get; set; } public int Unk1 { get; set; } public int VoiceType { get; set; } public short Unk2 { get; set; } public short VoicePitch { get; set; } public string Name { get; set; } public LooksParam Looks { get; set; } public byte[] LooksBinary { get { PacketWriter w = new PacketWriter(); w.WriteStruct(Looks); return w.ToArray(); } set { Looks = Helper.ByteArrayToStructure(value); } } public int Unk3 { get; set; } public JobParam Jobs { get; set; } public byte[] JobsBinary { get { PacketWriter w = new PacketWriter(); w.WriteStruct(Jobs); return w.ToArray(); } set { Jobs = Helper.ByteArrayToStructure(value); } } public byte[] Unk4 { get; set; } = new byte[148]; public byte[] Fulldata { get; set; } public PSO2Items[] EquipedItems { get; set; } = new PSO2Items[10]; public byte[] EquipedItemsBinary { get { PacketWriter w = new PacketWriter(); foreach (var item in EquipedItems) { w.WriteStruct(item); } return w.ToArray(); } set { // 每个PSO2Items的大小(包含uuid、ItemId、Items) int itemSize = Marshal.SizeOf(typeof(PSO2Items)); // 计算字节数组中包含多少个PSO2Items对象 int itemCount = value.Length / itemSize; // 创建一个新的数组来存储这些对象 EquipedItems = new PSO2Items[itemCount]; // 逐个反序列化 for (int i = 0; i < itemCount; i++) { // 提取对应的字节块 byte[] itemBytes = new byte[itemSize]; Array.Copy(value, i * itemSize, itemBytes, 0, itemSize); // 使用Helper.ByteArrayToStructure来反序列化每个PSO2Items对象 EquipedItems[i] = Helper.ByteArrayToStructure(itemBytes); } } } public byte[] BuildCharacterByteArray() { PacketWriter writer = new PacketWriter(); // 序列化字段:CharacterID, AccountID, Unk1, VoiceType, Unk2, VoicePitch writer.Write((uint)CharacterID); writer.Write((uint)AccountID); writer.Write((uint)Unk1); writer.Write((uint)VoiceType); writer.Write((ushort)Unk2); writer.Write(VoicePitch); // 序列化 name (假设固定长度字符串16个字节,UTF-16 编码) writer.WriteFixedLengthUtf16(Name, 0x10); // 序列化 Looks writer.WriteStruct(Looks); // 序列化 Unk3 writer.Write((uint)Unk3); // 序列化 Jobs writer.WriteStruct(Jobs); // 序列化 Unk4 writer.WriteBytes(0, 148); // 最后返回字节流 return writer.ToArray(); } public virtual Account Account { get; set; } } public class NPC { [Key, Column(Order = 1)] public int EntityID { get; set; } [Key, Column(Order = 2)] public string ZoneName { get; set; } public string NPCName { get; set; } public float RotX { get; set; } public float RotY { get; set; } public float RotZ { get; set; } public float RotW { get; set; } public float PosX { get; set; } public float PosY { get; set; } public float PosZ { get; set; } public int is_active { get; set; } } public class GameObject { [Key, Column(Order = 1)] public int ObjectID { get; set; } [Key, Column(Order = 2)] public string ZoneName { get; set; } public string ObjectName { get; set; } public byte[] ObjectFlags { get; set; } public float RotX { get; set; } public float RotY { get; set; } public float RotZ { get; set; } public float RotW { get; set; } public float PosX { get; set; } public float PosY { get; set; } public float PosZ { get; set; } } public class ServerInfo { [Key, MaxLength(255)] public string Info { get; set; } public string Setting { get; set; } } public class Teleport { [Key, Column(Order = 1)] public string ZoneName { get; set; } [Key, Column(Order = 2)] public int ObjectID { get; set; } public float RotX { get; set; } public float RotY { get; set; } public float RotZ { get; set; } public float RotW { get; set; } public float PosX { get; set; } public float PosY { get; set; } public float PosZ { get; set; } } [DbConfigurationType(typeof(MySqlEFConfiguration))] public class ServerEf : DbContext { public DbSet Accounts { get; set; } public DbSet AccountsNetInterFaces { get; set; } public DbSet AccountsSystemInfoes { get; set; } public DbSet Characters { get; set; } public DbSet GameObjects { get; set; } public DbSet NPCs { get; set; } public DbSet ServerInfoes { get; set; } public DbSet Teleports { get; set; } public ServerEf() : base( string.Format("server={0};port={1};database={2};username={3};password={4}", ServerApp.Config.DatabaseAddress, ServerApp.Config.DatabasePort, ServerApp.Config.DatabaseName, ServerApp.Config.DatabaseUsername, ServerApp.Config.DatabasePassword) ) { } public void SetupDB() { try { foreach ( var f in Directory.EnumerateFiles(Directory.GetCurrentDirectory() + "/Resources/sql/scripts/", "*.sql")) { Logger.WriteInternal("[DBC] 执行数据库脚本 {0}", f); Database.ExecuteSqlCommand(File.ReadAllText(f)); } var revision = ServerInfoes.Find("Revision"); if (revision == null) { revision = new ServerInfo { Info = "Revision", Setting = "0" }; ServerInfoes.Add(revision); //TODO Possibly move this somewhere else? Database.ExecuteSqlCommand("ALTER TABLE Accounts AUTO_INCREMENT=10000000"); } SaveChanges(); Logger.WriteInternal("[DBC] 加载数据集修订的数据库 {0}", revision.Setting); } catch (Exception ex) { Logger.WriteException("数据库异常", ex); } } public bool TestDatabaseConnection2() { try { using (var context = new ServerEf()) { // 执行一个简单的查询来测试数据库连接 context.Database.ExecuteSqlCommand("SELECT 1"); Logger.WriteInternal("[DBT] 数据库连接成功。"); return true; } } catch (Exception ex) { Logger.WriteException("数据库连接异常", ex); return false; } } public bool TestDatabaseConnection() { try { using (var context = new ServerEf()) { context.Database.Initialize(force: false); Logger.WriteInternal("[DBT] 数据库连接成功。"); return true; } } catch (Exception ex) { Logger.WriteException("数据库连接异常", ex); return false; } } } }