From f970fdff27fd775efb57c22317d9b04f536c44d8 Mon Sep 17 00:00:00 2001 From: Sancaros Date: Sat, 7 Dec 2024 14:55:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0float=2016=20=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/Models/BattleStats.cs | 98 ++++++++++ Server/Models/Stats.cs | 174 ++++++++++++++++++ Server/Protocol/PacketReader.cs | 26 +++ Server/Protocol/PacketWriter.cs | 6 + .../04-0F-EnemyKilledPacket.cs | 30 ++- .../08-04-CharacterSpawnPacket.cs | 86 ++++----- Server/Server.csproj | 5 + Server/packages.config | 1 + 8 files changed, 382 insertions(+), 44 deletions(-) create mode 100644 Server/Models/BattleStats.cs create mode 100644 Server/Models/Stats.cs diff --git a/Server/Models/BattleStats.cs b/Server/Models/BattleStats.cs new file mode 100644 index 0000000..e036af0 --- /dev/null +++ b/Server/Models/BattleStats.cs @@ -0,0 +1,98 @@ +using PSO2SERVER.Protocol.Packets; +using PSO2SERVER.Zone; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PSO2SERVER.Models +{ + // Class to represent PlayerStats + public class BattlePlayerStats + { + public uint MaxHp { get; set; } = 0; + public uint Hp { get; set; } = 0; + public uint Dex { get; set; } = 0; + + public uint BaseMelPwr { get; set; } = 0; + public uint WeaponMelPwr { get; set; } = 0; + public uint BaseRngPwr { get; set; } = 0; + public uint WeaponRngPwr { get; set; } = 0; + public uint BaseTecPwr { get; set; } = 0; + public uint WeaponTecPwr { get; set; } = 0; + + public uint BaseMelDef { get; set; } = 0; + public uint BaseRngDef { get; set; } = 0; + public uint BaseTecDef { get; set; } = 0; + + // Constructor to initialize with default values + public BattlePlayerStats() + { + } + } + + // Class to represent EnemyStats + public class BattleEnemyStats + { + public string Name { get; set; } = string.Empty; + public uint Level { get; set; } = 0; + public uint Exp { get; set; } = 0; + public PSOLocation Pos { get; set; } = new PSOLocation(); + + public uint MaxHp { get; set; } = 0; + public uint Hp { get; set; } = 0; + public uint Dex { get; set; } = 0; + + public uint MaxMelPwr { get; set; } = 0; + public uint MinMelPwr { get; set; } = 0; + public uint MaxRngPwr { get; set; } = 0; + public uint MinRngPwr { get; set; } = 0; + public uint MaxTecPwr { get; set; } = 0; + public uint MinTecPwr { get; set; } = 0; + + public uint MelDef { get; set; } = 0; + public uint RngDef { get; set; } = 0; + public uint TecDef { get; set; } = 0; + + public List Hitboxes { get; set; } = new List(); + + // Constructor to initialize with default values + public BattleEnemyStats() + { + } + } + + // Enum to represent the BattleResult type + public enum BattleResultType + { + Damaged, + Killed + } + + // Class to represent the BattleResult + public class BattleResult + { + public BattleResultType ResultType { get; set; } + + public DamageReceivePacket DmgPacket { get; set; } + public EnemyKilledPacket KillPacket { get; set; } + public uint ExpAmount { get; set; } + + // Constructor for Damaged result + public BattleResult(DamageReceivePacket dmgPacket) + { + ResultType = BattleResultType.Damaged; + DmgPacket = dmgPacket; + } + + // Constructor for Killed result + public BattleResult(DamageReceivePacket dmgPacket, EnemyKilledPacket killPacket, uint expAmount) + { + ResultType = BattleResultType.Killed; + DmgPacket = dmgPacket; + KillPacket = killPacket; + ExpAmount = expAmount; + } + } +} diff --git a/Server/Models/Stats.cs b/Server/Models/Stats.cs new file mode 100644 index 0000000..9d759ec --- /dev/null +++ b/Server/Models/Stats.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PSO2SERVER.Models +{ + public enum AttackType + { + Mel = 0, + Rng = 1, + Tec = 2 + } + + public class LevelStats + { + public ulong ExpToNext { get; set; } = 0; + public float Hp { get; set; } = 0; + public float Pp { get; set; } = 0; + public float MelPow { get; set; } = 0; + public float RngPow { get; set; } = 0; + public float TecPow { get; set; } = 0; + public float Dex { get; set; } = 0; + public float MelDef { get; set; } = 0; + public float RngDef { get; set; } = 0; + public float TecDef { get; set; } = 0; + } + + public class StatMultipliers + { + public sbyte Hp { get; set; } = 0; + public sbyte MelPow { get; set; } = 0; + public sbyte RngPow { get; set; } = 0; + public sbyte TecPow { get; set; } = 0; + public sbyte Dex { get; set; } = 0; + public sbyte MelDef { get; set; } = 0; + public sbyte RngDef { get; set; } = 0; + public sbyte TecDef { get; set; } = 0; + } + + public class ClassStatsStored + { + public ClassType Class { get; set; } + public List Stats { get; set; } = new List(); + } + + public class PlayerStats + { + public List> Stats { get; set; } = new List>(); + public List Modifiers { get; set; } = new List(); + } + + public class RaceModifierStored + { + public StatMultipliers HumanMale { get; set; } = new StatMultipliers(); + public StatMultipliers HumanFemale { get; set; } = new StatMultipliers(); + public StatMultipliers NewmanMale { get; set; } = new StatMultipliers(); + public StatMultipliers NewmanFemale { get; set; } = new StatMultipliers(); + public StatMultipliers CastMale { get; set; } = new StatMultipliers(); + public StatMultipliers CastFemale { get; set; } = new StatMultipliers(); + public StatMultipliers DeumanMale { get; set; } = new StatMultipliers(); + public StatMultipliers DeumanFemale { get; set; } = new StatMultipliers(); + } + + public class EnemyLevelBaseStats + { + public uint Level { get; set; } = 0; + public float Exp { get; set; } = 0; + public float Hp { get; set; } = 0; + public float MaxMelDmg { get; set; } = 0; + public float MinMelDmg { get; set; } = 0; + public float MaxRngDmg { get; set; } = 0; + public float MinRngDmg { get; set; } = 0; + public float MaxTecDmg { get; set; } = 0; + public float MinTecDmg { get; set; } = 0; + public float MelDef { get; set; } = 0; + public float RngDef { get; set; } = 0; + public float TecDef { get; set; } = 0; + public float Dex { get; set; } = 0; + } + + public class EnemyHitbox + { + public string Name { get; set; } = string.Empty; + public uint HitboxId { get; set; } = 0; + public float DamageMul { get; set; } = 1.0f; + public float MelMul { get; set; } = 1.0f; + public float RngMul { get; set; } = 1.0f; + public float TecMul { get; set; } = 1.0f; + public float FireMul { get; set; } = 1.0f; + public float IceMul { get; set; } = 1.0f; + public float ThunderMul { get; set; } = 1.0f; + public float WindMul { get; set; } = 1.0f; + public float LightMul { get; set; } = 1.0f; + public float DarkMul { get; set; } = 1.0f; + } + + public class EnemyBaseStats + { + public List Levels { get; set; } = new List(); + } + + public class EnemyStats + { + public List Levels { get; set; } = new List(); + public List Hitboxes { get; set; } = new List(); + } + + public class NamedEnemyStats + { + public string Name { get; set; } = string.Empty; + public EnemyStats Stats { get; set; } = new EnemyStats(); + } + + public class AllEnemyStats + { + public EnemyBaseStats Base { get; set; } = new EnemyBaseStats(); + public Dictionary Enemies { get; set; } = new Dictionary(); + } + + public class AttackStatsReadable + { + public string AttackName { get; set; } = string.Empty; + public string DamageName { get; set; } = string.Empty; + public AttackType AttackType { get; set; } = AttackType.Mel; + public AttackType DefenseType { get; set; } = AttackType.Mel; + public DamageTypeReadable Damage { get; set; } = new DamageTypeReadable(); + } + + public class AttackStats + { + public uint AttackId { get; set; } = 0; + public uint DamageId { get; set; } = 0; + public AttackType AttackType { get; set; } = AttackType.Mel; + public AttackType DefenseType { get; set; } = AttackType.Mel; + public DamageType Damage { get; set; } = new DamageType(); + } + + public class DamageTypeReadable + { + public static DamageTypeReadable Default => new DamageTypeReadable { Mul = 1.0f }; + + public float Mul { get; set; } = 1.0f; + public string Name { get; set; } = string.Empty; + } + + public class DamageType + { + public float Mul { get; set; } = 1.0f; + public Tuple Pa { get; set; } = new Tuple(0, 1.0f); + + public DamageType() { } + + public DamageType(float mul) + { + Mul = mul; + } + + public DamageType(Tuple pa) + { + Pa = pa; + } + } + + public class DamageTypeConverter + { + public static DamageType ConvertToDamageType(DamageTypeReadable value) + { + return new DamageType(value.Mul); + } + } +} diff --git a/Server/Protocol/PacketReader.cs b/Server/Protocol/PacketReader.cs index cbe64d9..71f2165 100644 --- a/Server/Protocol/PacketReader.cs +++ b/Server/Protocol/PacketReader.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; +using System.Runtime.Remoting.Messaging; using System.Text; namespace PSO2SERVER.Protocol @@ -143,5 +144,30 @@ namespace PSO2SERVER.Protocol return pos; } + + // 读取 Half 类型(16 位浮动点数) + public Half ReadHalf() + { + // 读取 2 个字节 + byte[] bytes = ReadBytes(2); + + // 使用 System.Half 来转换字节为 Half 类型 + if (bytes.Length < 2) + { + throw new EndOfStreamException("Not enough data to read Half value."); + } + + // 返回 Half 类型 + return BitConverter.ToUInt16(bytes, 0).ToHalf(); + } + } + + // Extension method to convert UInt16 to Half + public static class HalfExtensions + { + public static Half ToHalf(this ushort value) + { + return new Half(value); + } } } \ No newline at end of file diff --git a/Server/Protocol/PacketWriter.cs b/Server/Protocol/PacketWriter.cs index 35113a9..5d0e114 100644 --- a/Server/Protocol/PacketWriter.cs +++ b/Server/Protocol/PacketWriter.cs @@ -301,5 +301,11 @@ namespace PSO2SERVER.Protocol } } } + + // 写入 Half 类型(16 位浮动点数) + public void Write(Half value) + { + Write(Half.GetBytes(value)); // 写入 2 个字节 + } } } \ No newline at end of file diff --git a/Server/Protocol/Packets/04-ObjectRelatedPacket/04-0F-EnemyKilledPacket.cs b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-0F-EnemyKilledPacket.cs index 4f813b3..b0fbe62 100644 --- a/Server/Protocol/Packets/04-ObjectRelatedPacket/04-0F-EnemyKilledPacket.cs +++ b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-0F-EnemyKilledPacket.cs @@ -8,6 +8,33 @@ namespace PSO2SERVER.Protocol.Packets { public class EnemyKilledPacket : Packet { + /// Player that receives this packet. + public ObjectHeader Receiver { get; set; } = new ObjectHeader(); + /// Object that receives this damage. + public ObjectHeader Dmg_target { get; set; } = new ObjectHeader(); + /// Object that deals this damage. + public ObjectHeader Dmg_inflicter { get; set; } = new ObjectHeader(); + /// Inflicted damage ID. + public uint Damage_id { get; set; } = 0; + /// How much damage was inflicted. + public int Dmg_amount { get; set; } = 0; + /// New HP. + public uint New_hp { get; set; } = 0; + /// Hitbox ID (?). + public uint Hitbox_id { get; set; } = 0; + /// Hit x position. + public Half X_pos { get; set; } = 0; + /// Hit y position. + public Half Y_pos { get; set; } = 0; + /// Hit z position. + public Half Z_pos { get; set; } = 0; + public ushort unk1 { get; set; } = 0; + public ushort unk2 { get; set; } = 0; + public ushort unk3 { get; set; } = 0; + public ushort unk4 { get; set; } = 0; + public ushort unk5 { get; set; } = 0; + public ushort unk6 { get; set; } = 0; + public ushort unk7 { get; set; } = 0; public EnemyKilledPacket() { @@ -18,12 +45,13 @@ namespace PSO2SERVER.Protocol.Packets public override byte[] Build() { var pkt = new PacketWriter(); + pkt.Write(X_pos); return pkt.ToArray(); } public override PacketHeader GetHeader() { - return new PacketHeader(0x04, 0x0F, PacketFlags.None); + return new PacketHeader(0x04, 0x0F, PacketFlags.OBJECT_RELATED); } #endregion diff --git a/Server/Protocol/Packets/08-SpawnPacket/08-04-CharacterSpawnPacket.cs b/Server/Protocol/Packets/08-SpawnPacket/08-04-CharacterSpawnPacket.cs index 6282323..f5d0298 100644 --- a/Server/Protocol/Packets/08-SpawnPacket/08-04-CharacterSpawnPacket.cs +++ b/Server/Protocol/Packets/08-SpawnPacket/08-04-CharacterSpawnPacket.cs @@ -27,42 +27,42 @@ namespace PSO2SERVER.Protocol.Packets /// CharacterSpawnPacket Struct /// /// Spawned character's player object. - public ObjectHeader objHeader { get; set; } = new ObjectHeader(); - public PSOLocation objPosition { get; set; } = new PSOLocation(); - public ushort unk1 { get; set; } = 0; + public ObjectHeader ObjHeader { get; set; } = new ObjectHeader(); + public PSOLocation ObjPosition { get; set; } = new PSOLocation(); + public ushort Unk1 { get; set; } = 0; /// Always `Character`. (?) - public string objName { get; set; } = "Character";//0x20 - public ushort unk3 { get; set; } = 1; - public ushort unk4 { get; set; } - public uint unk5 { get; set; } - public uint unk6 { get; set; } - public uint unk7 { get; set; } - public uint unk8 { get; set; } - public CharacterSpawnType spawn_type { get; set; } - public byte unk9 { get; set; } - public ushort unk10 { get; set; } - public Character _character { get; set; } - public uint unk11 { get; set; } + public string ObjName { get; set; } = "Character";//0x20 + public ushort Unk3 { get; set; } = 1; + public ushort Unk4 { get; set; } + public uint Unk5 { get; set; } + public uint Unk6 { get; set; } + public uint Unk7 { get; set; } + public uint Unk8 { get; set; } + public CharacterSpawnType SpawnType { get; set; } + public byte Unk9 { get; set; } + public ushort Unk10 { get; set; } + public Character Character { get; set; } + public uint Unk11 { get; set; } /// Set to `1` if the player is a GM. - public uint gm_flag { get; set; } - public string nickname { get; set; } + public uint GmFlag { get; set; } + public string Nickname { get; set; } //#[SeekAfter(0x60)] - public byte[] unk12 { get; set; } = new byte[0x40]; + public byte[] Unk12 { get; set; } = new byte[0x40]; public CharacterSpawnPacket(Character character, PSOLocation locatiion) { - objHeader = new ObjectHeader((uint)character.Account.AccountId, ObjectType.Player); - _character = character; - objPosition = locatiion; + ObjHeader = new ObjectHeader((uint)character.Account.AccountId, ObjectType.Player); + Character = character; + ObjPosition = locatiion; } public CharacterSpawnPacket(Character character, PSOLocation locatiion, bool isme, bool isgm) { - objHeader = new ObjectHeader((uint)character.Account.AccountId, ObjectType.Player); - _character = character; - spawn_type = isme ? CharacterSpawnType.Myself : CharacterSpawnType.Other; - objPosition = locatiion; - gm_flag = isgm ? (uint)1 : 0; + ObjHeader = new ObjectHeader((uint)character.Account.AccountId, ObjectType.Player); + Character = character; + SpawnType = isme ? CharacterSpawnType.Myself : CharacterSpawnType.Other; + ObjPosition = locatiion; + GmFlag = isgm ? (uint)1 : 0; } #region implemented abstract members of Packet @@ -71,25 +71,25 @@ namespace PSO2SERVER.Protocol.Packets { var pkt = new PacketWriter(); // Accounts header - pkt.WriteStruct(objHeader); + pkt.WriteStruct(ObjHeader); // Spawn position - pkt.WritePosition(objPosition); - pkt.Write(unk1); - pkt.WriteFixedLengthASCII(objName, 0x20); - pkt.Write(unk3); // 0x44 - pkt.Write(unk4); - pkt.Write(unk5); - pkt.Write(unk6); - pkt.Write(unk7); - pkt.Write(unk8); - pkt.Write((byte)spawn_type); - pkt.Write(unk9); - pkt.Write(unk10); + pkt.WritePosition(ObjPosition); + pkt.Write(Unk1); + pkt.WriteFixedLengthASCII(ObjName, 0x20); + pkt.Write(Unk3); // 0x44 + pkt.Write(Unk4); + pkt.Write(Unk5); + pkt.Write(Unk6); + pkt.Write(Unk7); + pkt.Write(Unk8); + pkt.Write((byte)SpawnType); + pkt.Write(Unk9); + pkt.Write(Unk10); // Character data. - pkt.Write(_character.BuildCharacterByteArray()); - pkt.Write(unk11); - pkt.Write(gm_flag); - pkt.WriteFixedLengthUtf16(_character.Account.Nickname, 0x10); + pkt.Write(Character.BuildCharacterByteArray()); + pkt.Write(Unk11); + pkt.Write(GmFlag); + pkt.WriteFixedLengthUtf16(Character.Account.Nickname, 0x10); //for (var i = 0; i < 0x60; i++) // pkt.Write((byte)0); //pkt.Write(unk12); diff --git a/Server/Server.csproj b/Server/Server.csproj index 56e7932..c50bac1 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -135,6 +135,9 @@ ..\packages\System.Diagnostics.DiagnosticSource.8.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll + + ..\packages\Half.1.0.0\lib\netstandard2.0\System.Half.dll + ..\packages\System.IO.Pipelines.8.0.0\lib\net462\System.IO.Pipelines.dll @@ -165,6 +168,7 @@ + @@ -178,6 +182,7 @@ + diff --git a/Server/packages.config b/Server/packages.config index 173ab8b..31862be 100644 --- a/Server/packages.config +++ b/Server/packages.config @@ -4,6 +4,7 @@ +