diff --git a/Server/Client.cs b/Server/Client.cs
index 7ca1082..888929b 100644
--- a/Server/Client.cs
+++ b/Server/Client.cs
@@ -321,49 +321,6 @@ namespace PSO2SERVER
}
}
- public class NetInterface
- {
- ///
- /// Interface status.
- ///
- public uint State { get; set; }
-
- ///
- /// Interface MAC address.
- ///
- public string Mac { get; set; } = new string('\0', 0x18); // 以字符串形式存储
-
- public void ReadFromStream(PacketReader reader)
- {
- State = reader.ReadUInt32();
- Mac = Encoding.ASCII.GetString(reader.ReadBytes(0x18)).TrimEnd('\0');
- }
-
- public override string ToString()
- {
- return $"状态: {State}, MAC: {Mac}";
- }
-
- public void UpdateNetInterface(int id, int accountid, NetInterface updatedInterface)
- {
- using (var db = new ServerEf())
- {
- var existingInterface = db.AccountsNetInterFaces
- .FirstOrDefault(x => x.AccountId == accountid && x.id == id);
-
- if (existingInterface != null)
- {
- // 更新其他字段
- existingInterface.State = (int)updatedInterface.State;
- existingInterface.Mac = updatedInterface.Mac;
-
- // 保存更改
- db.SaveChanges();
- }
- }
- }
- }
-
public enum Language : uint
{
Japanese = 0,
diff --git a/Server/ConsoleSystem.cs b/Server/ConsoleSystem.cs
index f4b8d49..53de384 100644
--- a/Server/ConsoleSystem.cs
+++ b/Server/ConsoleSystem.cs
@@ -238,7 +238,7 @@ namespace PSO2SERVER
public void CreateCommands()
{
// Help
- var help = new ConsoleCommand(Help, "help") { Help = "显示服务端所有指令帮助" };
+ var help = new ConsoleCommand(Help, "help") { Help = "顯示服務端所有指令幫助" };
Commands.Add(help);
// Config
@@ -391,7 +391,7 @@ namespace PSO2SERVER
Commands.Add(tellLoc);
// Exit
- var exit = new ConsoleCommand(Exit, "exit", "quit") { Help = "关闭PSO2服务器" };
+ var exit = new ConsoleCommand(Exit, "exit", "quit") { Help = "關閉PSO2服務器" };
Commands.Add(exit);
}
@@ -456,7 +456,7 @@ namespace PSO2SERVER
// Stop if the command line is blank
if (string.IsNullOrEmpty(_commandLine))
- Logger.WriteWarning("[CMD] 未指定指令");
+ Logger.WriteWarning("[CMD] 空白指令");
else
{
// Iterate commands
@@ -476,7 +476,7 @@ namespace PSO2SERVER
}
if (!valid)
- Logger.WriteError("[CMD] {0} - 指令未找到", _commandLine.Split(' ')[0].Trim('\r'));
+ Logger.WriteError("[CMD] {0} - 指令不存在", _commandLine.Split(' ')[0].Trim('\r'));
// Add the command line to history and wipe it
_history.Add(_commandLine);
@@ -499,7 +499,7 @@ namespace PSO2SERVER
// TODO: Use that fancy popup box when sending help to a client
private void Help(string[] args, int length, string full, Client client)
{
- Logger.WriteCommand(client, "[CMD] 指令帮助菜单");
+ Logger.WriteCommand(client, "[CMD] 指令幫助菜單");
foreach (var command in Commands)
{
@@ -566,7 +566,7 @@ namespace PSO2SERVER
c.SendPacket(messagePacket);
}
- Logger.WriteCommand(client, "[CMD] 发送公告至所有玩家");
+ Logger.WriteCommand(client, "[CMD] 發送公告至所有玩家");
}
private void ClearLog(string[] args, int length, string full, Client client)
@@ -590,29 +590,29 @@ namespace PSO2SERVER
break;
case "list":
- Logger.WriteCommand(client, "[CMD] 设置选项");
+ Logger.WriteCommand(client, "[CMD] 設置選項");
foreach (var f in fields)
Logger.WriteCommand(client, "[CMD] {0} = {1}", f.Name, f.GetValue(ServerApp.Config));
break;
default: // Set a config option
if (args.Length < 3)
- Logger.WriteCommand(client, "[CMD] Too few arguments");
+ Logger.WriteCommand(client, "[CMD] 指令參數不足");
else if (field != null)
{
var value = args[2].Contains('\"') ? full.Split('"')[1].Split('"')[0].Trim('\"') : args[2];
if (!ServerApp.Config.SetField(args[1], value))
- Logger.WriteCommand(client, "[CMD] Config option {0} could not be changed to {1}", args[1],
+ Logger.WriteCommand(client, "[CMD] 設置選項 {0} 无法更改為 {1}", args[1],
value);
else
{
- Logger.WriteCommand(client, "[CMD] Config option {0} changed to {1}", args[1], value);
+ Logger.WriteCommand(client, "[CMD] 設置選項 {0} 更改為 {1}", args[1], value);
ServerApp.Config.SettingsChanged();
}
}
else
- Logger.WriteCommand(client, "[CMD] Config option {0} not found", args[1]);
+ Logger.WriteCommand(client, "[CMD] 設置選項 {0} 不存在", args[1]);
break;
}
}
@@ -640,7 +640,7 @@ namespace PSO2SERVER
// Couldn't find the username
if (!foundPlayer)
{
- Logger.WriteCommand(client, "[CMD] Could not find user " + name);
+ Logger.WriteCommand(client, "[CMD] 无法找到玩家 " + name);
return;
}
}
@@ -680,7 +680,7 @@ namespace PSO2SERVER
// Couldn't find the username
if (!foundPlayer)
{
- Logger.WriteError("[CMD] Could not find user " + name);
+ Logger.WriteError("[CMD] 无法找到玩家 " + name);
return;
}
@@ -712,7 +712,7 @@ namespace PSO2SERVER
};
- var fakePacket = new CharacterSpawnPacket(fakeChar, new PSOLocation(0f, 1f, 0f, 0f, x, y, z), false, 0)
+ var fakePacket = new CharacterSpawnPacket(fakeChar, new PSOLocation(0f, 1f, 0f, 0f, x, y, z), false, false)
{
};
client.SendPacket(fakePacket);
@@ -1113,7 +1113,7 @@ namespace PSO2SERVER
if (header.Type != 0x8 || header.Subtype != 0xC)
{
- Logger.WriteWarning($"[WRN] File {path} not an NPC spawn packet, skipping.");
+ Logger.WriteWarning($"[WRN] 文件 {path} 不是NPC生成数据包, 跳过.");
continue;
}
@@ -1128,13 +1128,14 @@ namespace PSO2SERVER
RotZ = reader.ReadEntityPosition().RotZ,
RotW = reader.ReadEntityPosition().RotW,
NPCName = reader.ReadFixedLengthAscii(0x20),
- ZoneName = zone
+ ZoneName = zone,
+ is_active = 1
};
reader.ReadInt16(); // Assuming this read is necessary
newNPCs.Add(npc);
- Logger.WriteInternal($"[NPC] Adding new NPC {npc.NPCName} to the database for zone {zone}");
+ Logger.WriteInternal($"[NPC] 新增 {npc.NPCName} 区域 {zone} 至数据库");
}
using (var db = new ServerEf())
diff --git a/Server/Database/ServerEf.cs b/Server/Database/ServerEf.cs
index fbf880e..2ee51b8 100644
--- a/Server/Database/ServerEf.cs
+++ b/Server/Database/ServerEf.cs
@@ -3,7 +3,9 @@ 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;
@@ -110,11 +112,80 @@ namespace PSO2SERVER.Database
}
}
- public string Unk4 { get; set; }
+ 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 byte[] fulldata { get; set; }
}
public class NPC
@@ -134,6 +205,7 @@ namespace PSO2SERVER.Database
public float PosX { get; set; }
public float PosY { get; set; }
public float PosZ { get; set; }
+ public int is_active { get; set; }
}
public class GameObject
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/block.cs b/Server/Models/BlockInfo.cs
similarity index 82%
rename from Server/Models/block.cs
rename to Server/Models/BlockInfo.cs
index 8d3dc77..f39b02b 100644
--- a/Server/Models/block.cs
+++ b/Server/Models/BlockInfo.cs
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace PSO2SERVER.Models
{
- public class block
+ public class BlockInfo
{
public uint unk1 { get; set; }
public byte unk2 { get; set; }
@@ -17,8 +17,8 @@ namespace PSO2SERVER.Models
public uint unk7 { get; set; }
public ushort unk8 { get; set; }
public ushort block_id { get; set; }
- public string block_name { get; set; }
- public byte[] ip { get; set; } = new byte[4];
+ public string block_name { get; set; }// 0x20
+ public Ipv4Addr ip { get; set; } = new Ipv4Addr(new byte[] { 127, 0, 0, 1 });
public ushort port { get; set; }
public ushort unk10 { get; set; }
public ushort unk11 { get; set; }
diff --git a/Server/Models/CharacterAddtionStruct.cs b/Server/Models/CharacterAddtionStruct.cs
new file mode 100644
index 0000000..231086c
--- /dev/null
+++ b/Server/Models/CharacterAddtionStruct.cs
@@ -0,0 +1,180 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PSO2SERVER.Models
+{
+ public class CharacterAddtionStruct
+ {
+
+ }
+
+ public enum ShortLanguage : byte
+ {
+ Japanese,
+ English,
+ Unknown,
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct CharParam
+ {
+ public int CharacterID;
+ public int player_id;
+ public uint unk1;
+ public uint voice_type;
+ public ushort unk2;
+ public short voice_pitch;
+ }
+
+ public enum ClassType : byte
+ {
+ Unknown = 0xFF,
+ Hunter = 0,
+ Ranger,
+ Force,
+ Fighter,
+ Gunner,
+ Techer,
+ Braver,
+ Bouncer,
+ Challenger,
+ Summoner,
+ BattleWarrior,
+ Hero,
+ Phantom,
+ Etole,
+ Luster,
+ }
+
+ // 每个枚举成员的值是通过位移操作计算的:
+
+ //Hunter:1 << 0,即 1(0x0001)
+ //Ranger:1 << 1,即 2(0x0002)
+ //Force:1 << 2,即 4(0x0004)
+ //Fighter:1 << 3,即 8(0x0008)
+ //Gunner:1 << 4,即 16(0x0010)
+ //Techer:1 << 5,即 32(0x0020)
+ //Braver:1 << 6,即 64(0x0040)
+ //Bouncer:1 << 7,即 128(0x0080)
+ //Challenger:1 << 8,即 256(0x0100)
+ //Summoner:1 << 9,即 512(0x0200)
+ //BattleWarrior:1 << 10,即 1024(0x0400)
+ //Hero:1 << 11,即 2048(0x0800)
+ //Phantom:1 << 12,即 4096(0x1000)
+ //Etole:1 << 13,即 8192(0x2000)
+ //Luster:1 << 14,即 16384(0x4000)
+
+ [Flags]
+ public enum ClassTypeField : ushort
+ {
+ Hunter = 1 << 0,
+ Ranger = 1 << 1,
+ Force = 1 << 2,
+ Fighter = 1 << 3,
+ Gunner = 1 << 4,
+ Techer = 1 << 5,
+ Braver = 1 << 6,
+ Bouncer = 1 << 7,
+ Challenger = 1 << 8,
+ Summoner = 1 << 9,
+ BattleWarrior = 1 << 10,
+ Hero = 1 << 11,
+ Phantom = 1 << 12,
+ Etole = 1 << 13,
+ Luster = 1 << 14
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct JobEntry
+ {
+ /// Main level.
+ public ushort level;
+ public ushort level2; // SubClass level
+ /// Current EXP.
+ public uint exp;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Entries
+ {
+ public JobEntry
+ hunter
+ , ranger
+ , force
+ , fighter
+ , gunner
+ , techer
+ , braver
+ , bouncer
+ , Challenger
+ , Summoner
+ , BattleWarrior
+ , Hero
+ , Phantom
+ , Etole
+ , Luster
+ , unk16
+ , unk17
+ , unk18
+ , unk19
+ , unk20
+ , unk21
+ , unk22
+ , unk23
+ , unk24
+ ;
+ }
+
+ public enum RunAnimation : ushort
+ {
+ Walking = 9,
+ Hovering = 11
+ }
+
+ public enum Race : ushort
+ {
+ Unknown = 0xFFFF,
+ Human = 0,
+ Newman,
+ Cast,
+ Dewman,
+ }
+
+ public enum Gender : ushort
+ {
+ Unknown = 0xFFFF,
+ Male = 0,
+ Female,
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Figure
+ {
+ public ushort x, y, z; // Great naming, SEGA
+ }
+
+ public struct AccessoryData
+ {
+ public sbyte Value1;
+ public sbyte Value2;
+ public sbyte Value3;
+ }
+
+ public enum SkinColor : byte
+ {
+ RaceDefined,
+ Human,
+ Deuman,
+ Cast
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct HSVColor
+ {
+ public ushort hue, saturation, value;
+ }
+}
diff --git a/Server/Models/CharacterStruct.cs b/Server/Models/CharacterStruct.cs
index aa7dd47..7fd418c 100644
--- a/Server/Models/CharacterStruct.cs
+++ b/Server/Models/CharacterStruct.cs
@@ -9,172 +9,6 @@ namespace PSO2SERVER.Models
{
public class CharacterStruct
{
- public enum ShortLanguage : byte
- {
- Japanese,
- English,
- Unknown,
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public unsafe struct CharParam
- {
- public int CharacterID;
- public int player_id;
- public uint unk1;
- public uint voice_type;
- public ushort unk2;
- public short voice_pitch;
- }
-
- public enum ClassType : byte
- {
- Unknown = 0xFF,
- Hunter = 0,
- Ranger,
- Force,
- Fighter,
- Gunner,
- Techer,
- Braver,
- Bouncer,
- Challenger,
- Summoner,
- BattleWarrior,
- Hero,
- Phantom,
- Etole,
- Luster,
- }
-
- // 每个枚举成员的值是通过位移操作计算的:
-
- //Hunter:1 << 0,即 1(0x0001)
- //Ranger:1 << 1,即 2(0x0002)
- //Force:1 << 2,即 4(0x0004)
- //Fighter:1 << 3,即 8(0x0008)
- //Gunner:1 << 4,即 16(0x0010)
- //Techer:1 << 5,即 32(0x0020)
- //Braver:1 << 6,即 64(0x0040)
- //Bouncer:1 << 7,即 128(0x0080)
- //Challenger:1 << 8,即 256(0x0100)
- //Summoner:1 << 9,即 512(0x0200)
- //BattleWarrior:1 << 10,即 1024(0x0400)
- //Hero:1 << 11,即 2048(0x0800)
- //Phantom:1 << 12,即 4096(0x1000)
- //Etole:1 << 13,即 8192(0x2000)
- //Luster:1 << 14,即 16384(0x4000)
-
- [Flags]
- public enum ClassTypeField : ushort
- {
- Hunter = 1 << 0,
- Ranger = 1 << 1,
- Force = 1 << 2,
- Fighter = 1 << 3,
- Gunner = 1 << 4,
- Techer = 1 << 5,
- Braver = 1 << 6,
- Bouncer = 1 << 7,
- Challenger = 1 << 8,
- Summoner = 1 << 9,
- BattleWarrior = 1 << 10,
- Hero = 1 << 11,
- Phantom = 1 << 12,
- Etole = 1 << 13,
- Luster = 1 << 14
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct JobEntry
- {
- /// Main level.
- public ushort level;
- public ushort level2; // SubClass level
- /// Current EXP.
- public uint exp;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct Entries
- {
- public JobEntry
- hunter
- , ranger
- , force
- , fighter
- , gunner
- , techer
- , braver
- , bouncer
- , Challenger
- , Summoner
- , BattleWarrior
- , Hero
- , Phantom
- , Etole
- , Luster
- , unk16
- , unk17
- , unk18
- , unk19
- , unk20
- , unk21
- , unk22
- , unk23
- , unk24
- ;
- }
-
- public enum RunAnimation : ushort
- {
- Walking = 9,
- Hovering = 11
- }
-
- public enum Race : ushort
- {
- Unknown = 0xFFFF,
- Human = 0,
- Newman,
- Cast,
- Dewman,
- }
-
- public enum Gender : ushort
- {
- Unknown = 0xFFFF,
- Male = 0,
- Female,
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct Figure
- {
- public ushort x, y, z; // Great naming, SEGA
- }
-
- public struct AccessoryData
- {
- public sbyte Value1;
- public sbyte Value2;
- public sbyte Value3;
- }
-
- public enum SkinColor : byte
- {
- RaceDefined,
- Human,
- Deuman,
- Cast
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct HSVColor
- {
- public ushort hue, saturation, value;
- }
-
[StructLayout(LayoutKind.Sequential)]
public unsafe struct LooksParam
{
diff --git a/Server/Models/FixedPackets.cs b/Server/Models/FixedPackets.cs
index 353fdcc..8a33068 100644
--- a/Server/Models/FixedPackets.cs
+++ b/Server/Models/FixedPackets.cs
@@ -1,4 +1,5 @@
using System;
+using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace PSO2SERVER.Models
@@ -20,6 +21,10 @@ namespace PSO2SERVER.Models
this.Flags2 = flags2;
}
+ public PacketHeader(int size, byte type, byte subtype, PacketFlags flags1, PacketFlags flags2) : this(size, type, subtype, (byte)flags1, (byte)flags1)
+ {
+ }
+
public PacketHeader(byte type, byte subtype) : this(type, subtype, (byte)0)
{
}
@@ -31,6 +36,24 @@ namespace PSO2SERVER.Models
public PacketHeader(byte type, byte subtype, PacketFlags packetFlags) : this(type, subtype, (byte)packetFlags)
{
}
+
+ // ToBytes 方法的实现
+ public byte[] ToBytes()
+ {
+ byte[] bytes = new byte[sizeof(UInt32) + sizeof(byte) * 4]; // 计算结构体的总大小
+ int index = 0;
+
+ // 将结构体每个字段转换为字节
+ Array.Copy(BitConverter.GetBytes(Size), 0, bytes, index, sizeof(UInt32));
+ index += sizeof(UInt32);
+
+ bytes[index++] = Type;
+ bytes[index++] = Subtype;
+ bytes[index++] = Flags1;
+ bytes[index++] = Flags2;
+
+ return bytes;
+ }
}
/// Packet flags.
@@ -48,7 +71,7 @@ namespace PSO2SERVER.Models
/// Set for all (?) of (0x04) packets. 0x40
OBJECT_RELATED = 1 << 6,
/// 0x44
- unk0x44 = 0x44
+ PACKED_OBJECT_RELATED = PACKED | OBJECT_RELATED
}
}
diff --git a/Server/Models/FixedTypes.cs b/Server/Models/FixedTypes.cs
new file mode 100644
index 0000000..118f020
--- /dev/null
+++ b/Server/Models/FixedTypes.cs
@@ -0,0 +1,399 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PSO2SERVER.Models
+{
+ public class FixedVec
+ {
+ private List _data;
+ private int _capacity;
+
+ public FixedVec(int size)
+ {
+ if (size <= 0)
+ throw new ArgumentException("Size must be greater than zero.", nameof(size));
+
+ _data = new List(size);
+ _capacity = size;
+ }
+ public FixedVec(int size, T defaultValue)
+ {
+ if (size <= 0)
+ throw new ArgumentException("Size must be greater than zero.", nameof(size));
+
+ _data = new List(size);
+ _capacity = size;
+ for (int i = 0; i < size; i++)
+ {
+ _data.Add(defaultValue);
+ }
+ }
+
+ public int Capacity => _capacity;
+
+ public int Length => _data.Count;
+
+ public void Add(T item)
+ {
+ if (_data.Count >= _capacity)
+ throw new InvalidOperationException("FixedVec is full.");
+
+ _data.Add(item);
+ }
+ public void Insert(int index, T item)
+ {
+ if (index < 0 || index > _data.Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ if (_data.Count >= _capacity)
+ throw new InvalidOperationException("FixedVec is full.");
+
+ _data.Insert(index, item);
+ }
+ public void RemoveAt(int index)
+ {
+ if (index < 0 || index >= _data.Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ _data.RemoveAt(index);
+ }
+
+ public void RemoveLast()
+ {
+ if (_data.Count == 0)
+ throw new InvalidOperationException("FixedVec is empty.");
+
+ _data.RemoveAt(_data.Count - 1);
+ }
+ public void Clear()
+ {
+ _data.Clear();
+ }
+
+ public T this[int index]
+ {
+ get
+ {
+ if (index < 0 || index >= _data.Count)
+ throw new IndexOutOfRangeException();
+ return _data[index];
+ }
+ set
+ {
+ if (index < 0 || index >= _data.Count)
+ throw new IndexOutOfRangeException();
+ _data[index] = value;
+ }
+ }
+
+ public override string ToString()
+ {
+ return $"[{string.Join(", ", _data)}]";
+ }
+ }
+
+ public class FixedSortedList where T : IComparable
+ {
+ private List _list;
+ private int _capacity;
+
+ public FixedSortedList(int capacity)
+ {
+ if (capacity <= 0)
+ throw new ArgumentException("Capacity must be greater than zero.", nameof(capacity));
+
+ _list = new List();
+ _capacity = capacity;
+ }
+
+ public int Capacity => _capacity;
+ public int Count => _list.Count;
+
+ // 添加元素
+ public void Add(T item)
+ {
+ if (_list.Count >= _capacity)
+ throw new InvalidOperationException("List is full.");
+
+ _list.Add(item);
+ _list.Sort(); // 自动排序
+ }
+
+ // 获取元素
+ public T Get(int index)
+ {
+ if (index < 0 || index >= _list.Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ return _list[index];
+ }
+
+ // 清空列表
+ public void Clear()
+ {
+ _list.Clear();
+ }
+
+ public override string ToString()
+ {
+ return $"[{string.Join(", ", _list)}]";
+ }
+ }
+
+ public class FixedHashSet
+ {
+ private HashSet _set;
+ private int _capacity;
+
+ public FixedHashSet(int capacity)
+ {
+ if (capacity <= 0)
+ throw new ArgumentException("Capacity must be greater than zero.", nameof(capacity));
+
+ _set = new HashSet();
+ _capacity = capacity;
+ }
+
+ public int Capacity => _capacity;
+ public int Count => _set.Count;
+
+ // 添加元素
+ public bool Add(T item)
+ {
+ if (_set.Count >= _capacity)
+ throw new InvalidOperationException("HashSet is full.");
+
+ return _set.Add(item);
+ }
+
+ // 判断元素是否存在
+ public bool Contains(T item)
+ {
+ return _set.Contains(item);
+ }
+
+ // 清空集合
+ public void Clear()
+ {
+ _set.Clear();
+ }
+
+ public override string ToString()
+ {
+ return $"[{string.Join(", ", _set)}]";
+ }
+ }
+
+ public class FixedString
+ {
+ private string[] _array;
+ private int _capacity;
+
+ public FixedString(int capacity)
+ {
+ if (capacity <= 0)
+ throw new ArgumentException("Capacity must be greater than zero.", nameof(capacity));
+
+ _array = new string[capacity];
+ _capacity = capacity;
+ }
+
+ public int Capacity => _capacity;
+ public int Count { get; private set; } = 0;
+
+ // 获取或设置指定索引的字符串
+ public string this[int index]
+ {
+ get
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ return _array[index];
+ }
+ set
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ _array[index] = value;
+ }
+ }
+
+ // 添加字符串
+ public void Add(string item)
+ {
+ if (Count >= _capacity)
+ throw new InvalidOperationException("Array is full.");
+
+ _array[Count++] = item;
+ }
+
+ // 将数组转换为字节数组
+ public byte[] ToBytes()
+ {
+ List byteList = new List();
+ foreach (var str in _array.Take(Count))
+ {
+ byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes(str);
+ byteList.AddRange(stringBytes);
+ }
+ return byteList.ToArray();
+ }
+
+ // 清空数组
+ public void Clear()
+ {
+ Array.Clear(_array, 0, _capacity);
+ Count = 0;
+ }
+
+ public override string ToString()
+ {
+ return $"[{string.Join(", ", _array.Take(Count))}]";
+ }
+ }
+
+ public class FixedInt
+ {
+ private int[] _array;
+ private int _capacity;
+
+ public FixedInt(int capacity)
+ {
+ if (capacity <= 0)
+ throw new ArgumentException("Capacity must be greater than zero.", nameof(capacity));
+
+ _array = new int[capacity];
+ _capacity = capacity;
+ }
+
+ public int Capacity => _capacity;
+ public int Count { get; private set; } = 0;
+
+ // 获取或设置指定索引的整数
+ public int this[int index]
+ {
+ get
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ return _array[index];
+ }
+ set
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ _array[index] = value;
+ }
+ }
+
+ // 添加整数
+ public void Add(int item)
+ {
+ if (Count >= _capacity)
+ throw new InvalidOperationException("Array is full.");
+
+ _array[Count++] = item;
+ }
+
+ // 清空数组
+ public void Clear()
+ {
+ Array.Clear(_array, 0, _capacity);
+ Count = 0;
+ }
+
+ // 将数组转换为字节数组
+ public byte[] ToBytes()
+ {
+ List byteList = new List();
+ foreach (var num in _array.Take(Count))
+ {
+ byte[] intBytes = BitConverter.GetBytes(num);
+ byteList.AddRange(intBytes);
+ }
+ return byteList.ToArray();
+ }
+
+ public override string ToString()
+ {
+ return $"[{string.Join(", ", _array.Take(Count))}]";
+ }
+ }
+
+ public class FixedUlong
+ {
+ private ulong[] _array;
+ private int _capacity;
+
+ public FixedUlong(int capacity)
+ {
+ if (capacity <= 0)
+ throw new ArgumentException("Capacity must be greater than zero.", nameof(capacity));
+
+ _array = new ulong[capacity];
+ _capacity = capacity;
+ }
+
+ public int Capacity => _capacity;
+ public int Count { get; private set; } = 0;
+
+ // 获取或设置指定索引的 ulong 数值
+ public ulong this[int index]
+ {
+ get
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ return _array[index];
+ }
+ set
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ _array[index] = value;
+ }
+ }
+
+ // 添加 ulong 数值
+ public void Add(ulong item)
+ {
+ if (Count >= _capacity)
+ throw new InvalidOperationException("Array is full.");
+
+ _array[Count++] = item;
+ }
+
+ // 清空数组
+ public void Clear()
+ {
+ Array.Clear(_array, 0, _capacity);
+ Count = 0;
+ }
+
+ // 将数组转换为字节数组
+ public byte[] ToBytes()
+ {
+ List byteList = new List();
+ foreach (var num in _array.Take(Count))
+ {
+ byte[] ulongBytes = BitConverter.GetBytes(num);
+ byteList.AddRange(ulongBytes);
+ }
+ return byteList.ToArray();
+ }
+
+ public override string ToString()
+ {
+ return $"[{string.Join(", ", _array.Take(Count))}]";
+ }
+ }
+}
diff --git a/Server/Models/NetInterface.cs b/Server/Models/NetInterface.cs
new file mode 100644
index 0000000..e3f7be8
--- /dev/null
+++ b/Server/Models/NetInterface.cs
@@ -0,0 +1,54 @@
+using PSO2SERVER.Database;
+using PSO2SERVER.Protocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PSO2SERVER.Models
+{
+ public class NetInterface
+ {
+ ///
+ /// Interface status.
+ ///
+ public uint State { get; set; }
+
+ ///
+ /// Interface MAC address.
+ ///
+ public string Mac { get; set; } = new string('\0', 0x18); // 以字符串形式存储
+
+ public void ReadFromStream(PacketReader reader)
+ {
+ State = reader.ReadUInt32();
+ Mac = Encoding.ASCII.GetString(reader.ReadBytes(0x18)).TrimEnd('\0');
+ }
+
+ public override string ToString()
+ {
+ return $"状态: {State}, MAC: {Mac}";
+ }
+
+ public void UpdateNetInterface(int id, int accountid, NetInterface updatedInterface)
+ {
+ using (var db = new ServerEf())
+ {
+ var existingInterface = db.AccountsNetInterFaces
+ .FirstOrDefault(x => x.AccountId == accountid && x.id == id);
+
+ if (existingInterface != null)
+ {
+ // 更新其他字段
+ existingInterface.State = (int)updatedInterface.State;
+ existingInterface.Mac = updatedInterface.Mac;
+
+ // 保存更改
+ db.SaveChanges();
+ }
+ }
+ }
+ }
+
+}
diff --git a/Server/Models/Orders.cs b/Server/Models/Orders.cs
new file mode 100644
index 0000000..fa40f77
--- /dev/null
+++ b/Server/Models/Orders.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PSO2SERVER.Models
+{
+ public class Orders
+ {
+ public struct ClientOrder
+ {
+ public uint unk1;
+ public uint id;
+ public uint status;
+ public uint finish_date;
+ }
+
+ public struct OrderStatus
+ {
+ public uint unk1;
+ public uint unk2;
+ public uint unk3;
+ public uint unk4;
+ public uint unk5;
+ public uint unk6;
+ }
+ }
+}
diff --git a/Server/Models/PSO2Item.cs b/Server/Models/PSO2Item.cs
index 83ff97d..21c2918 100644
--- a/Server/Models/PSO2Item.cs
+++ b/Server/Models/PSO2Item.cs
@@ -1,4 +1,5 @@
-using System;
+using PSO2SERVER.Protocol;
+using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
@@ -9,26 +10,18 @@ namespace PSO2SERVER.Models
[StructLayout(LayoutKind.Sequential)]
public unsafe struct ShortItemId
{
- byte ItemType;
- byte Id;
- ushort Subid;
+ public byte ItemType;
+ public byte Id;
+ public ushort Subid;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct ItemId
{
- ushort ItemType;
- ushort Id;
- ushort Unk3;
- ushort Subid;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct PSO2Items
- {
- long guid;
- ItemId id;
- Items data;
+ public ushort ItemType;
+ public ushort Id;
+ public ushort Unk3;
+ public ushort Subid;
}
[StructLayout(LayoutKind.Explicit)]
@@ -50,6 +43,25 @@ namespace PSO2SERVER.Models
//public PSO2ItemNone None;
}
+ [StructLayout(LayoutKind.Sequential)]
+ public struct PSO2Items
+ {
+ public ulong uuid;
+ public ItemId id;
+ public Items data;
+
+ public byte[] ToByteArray()
+ {
+ using (var pkt = new PacketWriter())
+ {
+ pkt.Write(uuid);
+ pkt.WriteStruct(id);
+ pkt.WriteStruct(data);
+ return pkt.ToArray();
+ }
+ }
+ }
+
[StructLayout(LayoutKind.Sequential)]
public unsafe struct PSO2ItemNone
{
@@ -58,73 +70,73 @@ namespace PSO2SERVER.Models
[StructLayout(LayoutKind.Sequential)]
public unsafe struct PSO2ItemWeapon
{
- byte flags;
- byte element;
- byte force;
- byte grind;
- byte grindPercent;
- byte unknown1;
- short unknown2;
- fixed short affixes[8];
- uint potential;
- byte extend;
- byte unknown3;
- ushort unknown4;
- uint unknown5;
- uint unknown6;
+ public byte flags;
+ public byte element;
+ public byte force;
+ public byte grind;
+ public byte grindPercent;
+ public byte unknown1;
+ public short unknown2;
+ public fixed short affixes[8];
+ public uint potential;
+ public byte extend;
+ public byte unknown3;
+ public ushort unknown4;
+ public uint unknown5;
+ public uint unknown6;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct PSO2ItemClothing
{
- ushort flags;
- fixed byte unk1[0x14];
+ public ushort flags;
+ public fixed byte unk1[0x14];
public HSVColor Color;
- fixed byte unk2[0xA];
- ushort Unk3;
+ public fixed byte unk2[0xA];
+ public ushort Unk3;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct PSO2ItemConsumable
{
- ushort flags;
- fixed byte unk1[0x24];
- ushort amount;
+ public ushort flags;
+ public fixed byte unk1[0x24];
+ public ushort amount;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct PSO2ItemCamo
{
- byte unk1;
- byte unk2;
- byte unk3;
- fixed byte unk4[0x24];
- byte unk5;
+ public byte unk1;
+ public byte unk2;
+ public byte unk3;
+ public fixed byte unk4[0x24];
+ public byte unk5;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct PSO2ItemUnit
{
- byte flags;
- byte EnhLevel;
- byte EnhPercent;
- byte Unk1;
+ public byte flags;
+ public byte EnhLevel;
+ public byte EnhPercent;
+ public byte Unk1;
// 使用 fixed 数组来存储附加信息
- fixed ushort Affixes[8]; // Item affix IDs (0 to 4095)
+ public fixed ushort Affixes[8]; // Item affix IDs (0 to 4095)
- fixed byte unk4[0x7];
- uint Potential;
+ public fixed byte unk4[0x7];
+ public uint Potential;
// 使用 fixed 数组来存储未知字段
- fixed byte Unk2[4];
+ public fixed byte Unk2[4];
- uint Unk3;
- ushort Unk4;
- ushort Unk5;
+ public uint Unk3;
+ public ushort Unk4;
+ public ushort Unk5;
// 提供访问固定数组的属性
- Span AffixSpan
+ public Span AffixSpan
{
get
{
@@ -148,8 +160,8 @@ namespace PSO2SERVER.Models
public TimeSpan EndDate; // 对应 Rust 的 Duration
/// Campaign title (固定长度 0x3E).
- private const int TitleLength = 0x3E;
- private byte[] titleBytes;
+ public const int TitleLength = 0x3E;
+ public byte[] titleBytes;
public string Title
{
@@ -163,8 +175,8 @@ namespace PSO2SERVER.Models
}
/// Campaign conditions (固定长度 0x102).
- private const int ConditionsLength = 0x102;
- private byte[] conditionsBytes;
+ public const int ConditionsLength = 0x102;
+ public byte[] conditionsBytes;
public string Conditions
{
@@ -243,8 +255,8 @@ namespace PSO2SERVER.Models
MemoryStream stream;
//TODO
- ItemTypes type = ItemTypes.Consumable;
- byte[] data = new byte[Size];
+ public ItemTypes type = ItemTypes.Consumable;
+ public byte[] data = new byte[Size];
public override string ToString()
{
@@ -305,6 +317,13 @@ namespace PSO2SERVER.Models
// ...
}
+
+ /// Player equiped item.
+ public struct EquipedItem
+ {
+ public PSO2Items item;
+ public uint unk;
+ }
}
public static class AffixUtils
{
diff --git a/Server/Models/PSOData.cs b/Server/Models/PSOData.cs
index ee60521..bfb5604 100644
--- a/Server/Models/PSOData.cs
+++ b/Server/Models/PSOData.cs
@@ -53,7 +53,7 @@ namespace PSO2SERVER.Models
}
// 从数据流中读取数据并填充到当前结构体
- public void ReadFromStream(PacketReader reader)
+ public void ReadObjectHeaderFromStream(PacketReader reader)
{
ID = reader.ReadUInt32(); // 读取对象ID
padding = reader.ReadUInt32(); // 读取填充部分
@@ -62,7 +62,7 @@ namespace PSO2SERVER.Models
}
// 将当前结构体的数据写入到数据流
- public void WriteToStream(PacketWriter writer)
+ public void WriteObjectHeaderToStream(PacketWriter writer)
{
writer.Write(ID); // 写入对象ID
writer.Write(padding); // 写入填充部分
diff --git a/Server/Models/PSOObject.cs b/Server/Models/PSOObject.cs
index 875ea89..2998558 100644
--- a/Server/Models/PSOObject.cs
+++ b/Server/Models/PSOObject.cs
@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Runtime.InteropServices;
+using System.Runtime.Remoting.Messaging;
using System.Text;
using PSO2SERVER.Database;
using PSO2SERVER.Protocol;
@@ -85,8 +87,37 @@ namespace PSO2SERVER.Models
public class PSONPC : PSOObject
{
+ public unsafe struct NPC {
+ public ObjectHeader objHeader;
+ public PSOLocation objPosition;
+ public ushort unk1;
+ public string objName;
+ public uint unk2;
+ public fixed byte unk3[0x0C];
+ public ushort unk4;
+ public ushort unk5;
+ public uint unk6;
+ public uint unk7;
+ public uint unk8;
+ public uint unk9;
+ public uint unk10;
+ public uint unk11;
+ public uint unk12;
+ public string unk13; //Ascii
+ }
+
public override byte[] GenerateSpawnBlob()
{
+ NPC npc = new NPC
+ {
+ objHeader = Header,
+ objPosition = Position,
+ objName = Name,
+ unk8 = 1101004800,
+ unk12 = 1,
+ unk13 = "",
+ };
+
PacketWriter writer = new PacketWriter();
writer.WriteStruct(Header);
writer.WritePosition(Position);
diff --git a/Server/Models/Quest.cs b/Server/Models/Quest.cs
index c576d14..77259c3 100644
--- a/Server/Models/Quest.cs
+++ b/Server/Models/Quest.cs
@@ -6,220 +6,298 @@ namespace PSO2SERVER.Models
{
public enum QuestType : byte
{
- Unk0 = 0,
- Extreme = 1,
- ARKS = 3,
- LimitedTime = 4,
- ExtremeDebug = 5,
- Blank1 = 6,
- NetCafe = 8,
- WarmingDebug = 9,
- Blank2 = 10,
- Advance = 11,
- Expedition = 12,
- FreeDebug = 13,
- ArksDebug = 14,
- Challenge = 16,
- Urgent = 17,
- UrgentDebug = 18,
- TimeAttack = 19,
- TimeDebug = 20,
- ArksDebug2 = 21,
- ArksDebug3 = 22,
- ArksDebug4 = 23,
- ArksDebug5 = 24,
- ArksDebug6 = 25,
- ArksDebug7 = 26,
- ArksDebug8 = 27,
- ArksDebug9 = 28,
- ArksDebug10 = 29,
- Blank3 = 30,
- Recommended = 32,
- Ultimate = 33,
- UltimateDebug = 34,
- AGP = 35,
- Bonus = 36,
- StandardTraining = 37,
- HunterTraining = 38,
- RangerTraining = 39,
- ForceTraining = 40,
- FighterTraining = 41,
- GunnerTraining = 42,
- TechterTraining = 43,
- BraverTraining = 44,
- BouncerTraining = 45,
- SummonerTraining = 46,
- AutoAccept = 47,
- Ridroid = 48,
- CafeAGP = 49,
- BattleBroken = 50,
- BusterDebug = 51,
- Poka12 = 52,
- StoryEP1 = 55,
- Buster = 56,
- HeroTraining = 57,
- Amplified = 58,
- DarkBlastTraining = 61,
- Endless = 62,
- Blank4 = 64,
- PhantomTraining = 65,
- AISTraining = 66,
- DamageCalculation = 68,
- EtoileTraining = 69,
- Divide = 70,
- Stars1 = 71,
- Stars2 = 72,
- Stars3 = 73,
- Stars4 = 74,
- Stars5 = 75,
- Stars6 = 76,
+ Unk0 = 0, // 0x00
+ Extreme = 1, // 0x01
+ ARKS = 3, // 0x03
+ LimitedTime = 4, // 0x04
+ ExtremeDebug = 5, // 0x05
+ Blank1 = 6, // 0x06
+ NetCafe = 8, // 0x08
+ WarmingDebug = 9, // 0x09
+ Blank2 = 10, // 0x0A
+ Advance = 11, // 0x0B
+ Expedition = 12, // 0x0C
+ FreeDebug = 13, // 0x0D
+ ArksDebug = 14, // 0x0E
+ Challenge = 16, // 0x10
+ Urgent = 17, // 0x11
+ UrgentDebug = 18, // 0x12
+ TimeAttack = 19, // 0x13
+ TimeDebug = 20, // 0x14
+ ArksDebug2 = 21, // 0x15
+ ArksDebug3 = 22, // 0x16
+ ArksDebug4 = 23, // 0x17
+ ArksDebug5 = 24, // 0x18
+ ArksDebug6 = 25, // 0x19
+ ArksDebug7 = 26, // 0x1A
+ ArksDebug8 = 27, // 0x1B
+ ArksDebug9 = 28, // 0x1C
+ ArksDebug10 = 29, // 0x1D
+ Blank3 = 30, // 0x1E
+ Recommended = 32, // 0x20
+ Ultimate = 33, // 0x21
+ UltimateDebug = 34, // 0x22
+ AGP = 35, // 0x23
+ Bonus = 36, // 0x24
+ StandardTraining = 37,// 0x25
+ HunterTraining = 38, // 0x26
+ RangerTraining = 39, // 0x27
+ ForceTraining = 40, // 0x28
+ FighterTraining = 41, // 0x29
+ GunnerTraining = 42, // 0x2A
+ TechterTraining = 43, // 0x2B
+ BraverTraining = 44, // 0x2C
+ BouncerTraining = 45, // 0x2D
+ SummonerTraining = 46,// 0x2E
+ AutoAccept = 47, // 0x2F
+ Ridroid = 48, // 0x30
+ CafeAGP = 49, // 0x31
+ BattleBroken = 50, // 0x32
+ BusterDebug = 51, // 0x33
+ Poka12 = 52, // 0x34
+ StoryEP1 = 55, // 0x37
+ Buster = 56, // 0x38
+ HeroTraining = 57, // 0x39
+ Amplified = 58, // 0x3A
+ DarkBlastTraining = 61,// 0x3D
+ Endless = 62, // 0x3E
+ Blank4 = 64, // 0x40
+ PhantomTraining = 65, // 0x41
+ AISTraining = 66, // 0x42
+ DamageCalculation = 68,// 0x44
+ EtoileTraining = 69, // 0x45
+ Divide = 70, // 0x46
+ Stars1 = 71, // 0x47
+ Stars2 = 72, // 0x48
+ Stars3 = 73, // 0x49
+ Stars4 = 74, // 0x4A
+ Stars5 = 75, // 0x4B
+ Stars6 = 76, // 0x4C
}
+
[Flags]
- public enum QuestTypeAvailable : UInt64
+ public enum AvailableQuestType : ulong
{
- None = 0x0000000000000000,
- All = 0xFFFFFFFFFFFFFFFF,
- Extreme = 0x0000000000000002,
- StoryEP1 = 0x0000000000000004,
- Arks = 0x0000000000000008,
- Limited = 0x0000000000000010,
- ExtremeDebug = 0x0000000000000020,
- Blank1 = 0x0000000000000040,
- StoryEP2 = 0x0000000000000080,
- NetCafeLimited = 0x0000000000000100,
- ポカポカDebug = 0x0000000000000200,
- Blank2 = 0x0000000000000400,
- Advance = 0x0000000000000800,
- FreeField = 0x0000000000001000,
- FreeDebug = 0x0000000000002000,
- ArksDebug1 = 0x0000000000004000,
- StoryDebug = 0x0000000000008000,
- Challange = 0x0000010000010000,
- Emergency = 0x0000020000020000,
- EmergencyDebug = 0x0000040000040000,
- TimeAttack = 0x0000080000080000,
- TimeDebug = 0x0000000000100000,
- ArksDebug2 = 0x0000000000200000,
- ArksDebug3 = 0x0000000000400000,
- ArksDebug4 = 0x0000000000800000,
- ArksDebug5 = 0x0000000001000000,
- ArksDebug6 = 0x0000000002000000,
- ArksDebug7 = 0x0000000004000000,
- ArksDebug8 = 0x0000000008000000,
- ArksDebug9 = 0x0000000010000000,
- ArksDebug10 = 0x0000000020000000,
- Blank3 = 0x0000000040000000,
- StoryEP3 = 0x0000000080000000,
- Featured = 0x0000000100000000,
- Ultimate = 0x0000000200000000,
- UltimateDebug = 0x0000000400000000,
- NotSet = 0x0000000800000000,
+ EXTREME = 1UL << 1,
+ STORY_EP1 = 1UL << 2,
+ ARKS = 1UL << 3,
+ LIMITED_TIME = 1UL << 4,
+ EXTREME_DEBUG = 1UL << 5,
+ BLANK1 = 1UL << 6,
+ STORY_EP2 = 1UL << 7,
+
+ NET_CAFE = 1UL << 8,
+ WARMING_DEBUG = 1UL << 9,
+ BLANK2 = 1UL << 10,
+ ADVANCE = 1UL << 11,
+ EXPEDITION = 1UL << 12,
+ FREE_DEBUG = 1UL << 13,
+ ARKS_DEBUG = 1UL << 14,
+ STORY_DEBUG = 1UL << 15,
+
+ CHALLENGE = 1UL << 16,
+ URGENT = 1UL << 17,
+ URGENT_DEBUG = 1UL << 18,
+ TIME_ATTACK = 1UL << 19,
+ TIME_DEBUG = 1UL << 20,
+ ARKS_DEBUG2 = 1UL << 21,
+ ARKS_DEBUG3 = 1UL << 22,
+ ARKS_DEBUG4 = 1UL << 23,
+
+ ARKS_DEBUG5 = 1UL << 24,
+ ARKS_DEBUG6 = 1UL << 25,
+ ARKS_DEBUG7 = 1UL << 26,
+ ARKS_DEBUG8 = 1UL << 27,
+ ARKS_DEBUG9 = 1UL << 28,
+ ARKS_DEBUG10 = 1UL << 29,
+ BLANK3 = 1UL << 30,
+ STORY_EP3 = 1UL << 31,
+
+ RECOMMENDED = 1UL << 32,
+ ULTIMATE = 1UL << 33,
+ ULTIMATE_DEBUG = 1UL << 34,
+ AGP = 1UL << 35,
+ BONUS = 1UL << 36,
+ UNK1 = 1UL << 37,
+ STANDARD_TRAINING = 1UL << 38,
+ HUNTER_TRAINING = 1UL << 39,
+
+ RANGER_TRAINING = 1UL << 40,
+ FORCE_TRAINING = 1UL << 41,
+ FIGHTER_TRAINING = 1UL << 42,
+ GUNNER_TRAINING = 1UL << 43,
+ TECHTER_TRAINING = 1UL << 44,
+ BRAVER_TRAINING = 1UL << 45,
+ BOUNCER_TRAINING = 1UL << 46,
+ SUMMONER_TRAINING = 1UL << 47,
+
+ AUTO_ACCEPT = 1UL << 48,
+ RIDROID = 1UL << 49,
+ NET_CAFE_AGP = 1UL << 50,
+ BATTLE_BROKEN = 1UL << 51,
+ BUSTER_DEBUG = 1UL << 52,
+ POKA12 = 1UL << 53,
+ UNK2 = 1UL << 54,
+ UNK3 = 1UL << 55,
+
+ BUSTER = 1UL << 56,
+ HERO_TRAINING = 1UL << 57,
+ AMPLIFIED = 1UL << 58,
+ UNK4 = 1UL << 59,
+ UNK5 = 1UL << 60,
+ DARK_BLAST_TRAINING = 1UL << 61,
+ ENDLESS = 1UL << 62,
+ UNK6 = 1UL << 63,
+
+ BLANK4 = 1UL << 64,
+ PHANTOM_TRAINING = 1UL << 65,
+ AIS_TRAINING = 1UL << 66,
+ UNK7 = 1UL << 67,
+ DAMAGE_CALC = 1UL << 68,
+ ETOILE_TRAINING = 1UL << 69,
+ DIVIDE = 1UL << 70,
+ STARS1 = 1UL << 71,
+
+ STARS2 = 1UL << 72,
+ STARS3 = 1UL << 73,
+ STARS4 = 1UL << 74,
+ STARS5 = 1UL << 75,
+ STARS6 = 1UL << 76,
+ UNK8 = 1UL << 77
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public unsafe struct QuestDefiniton
{
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32 - 8)]
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dateOrSomething;
- public int field_18;
- public int field_1C;
- public int needsToBeNonzero;
- public int alsoGetsSetToDword;
- public UInt16 getsSetToWord;
- public UInt16 moreWordSetting;
- public int questNameString;
- public int field_30;
- public int field_34;
- public int field_38;
- public int field_3C;
- public int field_40;
- public int field_44;
- public int field_48;
- public int field_4C;
- public int field_50;
- public int field_54;
- public int field_58;
- public int field_5C;
- public int field_60;
- public int field_64;
- public int field_68;
- public int field_6C;
- public int field_70;
- public int field_74;
- public int field_78;
- public int field_7C;
- public int field_80;
- public int field_84;
- public int field_88;
- public int field_8C;
- public int field_90;
- public int field_94;
- public int field_98;
+ public ObjectHeader quest_obj;
+ public uint questNameid;
+ //27个uint
+ public uint field_30;
+ public uint field_34;
+ public uint field_38;
+ public uint field_3C;
+ public uint field_40;
+ public uint field_44;
+ public uint field_48;
+ public uint field_4C;
+ public uint field_50;
+ public uint field_54;
+ public uint field_58;
+ public uint field_5C;
+ public uint field_60;
+ public uint field_64;
+ public uint field_68;
+ public uint field_6C;
+ public uint field_70;
+ public uint field_74;
+ public uint field_78;
+ public uint field_7C;
+ public uint field_80;
+ public uint field_84;
+ public uint field_88;
+ public uint field_8C;
+ public uint field_90;
+ public uint field_94;
+ public uint field_98;
+
public UInt16 field_9C;
public byte field_9E;
public byte field_9F;
- public int field_A0;
- public int field_A4;
- public int field_A8;
- public int field_AC;
- public int field_B0;
- public int field_B4;
- public int field_B8;
- public int field_BC;
- public int field_C0;
- public int field_C4;
- public int field_C8;
- public int field_CC;
- public int field_D0;
- public int field_D4;
- public int field_D8;
- public int field_DC;
- public int field_E0;
- public int field_E4;
- public int field_E8; // Maybe a flags
- public int field_EC;
- public UInt16 field_F0;
- public UInt16 field_F2;
- public UInt16 questBitfield1;
- public byte playTime;
- public byte partyType;
- public byte difficulties;
- public byte difficultiesCompleted;
+ //20个uint
+ public uint field_A0;
+ public uint field_A4;
+ public uint field_A8;
+ public uint field_AC;
+ public uint field_B0;
+ public uint field_B4;
+ public uint field_B8;
+ public uint field_BC;
+ public uint field_C0;
+ public uint field_C4;
+ public uint field_C8;
+ public uint field_CC;
+ public uint field_D0;
+ public uint field_D4;
+ public uint field_D8;
+ public uint field_DC;
+ public uint field_E0;
+ public uint field_E4;
+ public uint field_E8; // Maybe a flags
+ public uint field_EC;
+
+ public ushort field_F0;
+ public ushort field_F2;
+ public QuestBitfield1 questBitfield1;
+
+ /// Length of the quest.
+ public QuestEstimatedTime playTime;
+
+ /// Party type of the quest.
+ public QuestPartyType partyType;
+
+ /// Available difficulties.
+ public QuestDifficultyType difficulties;
+
+ /// Completed difficulties.
+ public QuestDifficultyType difficultiesCompleted;
+
public byte field_FA;
- public byte field_FB;
- public byte requiredLevel;
- public byte field_FD;
+ /// Required level.
+ public byte req_level;
+ /// Required sub level.
+ public byte sub_class_req_level;
+ /// Enemy level.
+ public byte enemy_level;
+
public byte field_FE;
- public byte field_FF;
+
+ /// Type of the quest.
+ public QuestType quest_type;
+
public byte field_100;
public byte field_101;
public byte field_102;
public byte field_103;
public byte field_104;
public byte field_105;
- public UInt16 field_106;
- public int field_108;
- public int field_10C;
- public short field_110;
+
+ public ushort field_106;
+
+ public uint field_108;
+ public uint field_10C;
+
+ public ushort field_110;
+
public byte field_112;
public byte field_113;
- public QuestDefThing field_114_1;
- public QuestDefThing field_114_2;
- public QuestDefThing field_114_3;
- public QuestDefThing field_114_4;
- public QuestDefThing field_114_5;
- public QuestDefThing field_114_6;
- public QuestDefThing field_114_7;
- public QuestDefThing field_114_8;
- public QuestDefThing field_114_9;
- public QuestDefThing field_114_10;
- public QuestDefThing field_114_11;
- public QuestDefThing field_114_12;
- public QuestDefThing field_114_13;
- public QuestDefThing field_114_14;
- public QuestDefThing field_114_15;
- public QuestDefThing field_114_16;
+
+ //public QuestThing field_114_1;
+ //public QuestThing field_114_2;
+ //public QuestThing field_114_3;
+ //public QuestThing field_114_4;
+ //public QuestThing field_114_5;
+ //public QuestThing field_114_6;
+ //public QuestThing field_114_7;
+ //public QuestThing field_114_8;
+ //public QuestThing field_114_9;
+ //public QuestThing field_114_10;
+ //public QuestThing field_114_11;
+ //public QuestThing field_114_12;
+ //public QuestThing field_114_13;
+ //public QuestThing field_114_14;
+ //public QuestThing field_114_15;
+ //public QuestThing field_114_16;
+ public fixed byte field_114[0x320];
+ }
+
+ public enum QuestEstimatedTime : byte
+ {
+ Short = 1,
+ Medium,
+ Long
}
public class Quest
@@ -235,12 +313,85 @@ namespace PSO2SERVER.Models
}
}
- public struct QuestDefThing
+ public struct QuestThing
{
- public int field_0;
- public int field_4;
+ public uint field_0;
+ public uint field_4;
public byte field_8;
public byte field_9;
- public UInt16 field_A;
+ public ushort field_A;
+ }
+
+ [Flags]
+ public enum QuestDifficultyType : byte
+ {
+ NORMAL = 1 << 0, // 1 0x01 (0000 0001)
+ HARD = 1 << 1, // 2 0x02 (0000 0010)
+ VERY_HARD = 1 << 2, // 4 0x04 (0000 0100)
+ SUPER_HARD = 1 << 3, // 8 0x08 (0000 1000)
+ EX_HARD = 1 << 4, // 16 0x10 (0001 0000)
+ ULTRA_HARD = 1 << 5, // 32 0x20 (0010 0000)
+ Dummy2 = 1 << 6, // 64 0x40 (0100 0000)
+ Dummy3 = 1 << 7 // 128 0x80 (1000 0000)
+ }
+
+ public enum QuestPartyType : byte
+ {
+ SoloQuest,
+ /// Only one party can join (up to 4 players).
+ SinglePartyQuest,
+ /// Multiple parties can join (up to 12 players).
+ MultiPartyQuest,
+ }
+
+ [Flags]
+ public enum QuestBitfield1 : ushort
+ {
+ MatterObjectiveQuest = 0x0001,
+ ClientOrderOnQuest = 0x0008,
+ NewQuest = 0x0100,
+ ClientOrder = 0x0800,
+ UnknownLevel = 0x1000
+ }
+
+ //Size: 308 bytes, confirmed in unpacker
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ public unsafe struct QuestDifficulty
+ {
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
+ public string dateOrSomething;
+ public ObjectHeader quest_obj;
+ public uint name_id;
+ public byte area;
+ public byte planet;
+ public byte unk1;
+ public byte unk2;
+ public QuestDifficultyEntry difficulty1;
+ public QuestDifficultyEntry difficulty2;
+ public QuestDifficultyEntry difficulty3;
+ public QuestDifficultyEntry difficulty4;
+ public QuestDifficultyEntry difficulty5;
+ public QuestDifficultyEntry difficulty6;
+ public QuestDifficultyEntry difficulty7;
+ public QuestDifficultyEntry difficulty8;
+ }
+
+ //Size: 32, confirmed in ctor TODO
+ public struct QuestDifficultyEntry
+ {
+ public byte ReqLevel;
+ public byte SubClassReqLevel;
+ public byte MonsterLevel;
+ public byte Unk1;
+ public uint AbilityAdj;
+ public uint DmgLimit;
+ public uint TimeLimit;
+ public uint TimeLimit2;
+ public uint SuppTarget;
+ public uint Unk2;
+ public uint Enemy1;
+ public uint Unk3;
+ public uint Enemy2;
+ public uint Unk4;
}
}
\ No newline at end of file
diff --git a/Server/Models/RevealedRegions.cs b/Server/Models/RevealedRegions.cs
new file mode 100644
index 0000000..759aa38
--- /dev/null
+++ b/Server/Models/RevealedRegions.cs
@@ -0,0 +1,207 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PSO2SERVER.Models
+{
+ ///
+ /// Revealed minimap regions
+ ///
+ public class RevealedRegions
+ {
+ // 内部实现:
+ // 该结构体由一个大小为[10]的字节数组组成,表示一个 8x10 的已揭示区域网格
+ // (即 10 列 8 行)。每一位(bit)表示一个区域是否已揭示。
+ // 位是从右到左索引的(即从 LSB 到 MSB),字节是从左到右索引的(arr[0]表示区域A1-8)。
+ // 获取该数组中的某个值可以通过如下方式:
+ //
+ // 示例代码:
+ // bool GetBit(byte[] arr, int row, int col)
+ // {
+ // // 确保行和列的范围有效
+ // if (row < 0 || row >= 8 || col < 0 || col >= 10)
+ // throw new ArgumentOutOfRangeException();
+ //
+ // // 计算对应位置的位偏移
+ // int offset = row * 10 + col;
+ // int byteOffset = offset / 8;
+ // int bitOffset = offset % 8;
+ //
+ // // 获取相应的位值
+ // return (arr[byteOffset] >> bitOffset & 1) == 1;
+ // }
+
+ // 使用一个长度为 10 的字节数组,表示 8 行 10 列的区域位图。
+ // Lsb0 表示最低有效位在字节的最右侧。
+ public byte[] zones = new byte[10];
+
+ // 默认构造函数
+ public RevealedRegions()
+ {
+ }
+
+ // 构造函数 - 用指定数据初始化
+ public RevealedRegions(byte[] data)
+ {
+ // 如果传入的 unk2 长度小于 10,则填充剩余部分为 0
+ if (data.Length <= 10)
+ {
+ zones = data.Concat(new byte[10 - data.Length]).ToArray();
+ }
+ else
+ {
+ // 如果传入的 unk2 长度大于 10,则截取前 10 字节
+ zones = data.Take(10).ToArray();
+ }
+ }
+
+ ///
+ /// 获取指定行列位置的位值
+ ///
+ /// 行索引(0 到 7)
+ /// 列索引(0 到 9)
+ /// 该位置的位值(true = 已揭示,false = 未揭示)
+ public bool GetBit(int row, int col)
+ {
+ // 确保行列值在有效范围内
+ if (row < 0 || row >= 8 || col < 0 || col >= 10)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ // 计算对应位置的偏移
+ int offset = row * 10 + col;
+ int byteOffset = offset / 8;
+ int bitOffset = offset % 8;
+
+ // 获取并返回该位置的位值
+ return (zones[byteOffset] >> bitOffset & 1) == 1;
+ }
+
+ ///
+ /// 设置指定行列位置的位值
+ ///
+ /// 行索引(0 到 7)
+ /// 列索引(0 到 9)
+ /// 设置的值(true = 设置为已揭示,false = 设置为未揭示)
+ public void SetBit(int row, int col, bool value)
+ {
+ // 确保行列值在有效范围内
+ if (row < 0 || row >= 8 || col < 0 || col >= 10)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ // 计算对应位置的偏移
+ int offset = row * 10 + col;
+ int byteOffset = offset / 8;
+ int bitOffset = offset % 8;
+
+ // 设置对应位置的位值
+ if (value)
+ {
+ // 设置为1
+ zones[byteOffset] |= (byte)(1 << bitOffset);
+ }
+ else
+ {
+ // 设置为0
+ zones[byteOffset] &= (byte)~(1 << bitOffset);
+ }
+ }
+ // 读取数据
+ public static RevealedRegions Read(BinaryReader reader)
+ {
+ try
+ {
+ byte[] data = reader.ReadBytes(10);
+ if (data.Length != 10)
+ throw new InvalidDataException("Failed to read 10 bytes for zones data.");
+
+ return new RevealedRegions(data);
+ }
+ catch (Exception ex)
+ {
+ throw new PacketError("RevealedRegions", "zone_bits", ex);
+ }
+ }
+
+ // 写入数据
+ public void Write(BinaryWriter writer)
+ {
+ try
+ {
+ writer.Write(zones);
+ }
+ catch (Exception ex)
+ {
+ throw new PacketError("RevealedRegions", "zone_bits", ex);
+ }
+ }
+
+ // 索引操作,获取指定行的 10 位数据
+ public BitSlice GetRow(int index)
+ {
+ if (index < 0 || index >= 8)
+ throw new ArgumentOutOfRangeException();
+
+ byte[] row = new byte[10];
+ Array.Copy(zones, index * 10, row, 0, 10);
+ return new BitSlice(row);
+ }
+
+ // 用于调试输出
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("[");
+ for (int i = 0; i < 8; i++)
+ {
+ sb.Append(GetRow(i).ToString());
+ if (i < 7) sb.Append(", ");
+ }
+ sb.Append("]");
+ return sb.ToString();
+ }
+
+ // Helper class to represent a slice of bits
+ public class BitSlice
+ {
+ private byte[] data;
+
+ public BitSlice(byte[] data)
+ {
+ this.data = data;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach (byte b in data)
+ {
+ sb.Append(Convert.ToString(b, 2).PadLeft(8, '0'));
+ }
+ return sb.ToString();
+ }
+ }
+
+ // Error handling class for packet-related errors
+ public class PacketError : Exception
+ {
+ public string PacketName { get; }
+ public string FieldName { get; }
+ public new Exception InnerException { get; }
+
+ public PacketError(string packetName, string fieldName, Exception innerException)
+ : base($"Error in packet '{packetName}', field '{fieldName}'", innerException)
+ {
+ PacketName = packetName;
+ FieldName = fieldName;
+ InnerException = innerException;
+ }
+ }
+ }
+}
diff --git a/Server/Models/ShipData.cs b/Server/Models/ShipData.cs
index 143a1d8..c1a5f2c 100644
--- a/Server/Models/ShipData.cs
+++ b/Server/Models/ShipData.cs
@@ -9,7 +9,8 @@ namespace PSO2SERVER.Models
Online,
Busy,
Full,
- Offline
+ Offline,
+ Undefined = 0xFFFF
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
diff --git a/Server/Models/Stats.cs b/Server/Models/Stats.cs
new file mode 100644
index 0000000..1616973
--- /dev/null
+++ b/Server/Models/Stats.cs
@@ -0,0 +1,241 @@
+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);
+ }
+ }
+
+ public struct EXPReceiver
+ {
+ // 玩家获得经验的对象
+ public ObjectHeader Object; // 如果 ObjectHeader 是结构体,则可以这样使用;如果是类,需要修改或替代
+
+ public byte Unk1; // 未知字段 1
+ public byte Unk2; // 未知字段 2
+ public byte[] Unk3; // 6 字节的数组(未知数据),这部分需要额外处理
+
+ // 主职业获得的经验
+ public ulong Gained;
+
+ // 主职业的总经验
+ public ulong Total;
+
+ // 主职业的新子等级(?)
+ public ushort Level2;
+
+ // 主职业的新等级
+ public ushort Level;
+
+ // 主职业
+ public ClassType Class; // 假设 ClassType 是一个枚举类型或者结构体
+
+ public byte[] Pad1; // 填充字节数组(3 字节),需要额外处理
+
+ // 副职业获得的经验
+ public ulong GainedSub;
+
+ // 副职业的总经验
+ public ulong TotalSub;
+
+ // 副职业的新子等级(?)
+ public ushort Level2Sub;
+
+ // 副职业的新等级
+ public ushort LevelSub;
+
+ // 副职业
+ public ClassType Subclass; // 假设 ClassType 是一个枚举类型或者结构体
+
+ public byte[] Pad2; // 填充字节数组(3 字节),需要额外处理
+
+ // 结构体构造函数,用于初始化数组和其他默认值
+ public EXPReceiver(ObjectHeader objectHeader, byte unk1, byte unk2, byte[] unk3, ulong gained, ulong total,
+ ushort level2, ushort level, ClassType classType, byte[] pad1, ulong gainedSub,
+ ulong totalSub, ushort level2Sub, ushort levelSub, ClassType subclass, byte[] pad2)
+ {
+ Object = objectHeader;
+ Unk1 = unk1;
+ Unk2 = unk2;
+ Unk3 = unk3 ?? new byte[6]; // 默认值为6字节数组
+ Gained = gained;
+ Total = total;
+ Level2 = level2;
+ Level = level;
+ Class = classType;
+ Pad1 = pad1 ?? new byte[3]; // 默认值为3字节数组
+ GainedSub = gainedSub;
+ TotalSub = totalSub;
+ Level2Sub = level2Sub;
+ LevelSub = levelSub;
+ Subclass = subclass;
+ Pad2 = pad2 ?? new byte[3]; // 默认值为3字节数组
+ }
+ }
+}
diff --git a/Server/Models/Time.cs b/Server/Models/Time.cs
new file mode 100644
index 0000000..65293ba
--- /dev/null
+++ b/Server/Models/Time.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PSO2SERVER.Models
+{
+ public struct Duration : IEquatable, IComparable
+ {
+ // The number of seconds in the duration
+ public long Seconds { get; }
+
+ // The number of nanoseconds, should always be between 0 and 999,999,999
+ public int Nanoseconds { get; }
+
+ // Constructor to initialize a Duration with seconds and nanoseconds
+ public Duration(long seconds, int nanoseconds)
+ {
+ if (nanoseconds < 0 || nanoseconds >= 1_000_000_000)
+ {
+ throw new ArgumentOutOfRangeException(nameof(nanoseconds), "Nanoseconds must be between 0 and 999,999,999.");
+ }
+
+ Seconds = seconds;
+ Nanoseconds = nanoseconds;
+ }
+
+ // A default zero-length duration
+ public static Duration Zero => new Duration(0, 0);
+
+ // Adds two durations together
+ public static Duration operator +(Duration left, Duration right)
+ {
+ long totalSeconds = left.Seconds + right.Seconds;
+ int totalNanoseconds = left.Nanoseconds + right.Nanoseconds;
+
+ // Handle carry over for nanoseconds
+ if (totalNanoseconds >= 1_000_000_000)
+ {
+ totalSeconds++;
+ totalNanoseconds -= 1_000_000_000;
+ }
+
+ return new Duration(totalSeconds, totalNanoseconds);
+ }
+
+ // Subtracts one duration from another
+ public static Duration operator -(Duration left, Duration right)
+ {
+ long totalSeconds = left.Seconds - right.Seconds;
+ int totalNanoseconds = left.Nanoseconds - right.Nanoseconds;
+
+ // Handle borrow for nanoseconds
+ if (totalNanoseconds < 0)
+ {
+ totalSeconds--;
+ totalNanoseconds += 1_000_000_000;
+ }
+
+ return new Duration(totalSeconds, totalNanoseconds);
+ }
+
+ // Compare two durations
+ public int CompareTo(Duration other)
+ {
+ if (Seconds < other.Seconds) return -1;
+ if (Seconds > other.Seconds) return 1;
+
+ return Nanoseconds.CompareTo(other.Nanoseconds);
+ }
+
+ // Check if two durations are equal
+ public bool Equals(Duration other)
+ {
+ return Seconds == other.Seconds && Nanoseconds == other.Nanoseconds;
+ }
+
+ // Override ToString() for custom string representation
+ public override string ToString()
+ {
+ return $"{Seconds}s {Nanoseconds}ns";
+ }
+
+ // Static method to create a Duration from milliseconds
+ public static Duration FromMilliseconds(long milliseconds)
+ {
+ long seconds = milliseconds / 1000;
+ int nanoseconds = (int)((milliseconds % 1000) * 1_000_000);
+ return new Duration(seconds, nanoseconds);
+ }
+
+ // Static method to create a Duration from seconds and nanoseconds
+ public static Duration FromSeconds(long seconds, int nanoseconds)
+ {
+ return new Duration(seconds, nanoseconds);
+ }
+ }
+
+}
diff --git a/Server/Network/Ipv4Addr.cs b/Server/Network/Ipv4Addr.cs
new file mode 100644
index 0000000..acf6b67
--- /dev/null
+++ b/Server/Network/Ipv4Addr.cs
@@ -0,0 +1,76 @@
+using System;
+
+public struct Ipv4Addr : IEquatable
+{
+ private byte[] octets;
+
+ // 构造函数
+ public Ipv4Addr(byte[] octets)
+ {
+ if (octets == null || octets.Length != 4)
+ throw new ArgumentException("IPv4 address must have exactly 4 octets.");
+ this.octets = octets;
+ }
+
+ // 允许访问 octets 数组
+ public byte[] Octets => octets;
+
+ // 重写 Equals 方法
+ public override bool Equals(object obj)
+ {
+ return obj is Ipv4Addr other && Equals(other);
+ }
+
+ // 实现 IEquatable 接口
+ public bool Equals(Ipv4Addr other)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ if (this.octets[i] != other.octets[i])
+ return false;
+ }
+ return true;
+ }
+
+ // 重写 GetHashCode 方法
+ public override int GetHashCode()
+ {
+ int hash = 17;
+ foreach (var octet in octets)
+ {
+ hash = hash * 31 + octet;
+ }
+ return hash;
+ }
+
+ // 提供一个用于显示的字符串格式
+ public override string ToString()
+ {
+ return string.Join(".", octets);
+ }
+
+ // 静态方法来解析字符串形式的 IPv4 地址
+ public static Ipv4Addr Parse(string ipString)
+ {
+ var parts = ipString.Split('.');
+ if (parts.Length != 4)
+ throw new ArgumentException("Invalid IPv4 address format.");
+ var octets = new byte[4];
+ for (int i = 0; i < 4; i++)
+ {
+ octets[i] = byte.Parse(parts[i]);
+ }
+ return new Ipv4Addr(octets);
+ }
+
+ // 重载 == 和 != 操作符
+ public static bool operator ==(Ipv4Addr left, Ipv4Addr right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Ipv4Addr left, Ipv4Addr right)
+ {
+ return !(left == right);
+ }
+}
diff --git a/Server/Object/ObjectManager.cs b/Server/Object/ObjectManager.cs
index 8545f7c..6326738 100644
--- a/Server/Object/ObjectManager.cs
+++ b/Server/Object/ObjectManager.cs
@@ -96,7 +96,7 @@ namespace PSO2SERVER.Object
using (var db = new ServerEf())
{
var dbNpcs = from n in db.NPCs
- where n.ZoneName == zone
+ where n.ZoneName == zone && n.is_active == 1
select n;
foreach (NPC npc in dbNpcs)
diff --git a/Server/Party/Party.cs b/Server/Party/Party.cs
index 32a8b82..18daa9f 100644
--- a/Server/Party/Party.cs
+++ b/Server/Party/Party.cs
@@ -76,6 +76,22 @@ namespace PSO2SERVER.Party
{
return members;
}
+ }
+ [Flags]
+ public enum PartyFlags : byte
+ {
+ /// Is the party only for friends.
+ FRIENDS_ONLY = 1 << 0, // 0x01
+ /// Is the party only for alliance members.
+ ALLIANCE_ONLY = 1 << 1, // 0x02
+ /// Limit multiplayer requests from other parties.
+ LIMIT_OTHERS = 1 << 2, // 0x04
+ /// Is the party only for a single run.
+ SINGLE_RUN = 1 << 3, // 0x08
+ /// Is the party actively looking for members.
+ OPEN = 1 << 4, // 0x10
+ /// Is the party voice chat focused.
+ VC_FOCUS = 1 << 6 // 0x40
}
}
diff --git a/Server/Party/PartyManager.cs b/Server/Party/PartyManager.cs
index 1f2ec80..55d32ad 100644
--- a/Server/Party/PartyManager.cs
+++ b/Server/Party/PartyManager.cs
@@ -5,11 +5,11 @@ using System.Text;
namespace PSO2SERVER.Party
{
- class PartyManager
+ public class PartyManager
{
- private static readonly PartyManager instance = new PartyManager();
+ public static readonly PartyManager instance = new PartyManager();
- private Dictionary parties = new Dictionary(); // Key: Party, Value: Name? (for now)
+ public Dictionary parties = new Dictionary(); // Key: Party, Value: Name? (for now)
public static PartyManager Instance
{
@@ -19,7 +19,7 @@ namespace PSO2SERVER.Party
}
}
- private PartyManager()
+ public PartyManager()
{
Logger.WriteInternal("[PTY] PartyManager 初始化完成.");
}
diff --git a/Server/Protocol/Handlers/03-ServerHandler/03-35-TeleportLobbyToCasino.cs b/Server/Protocol/Handlers/03-ServerHandler/03-35-TeleportLobbyToCasino.cs
index 1fdba8b..1ee3848 100644
--- a/Server/Protocol/Handlers/03-ServerHandler/03-35-TeleportLobbyToCasino.cs
+++ b/Server/Protocol/Handlers/03-ServerHandler/03-35-TeleportLobbyToCasino.cs
@@ -19,8 +19,8 @@ namespace PSO2SERVER.Protocol.Handlers
return;
// Dunno what these are yet.
- context.SendPacket(0x11, 0xA, 0x0, BitConverter.GetBytes(context._account.AccountId));
- context.SendPacket(0x1E, 0xC, 0x0, BitConverter.GetBytes(101));
+ context.SendPacket(0x11, 0x0A, 0x0, BitConverter.GetBytes(context._account.AccountId));
+ context.SendPacket(new Unk1E0CPacket(101));
Map casinoMap = ZoneManager.Instance.MapFromInstance("casino", "lobby");
casinoMap.SpawnClient(context, casinoMap.GetDefaultLocation());
diff --git a/Server/Protocol/Handlers/03-ServerHandler/03-39-TeleportLobbyToBridge.cs b/Server/Protocol/Handlers/03-ServerHandler/03-39-TeleportLobbyToBridge.cs
index 3f9ec78..b00befb 100644
--- a/Server/Protocol/Handlers/03-ServerHandler/03-39-TeleportLobbyToBridge.cs
+++ b/Server/Protocol/Handlers/03-ServerHandler/03-39-TeleportLobbyToBridge.cs
@@ -19,8 +19,8 @@ namespace PSO2SERVER.Protocol.Handlers
return;
// Dunno what these are yet.
- context.SendPacket(0x11, 0xA, 0x0, BitConverter.GetBytes(context._account.AccountId));
- context.SendPacket(0x1E, 0xC, 0x0, BitConverter.GetBytes(101));
+ context.SendPacket(0x11, 0x0A, 0x0, BitConverter.GetBytes(context._account.AccountId));
+ context.SendPacket(new Unk1E0CPacket(101));
Map bridgeMap = ZoneManager.Instance.MapFromInstance("bridge", "lobby");
bridgeMap.SpawnClient(context, bridgeMap.GetDefaultLocation());
diff --git a/Server/Protocol/Handlers/03-ServerHandler/03-3C-TeleportLobbyToCafe.cs b/Server/Protocol/Handlers/03-ServerHandler/03-3C-TeleportLobbyToCafe.cs
index 99a9a41..ffe5889 100644
--- a/Server/Protocol/Handlers/03-ServerHandler/03-3C-TeleportLobbyToCafe.cs
+++ b/Server/Protocol/Handlers/03-ServerHandler/03-3C-TeleportLobbyToCafe.cs
@@ -19,8 +19,8 @@ namespace PSO2SERVER.Protocol.Handlers
return;
// Dunno what these are yet.
- context.SendPacket(0x11, 0xA, 0x0, BitConverter.GetBytes(context._account.AccountId));
- context.SendPacket(0x1E, 0xC, 0x0, BitConverter.GetBytes(101));
+ context.SendPacket(0x11, 0x0A, 0x0, BitConverter.GetBytes(context._account.AccountId));
+ context.SendPacket(new Unk1E0CPacket(101));
Map dstMap = ZoneManager.Instance.MapFromInstance("cafe", "lobby");
if(dstMap != null)
diff --git a/Server/Protocol/Handlers/04-ObjectHandler/04-07-MovementHandlers.cs b/Server/Protocol/Handlers/04-ObjectHandler/04-07-MovementHandlers.cs
index d074aae..9c3c3d3 100644
--- a/Server/Protocol/Handlers/04-ObjectHandler/04-07-MovementHandlers.cs
+++ b/Server/Protocol/Handlers/04-ObjectHandler/04-07-MovementHandlers.cs
@@ -137,8 +137,8 @@ namespace PSO2SERVER.Protocol.Handlers
if (c.Character == null || c == context || c.CurrentZone != context.CurrentZone)
continue;
- c.SendPacket(0x04, 0x07, flags, data);
- //c.SendPacket(new MovementPacket(dstData));
+ //c.SendPacket(0x04, 0x07, flags, data);
+ c.SendPacket(new MovementPacket(dstData));
}
}
diff --git a/Server/Protocol/Handlers/04-ObjectHandler/04-71-MovementEndHandler.cs b/Server/Protocol/Handlers/04-ObjectHandler/04-71-MovementEndHandler.cs
index 93ac4e4..8d8ab57 100644
--- a/Server/Protocol/Handlers/04-ObjectHandler/04-71-MovementEndHandler.cs
+++ b/Server/Protocol/Handlers/04-ObjectHandler/04-71-MovementEndHandler.cs
@@ -23,9 +23,6 @@ namespace PSO2SERVER.Protocol.Handlers
movData.timestamp = 0;
- // This could be simplified
- PacketWriter writer = new PacketWriter();
- writer.WriteStruct(movData);
//Logger.WriteInternal("[移动] 玩家 {0} 停止移动 (坐标:{1}, {2}, {3})", context.Character.Name,
// Helper.FloatFromHalfPrecision(movData.currentPos.x), Helper.FloatFromHalfPrecision(movData.currentPos.y),
@@ -36,7 +33,7 @@ namespace PSO2SERVER.Protocol.Handlers
if (c == context || c.Character == null || c.CurrentZone != context.CurrentZone)
continue;
- c.SendPacket(0x04, 0x71, 0x40, writer.ToArray());
+ c.SendPacket(new MovementEndPacket(movData));
}
}
diff --git a/Server/Protocol/Handlers/04-ObjectHandler/04-75-ActionEnd.cs b/Server/Protocol/Handlers/04-ObjectHandler/04-75-ActionEnd.cs
new file mode 100644
index 0000000..a74c3cf
--- /dev/null
+++ b/Server/Protocol/Handlers/04-ObjectHandler/04-75-ActionEnd.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using PSO2SERVER.Models;
+using PSO2SERVER.Party;
+using PSO2SERVER.Protocol.Packets;
+using PSO2SERVER.Zone;
+
+namespace PSO2SERVER.Protocol.Handlers
+{
+ [PacketHandlerAttr(0x04, 0x75)]
+ public class ActionEnd : PacketHandler
+ {
+ public ObjectHeader unk1 { get; set; } = new ObjectHeader();
+ /// Object that was performing an action.
+ public ObjectHeader performer { get; set; } = new ObjectHeader();
+ public uint unk2 { get; set; } = 0;
+ public ObjectHeader unk3 { get; set; } = new ObjectHeader();
+ public ObjectHeader unk4 { get; set; } = new ObjectHeader();
+ public byte[] unk5 { get; set; } = new byte[0x04];
+ public string action { get; set; } = string.Empty;//Ascii
+
+ public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
+ {
+ var info = string.Format("[<--] 接收到的数据 (hex): {0} 字节", data.Length);
+ Logger.WriteHex(info, data);
+
+ var reader = new PacketReader(data, position, size);
+ unk1 = reader.ReadStruct();
+ performer = reader.ReadStruct();
+ unk2 = reader.ReadUInt32();
+ unk3 = reader.ReadStruct();
+ unk4 = reader.ReadStruct();
+ unk5 = reader.ReadBytes(4);
+ action = reader.ReadAscii(0x83EF, 0x40);
+ // 打印各个字段的内容
+ Logger.Write("unk1: " + unk1); // 假设 ObjectHeader 重载了 ToString() 方法
+ Logger.Write("performer: " + performer); // 同上
+ Logger.Write("unk2: " + unk2);
+ Logger.Write("unk3: " + unk3);
+ Logger.Write("unk4: " + unk4);
+ Logger.Write("unk5: " + BitConverter.ToString(unk5)); // 打印 byte 数组
+ Logger.Write("action: " + action); // 打印 ASCII 字符串
+
+ foreach (Client cl in PartyManager.instance.GetCurrentPartyForClient(context).getMembers())
+ {
+ cl.SendPacket(new ActionEndPacket(unk1, performer, unk2, unk3, unk4, unk5, action));
+ }
+ }
+ }
+}
diff --git a/Server/Protocol/Handlers/06-PlayerStatusHandler/06-01-DealDamage.cs b/Server/Protocol/Handlers/06-PlayerStatusHandler/06-01-DealDamage.cs
new file mode 100644
index 0000000..f779d9e
--- /dev/null
+++ b/Server/Protocol/Handlers/06-PlayerStatusHandler/06-01-DealDamage.cs
@@ -0,0 +1,38 @@
+using System;
+using PSO2SERVER.Models;
+using PSO2SERVER.Protocol.Packets;
+
+namespace PSO2SERVER.Protocol.Handlers
+{
+ [PacketHandlerAttr(0x06, 0x01)]
+ public class DealDamage : PacketHandler
+ {
+ public unsafe struct DealDamagePacket
+ {
+ /// Object that inflicted the damage.
+ public ObjectHeader Inflicter;
+ /// Object that received the damage.
+ public ObjectHeader Target;
+ public uint Attack_id;
+ public ulong Unk2;
+ /// Hitbox ID (?).
+ public uint Hitbox_id;
+ /// Hit x position.
+ public Half X_pos;
+ /// Hit y position.
+ public Half Y_pos;
+ /// Hit z position.
+ public Half Z_pos;
+
+ public ushort unk4;
+ public ulong unk5;
+ public fixed byte unk6[0x18];
+ }
+
+ public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
+ {
+ var info = string.Format("[<--] 接收到的数据 (hex): {0} 字节", data.Length);
+ Logger.WriteHex(info, data);
+ }
+ }
+}
diff --git a/Server/Protocol/Handlers/0B-QuestHandler/0B-17-QuestCategoryRequest.cs b/Server/Protocol/Handlers/0B-QuestHandler/0B-17-QuestCategoryRequest.cs
new file mode 100644
index 0000000..1718ba1
--- /dev/null
+++ b/Server/Protocol/Handlers/0B-QuestHandler/0B-17-QuestCategoryRequest.cs
@@ -0,0 +1,51 @@
+using PSO2SERVER.Models;
+using PSO2SERVER.Protocol.Packets;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PSO2SERVER.Protocol.Handlers
+{
+ [PacketHandlerAttr(0x0B, 0x17)]
+ public class QuestCategoryRequest : PacketHandler
+ {
+ public uint unk1 { get; set; }
+ public QuestType category { get; set; }
+
+ public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
+ {
+ var info = string.Format("[<--] 接收到的数据 (hex): {0}字节", data.Length);
+ Logger.WriteHex(info, data);
+
+ var reader = new PacketReader(data, position, size);
+ unk1 = reader.ReadUInt32();
+ category = (QuestType)reader.ReadUInt32();
+
+ Logger.Write($"unk1 = {unk1} category = {category}");
+
+ // What am I doing
+ QuestDefiniton[] defs = new QuestDefiniton[1];
+ for (int i = 0; i < defs.Length; i++)
+ {
+ defs[i].dateOrSomething = "2012/01/05";
+ defs[i].quest_obj = new ObjectHeader(0x20, ObjectType.Quest);
+ defs[i].questNameid = 30010;
+ defs[i].playTime = QuestEstimatedTime.Short;
+ defs[i].partyType = QuestPartyType.SinglePartyQuest;
+ defs[i].difficulties = QuestDifficultyType.NORMAL | QuestDifficultyType.HARD | QuestDifficultyType.VERY_HARD | QuestDifficultyType.SUPER_HARD;
+ defs[i].req_level = 1;
+ // Not sure why but these need to be set for the quest to be enabled
+ defs[i].quest_type = (QuestType)0xF1;
+ defs[i].field_101 = 1;
+ }
+
+ context.SendPacket(new QuestCategoryPacket(defs));
+ context.SendPacket(new QuestCategoryStopperPacket());
+ }
+ }
+
+}
diff --git a/Server/Protocol/Handlers/0B-QuestHandler/0B-17-QuestListRequestHandler.cs b/Server/Protocol/Handlers/0B-QuestHandler/0B-17-QuestListRequestHandler.cs
deleted file mode 100644
index d4c3e94..0000000
--- a/Server/Protocol/Handlers/0B-QuestHandler/0B-17-QuestListRequestHandler.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using PSO2SERVER.Models;
-using PSO2SERVER.Protocol.Packets;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace PSO2SERVER.Protocol.Handlers
-{
- [PacketHandlerAttr(0xB, 0x17)]
- public class QuestListRequestHandler : PacketHandler
- {
- public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
- {
- // What am I doing
- QuestDefiniton[] defs = new QuestDefiniton[1];
- for (int i = 0; i < defs.Length; i++)
- {
- defs[i].dateOrSomething = "2012/01/05";
- defs[i].needsToBeNonzero = 0x00000020;
- defs[i].getsSetToWord = 0x0000000B;
- defs[i].questNameString = 30010;
- defs[i].playTime = (byte)QuestListPacket.EstimatedTime.Short;
- defs[i].partyType = (byte)QuestListPacket.PartyType.SinglePartyQuest;
- defs[i].difficulties = (byte)QuestListPacket.Difficulties.Normal | (byte)QuestListPacket.Difficulties.hard | (byte)QuestListPacket.Difficulties.VeryHard | (byte)QuestListPacket.Difficulties.SuperHard;
- defs[i].requiredLevel = 1;
- // Not sure why but these need to be set for the quest to be enabled
- defs[i].field_FF = 0xF1;
- defs[i].field_101 = 1;
- }
-
- context.SendPacket(new QuestListPacket(defs));
- context.SendPacket(new NoPayloadPacket(0xB, 0x1B));
- }
- }
-
-}
diff --git a/Server/Protocol/Handlers/0B-QuestHandler/0B-19-QuestDifficultyRequestHandler.cs b/Server/Protocol/Handlers/0B-QuestHandler/0B-19-QuestDifficultyRequestHandler.cs
index c6ea0d8..c690ef5 100644
--- a/Server/Protocol/Handlers/0B-QuestHandler/0B-19-QuestDifficultyRequestHandler.cs
+++ b/Server/Protocol/Handlers/0B-QuestHandler/0B-19-QuestDifficultyRequestHandler.cs
@@ -1,15 +1,7 @@
using Org.BouncyCastle.Bcpg;
-using Org.BouncyCastle.Utilities.Encoders;
using PSO2SERVER.Models;
using PSO2SERVER.Protocol.Packets;
-using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Security.Policy;
-using System.Text;
-using System.Threading.Tasks;
-using static PSO2SERVER.Protocol.Handlers.CharacterSelected;
-using static PSO2SERVER.Protocol.Packets.QuestDifficultyPacket;
namespace PSO2SERVER.Protocol.Handlers
{
@@ -26,43 +18,50 @@ namespace PSO2SERVER.Protocol.Handlers
var reader = new PacketReader(data, position, size);
- Quests = reader.ReadList();
-
- // 现在可以对任务列表(Quests)进行处理
- // 例如,可以记录任务的信息或进行其他处理
- foreach (var quest in Quests)
+ var questCount = reader.ReadMagic(0xA36E, 0x10);
+ for (uint i = 0; i < questCount; i++)
{
- if(quest.ObjectType == ObjectType.Quest)
- {
- QuestDifficulty[] diffs = new QuestDifficulty[1];
- for (int i = 0; i < diffs.Length; i++)
- {
- diffs[i].dateOrSomething = "2017/2/20";
- diffs[i].quest_obj.ID = quest.ID;
- diffs[i].quest_obj.padding = 0;
- diffs[i].quest_obj.ObjectType = ObjectType.Quest;
- diffs[i].name_id = 30010;
- diffs[i].area = 0x01;
- diffs[i].planet = 0x03;
- diffs[i].unk1 = 0x03;
- diffs[i].unk2 = 0x00;
- diffs[i].difficulty1 = new QuestDifficultyEntry();
- diffs[i].difficulty2 = new QuestDifficultyEntry();
- diffs[i].difficulty3 = new QuestDifficultyEntry();
- diffs[i].difficulty4 = new QuestDifficultyEntry();
- diffs[i].difficulty5 = new QuestDifficultyEntry();
- diffs[i].difficulty6 = new QuestDifficultyEntry();
- diffs[i].difficulty7 = new QuestDifficultyEntry();
- diffs[i].difficulty8 = new QuestDifficultyEntry();
- }
- // 打印每个任务的 ID 和类型,方便调试
- Logger.Write($"任务 ID: {quest.ID}, 任务类型: {quest.ObjectType}");
- context.SendPacket(new QuestDifficultyPacket(diffs));
- }
+ var qobj = new ObjectHeader();
+ qobj.ReadObjectHeaderFromStream(reader);
+ Quests.Add(qobj);
+
+ // 打印每个任务的 ID 和类型,方便调试
+ Logger.Write($"任务 ID: {qobj.ID}, 任务类型: {qobj.ObjectType}");
}
+ QuestDifficulty[] diffs = new QuestDifficulty[1];
+ for (int i = 0; i < diffs.Length; i++)
+ {
+ diffs[i].dateOrSomething = "2012/01/05";
+ diffs[i].quest_obj = new ObjectHeader(0x20, ObjectType.Quest);
+ diffs[i].name_id = 30010;
+ diffs[i].area = 0x01;
+ diffs[i].planet = 0x03;
+ diffs[i].unk1 = 0x03;
+ diffs[i].unk2 = 0x00;
+ diffs[i].difficulty1 = new QuestDifficultyEntry();
+ diffs[i].difficulty2 = new QuestDifficultyEntry();
+ diffs[i].difficulty3 = new QuestDifficultyEntry();
+ diffs[i].difficulty4 = new QuestDifficultyEntry();
+ diffs[i].difficulty5 = new QuestDifficultyEntry();
+ diffs[i].difficulty6 = new QuestDifficultyEntry();
+ diffs[i].difficulty7 = new QuestDifficultyEntry();
+ diffs[i].difficulty8 = new QuestDifficultyEntry();
+ }
+ context.SendPacket(new QuestDifficultyPacket(diffs));
// [K873] I believe this is the correct packet, but it causes an infinite send/recieve loop, we're probably just missing something else
context.SendPacket(new QuestDifficultySendFinishedPacket());
+
+
+ //// 现在可以对任务列表(Quests)进行处理
+ //// 例如,可以记录任务的信息或进行其他处理
+ //foreach (var quest in Quests)
+ //{
+ // if(quest.ObjectType == ObjectType.Quest)
+ // {
+ // }
+ //}
+
}
}
diff --git a/Server/Protocol/Handlers/0B-QuestHandler/0B-30-QuestCounterHandler.cs b/Server/Protocol/Handlers/0B-QuestHandler/0B-30-QuestCounterHandler.cs
index f3eeda3..a158fd4 100644
--- a/Server/Protocol/Handlers/0B-QuestHandler/0B-30-QuestCounterHandler.cs
+++ b/Server/Protocol/Handlers/0B-QuestHandler/0B-30-QuestCounterHandler.cs
@@ -12,7 +12,7 @@ namespace PSO2SERVER.Protocol.Handlers
{
public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
{
- // Not sure what this does yet
+ //Not sure what this does yet
context.SendPacket(new Unk4901Packet());
context.SendPacket(new Unk0E65Packet());
context.SendPacket(new Unk0B22Packet());
diff --git a/Server/Protocol/Handlers/0E-PartyHandler/0E-0C-QuestCounterHandler.cs b/Server/Protocol/Handlers/0E-PartyHandler/0E-0C-QuestCounterHandler.cs
index 93c1cfd..829bb76 100644
--- a/Server/Protocol/Handlers/0E-PartyHandler/0E-0C-QuestCounterHandler.cs
+++ b/Server/Protocol/Handlers/0E-PartyHandler/0E-0C-QuestCounterHandler.cs
@@ -1,54 +1,83 @@
using System;
using PSO2SERVER.Models;
using PSO2SERVER.Protocol.Packets;
+using PSO2SERVER.Party;
namespace PSO2SERVER.Protocol.Handlers
{
[PacketHandlerAttr(0x0E, 0x0C)]
public class QuestDifficultyStartHandler : PacketHandler
{
+ public string name { get; set; }
+ public string password { get; set; }
+ public string comments { get; set; }
+ public string questname { get; set; }
+ public byte min_level { get; set; }
+ public byte max_level { get; set; }
+ public byte playstyle { get; set; }
+ public PartyFlags partyFlags { get; set; }
+ public ulong unk { get; set; }
// Go go maximum code duplication (for now)
public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
{
- var reader = new PacketReader(data, position, size);
-
var info = string.Format("[<--] 接收到的数据 (hex): {0}字节", data.Length);
Logger.WriteHex(info, data);
- //QuestDefiniton def = new QuestDefiniton
- //{
- // dateOrSomething = "2024/09/21",
- // needsToBeNonzero = 0x00000020,
- // getsSetToWord = 0x0000000B,
- // questNameString = 30010,
- // playTime = (byte)QuestListPacket.EstimatedTime.Short,
- // partyType = (byte)QuestListPacket.PartyType.SinglePartyQuest,
- // difficulties = (byte)QuestListPacket.Difficulties.Normal | (byte)QuestListPacket.Difficulties.hard | (byte)QuestListPacket.Difficulties.VeryHard | (byte)QuestListPacket.Difficulties.SuperHard,
- // requiredLevel = 1,
- // // Not sure why but these need to be set for the quest to be enabled
- // field_FF = 0xF1,
- // field_101 = 1
- //};
+ var reader = new PacketReader(data, position, size);
+ name = reader.ReadUtf16(0x11CB, 0x98);
+ password = reader.ReadUtf16(0x11CB, 0x98);
+ comments = reader.ReadUtf16(0x11CB, 0x98);
+ questname = reader.ReadUtf16(0x11CB, 0x98);
+ min_level = reader.ReadByte();
+ max_level = reader.ReadByte();
+ playstyle = reader.ReadByte();
+ partyFlags = (PartyFlags)reader.ReadByte();
+ unk = reader.ReadUInt64();
+ // 打印输出
+ Logger.Write($"Name: {name}");
+ Logger.Write($"Password: {password}");
+ Logger.Write($"Comments: {comments}");
+ Logger.Write($"Quest Name: {questname}");
+ Logger.Write($"Min Level: {min_level}");
+ Logger.Write($"Max Level: {max_level}");
+ Logger.Write($"Playstyle: {playstyle}");
+ Logger.Write($"Party Flags: {partyFlags}"); // 如果 PartyFlags 是枚举类型
+ Logger.Write($"Unknown Value: {unk}");
- //QuestDifficultyPacket.QuestDifficulty diff = new QuestDifficultyPacket.QuestDifficulty
- //{
- // dateOrSomething = "2024/09/21",
- // something = 0x20,
- // something2 = 0x0B,
- // questNameString = 30010,
+ QuestDefiniton def = new QuestDefiniton
+ {
+ dateOrSomething = "2012/01/05",
+ quest_obj = new ObjectHeader(0x20, ObjectType.Quest),
+ questNameid = 30010,
+ playTime = QuestEstimatedTime.Short,
+ partyType = QuestPartyType.SinglePartyQuest,
+ difficulties = QuestDifficultyType.NORMAL | QuestDifficultyType.HARD | QuestDifficultyType.VERY_HARD | QuestDifficultyType.SUPER_HARD,
+ sub_class_req_level = 1,
+ // Not sure why but these need to be set for the quest to be enabled
+ quest_type = (QuestType)0xF1,
+ field_101 = 1
+ };
- // // These are likely bitfields
- // something3 = 0x00030301
- //};
+ QuestDifficulty diff = new QuestDifficulty
+ {
+ dateOrSomething = "2012/01/05",
+ quest_obj = new ObjectHeader(0x20, ObjectType.Quest),
+ name_id = 30010,
+ // These are likely bitfields
+ area = 0x01,
+ planet = 0x03,
+ unk1 = 0x03,
+ unk2 = 0x00
+ };
+
+ var quest = new Quest("arks_010120")
+ {
+ questDef = def
+ };
+ context.currentParty.currentQuest = quest;
+ context.SendPacket(new SetQuestInfoPacket(def, context._account.AccountId));
+ context.SendPacket(new PartySetQuestPacket(0x753A, 0, def, diff, new ObjectHeader((uint)context._account.AccountId, ObjectType.Player)));
- //var quest = new Quest("arks_010120")
- //{
- // questDef = def
- //};
- //context.currentParty.currentQuest = quest;
- //context.SendPacket(new SetQuestInfoPacket(def, context._account.AccountId));
- //context.SendPacket(new QuestStartPacket(def, diff));
-
}
}
}
diff --git a/Server/Protocol/Handlers/11-ClientHandler/11-00-SegaIDLogin.cs b/Server/Protocol/Handlers/11-ClientHandler/11-00-SegaIDLogin.cs
index 70db29f..be97d65 100644
--- a/Server/Protocol/Handlers/11-ClientHandler/11-00-SegaIDLogin.cs
+++ b/Server/Protocol/Handlers/11-ClientHandler/11-00-SegaIDLogin.cs
@@ -199,25 +199,25 @@ namespace PSO2SERVER.Protocol.Handlers
// 提交更改到数据库
db.SaveChanges();
+
+ SaveNetInterfacesToDatabase(user.AccountId, Username, Interfaces);
}
//TODO 方便GM测试
- if (Password != user.Password)
- {
- if (Password == "")
- {
- error = "密碼為空 #2.";
- user = null;
- }
- else
- if (!BCrypt.Net.BCrypt.Verify(Password, user.Password))
- {
- error = "密碼錯誤.";
- user = null;
- }
- }
-
- SaveNetInterfacesToDatabase(user.AccountId, Username, Interfaces);
+ //if (Password != user.Password)
+ //{
+ // if (Password == "")
+ // {
+ // error = "密碼為空 #2.";
+ // user = null;
+ // }
+ // else
+ // if (!BCrypt.Net.BCrypt.Verify(Password, user.Password))
+ // {
+ // error = "密碼錯誤.";
+ // user = null;
+ // }
+ //}
}
context.SendPacket(new LoginDataPacket("夢幻之星2", error, (user == null) ? (uint)0 : (uint)user.AccountId));
diff --git a/Server/Protocol/Handlers/11-ClientHandler/11-05-CharacterCreate.cs b/Server/Protocol/Handlers/11-ClientHandler/11-05-CharacterCreate.cs
index a6c8c42..3dfdef7 100644
--- a/Server/Protocol/Handlers/11-ClientHandler/11-05-CharacterCreate.cs
+++ b/Server/Protocol/Handlers/11-ClientHandler/11-05-CharacterCreate.cs
@@ -15,58 +15,79 @@ namespace PSO2SERVER.Protocol.Handlers
{
#region implemented abstract members of PacketHandler
- public struct CharacterCreatePacket
- {
- public int CharacterID;
- public int AccountID;
- public int Unk1;
- public int VoiceType;
- public short Unk2;
- public short VoicePitch;
- public string Name;
- public int Unk3;
- public LooksParam Looks;
- public JobParam Jobs;
- public string unk4; // 148 字节的字节数组
- }
-
- private CharacterCreatePacket packet = new CharacterCreatePacket();
+ public uint CharacterID { get; set; }
+ public uint AccountID { get; set; }
+ public uint Unk1 { get; set; }
+ public uint VoiceType { get; set; }
+ public ushort Unk2 { get; set; }
+ public short VoicePitch { get; set; }
+ public string Name { get; set; }
+ public LooksParam Looks { get; set; }
+ public uint Unk3 { get; set; }
+ public JobParam Jobs { get; set; }
+ public byte[] unk4 { get; set; } = new byte[148]; // 148 字节的字节数组
public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
{
if (context._account == null)
return;
- var reader = new PacketReader(data, position, size);
var info = string.Format("[<--] 接收到的数据 (hex): {0} 字节", data.Length);
Logger.WriteHex(info, data);
- packet.CharacterID = reader.ReadInt32();
- packet.AccountID = reader.ReadInt32();
- packet.Unk1 = reader.ReadInt32();//Unk1
- packet.VoiceType = reader.ReadInt32(); // VoiceType
- packet.Unk2 = reader.ReadInt16(); // 5 unknown bytes
- packet.VoicePitch = reader.ReadInt16(); // VoiceData
- packet.Name = reader.ReadFixedLengthUtf16(16);
- packet.Unk3 = reader.ReadInt32();
- packet.Looks = reader.ReadStruct();
- packet.Jobs = reader.ReadStruct();
- packet.unk4 = reader.ReadFixedLengthUtf16(76);
+ var reader = new PacketReader(data, position, size);
+ CharacterID = reader.ReadUInt32();
+ AccountID = reader.ReadUInt32();
+ Unk1 = reader.ReadUInt32();//Unk1
+ VoiceType = reader.ReadUInt32(); // VoiceType
+ Unk2 = reader.ReadUInt16(); // 5 unknown bytes
+ VoicePitch = reader.ReadInt16(); // VoiceData
+ Name = reader.ReadFixedLengthUtf16(16);
+ Looks = reader.ReadStruct();
+ Unk3 = reader.ReadUInt32();
+ Jobs = reader.ReadStruct();
+ unk4 = reader.ReadBytes(unk4.Length);
+
+ // 创建一个 Consumable 类型的物品
+ PSO2ItemConsumable consumableItem = new PSO2ItemConsumable
+ {
+ amount = 10,
+ };
+
+ PSO2Items[] items = new PSO2Items[10];
+
+ for (var i = 0; i < 10; i++)
+ {
+ items[i] = new PSO2Items
+ {
+ uuid = (ulong)i,
+ id = new ItemId
+ {
+ ItemType = 3,
+ Id = 1,
+ Subid = 0,
+ },
+ data = new Items { Consumable = consumableItem }
+ };
+ }
var newCharacter = new Character
{
CharacterID = 0,
AccountID = context._account.AccountId,
- Unk1 = packet.Unk1,
- VoiceType = packet.VoiceType,
- Unk2 = packet.Unk2,
- VoicePitch = packet.VoicePitch,
- Name = packet.Name,
- Unk3 = packet.Unk3,
- Jobs = packet.Jobs,
- Looks = packet.Looks,
+ Unk1 = (int)Unk1,
+ VoiceType = (int)VoiceType,
+ Unk2 = (short)Unk2,
+ VoicePitch = VoicePitch,
+ Name = Name,
+ Looks = Looks,
+ Unk3 = (int)Unk3,
+ Jobs = Jobs,
+ Unk4 = unk4,
+ EquipedItems = items,
+
Account = context._account,
- fulldata = data
+ Fulldata = data
};
// Add to database
diff --git a/Server/Protocol/Handlers/11-ClientHandler/11-14-BlockLogin.cs b/Server/Protocol/Handlers/11-ClientHandler/11-14-BlockLogin.cs
index 0f074e5..a4d9544 100644
--- a/Server/Protocol/Handlers/11-ClientHandler/11-14-BlockLogin.cs
+++ b/Server/Protocol/Handlers/11-ClientHandler/11-14-BlockLogin.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using PSO2SERVER.Models;
using PSO2SERVER.Protocol.Packets;
@@ -7,10 +8,38 @@ namespace PSO2SERVER.Protocol.Handlers
[PacketHandlerAttr(0x11, 0x14)]
public class BlockLogin : PacketHandler
{
+ // Player ID
+ public ulong PlayerId { get; set; }
+
+ // Unknown fields
+ public byte Unk1 { get; set; }
+ public byte Unk2 { get; set; }
+ public ushort Unk3 { get; set; }
+ public uint Unk4 { get; set; }
+ public uint Unk5 { get; set; }
+
+ // Version ID (32 bytes)
+ public byte[] VerId { get; set; } = new byte[0x20];
+
+ // Clients' active network interfaces
+ public List Interfaces { get; set; }
+
+ // Login challenge
+ public uint Challenge { get; set; }
+
+ // Unknown data, fixed length (196 bytes)
+ public byte[] Unk6 { get; set; } = new byte[0xC4];
+
+ // Unknown field, 16 bytes
+ public byte[] Unk7 { get; set; } = new byte[0x10];
+
public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
{
var info = string.Format("[<--] 接收到的数据 (hex): {0} 字节", data.Length);
Logger.WriteHex(info, data);
+
+ var reader = new PacketReader(data, position, size);
+
}
}
}
diff --git a/Server/Protocol/Handlers/11-ClientHandler/11-41-CreateCharacterOne.cs b/Server/Protocol/Handlers/11-ClientHandler/11-41-CreateCharacterOne.cs
index db973c9..03b062d 100644
--- a/Server/Protocol/Handlers/11-ClientHandler/11-41-CreateCharacterOne.cs
+++ b/Server/Protocol/Handlers/11-ClientHandler/11-41-CreateCharacterOne.cs
@@ -14,14 +14,8 @@ namespace PSO2SERVER.Protocol.Handlers
public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
{
- //var writer = new PacketWriter();
- //writer.Write((uint)0);
- //writer.Write((uint)0);
- //writer.Write((uint)0);
- //writer.Write((uint)0);
-
- //context.SendPacket(0x11, 0x42, 0x0, writer.ToArray());
- context.SendPacket(new CreateCharacterOneResponsePacket());
+ //TODO 这里需要获取账号的AC是否足够,并且判断角色槽位是否已满
+ context.SendPacket(new CreateCharacterOneResponsePacket(0, 0, 0, 0));
}
#endregion
diff --git a/Server/Protocol/Handlers/11-ClientHandler/11-52-CreateCharacterInviteNickname.cs b/Server/Protocol/Handlers/11-ClientHandler/11-52-CreateCharacterInviteNickname.cs
index 6f9c1ca..7083c6e 100644
--- a/Server/Protocol/Handlers/11-ClientHandler/11-52-CreateCharacterInviteNickname.cs
+++ b/Server/Protocol/Handlers/11-ClientHandler/11-52-CreateCharacterInviteNickname.cs
@@ -5,7 +5,7 @@ using PSO2SERVER.Protocol.Packets;
namespace PSO2SERVER.Protocol.Handlers
{
[PacketHandlerAttr(0x11, 0x52)]
- public class _11_52_UNK : PacketHandler
+ public class CreateCharacterInviteNickname : PacketHandler
{
private uint PlayerID { get; set; }
private uint unk1 { get; set; }
@@ -22,7 +22,8 @@ namespace PSO2SERVER.Protocol.Handlers
Logger.Write($"PlayerID:{PlayerID} unk1:{unk1}");
- context.SendPacket(new CreateCharacterInviteNicknameResponse());
+ //TODO 数据包结构不对的 需要探索
+ context.SendPacket(new CreateCharacterInviteNicknameResponsePacket(0, 0, 0, 0));
}
}
}
diff --git a/Server/Protocol/Handlers/1F-UNKHandler/1F-09-UNK1F09.cs b/Server/Protocol/Handlers/11-ClientHandler/11-64-AllBlocksListRequest.cs
similarity index 58%
rename from Server/Protocol/Handlers/1F-UNKHandler/1F-09-UNK1F09.cs
rename to Server/Protocol/Handlers/11-ClientHandler/11-64-AllBlocksListRequest.cs
index 5c73948..d4df3d4 100644
--- a/Server/Protocol/Handlers/1F-UNKHandler/1F-09-UNK1F09.cs
+++ b/Server/Protocol/Handlers/11-ClientHandler/11-64-AllBlocksListRequest.cs
@@ -1,12 +1,16 @@
using System;
using PSO2SERVER.Models;
using PSO2SERVER.Protocol.Packets;
+using static PSO2SERVER.Protocol.Handlers.CharacterNewNameRequest;
namespace PSO2SERVER.Protocol.Handlers
{
- [PacketHandlerAttr(0x1F, 0x09)]
- public class _1F_09_UNK : PacketHandler
+ [PacketHandlerAttr(0x11, 0x64)]
+ public class AllBlocksListRequest : PacketHandler
{
+ public FixedVec Blocks { get; set; } = new FixedVec (200);
+ public uint unk { get; set; }
+
public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
{
var info = string.Format("[<--] 接收到的数据 (hex): {0} 字节", data.Length);
diff --git a/Server/Protocol/Handlers/1F-ClientOrderHandler/1F-01-TakenOrdersRequest.cs b/Server/Protocol/Handlers/1F-ClientOrderHandler/1F-01-TakenOrdersRequest.cs
new file mode 100644
index 0000000..4f04db9
--- /dev/null
+++ b/Server/Protocol/Handlers/1F-ClientOrderHandler/1F-01-TakenOrdersRequest.cs
@@ -0,0 +1,29 @@
+using System;
+using PSO2SERVER.Models;
+using PSO2SERVER.Protocol.Packets;
+
+namespace PSO2SERVER.Protocol.Handlers
+{
+ [PacketHandlerAttr(0x1F, 0x01)]
+ public class TakenOrdersRequest : PacketHandler
+ {
+ public struct TakenOrdersRequestPacket
+ {
+ public uint unk1;
+ public uint unk2;
+ public uint unk3;
+ public uint unk4;
+ }
+
+ public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
+ {
+ var info = string.Format("[<--] 接收到的数据 (hex): {0} 字节", data.Length);
+ Logger.WriteHex(info, data);
+
+ var reader = new PacketReader(data, position, size);
+ var packet = reader.ReadStruct();
+
+ Logger.Write($"unk1 = {packet.unk1},unk2 = {packet.unk2},unk3 = {packet.unk3},unk4 = {packet.unk4}");
+ }
+ }
+}
diff --git a/Server/Protocol/Handlers/1F-ClientOrderHandler/1F-02-OrderListRequest.cs b/Server/Protocol/Handlers/1F-ClientOrderHandler/1F-02-OrderListRequest.cs
new file mode 100644
index 0000000..a1e6b00
--- /dev/null
+++ b/Server/Protocol/Handlers/1F-ClientOrderHandler/1F-02-OrderListRequest.cs
@@ -0,0 +1,41 @@
+using System;
+using PSO2SERVER.Models;
+using PSO2SERVER.Protocol.Packets;
+
+namespace PSO2SERVER.Protocol.Handlers
+{
+ [PacketHandlerAttr(0x1F, 0x02)]
+ public class OrderListRequest : PacketHandler
+ {
+ public struct OrderListRequestPacket
+ {
+ public uint unk1;
+ public string source;
+ public uint unk3;
+ public uint unk4;
+ public uint unk5;
+ public uint unk6;
+ }
+
+ public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
+ {
+ var info = string.Format("[<--] 接收到的数据 (hex): {0} 字节", data.Length);
+ Logger.WriteHex(info, data);
+
+ var reader = new PacketReader(data, position, size);
+ var packet = new OrderListRequestPacket
+ {
+ unk1 = reader.ReadUInt32(),
+ source = reader.ReadAscii(0x70B2, 0x9E),
+ unk3 = reader.ReadUInt32(),
+ unk4 = reader.ReadUInt32(),
+ unk5 = reader.ReadUInt32(),
+ unk6 = reader.ReadUInt32(),
+ };
+
+ Logger.Write($"unk1 = {packet.unk1},source = {packet.source},unk3 = {packet.unk3},unk4 = {packet.unk4},unk5 = {packet.unk5},unk6 = {packet.unk6}");
+
+ context.SendPacket(new OrderListPacket());
+ }
+ }
+}
diff --git a/Server/Protocol/Handlers/1F-ClientOrderHandler/1F-09-UNK1F09.cs b/Server/Protocol/Handlers/1F-ClientOrderHandler/1F-09-UNK1F09.cs
new file mode 100644
index 0000000..5eb60ea
--- /dev/null
+++ b/Server/Protocol/Handlers/1F-ClientOrderHandler/1F-09-UNK1F09.cs
@@ -0,0 +1,26 @@
+using System;
+using PSO2SERVER.Models;
+using PSO2SERVER.Protocol.Packets;
+
+namespace PSO2SERVER.Protocol.Handlers
+{
+ [PacketHandlerAttr(0x1F, 0x09)]
+ public class _1F_09_UNK : PacketHandler
+ {
+ public uint unk1 { get; set; }
+ public string source { get; set; }
+ public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
+ {
+ var info = string.Format("[<--] 接收到的数据 (hex): {0} 字节", data.Length);
+ Logger.WriteHex(info, data);
+
+ var reader = new PacketReader(data, position, size);
+ //unk1 = reader.ReadUInt32();
+ //source = reader.ReadAscii(0x1875, 0x1F);
+
+ //// 打印日志
+ //Logger.Write($"unk1: {unk1}");
+ //Logger.Write($"source: {source}");
+ }
+ }
+}
diff --git a/Server/Protocol/Handlers/1F-ClientOrderHandler/1F-0F-UNK.cs b/Server/Protocol/Handlers/1F-ClientOrderHandler/1F-0F-UNK.cs
new file mode 100644
index 0000000..a3788d9
--- /dev/null
+++ b/Server/Protocol/Handlers/1F-ClientOrderHandler/1F-0F-UNK.cs
@@ -0,0 +1,27 @@
+using System;
+using PSO2SERVER.Models;
+using PSO2SERVER.Protocol.Packets;
+
+namespace PSO2SERVER.Protocol.Handlers
+{
+ [PacketHandlerAttr(0x1F, 0x0F)]
+ public class _1F_0F_UNK : PacketHandler
+ {
+ public struct Unk1F0FPacket
+ {
+ public uint unk1;
+ public uint unk2;
+ }
+
+ public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size)
+ {
+ var info = string.Format("[<--] 接收到的数据 (hex): {0} 字节", data.Length);
+ Logger.WriteHex(info, data);
+
+ var reader = new PacketReader(data, position, size);
+ var packet = reader.ReadStruct();
+
+ Logger.Write($"unk1 = {packet.unk1}, unk2 = {packet.unk2}");
+ }
+ }
+}
diff --git a/Server/Protocol/Packet.cs b/Server/Protocol/Packet.cs
index 8e4adfb..6b9dc24 100644
--- a/Server/Protocol/Packet.cs
+++ b/Server/Protocol/Packet.cs
@@ -1,4 +1,5 @@
using PSO2SERVER.Models;
+using System.Collections.Generic;
namespace PSO2SERVER.Protocol
{
@@ -6,5 +7,26 @@ namespace PSO2SERVER.Protocol
{
public abstract byte[] Build();
public abstract PacketHeader GetHeader();
+
+ // 新增:获取整体数据包字节表示
+ public byte[] GetPacketBytes()
+ {
+ var header = GetHeader(); // 获取包头
+ var packetBody = Build(); // 获取包体
+
+ // 如果包头的大小为0,则重新计算
+ if (header.Size == 0)
+ {
+ // 包头(包括固定的前四个字节)的大小加上包体的大小
+ header.Size = (uint)(header.ToBytes().Length + packetBody.Length);
+ }
+
+ // 将包头与包体合并
+ var totalPacket = new List();
+ totalPacket.AddRange(header.ToBytes()); // 包头转换为字节并加入
+ totalPacket.AddRange(packetBody); // 包体加入
+
+ return totalPacket.ToArray(); // 返回整个数据包字节数组
+ }
}
}
\ No newline at end of file
diff --git a/Server/Protocol/PacketReader.cs b/Server/Protocol/PacketReader.cs
index 5be226f..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
@@ -37,27 +38,6 @@ namespace PSO2SERVER.Protocol
return (ReadUInt32() ^ xor) - sub;
}
- // 示例方法:读取 ASCII 字符串
- public string ReadAsciiForPosition(int start, int length)
- {
- try
- {
- if (_position + length > _size)
- {
- throw new ArgumentOutOfRangeException(nameof(start), $"Reading beyond data boundary in {nameof(ReadAsciiForPosition)}");
- }
-
- var result = Encoding.ASCII.GetString(_data, _position, length);
- _position += length;
- return result;
- }
- catch (Exception ex)
- {
- Logger.WriteException(nameof(ReadAsciiForPosition), ex);
- throw;
- }
- }
-
public string ReadAscii(uint xor, uint sub)
{
var magic = ReadMagic(xor, sub);
@@ -164,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 20bde56..92d7592 100644
--- a/Server/Protocol/PacketWriter.cs
+++ b/Server/Protocol/PacketWriter.cs
@@ -70,9 +70,6 @@ namespace PSO2SERVER.Protocol
// 写入字符串的字节数据
Write(str.ToBytes());
- // 写入 null 终结符
- Write((byte)0);
-
// 填充 null 字节以保证 4 字节对齐
for (var i = 0; i < padding; i++)
{
@@ -81,17 +78,6 @@ namespace PSO2SERVER.Protocol
}
}
- internal void WritePosition(PSOLocation location)
- {
- Write(Helper.FloatToHalfPrecision(location.RotX));
- Write(Helper.FloatToHalfPrecision(location.RotY));
- Write(Helper.FloatToHalfPrecision(location.RotZ));
- Write(Helper.FloatToHalfPrecision(location.RotW));
- Write(Helper.FloatToHalfPrecision(location.PosX));
- Write(Helper.FloatToHalfPrecision(location.PosY));
- Write(Helper.FloatToHalfPrecision(location.PosZ));
- }
-
public void WriteUtf16(string str, uint xor, uint sub)
{
if ((str == null) || (str == "") || (str.Length == 0))
@@ -151,6 +137,17 @@ namespace PSO2SERVER.Protocol
}
}
+ internal void WritePosition(PSOLocation location)
+ {
+ Write(Helper.FloatToHalfPrecision(location.RotX));
+ Write(Helper.FloatToHalfPrecision(location.RotY));
+ Write(Helper.FloatToHalfPrecision(location.RotZ));
+ Write(Helper.FloatToHalfPrecision(location.RotW));
+ Write(Helper.FloatToHalfPrecision(location.PosX));
+ Write(Helper.FloatToHalfPrecision(location.PosY));
+ Write(Helper.FloatToHalfPrecision(location.PosZ));
+ }
+
public void WriteAccountHeader(uint AccountId)
{
Write(AccountId);
@@ -174,18 +171,141 @@ namespace PSO2SERVER.Protocol
Write(strArr);
}
+ // 新增的 WriteStructArray 方法
+ public unsafe void WriteStructArray(T[] structures) where T : struct
+ {
+ int totalSize = Marshal.SizeOf(typeof(T)) * structures.Length;
+ var strArr = new byte[totalSize];
+
+ fixed (byte* ptr = strArr)
+ {
+ for (int i = 0; i < structures.Length; i++)
+ {
+ byte* currentPtr = ptr + (i * Marshal.SizeOf(typeof(T)));
+ Marshal.StructureToPtr(structures[i], (IntPtr)currentPtr, false);
+ }
+ }
+
+ Write(strArr);
+ }
+
public byte[] ToArray()
{
var ms = (MemoryStream) BaseStream;
return ms.ToArray();
}
- internal void WriteBytes(byte b, uint count)
+ public void WriteBytes(byte b, uint count)
{
for(int i = 0; i < count; i++)
{
Write(b);
}
}
+
+ public void WriteByteArray(byte[] b)
+ {
+ Write(b);
+ }
+
+ public void WriteUintArray(uint[] array)
+ {
+ foreach (var item in array)
+ {
+ Write(item);
+ }
+ }
+
+ public void WriteIntArray(int[] array)
+ {
+ foreach (var item in array)
+ {
+ Write(item);
+ }
+ }
+
+ public void WriteFloatArray(float[] array)
+ {
+ foreach (var item in array)
+ {
+ Write(item);
+ }
+ }
+
+ public void WriteObjectHeader(ObjectHeader obj)
+ {
+ Write(obj.ID);
+ Write(obj.padding);
+ Write((ushort)obj.ObjectType);
+ Write(obj.MapID);
+ }
+
+ public void WriteObjectHeaderArray(ObjectHeader[] objlist)
+ {
+ foreach (var obj in objlist)
+ {
+ WriteObjectHeader(obj);
+ }
+ }
+
+ public void WriteObjectHeaderList(List objlist)
+ {
+ foreach (var obj in objlist)
+ {
+ WriteObjectHeader(obj); // 直接写入每个 ObjectHeader 对象
+ }
+ }
+
+ // 支持 Guid 类型
+ public void WriteGuid(Guid guid)
+ {
+ Write(guid.ToByteArray());
+ }
+
+ // 支持 DateTime 类型
+ public void WriteDateTime(DateTime dateTime)
+ {
+ var timestamp = new DateTimeOffset(dateTime).ToUnixTimeSeconds();
+ Write(timestamp);
+ }
+
+ // 支持 List
+ public void WriteList(List list) where T : struct
+ {
+ foreach (var item in list)
+ {
+ WriteStruct(item);
+ }
+ }
+
+ // 支持 2D 数组
+ public void Write2DIntArray(int[,] array)
+ {
+ for (int i = 0; i < array.GetLength(0); i++)
+ {
+ for (int j = 0; j < array.GetLength(1); j++)
+ {
+ Write(array[i, j]);
+ }
+ }
+ }
+
+ // 支持 2D 数组
+ public void Write2DUIntArray(uint[,] array)
+ {
+ for (int i = 0; i < array.GetLength(0); i++)
+ {
+ for (int j = 0; j < array.GetLength(1); j++)
+ {
+ Write(array[i, j]);
+ }
+ }
+ }
+
+ // 写入 Half 类型(16 位浮动点数)
+ public void WriteHalf(Half value)
+ {
+ Write(Half.GetBytes(value)); // 写入 2 个字节
+ }
}
}
\ No newline at end of file
diff --git a/Server/Protocol/Packets/03-ServerPacket/03-00-MapTransferPacket.cs b/Server/Protocol/Packets/03-ServerPacket/03-00-MapTransferPacket.cs
index 2f23ee3..b55567e 100644
--- a/Server/Protocol/Packets/03-ServerPacket/03-00-MapTransferPacket.cs
+++ b/Server/Protocol/Packets/03-ServerPacket/03-00-MapTransferPacket.cs
@@ -13,41 +13,22 @@ namespace PSO2SERVER.Protocol.Packets
{
private readonly Map _map;
private readonly int _playerid;
- private ZoneSettings _zonesettings;
public MapTransferPacket(Map map, int PlayerId)
{
_map = map;
_playerid = PlayerId;
- _zonesettings = new ZoneSettings
- {
- WorldId = 1,
- Unk1 = 0,
- };
}
#region implemented abstract members of Packet
public override byte[] Build()
{
- PacketWriter writer = new PacketWriter();
- writer.WriteStruct(new ObjectHeader(3, ObjectType.Map));
- writer.WriteStruct(new ObjectHeader((uint)_playerid, ObjectType.Player));
- writer.Write(0x1); // 8 Zeros
- writer.Write(0); // 8 Zeros
- writer.Write(~(uint)_map.Type); // F4 FF FF FF
- writer.Write(_map.MapID); // Map ID maybe
- writer.Write((uint)_map.Flags);
- writer.Write(_map.GenerationArgs.seed); // 81 8F E6 19 (Maybe seed)
- writer.Write(_map.VariantID); // Randomgen enable / disable maybe
- writer.Write(_map.GenerationArgs.xsize); // X Size
- writer.Write(_map.GenerationArgs.ysize); // Y Size
- writer.Write(1);
- writer.Write(1);
- writer.Write(~0); // FF FF FF FF FF FF FF FF
- writer.Write(0x301);
-
- return writer.ToArray();
+ PacketWriter pkt = new PacketWriter();
+ pkt.WriteStruct(Map);
+ pkt.WriteStruct(target);
+ pkt.WriteStruct(settings);
+ return pkt.ToArray();
}
public override PacketHeader GetHeader()
diff --git a/Server/Protocol/Packets/03-ServerPacket/03-04-LoadingScreenTransitionPacket.cs b/Server/Protocol/Packets/03-ServerPacket/03-04-LoadingScreenTransitionPacket.cs
index c3c7fac..2d89df4 100644
--- a/Server/Protocol/Packets/03-ServerPacket/03-04-LoadingScreenTransitionPacket.cs
+++ b/Server/Protocol/Packets/03-ServerPacket/03-04-LoadingScreenTransitionPacket.cs
@@ -16,7 +16,8 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
- return new byte[0];
+ var pkt = new PacketWriter();
+ return pkt.ToArray();
}
public override PacketHeader GetHeader()
diff --git a/Server/Protocol/Packets/03-ServerPacket/03-23-LoadingScreenRemovePacket.cs b/Server/Protocol/Packets/03-ServerPacket/03-23-LoadingScreenRemovePacket.cs
index 2fa454f..039472a 100644
--- a/Server/Protocol/Packets/03-ServerPacket/03-23-LoadingScreenRemovePacket.cs
+++ b/Server/Protocol/Packets/03-ServerPacket/03-23-LoadingScreenRemovePacket.cs
@@ -12,7 +12,8 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
- return new byte[0];
+ var pkt = new PacketWriter();
+ return pkt.ToArray();
}
public override PacketHeader GetHeader()
diff --git a/Server/Protocol/Packets/04-ObjectRelatedPacket/04-06-DespawnObjectPacket.cs b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-06-DespawnObjectPacket.cs
index e387e76..2ae419e 100644
--- a/Server/Protocol/Packets/04-ObjectRelatedPacket/04-06-DespawnObjectPacket.cs
+++ b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-06-DespawnObjectPacket.cs
@@ -29,7 +29,7 @@ namespace PSO2SERVER.Protocol.Packets
public override PacketHeader GetHeader()
{
- return new PacketHeader(0x04, 0x06, PacketFlags.None);
+ return new PacketHeader(0x04, 0x06, PacketFlags.OBJECT_RELATED);
}
#endregion
diff --git a/Server/Protocol/Packets/04-ObjectRelatedPacket/04-07-MovementPacket.cs b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-07-MovementPacket.cs
index bfd853b..3509f85 100644
--- a/Server/Protocol/Packets/04-ObjectRelatedPacket/04-07-MovementPacket.cs
+++ b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-07-MovementPacket.cs
@@ -9,7 +9,7 @@ using PSO2SERVER.Zone;
namespace PSO2SERVER.Protocol.Packets
{
- class MovementPacket : Packet
+ public class MovementPacket : Packet
{
public struct PackedVec4
{
diff --git a/Server/Protocol/Packets/04-ObjectRelatedPacket/04-0F-EnemyKilledPacket.cs b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-0F-EnemyKilledPacket.cs
index 4f813b3..8840913 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,29 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.WriteStruct(Receiver);
+ pkt.WriteStruct(Dmg_target);
+ pkt.WriteStruct(Dmg_inflicter);
+ pkt.Write(Damage_id);
+ pkt.Write(Dmg_amount);
+ pkt.Write(New_hp);
+ pkt.Write(Hitbox_id);
+ pkt.WriteHalf(X_pos);
+ pkt.WriteHalf(Y_pos);
+ pkt.WriteHalf(Z_pos);
+ pkt.Write(unk1);
+ pkt.Write(unk2);
+ pkt.Write(unk3);
+ pkt.Write(unk4);
+ pkt.Write(unk5);
+ pkt.Write(unk6);
+ pkt.Write(unk7);
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/04-ObjectRelatedPacket/04-52-DamageReceivePacket.cs b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-52-DamageReceivePacket.cs
index 8c41aff..acfbba9 100644
--- a/Server/Protocol/Packets/04-ObjectRelatedPacket/04-52-DamageReceivePacket.cs
+++ b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-52-DamageReceivePacket.cs
@@ -8,6 +8,33 @@ namespace PSO2SERVER.Protocol.Packets
{
public class DamageReceivePacket : 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 DamageReceivePacket()
{
@@ -18,12 +45,29 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.WriteStruct(Receiver);
+ pkt.WriteStruct(Dmg_target);
+ pkt.WriteStruct(Dmg_inflicter);
+ pkt.Write(Damage_id);
+ pkt.Write(Dmg_amount);
+ pkt.Write(New_hp);
+ pkt.Write(Hitbox_id);
+ pkt.WriteHalf(X_pos);
+ pkt.WriteHalf(Y_pos);
+ pkt.WriteHalf(Z_pos);
+ pkt.Write(unk1);
+ pkt.Write(unk2);
+ pkt.Write(unk3);
+ pkt.Write(unk4);
+ pkt.Write(unk5);
+ pkt.Write(unk6);
+ pkt.Write(unk7);
return pkt.ToArray();
}
public override PacketHeader GetHeader()
{
- return new PacketHeader(0x04, 0x52, PacketFlags.None);
+ return new PacketHeader(0x04, 0x52, PacketFlags.OBJECT_RELATED);
}
#endregion
diff --git a/Server/Protocol/Packets/04-ObjectRelatedPacket/04-71-MovementEndPacket.cs b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-71-MovementEndPacket.cs
new file mode 100644
index 0000000..db6a39b
--- /dev/null
+++ b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-71-MovementEndPacket.cs
@@ -0,0 +1,34 @@
+using PSO2SERVER.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace PSO2SERVER.Protocol.Packets
+{
+ public class MovementEndPacket : Packet
+ {
+ MovementPacket.FullMovementData movData = new MovementPacket.FullMovementData();
+
+ public MovementEndPacket(MovementPacket.FullMovementData movedata)
+ {
+ this.movData = movedata;
+ }
+
+ #region implemented abstract members of Packet
+
+ public override byte[] Build()
+ {
+ PacketWriter writer = new PacketWriter();
+ writer.WriteStruct(movData);
+ return writer.ToArray();
+ }
+
+ public override PacketHeader GetHeader()
+ {
+ return new PacketHeader(0x04, 0x71, PacketFlags.OBJECT_RELATED);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Server/Protocol/Packets/04-ObjectRelatedPacket/04-75-ActionEndPacket.cs b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-75-ActionEndPacket.cs
new file mode 100644
index 0000000..5c57c4d
--- /dev/null
+++ b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-75-ActionEndPacket.cs
@@ -0,0 +1,54 @@
+using PSO2SERVER.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace PSO2SERVER.Protocol.Packets
+{
+ public class ActionEndPacket : Packet
+ {
+ public ObjectHeader unk1 { get; set; } = new ObjectHeader();
+ /// Object that was performing an action.
+ public ObjectHeader performer { get; set; } = new ObjectHeader();
+ public uint unk2 { get; set; } = 0;
+ public ObjectHeader unk3 { get; set; } = new ObjectHeader();
+ public ObjectHeader unk4 { get; set; } = new ObjectHeader();
+ public byte[] unk5 { get; set; } = new byte[0x04];
+ public string action { get; set; } = string.Empty;//Ascii
+
+ // 带参数的构造函数,用来初始化字段
+ public ActionEndPacket(ObjectHeader unk1, ObjectHeader performer, uint unk2, ObjectHeader unk3, ObjectHeader unk4, byte[] unk5, string action)
+ {
+ this.unk1 = unk1; // 如果传入的值为 null, 使用默认值
+ this.performer = performer;
+ this.unk2 = unk2;
+ this.unk3 = unk3;
+ this.unk4 = unk4;
+ this.unk5 = unk5 ?? new byte[0x04]; // 如果传入的值为 null, 使用默认字节数组
+ this.action = action ?? string.Empty; // 如果传入的值为 null, 使用空字符串
+ }
+
+ #region implemented abstract members of Packet
+
+ public override byte[] Build()
+ {
+ var pkt = new PacketWriter();
+ pkt.WriteStruct(unk1);
+ pkt.WriteStruct(performer);
+ pkt.Write(unk2);
+ pkt.WriteStruct(unk3);
+ pkt.WriteStruct(unk4);
+ pkt.WriteByteArray(unk5);
+ pkt.WriteAscii(action, 0x83EF, 0x40);
+ return pkt.ToArray();
+ }
+
+ public override PacketHeader GetHeader()
+ {
+ return new PacketHeader(0x04, 0x75, PacketFlags.PACKED_OBJECT_RELATED);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Server/Protocol/Packets/04-ObjectRelatedPacket/04-80-MovementActionServerPacket.cs b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-80-MovementActionServerPacket.cs
index 65bea8c..62fd77b 100644
--- a/Server/Protocol/Packets/04-ObjectRelatedPacket/04-80-MovementActionServerPacket.cs
+++ b/Server/Protocol/Packets/04-ObjectRelatedPacket/04-80-MovementActionServerPacket.cs
@@ -39,22 +39,22 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
- PacketWriter output = new PacketWriter();
- output.WriteStruct(new ObjectHeader((uint)_user_playerid, ObjectType.Player));
- output.WriteStruct(_preformer);
- output.Write(_preData);
- output.WriteAscii(_command, 0x4315, 0x7A);
- output.Write(_rest);
- output.WriteMagic(_thingCount, 0x4315, 0x7A);
- output.Write(_things);
- output.Write(_final);
+ PacketWriter pkt = new PacketWriter();
+ pkt.WriteStruct(new ObjectHeader((uint)_user_playerid, ObjectType.Player));
+ pkt.WriteStruct(_preformer);
+ pkt.Write(_preData);
+ pkt.WriteAscii(_command, 0x4315, 0x7A);
+ pkt.Write(_rest);
+ pkt.WriteMagic(_thingCount, 0x4315, 0x7A);
+ pkt.Write(_things);
+ pkt.Write(_final);
- return output.ToArray();
+ return pkt.ToArray();
}
public override PacketHeader GetHeader()
{
- return new PacketHeader(0x04, 0x80, PacketFlags.unk0x44);
+ return new PacketHeader(0x04, 0x80, PacketFlags.PACKED_OBJECT_RELATED);
}
#endregion
diff --git a/Server/Protocol/Packets/06-PlayerStatusPacket/06-05-GainedEXPPacket.cs b/Server/Protocol/Packets/06-PlayerStatusPacket/06-05-GainedEXPPacket.cs
index ab9ee1c..81e5032 100644
--- a/Server/Protocol/Packets/06-PlayerStatusPacket/06-05-GainedEXPPacket.cs
+++ b/Server/Protocol/Packets/06-PlayerStatusPacket/06-05-GainedEXPPacket.cs
@@ -1,16 +1,47 @@
using PSO2SERVER.Models;
+using PSO2SERVER.Party;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using static Mysqlx.Notice.Warning.Types;
namespace PSO2SERVER.Protocol.Packets
{
public class GainedEXPPacket : Packet
{
+ /// Packet receiver.
+ public ObjectHeader sender { get; set; } = new ObjectHeader();
+ /// All players that gained EXP.
+ public List receivers { get; set; } = new List();
- public GainedEXPPacket()
+ public GainedEXPPacket(Client c, ulong gainedxp)
{
+ sender = new ObjectHeader((uint)c._account.AccountId, ObjectType.Player);
+
+ foreach (Client cl in PartyManager.instance.GetCurrentPartyForClient(c).getMembers())
+ {
+ EXPReceiver receiver = new EXPReceiver
+ {
+ Object = new ObjectHeader((uint)cl._account.AccountId, ObjectType.Player),
+ //Unk1 = unk1,
+ //Unk2 = unk2,
+ //Unk3 = unk3 ?? new byte[6], // 默认值为6字节数组
+ Gained = gainedxp,
+ //Total = total,
+ //Level2 = level2,
+ //Level = level,
+ Class = cl.Character.Jobs.mainClass,
+ //Pad1 = pad1 ?? new byte[3], // 默认值为3字节数组
+ //GainedSub = gainedSub,
+ //TotalSub = totalSub,
+ //Level2Sub = level2Sub,
+ //LevelSub = levelSub,
+ //Subclass = subclass,
+ //Pad2 = pad2 ?? new byte[3], // 默认值为3字节数组
+ };
+ receivers.Add(receiver);
+ }
}
#region implemented abstract members of Packet
@@ -18,12 +49,18 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.WriteStruct(sender);
+ pkt.WriteMagic((uint)receivers.Count, 0x7C49, 0x9E);
+ foreach (var entry in receivers)
+ {
+ pkt.WriteStruct(entry);
+ }
return pkt.ToArray();
}
public override PacketHeader GetHeader()
{
- return new PacketHeader(0x06, 0x05, PacketFlags.None);
+ return new PacketHeader(0x06, 0x05, PacketFlags.PACKED);
}
#endregion
diff --git a/Server/Protocol/Packets/08-SpawnPacket/08-04-CharacterSpawnPacket.cs b/Server/Protocol/Packets/08-SpawnPacket/08-04-CharacterSpawnPacket.cs
index 09babc6..cc40281 100644
--- a/Server/Protocol/Packets/08-SpawnPacket/08-04-CharacterSpawnPacket.cs
+++ b/Server/Protocol/Packets/08-SpawnPacket/08-04-CharacterSpawnPacket.cs
@@ -1,6 +1,7 @@
using PSO2SERVER.Database;
using PSO2SERVER.Models;
using PSO2SERVER.Zone;
+using System.IO;
using System.Runtime.InteropServices;
using static PSO2SERVER.Models.CharacterStruct;
using static PSO2SERVER.Protocol.Packets.CharacterSpawnPacket;
@@ -19,87 +20,84 @@ namespace PSO2SERVER.Protocol.Packets
Undefined = 0xFF,
}
- private readonly Character _character;
- public byte IsItMe = (byte)CharacterSpawnType.Myself;
- public uint IsGM = 0;
- public PSOLocation Position;
+ //private readonly Character _character;
+ //public byte IsItMe = (byte)CharacterSpawnType.Myself;
+ //public uint IsGM = 0;
+
+ ///
+ /// 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;
+ /// Always `Character`. (?)
+ public string ObjName { get; set; } = "Character";//Ascii 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; }
+ /// Character spawn type.
+ public CharacterSpawnType SpawnType { get; set; }
+ public byte Unk9 { get; set; }
+ public ushort Unk10 { get; set; }
+ /// Character data.
+ public Character Character { get; set; }
+ public uint Unk11 { get; set; }
+ /// Set to `1` if the player is a GM.
+ public uint GmFlag { get; set; }
+ /// Player's nickname.
+ public string Nickname { get; set; }//0x10
+ //#[SeekAfter(0x60)]
+ public byte[] Unk12 { get; set; } = new byte[0x40];
public CharacterSpawnPacket(Character character, PSOLocation locatiion)
{
- _character = character;
- Position = locatiion;
+ ObjHeader = new ObjectHeader((uint)character.Account.AccountId, ObjectType.Player);
+ ObjPosition = locatiion;
+ Character = character;
}
- public CharacterSpawnPacket(Character character, PSOLocation locatiion, bool isme, uint isgm)
+ public CharacterSpawnPacket(Character character, PSOLocation locatiion, bool isme, bool isgm)
{
- _character = character;
- IsItMe = isme ? (byte)CharacterSpawnType.Myself : (byte)CharacterSpawnType.Other;
- Position = locatiion;
- IsGM = isgm;
+ ObjHeader = new ObjectHeader((uint)character.Account.AccountId, ObjectType.Player);
+ ObjPosition = locatiion;
+ Character = character;
+ SpawnType = isme ? CharacterSpawnType.Myself : CharacterSpawnType.Other;
+ GmFlag = isgm ? (uint)1 : 0;
}
#region implemented abstract members of Packet
public override byte[] Build()
{
- var writer = new PacketWriter();
-
+ var pkt = new PacketWriter();
// Accounts header
- writer.WriteAccountHeader((uint)_character.Account.AccountId);
-
+ pkt.WriteStruct(ObjHeader);
// Spawn position
- writer.WritePosition(Position);
-
- writer.Write((ushort)0);
- writer.WriteFixedLengthASCII("Character", 32);
- writer.Write((ushort)1); // 0x44
- writer.Write((ushort)0); // 0x46
- writer.Write((uint)602); // 0x48
- writer.Write((uint)1); // 0x4C
- writer.Write((uint)53); // 0x50
- writer.Write((uint)0); // 0x54
-
- // Character spawn type.
- writer.Write(IsItMe); // 0x58
- writer.Write((byte)0x00);
- writer.Write((ushort)0x00);
-
- ////writer.write((ushort)0x022f); // 0x5c
- //writer.write((byte)0x2f); // 0x5c
- //writer.write((byte)0x02);
- ////writer.write((ushort)0x0132); // 0x5e
- //writer.write((byte)0x32);
- //writer.write((byte)0x01);
-
+ 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.
- writer.Write((uint)_character.AccountID);
- writer.Write((uint)_character.CharacterID);
- writer.Write((uint)_character.Unk1);//4
- writer.Write((uint)_character.VoiceType);//4
- writer.Write((ushort)_character.Unk2);//2
- writer.Write(_character.VoicePitch);//2
- writer.WriteFixedLengthUtf16(_character.Name, 16);
- writer.Write((uint)_character.Unk3); // 0x90
- writer.WriteStruct(_character.Looks);
- JobParam jobParam = _character.Jobs;
- jobParam.mainClass = ClassType.Luster;
- jobParam.subClass = ClassType.Phantom;
- jobParam.entries.Luster.level = 100;
- writer.WriteStruct(jobParam);
- writer.WriteFixedLengthUtf16(_character.Account.Nickname, 16);
- writer.WriteBytes(0, 116);
+ pkt.Write(Character.BuildCharacterByteArray());
+ pkt.Write(Unk11);
+ pkt.Write(GmFlag);
+ pkt.WriteFixedLengthUtf16(Character.Account.Nickname, 0x10);
+ pkt.BaseStream.Seek(0x60, SeekOrigin.Current);
+ pkt.Write(Unk12);
- writer.Write((uint)0); // 0x204
- writer.Write(IsGM); // gmflag?
-
-
- for (var i = 0; i < 0x60; i++)
- writer.Write((byte)0);
-
- //for (var i = 0; i < 0x40; i++)
- // writer.Write((byte)0);
-
- return writer.ToArray();
+ return pkt.ToArray();
}
public override PacketHeader GetHeader()
diff --git a/Server/Protocol/Packets/08-SpawnPacket/08-05-TransporterSpawnPacket.cs b/Server/Protocol/Packets/08-SpawnPacket/08-05-TransporterSpawnPacket.cs
index 0345211..08395d6 100644
--- a/Server/Protocol/Packets/08-SpawnPacket/08-05-TransporterSpawnPacket.cs
+++ b/Server/Protocol/Packets/08-SpawnPacket/08-05-TransporterSpawnPacket.cs
@@ -1,4 +1,5 @@
using PSO2SERVER.Models;
+using PSO2SERVER.Zone;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -8,9 +9,25 @@ namespace PSO2SERVER.Protocol.Packets
{
public class TransporterSpawnPacket : Packet
{
+ public ObjectHeader objHeader { get; set; } = new ObjectHeader();
+ public PSOLocation objPosition { get; set; } = new PSOLocation();
+ public ushort unk1 { get; set; } = 0;
+ /// Enemy name.
+ public string objName { get; set; } = string.Empty;//0x20
+ public uint 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 uint unk7 { get; set; } = 0;
+ public uint unk8 { get; set; } = 0;
- public TransporterSpawnPacket()
+
+ public TransporterSpawnPacket(PSOObject obj)
{
+ objHeader = obj.Header;
+ objPosition = obj.Position;
+ objName = obj.Name;
}
#region implemented abstract members of Packet
@@ -18,6 +35,17 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.WriteStruct(objHeader);
+ pkt.WritePosition(objPosition);
+ pkt.Write(unk1); // Padding?
+ pkt.WriteFixedLengthASCII(objName, 0x20);
+ pkt.Write(unk2);
+ pkt.Write(unk3);
+ pkt.Write(unk4);
+ pkt.Write(unk5);
+ pkt.Write(unk6);
+ pkt.Write(unk7);
+ pkt.Write(unk8);
return pkt.ToArray();
}
diff --git a/Server/Protocol/Packets/08-SpawnPacket/08-09-EventSpawnPacket.cs b/Server/Protocol/Packets/08-SpawnPacket/08-09-EventSpawnPacket.cs
index 005730a..3ad35b7 100644
--- a/Server/Protocol/Packets/08-SpawnPacket/08-09-EventSpawnPacket.cs
+++ b/Server/Protocol/Packets/08-SpawnPacket/08-09-EventSpawnPacket.cs
@@ -1,16 +1,43 @@
using PSO2SERVER.Models;
+using PSO2SERVER.Zone;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using static PSO2SERVER.Models.PSOObject;
namespace PSO2SERVER.Protocol.Packets
{
public class EventSpawnPacket : Packet
{
+ public readonly PSOObject _obj;
+ public ObjectHeader objHeader { get; set; } = new ObjectHeader();
+ public PSOLocation objPosition { get; set; } = new PSOLocation();
+ public ushort unk1 { get; set; } = 0;
+ public string objName { get; set; } = string.Empty;
+ public uint unk3 { get; set; } = 0;
+ public byte[] unk4 { get; set; } = new byte[0x0C];
+ public ushort unk5 { get; set; } = 0;
+ public ushort unk6 { get; set; } = 0;
+ public uint unk7 { get; set; } = 0;
+ public uint unk8 { get; set; } = 0;
+ public uint unk9 { get; set; } = 0;
+ public uint unk10 { get; set; } = 0;
+ public uint unk11 { get; set; } = 0;
+ public uint unk12 { get; set; } = 0;
+ public uint unk13 { get; set; } = 0;
+ public uint unk14 { get; set; } = 0;
+ public uint flags { get; set; } = 0;
+ /// Event data.
+ public List data { get; set; } = new List();
- public EventSpawnPacket()
+ public EventSpawnPacket(PSOObject obj)
{
+ objHeader = obj.Header;
+ objPosition = obj.Position;
+ objName = obj.Name;
+ flags = 4;
+ _obj = obj;
}
#region implemented abstract members of Packet
@@ -18,6 +45,29 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.WriteStruct(objHeader);
+ pkt.WritePosition(objPosition);
+ pkt.Write(unk1); // Padding?
+ pkt.WriteFixedLengthASCII(objName, 0x20);
+ pkt.Write(unk3);
+ pkt.Write(unk4);
+ pkt.Write(unk5);
+ pkt.Write(unk6);
+ pkt.Write(unk7);
+ pkt.Write(unk8);
+ pkt.Write(unk9);
+ pkt.Write(unk10);
+ pkt.Write(unk11);
+ pkt.Write(unk12);
+ pkt.Write(unk13);
+ pkt.Write(unk14);
+ pkt.Write(flags);
+
+ pkt.Write(_obj.Things.Length);
+ foreach (PSOObjectThing thing in _obj.Things)
+ {
+ pkt.WriteStruct(thing);
+ }
return pkt.ToArray();
}
diff --git a/Server/Protocol/Packets/08-SpawnPacket/08-0B-ObjectSpawnPacket.cs b/Server/Protocol/Packets/08-SpawnPacket/08-0B-ObjectSpawnPacket.cs
index ec7fe74..68edf50 100644
--- a/Server/Protocol/Packets/08-SpawnPacket/08-0B-ObjectSpawnPacket.cs
+++ b/Server/Protocol/Packets/08-SpawnPacket/08-0B-ObjectSpawnPacket.cs
@@ -1,5 +1,6 @@
using Org.BouncyCastle.Utilities;
using PSO2SERVER.Models;
+using PSO2SERVER.Zone;
using System;
using System.Collections.Generic;
using System.IO;
@@ -12,9 +13,21 @@ namespace PSO2SERVER.Protocol.Packets
{
public class ObjectSpawnPacket : Packet
{
- private readonly PSOObject _obj;
+ public readonly PSOObject _obj;
+ public ObjectHeader objHeader { get; set; } = new ObjectHeader();
+ public PSOLocation objPosition { get; set; } = new PSOLocation();
+ public ushort unk1 { get; set; } = 0;
+ public string objName { get; set; } = string.Empty;//0x20
+ public uint[] unk2 { get; set; } = new uint[0x05];
+ public uint flags { get; set; } = 0;
+ public List data { get; set; } = new List();
+
public ObjectSpawnPacket(PSOObject obj)
{
+ objHeader = obj.Header;
+ objPosition = obj.Position;
+ objName = obj.Name;
+ flags = 4;
_obj = obj;
}
@@ -22,19 +35,20 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
- PacketWriter writer = new PacketWriter();
- writer.WriteStruct(_obj.Header);
- writer.WritePosition(_obj.Position);
- writer.Seek(2, SeekOrigin.Current); // Padding I guess...
- writer.WriteFixedLengthASCII(_obj.Name, 0x34);
- writer.Write(_obj.ThingFlag);
- writer.Write(_obj.Things.Length);
+ PacketWriter pkt = new PacketWriter();
+ pkt.WriteStruct(objHeader);
+ pkt.WritePosition(objPosition);
+ pkt.Write(unk1); // Padding?
+ pkt.WriteFixedLengthASCII(objName, 0x20);
+ pkt.WriteUintArray(unk2);
+ pkt.Write(flags);
+ pkt.Write(_obj.Things.Length);
foreach (PSOObjectThing thing in _obj.Things)
{
- writer.WriteStruct(thing);
+ pkt.WriteStruct(thing);
}
- return writer.ToArray();
+ return pkt.ToArray();
}
public override PacketHeader GetHeader()
diff --git a/Server/Protocol/Packets/08-SpawnPacket/08-0C-NPCSpawnPacket.cs b/Server/Protocol/Packets/08-SpawnPacket/08-0C-NPCSpawnPacket.cs
index 8f155b3..51d6b47 100644
--- a/Server/Protocol/Packets/08-SpawnPacket/08-0C-NPCSpawnPacket.cs
+++ b/Server/Protocol/Packets/08-SpawnPacket/08-0C-NPCSpawnPacket.cs
@@ -1,5 +1,6 @@
using Org.BouncyCastle.Utilities;
using PSO2SERVER.Models;
+using PSO2SERVER.Zone;
using System;
using System.Collections.Generic;
using System.IO;
@@ -12,48 +13,60 @@ namespace PSO2SERVER.Protocol.Packets
{
public class NPCSpawnPacket : Packet
{
- private readonly PSOObject _obj;
+ public ObjectHeader objHeader { get; set; } = new ObjectHeader();
+ public PSOLocation objPosition { get; set; } = new PSOLocation();
+ public ushort unk1 { get; set; } = 0;
+ public string objName { get; set; } = string.Empty;
+ public uint unk2 { get; set; } = 0;
+ public byte[] unk3 { get; set; } = new byte[0x0C];
+ public ushort unk4 { get; set; } = 0;
+ public ushort unk5 { get; set; } = 0;
+ public uint unk6 { get; set; } = 0;
+ public uint unk7 { get; set; } = 0;
+ public uint unk8 { get; set; } = 0;
+ public uint unk9 { get; set; } = 0;
+ public uint unk10 { get; set; } = 0;
+ public uint unk11 { get; set; } = 0;
+ public uint unk12 { get; set; } = 0;
+ public string unk13 { get; set; } = string.Empty; //Ascii
+
public NPCSpawnPacket(PSOObject obj)
{
- _obj = obj;
+ objHeader = obj.Header;
+ objPosition = obj.Position;
+ objName = obj.Name;
+ unk8 = 1101004800;
+ unk12 = 1;
}
#region implemented abstract members of Packet
public override byte[] Build()
{
- PacketWriter writer = new PacketWriter();
- writer.WriteStruct(_obj.Header);
- writer.WritePosition(_obj.Position);
- writer.Seek(2, SeekOrigin.Current); // Padding I guess...
- writer.WriteFixedLengthASCII(_obj.Name, 0x20);
+ PacketWriter pkt = new PacketWriter();
+ pkt.WriteStruct(objHeader);
+ pkt.WritePosition(objPosition);
+ pkt.Write(unk1); // Padding?
+ pkt.WriteFixedLengthASCII(objName, 0x20);
+ pkt.Write(unk2); // Padding?
+ pkt.Write(unk3); // Unknown, usually zero
+ pkt.Write(unk4);
+ pkt.Write(unk5);
+ pkt.Write(unk6);
+ pkt.Write(unk7);
+ pkt.Write(unk8); // Always this 1101004800
+ pkt.Write(unk9);
+ pkt.Write(unk10);
+ pkt.Write(unk11);
+ pkt.Write(unk12);
+ pkt.WriteAscii(unk13, 0x9FCD, 0xE7);
- writer.Write(0); // Padding?
- writer.Write(new byte[0xC]); // Unknown, usually zero
-
- writer.Write((UInt16)0);
- writer.Write((UInt16)0);
-
- writer.Write((UInt32)0);
- writer.Write((UInt32)0);
-
- writer.Write((UInt32)1101004800); // Always this
-
- writer.Write((UInt32)0);
- writer.Write((UInt32)0);
- writer.Write((UInt32)0);
-
- writer.Write((UInt32)1);
-
- writer.WriteMagic(1, 0x9FCD, 0xE7);
- writer.Write((UInt32)0);
-
- return writer.ToArray();
+ return pkt.ToArray();
}
public override PacketHeader GetHeader()
{
- return new PacketHeader(0x08, 0x0C, PacketFlags.None);
+ return new PacketHeader(0x08, 0x0C, PacketFlags.PACKED);
}
#endregion
diff --git a/Server/Protocol/Packets/08-SpawnPacket/08-0D-EnemySpawnPacket.cs b/Server/Protocol/Packets/08-SpawnPacket/08-0D-EnemySpawnPacket.cs
index e5eff72..593c828 100644
--- a/Server/Protocol/Packets/08-SpawnPacket/08-0D-EnemySpawnPacket.cs
+++ b/Server/Protocol/Packets/08-SpawnPacket/08-0D-EnemySpawnPacket.cs
@@ -1,4 +1,5 @@
using PSO2SERVER.Models;
+using PSO2SERVER.Zone;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -8,9 +9,43 @@ namespace PSO2SERVER.Protocol.Packets
{
public class EnemySpawnPacket : Packet
{
+ public ObjectHeader objHeader { get; set; } = new ObjectHeader();
+ public PSOLocation objPosition { get; set; } = new PSOLocation();
+ public ushort unk1 { get; set; } = 0;
+ /// Enemy name.
+ public string objName { get; set; } = string.Empty;//0x20
+ public uint unk2 { get; set; } = 0;
+ /// Enemy HP.
+ public uint hp { get; set; } = 0;
+ public uint unk4 { get; set; } = 0;
+ /// Enemy level.
+ public uint level { get; set; } = 0;
+ public uint unk5 { get; set; } = 0;
+ public uint unk6 { get; set; } = 0;
+ public ushort unk7 { get; set; } = 0;
+ public ushort unk8 { get; set; } = 0;
+ public uint[] unk9 { get; set; } = new uint[0x10];
+ public string unk10 { get; set; } = string.Empty; //Ascii
+ public byte unk11 { get; set; } = 0;
+ public byte unk12 { get; set; } = 0;
+ public ushort unk13 { get; set; } = 0;
+ public byte[] unk14 { get; set; } = new byte[0x0C];
- public EnemySpawnPacket()
+ public EnemySpawnPacket(PSOObject obj)
{
+ objHeader = obj.Header;
+ objPosition = obj.Position;
+ objName = obj.Name;
+ unk2 = 2;
+ unk4 = 2;
+ unk6 = 1029;
+ unk7 = 255;
+ unk8 = 65535;
+ unk9 = new uint[] {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1062804813, 3212836864, 0, 1570802465, 0, 0, 0
+ };
+ unk11 = 1;
+ unk12 = 255;
}
#region implemented abstract members of Packet
@@ -18,12 +53,30 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.WriteStruct(objHeader);
+ pkt.WritePosition(objPosition);
+ pkt.Write(unk1); // Padding?
+ pkt.WriteFixedLengthASCII(objName, 0x20);
+ pkt.Write(unk2);
+ pkt.Write(hp);
+ pkt.Write(unk4);
+ pkt.Write(level);
+ pkt.Write(unk5);
+ pkt.Write(unk6);
+ pkt.Write(unk7);
+ pkt.Write(unk8);
+ pkt.WriteUintArray(unk9);
+ pkt.WriteAscii(unk10, 0x258B, 0x32);
+ pkt.Write(unk11);
+ pkt.Write(unk12);
+ pkt.Write(unk13);
+ pkt.Write(unk14);
return pkt.ToArray();
}
public override PacketHeader GetHeader()
{
- return new PacketHeader(0x08, 0x0D, PacketFlags.None);
+ return new PacketHeader(0x08, 0x0D, PacketFlags.PACKED);
}
#endregion
diff --git a/Server/Protocol/Packets/0B-QuestPacket/0B-06-StartCutscenePacket.cs b/Server/Protocol/Packets/0B-QuestPacket/0B-06-StartCutscenePacket.cs
index c234c6f..c7ae363 100644
--- a/Server/Protocol/Packets/0B-QuestPacket/0B-06-StartCutscenePacket.cs
+++ b/Server/Protocol/Packets/0B-QuestPacket/0B-06-StartCutscenePacket.cs
@@ -1,4 +1,5 @@
-using PSO2SERVER.Models;
+using Org.BouncyCastle.Utilities;
+using PSO2SERVER.Models;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -8,9 +9,32 @@ namespace PSO2SERVER.Protocol.Packets
{
public class StartCutscenePacket : Packet
{
+ public string scene_name { get; set; }
+ public uint[] unk1 { get; set; } = new uint[9];
+ public List unk2 { get; set; } = new List();
+ public ulong unk3 { get; set; }
+ public uint unk4 { get; set; }
+ public byte unk5 { get; set; }
+ public byte unk6 { get; set; }
+ public byte unk7 { get; set; }
+ public string unk8 { get; set; }
+ public string unk9 { get; set; }
+ public uint unk10 { get; set; }
+ public ObjectHeader unk11 { get; set; }
- public StartCutscenePacket()
+ public StartCutscenePacket(string scene_name, List unk2, ulong unk3, uint unk4, byte unk5, byte unk6, byte unk7, string unk8, string unk9, uint unk10, ObjectHeader unk11)
{
+ this.scene_name = scene_name;
+ this.unk2 = unk2;
+ this.unk3 = unk3;
+ this.unk4 = unk4;
+ this.unk5 = unk5;
+ this.unk6 = unk6;
+ this.unk7 = unk7;
+ this.unk8 = unk8;
+ this.unk9 = unk9;
+ this.unk10 = unk10;
+ this.unk11 = unk11;
}
#region implemented abstract members of Packet
@@ -18,6 +42,18 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.WriteAscii(scene_name, 0xB65A, 0x7D);
+ pkt.WriteUintArray(unk1);
+ pkt.WriteObjectHeaderList(unk2);
+ pkt.Write(unk3);
+ pkt.Write(unk4);
+ pkt.Write(unk5);
+ pkt.Write(unk6);
+ pkt.Write(unk7);
+ pkt.WriteAscii(unk8, 0xB65A, 0x7D);
+ pkt.WriteAscii(unk9, 0xB65A, 0x7D);
+ pkt.Write(unk10);
+ pkt.WriteObjectHeader(unk11);
return pkt.ToArray();
}
diff --git a/Server/Protocol/Packets/0B-QuestPacket/0B-13-MinimapRevealPacket.cs b/Server/Protocol/Packets/0B-QuestPacket/0B-13-MinimapRevealPacket.cs
index 88ec131..effbae2 100644
--- a/Server/Protocol/Packets/0B-QuestPacket/0B-13-MinimapRevealPacket.cs
+++ b/Server/Protocol/Packets/0B-QuestPacket/0B-13-MinimapRevealPacket.cs
@@ -8,32 +8,21 @@ namespace PSO2SERVER.Protocol.Packets
{
public class MinimapRevealPacket : Packet
{
- private ObjectHeader unk1 { get; set; }
+ /// World object where revealing was done.
+ private ObjectHeader world { get; set; }
+ /// Receivers party object (?).
private ObjectHeader party { get; set; }
private uint zone_id { get; set; }
- private byte[] unk2 { get; set; } = new byte[10];
+ /// Bitset of revealed regions.
+ private RevealedRegions revealed_zones { get; set; } = new RevealedRegions();
// 构造函数,允许在创建时初始化字段
- public MinimapRevealPacket(ObjectHeader unk1, ObjectHeader party, uint zone_id, byte[] unk2)
+ public MinimapRevealPacket(ObjectHeader world, ObjectHeader party, uint zone_id, byte[] revealed_zones)
{
- this.unk1 = unk1;
+ this.world = world;
this.party = party;
this.zone_id = zone_id;
-
- // 如果传入的 unk2 长度小于 10,则填充剩余部分为 0
- if (unk2.Length <= 10)
- {
- this.unk2 = unk2.Concat(new byte[10 - unk2.Length]).ToArray();
- }
- else
- {
- // 如果传入的 unk2 长度大于 10,则截取前 10 字节
- this.unk2 = unk2.Take(10).ToArray();
- }
- }
-
- public MinimapRevealPacket()
- {
+ this.revealed_zones = new RevealedRegions(revealed_zones);
}
#region implemented abstract members of Packet
@@ -41,11 +30,10 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
- pkt.WriteStruct(unk1);
+ pkt.WriteStruct(world);
pkt.WriteStruct(party);
pkt.Write(zone_id);
- for (var i = 0; i < 10; i++)
- pkt.Write(unk2[i]);
+ revealed_zones.Write(pkt);
return pkt.ToArray();
}
diff --git a/Server/Protocol/Packets/0B-QuestPacket/0B-16-QuestAvailablePacket.cs b/Server/Protocol/Packets/0B-QuestPacket/0B-16-QuestAvailablePacket.cs
index 267d9d7..a3d6e15 100644
--- a/Server/Protocol/Packets/0B-QuestPacket/0B-16-QuestAvailablePacket.cs
+++ b/Server/Protocol/Packets/0B-QuestPacket/0B-16-QuestAvailablePacket.cs
@@ -4,45 +4,220 @@ using System.Linq;
using System.Text;
using PSO2SERVER.Models;
using System.Runtime.InteropServices;
+using System.IO;
namespace PSO2SERVER.Protocol.Packets
{
- class QuestAvailablePacket : Packet
+ public class QuestAvailablePacket : Packet
{
- public short[] amount = new short[Enum.GetValues(typeof(QuestType)).Length];
- QuestTypeAvailable available = QuestTypeAvailable.Arks;
+ public ushort Unk1 { get; set; }
+ public ushort ExtremeCount { get; set; }
+ public ushort Unk2 { get; set; }
+ public ushort ArksCount { get; set; }
+ public ushort LimitedTimeCount { get; set; }
+ public ushort ExtremeDebugCount { get; set; }
+ public ushort Blank1Count { get; set; }
+ public ushort Unk3 { get; set; }
+ public ushort NetCafeCount { get; set; }
+ public ushort WarmingDebugCount { get; set; }
+ public ushort Blank2Count { get; set; }
+ public ushort AdvanceCount { get; set; }
+ public ushort ExpeditionCount { get; set; }
+ public ushort ExpeditionDebugCount { get; set; }
+ public ushort ArksDebugCount { get; set; }
+ public ushort Unk4Count { get; set; }
+ public ushort ChallengeCount { get; set; }
+ public ushort UrgentCount { get; set; }
+ public ushort UrgentDebugCount { get; set; }
+ public ushort TimeAttackCount { get; set; }
+ public ushort TimeAttackDebugCount { get; set; }
+
+ // Array of 9 ushort values (arks_debug2_count)
+ public ushort[] ArksDebug2Count { get; set; } = new ushort[9];
+
+ public ushort Blank3Count { get; set; }
+ public ushort Unk5 { get; set; }
+ public ushort RecommendedCount { get; set; }
+ public ushort Unk6 { get; set; }
+ public ushort UltimateDebugCount { get; set; }
+ public ushort AgpCount { get; set; }
+ public ushort BonusCount { get; set; }
+ public ushort Unk7 { get; set; }
+
+ // Array of 10 ushort values (training_count)
+ public ushort[] TrainingCount { get; set; } = new ushort[10];
+
+ public ushort TriggerCount { get; set; }
+ public ushort RidroidCount { get; set; }
+ public ushort NetCafeAgpCount { get; set; }
+ public ushort BattleBrokenCount { get; set; }
+ public ushort BusterDebugCount { get; set; }
+ public ushort Poka12Count { get; set; }
+ public ushort Unk8 { get; set; }
+ public ushort Unk9 { get; set; }
+ public ushort BusterCount { get; set; }
+ public ushort HeroTrainingCount { get; set; }
+ public ushort AmplifiedCount { get; set; }
+ public ushort Unk10 { get; set; }
+ public ushort Unk11 { get; set; }
+ public ushort DarkBlastTrainingCount { get; set; }
+ public ushort EndlessCount { get; set; }
+ public ushort Unk12 { get; set; }
+ public ushort Unk13 { get; set; }
+ public ushort PhantomTrainingCount { get; set; }
+ public ushort AisTrainingCount { get; set; }
+ public ushort Unk14 { get; set; }
+ public ushort DamageCalcCount { get; set; }
+ public ushort EtoileTrainingCount { get; set; }
+ public ushort DivideCount { get; set; }
+
+ // Unsure fields (stars1_count, stars2_count, stars3_count)
+ public ushort Stars1Count { get; set; }
+ public ushort Stars2Count { get; set; }
+ public ushort Stars3Count { get; set; }
+
+ // Array of 2 ushort values (unk15)
+ public ushort[] Unk15 { get; set; } = new ushort[2];
+
+ // NotOn attribute is specific to certain frameworks (e.g., Vita platform)
+ // You can use attributes like [Obsolete] in C# if needed
+ public ushort[] Unk16 { get; set; } = new ushort[2];
+
+ // AvailableQuestType values
+ public AvailableQuestType AvailableTypes { get; set; }
+ public AvailableQuestType Unk19 { get; set; }
+
+ // Round boost active flag
+ public uint RoundBoost { get; set; }
+ public uint Unk21 { get; set; }
+
+ // Constructor
+ public QuestAvailablePacket()
+ {
+ }
+
+ //public short[] amount = new short[Enum.GetValues(typeof(QuestType)).Length];
+ //AvailableQuestType available = AvailableQuestType.ARKS;
public override byte[] Build()
{
PacketWriter writer = new PacketWriter();
+ writer.Write(Unk1);
+ writer.Write(ExtremeCount);
+ writer.Write(Unk2);
+ writer.Write(ArksCount);
+ writer.Write(LimitedTimeCount);
+ writer.Write(ExtremeDebugCount);
+ writer.Write(Blank1Count);
+ writer.Write(Unk3);
+ writer.Write(NetCafeCount);
+ writer.Write(WarmingDebugCount);
+ writer.Write(Blank2Count);
+ writer.Write(AdvanceCount);
+ writer.Write(ExpeditionCount);
+ writer.Write(ExpeditionDebugCount);
+ writer.Write(ArksDebugCount);
+ writer.Write(Unk4Count);
+ writer.Write(ChallengeCount);
+ writer.Write(UrgentCount);
+ writer.Write(UrgentDebugCount);
+ writer.Write(TimeAttackCount);
+ writer.Write(TimeAttackDebugCount);
- // Filler/Padding?
- writer.Write((UInt16)0);
-
- // Amounts
- for (int i = 0; i < amount.Length; i++)
+ // 写入 ArksDebug2Count 数组
+ foreach (var count in ArksDebug2Count)
{
- amount[i] = 1; // Just for testing
- writer.Write(amount[i]);
+ writer.Write(count);
}
- // Padding/Blank entries?
- for (int i = 0; i < 2; i++)
- writer.Write((int)0);
+ writer.Write(Blank3Count);
+ writer.Write(Unk5);
+ writer.Write(RecommendedCount);
+ writer.Write(Unk6);
+ writer.Write(UltimateDebugCount);
+ writer.Write(AgpCount);
+ writer.Write(BonusCount);
+ writer.Write(Unk7);
- // Available Bitfield
- writer.Write((UInt64)available);
+ // 写入 TrainingCount 数组
+ foreach (var count in TrainingCount)
+ {
+ writer.Write(count);
+ }
- // Filler/Padding?
- for (int i = 0; i < 2; i++)
- writer.Write((int)0);
+ writer.Write(TriggerCount);
+ writer.Write(RidroidCount);
+ writer.Write(NetCafeAgpCount);
+ writer.Write(BattleBrokenCount);
+ writer.Write(BusterDebugCount);
+ writer.Write(Poka12Count);
+ writer.Write(Unk8);
+ writer.Write(Unk9);
+ writer.Write(BusterCount);
+ writer.Write(HeroTrainingCount);
+ writer.Write(AmplifiedCount);
+ writer.Write(Unk10);
+ writer.Write(Unk11);
+ writer.Write(DarkBlastTrainingCount);
+ writer.Write(EndlessCount);
+ writer.Write(Unk12);
+ writer.Write(Unk13);
+ writer.Write(PhantomTrainingCount);
+ writer.Write(AisTrainingCount);
+ writer.Write(Unk14);
+ writer.Write(DamageCalcCount);
+ writer.Write(EtoileTrainingCount);
+ writer.Write(DivideCount);
+ writer.Write(Stars1Count);
+ writer.Write(Stars2Count);
+ writer.Write(Stars3Count);
+
+ // 写入 Unk15 数组
+ foreach (var count in Unk15)
+ {
+ writer.Write(count);
+ }
+
+ // 写入 Unk16 数组
+ foreach (var count in Unk16)
+ {
+ writer.Write(count);
+ }
+
+ // 写入 AvailableTypes 和 Unk19 (作为枚举类型,转换为整数)
+ writer.Write((ulong)AvailableTypes);
+ writer.Write((ulong)Unk19);
+
+ writer.Write(RoundBoost);
+ writer.Write(Unk21);
+
+ //// Filler/Padding?
+ //writer.Write((UInt16)0);
+
+ //// Amounts
+ //for (int i = 0; i < amount.Length; i++)
+ //{
+ // amount[i] = 1; // Just for testing
+ // writer.Write(amount[i]);
+ //}
+
+ //// Padding/Blank entries?
+ //for (int i = 0; i < 2; i++)
+ // writer.Write((int)0);
+
+ //// Available Bitfield
+ //writer.Write((UInt64)available);
+
+ //// Filler/Padding?
+ //for (int i = 0; i < 2; i++)
+ // writer.Write((int)0);
return writer.ToArray();
}
public override PacketHeader GetHeader()
{
- return new PacketHeader(0x0B, 0x16);
+ return new PacketHeader(0x0B, 0x16, PacketFlags.None);
}
}
}
diff --git a/Server/Protocol/Packets/0B-QuestPacket/0B-18-QuestListPacket.cs b/Server/Protocol/Packets/0B-QuestPacket/0B-18-QuestCategoryPacket.cs
similarity index 58%
rename from Server/Protocol/Packets/0B-QuestPacket/0B-18-QuestListPacket.cs
rename to Server/Protocol/Packets/0B-QuestPacket/0B-18-QuestCategoryPacket.cs
index 13ef9b6..238c194 100644
--- a/Server/Protocol/Packets/0B-QuestPacket/0B-18-QuestListPacket.cs
+++ b/Server/Protocol/Packets/0B-QuestPacket/0B-18-QuestCategoryPacket.cs
@@ -3,11 +3,11 @@ using PSO2SERVER.Models;
namespace PSO2SERVER.Protocol.Packets
{
- class QuestListPacket : Packet
+ public class QuestCategoryPacket : Packet
{
private QuestDefiniton[] questdefs;
- public QuestListPacket(QuestDefiniton[] questdefs)
+ public QuestCategoryPacket(QuestDefiniton[] questdefs)
{
this.questdefs = questdefs;
}
@@ -44,42 +44,5 @@ namespace PSO2SERVER.Protocol.Packets
120 -> Item Data 1?
12C -> Item Data 2?
*/
-
- [Flags]
- public enum QuestBitfield1 : ushort
- {
- MatterObjectiveQuest = 0x0001,
- ClientOrderOnQuest = 0x0008,
- NewQuest = 0x0100,
- ClientOrder = 0x0800,
- UnknownLevel = 0x1000
- }
-
- public enum PartyType
- {
- SoloQuest,
- SinglePartyQuest,
- MultiPartyQuest,
- }
-
- public enum EstimatedTime
- {
- Short = 1,
- Medium,
- Long
- }
-
- [Flags]
- public enum Difficulties
- {
- Normal = 0x01,
- hard = 0x02,
- VeryHard = 0x04,
- SuperHard = 0x08,
- ExtraHard = 0x10,
- Dummy1 = 0x20,
- Dummy2 = 0x40,
- Dummy3 = 0x80,
- }
}
}
diff --git a/Server/Protocol/Packets/0B-QuestPacket/0B-1A-QuestDifficultyPacket.cs b/Server/Protocol/Packets/0B-QuestPacket/0B-1A-QuestDifficultyPacket.cs
index 2e66606..f2be68f 100644
--- a/Server/Protocol/Packets/0B-QuestPacket/0B-1A-QuestDifficultyPacket.cs
+++ b/Server/Protocol/Packets/0B-QuestPacket/0B-1A-QuestDifficultyPacket.cs
@@ -61,46 +61,5 @@ namespace PSO2SERVER.Protocol.Packets
{
return new PacketHeader(0x0B, 0x1A, PacketFlags.PACKED);
}
-
- //Size: 308 bytes, confirmed in unpacker
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
- public unsafe struct QuestDifficulty
- {
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
- public string dateOrSomething;
- public ObjectHeader quest_obj;
- public uint name_id;
- public byte area;
- public byte planet;
- public byte unk1;
- public byte unk2;
- public QuestDifficultyEntry difficulty1;
- public QuestDifficultyEntry difficulty2;
- public QuestDifficultyEntry difficulty3;
- public QuestDifficultyEntry difficulty4;
- public QuestDifficultyEntry difficulty5;
- public QuestDifficultyEntry difficulty6;
- public QuestDifficultyEntry difficulty7;
- public QuestDifficultyEntry difficulty8;
- }
-
- //Size: 32, confirmed in ctor TODO
- public struct QuestDifficultyEntry
- {
- public byte ReqLevel;
- public byte SubClassReqLevel;
- public byte MonsterLevel;
- public byte Unk1;
- public uint AbilityAdj;
- public uint DmgLimit;
- public uint TimeLimit;
- public uint TimeLimit2;
- public uint SuppTarget;
- public uint Unk2;
- public uint Enemy1;
- public uint Unk3;
- public uint Enemy2;
- public uint Unk4;
- }
}
}
diff --git a/Server/Protocol/Packets/0B-QuestPacket/0B-1B-QuestCategoryStopperPacket.cs b/Server/Protocol/Packets/0B-QuestPacket/0B-1B-QuestCategoryStopperPacket.cs
new file mode 100644
index 0000000..672be49
--- /dev/null
+++ b/Server/Protocol/Packets/0B-QuestPacket/0B-1B-QuestCategoryStopperPacket.cs
@@ -0,0 +1,24 @@
+using System;
+using PSO2SERVER.Models;
+
+namespace PSO2SERVER.Protocol.Packets
+{
+ public class QuestCategoryStopperPacket : Packet
+ {
+ /// Following: [`crate::protocol::Packet::QuestCategory`]
+ public QuestCategoryStopperPacket()
+ {
+ }
+
+ public override byte[] Build()
+ {
+ PacketWriter writer = new PacketWriter();
+ return writer.ToArray();
+ }
+
+ public override PacketHeader GetHeader()
+ {
+ return new PacketHeader(0x0B, 0x1B, PacketFlags.None);
+ }
+ }
+}
diff --git a/Server/Protocol/Packets/0E-PartyPacket/0E-02-PartyInitPacket.cs b/Server/Protocol/Packets/0E-PartyPacket/0E-02-PartyInitPacket.cs
index 9f88d86..115b9ba 100644
--- a/Server/Protocol/Packets/0E-PartyPacket/0E-02-PartyInitPacket.cs
+++ b/Server/Protocol/Packets/0E-PartyPacket/0E-02-PartyInitPacket.cs
@@ -97,8 +97,6 @@ namespace PSO2SERVER.Protocol.Packets
entries[i].id = new ObjectHeader((uint)players[i].Account.AccountId, ObjectType.Player); // Header of player
entries[i].nickname = players[i].Account.Nickname;
entries[i].char_name = players[i].Name;
- //writer.WriteUtf16(players[i].Name, 0xD863, 0xA9);
- //writer.WriteUtf16(players[i].Accounts.Nickname, 0xD863, 0xA9);
entries[i].level = (byte)players[i].Jobs.entries.hunter.level;
entries[i].sublevel = (byte)players[i].Jobs.entries.hunter.level2;
entries[i].mainClass = players[i].Jobs.mainClass;
diff --git a/Server/Protocol/Packets/0E-PartyPacket/0E-25-SetQuestInfoPacket.cs b/Server/Protocol/Packets/0E-PartyPacket/0E-25-SetQuestInfoPacket.cs
index f0af4af..4e532ea 100644
--- a/Server/Protocol/Packets/0E-PartyPacket/0E-25-SetQuestInfoPacket.cs
+++ b/Server/Protocol/Packets/0E-PartyPacket/0E-25-SetQuestInfoPacket.cs
@@ -49,7 +49,7 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
PacketWriter writer = new PacketWriter();
- writer.Write(questdef.questNameString);
+ writer.Write(questdef.questNameid);
writer.Write(Unk1);
writer.Write(Unk2);
writer.Write(Unk3);
diff --git a/Server/Protocol/Packets/0E-PartyPacket/0E-31-PartySetQuestPacket.cs b/Server/Protocol/Packets/0E-PartyPacket/0E-31-PartySetQuestPacket.cs
new file mode 100644
index 0000000..33c6958
--- /dev/null
+++ b/Server/Protocol/Packets/0E-PartyPacket/0E-31-PartySetQuestPacket.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Runtime.InteropServices;
+
+using PSO2SERVER.Models;
+
+namespace PSO2SERVER.Protocol.Packets
+{
+ class PartySetQuestPacket : Packet
+ {
+ public uint Name { get; set; }
+ public uint Difficulty { get; set; }
+ public QuestType QuestType { get; set; }
+ public QuestDefiniton QuestDefiniton { get; set; }
+ public QuestDifficulty QuestDifficulty { get; set; }
+ public ObjectHeader Player { get; set; }
+ public ushort unk1 { get; set; } = 0;
+ public ushort unk2 { get; set; } = 0;
+
+ public PartySetQuestPacket(uint name, uint difficulty, QuestDefiniton data, QuestDifficulty Questdifficulty, ObjectHeader player)
+ {
+ Name = name;
+ Difficulty = difficulty;
+ QuestDefiniton = data;
+ QuestDifficulty = Questdifficulty;
+ Player = player;
+ }
+
+ public override byte[] Build()
+ {
+ PacketWriter writer = new PacketWriter();
+ writer.Write(Name); // Unknown
+ writer.Write(Difficulty); // Unknown
+ writer.Write((uint)QuestType);
+ writer.WriteStruct(QuestDefiniton);
+ writer.WriteStruct(QuestDifficulty);
+ writer.WriteStruct(Player);
+ writer.Write(unk1);
+ writer.Write(unk2);
+ return writer.ToArray();
+ }
+
+ public override PacketHeader GetHeader()
+ {
+ return new PacketHeader(0x0E, 0x31, PacketFlags.None);
+ }
+ }
+}
diff --git a/Server/Protocol/Packets/0E-PartyPacket/0E-31-QuestStartPacket.cs b/Server/Protocol/Packets/0E-PartyPacket/0E-31-QuestStartPacket.cs
deleted file mode 100644
index 0818972..0000000
--- a/Server/Protocol/Packets/0E-PartyPacket/0E-31-QuestStartPacket.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-using PSO2SERVER.Models;
-
-namespace PSO2SERVER.Protocol.Packets
-{
- class QuestStartPacket : Packet
- {
- QuestDefiniton data;
- QuestDifficultyPacket.QuestDifficulty difficulty;
-
- public QuestStartPacket(QuestDefiniton data, QuestDifficultyPacket.QuestDifficulty difficulty)
- {
- this.data = data;
- this.difficulty = difficulty;
- }
-
- public override byte[] Build()
- {
- PacketWriter writer = new PacketWriter();
-
- writer.Write(0x753A); // Unknown
- writer.Write((int)0); // Unknown
- writer.WriteStruct(data);
- writer.WriteStruct(difficulty);
-
- return writer.ToArray();
- }
-
- public override PacketHeader GetHeader()
- {
- return new PacketHeader(0x0E, 0x31);
- }
- }
-}
diff --git a/Server/Protocol/Packets/0F-ItemPacket/0F-0C-LoadEquipedPacket.cs b/Server/Protocol/Packets/0F-ItemPacket/0F-0C-LoadEquipedPacket.cs
index 3d4d444..7e5e48f 100644
--- a/Server/Protocol/Packets/0F-ItemPacket/0F-0C-LoadEquipedPacket.cs
+++ b/Server/Protocol/Packets/0F-ItemPacket/0F-0C-LoadEquipedPacket.cs
@@ -1,21 +1,43 @@
using System;
+using System.Collections.Generic;
using PSO2SERVER.Models;
namespace PSO2SERVER.Protocol.Packets
{
public class LoadEquipedPacket : Packet
{
- public Int64 NewAmount = 0;
+ /// Player whose equipment is loaded.
+ public ObjectHeader Player { get; set; } = new ObjectHeader();
+ /// Player's equiped items.
+ public List Items { get; set; } = new List();
+ // 0-back unit
+ // 1-arm unit
+ // 2-leg unit
+ // 3-outfit
+ // 9-weapon
+ public uint Unk1 { get; set; } = 0;
+ public byte[] Unk2 { get; set; } = new byte[0x28];
#region implemented abstract members of Packet
+ public LoadEquipedPacket(uint accountid, List items)
+ {
+ Player = new ObjectHeader(accountid, ObjectType.Player);
+ Items = items;
+ }
+
public override byte[] Build()
{
- var writer = new PacketWriter();
-
- writer.Write(NewAmount);
-
- return writer.ToArray();
+ var pkt = new PacketWriter();
+ pkt.WriteStruct(Player);
+ pkt.WriteMagic((uint)Items.Count, 0xCF76, 0xB5);
+ foreach (var item in Items)
+ {
+ pkt.WriteStruct(item);
+ }
+ pkt.Write(Unk1);
+ pkt.Write(Unk2);
+ return pkt.ToArray();
}
public override PacketHeader GetHeader()
diff --git a/Server/Protocol/Packets/11-ClientPacket/11-01-LoginDataPacket.cs b/Server/Protocol/Packets/11-ClientPacket/11-01-LoginDataPacket.cs
index 01b641f..1ca824f 100644
--- a/Server/Protocol/Packets/11-ClientPacket/11-01-LoginDataPacket.cs
+++ b/Server/Protocol/Packets/11-ClientPacket/11-01-LoginDataPacket.cs
@@ -1,16 +1,11 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using Mysqlx;
-using PSO2SERVER.Models;
+using PSO2SERVER.Models;
+using System;
namespace PSO2SERVER.Protocol.Packets
{
class LoginDataPacket : Packet
{
- public enum LoginStatus : UInt32
+ public enum LoginStatus : uint
{
///
/// Login was successful.
@@ -31,249 +26,144 @@ namespace PSO2SERVER.Protocol.Packets
public LoginStatus Status;
public string Error;
public ObjectHeader Player;
- public string BlockName;
- public float Unk1;
- public uint Unk2;
- public uint LevelCap;
- public uint LevelCap2;
- public uint Unk5;
- public float Unk6;
- public float Unk7;
- public uint Unk8;
- public float Unk9;
- public float Unk10;
- public uint Unk11;
- public float Unk12;
- public uint Unk13;
- public float[] Unk14; // Length: 10
- public float[] Unk15; // Length: 21
- public float Unk16;
- public float Unk17;
- public float[] Unk18; // Length: 9
- public uint[] Unk19; // Length: 2
- public uint Unk20;
- public uint Unk21;
- public float[] Unk22; // Length: 3
- public uint Unk23;
- public float Unk24;
- public float Unk25;
- public uint Unk26;
- public byte[] Unk27; // Length: 12
- public string Unk28;
- public uint Unk29;
- public string Unk30;
- public uint Unk31;
-
- private string ReadFixedString(PacketReader reader, int length)
- {
- byte[] bytes = reader.ReadBytes(length);
- return Encoding.UTF8.GetString(bytes).TrimEnd('\0');
- }
- public void ReadFromStream(PacketReader reader)
- {
- Status = (LoginStatus)reader.ReadInt32();
- Error = ReadFixedString(reader, 32); // 0x20 = 32
- Player.ReadFromStream(reader);
- BlockName = ReadFixedString(reader, 32);
- Unk1 = reader.ReadSingle();
- Unk2 = reader.ReadUInt32();
- LevelCap = reader.ReadUInt32();
- LevelCap2 = reader.ReadUInt32();
- Unk5 = reader.ReadUInt32();
- Unk6 = reader.ReadSingle();
- Unk7 = reader.ReadSingle();
- Unk8 = reader.ReadUInt32();
- Unk9 = reader.ReadSingle();
- Unk10 = reader.ReadSingle();
- Unk11 = reader.ReadUInt32();
- Unk12 = reader.ReadSingle();
- Unk13 = reader.ReadUInt32();
-
- Unk14 = new float[10];
- for (int i = 0; i < Unk14.Length; i++)
- {
- Unk14[i] = reader.ReadSingle();
- }
-
- Unk15 = new float[21];
- for (int i = 0; i < Unk15.Length; i++)
- {
- Unk15[i] = reader.ReadSingle();
- }
-
- Unk16 = reader.ReadSingle();
- Unk17 = reader.ReadSingle();
-
- Unk18 = new float[9];
- for (int i = 0; i < Unk18.Length; i++)
- {
- Unk18[i] = reader.ReadSingle();
- }
-
- Unk19 = new uint[2];
- for (int i = 0; i < Unk19.Length; i++)
- {
- Unk19[i] = reader.ReadUInt32();
- }
-
- Unk20 = reader.ReadUInt32();
- Unk21 = reader.ReadUInt32();
-
- Unk22 = new float[3];
- for (int i = 0; i < Unk22.Length; i++)
- {
- Unk22[i] = reader.ReadSingle();
- }
-
- Unk23 = reader.ReadUInt32();
- Unk24 = reader.ReadSingle();
- Unk25 = reader.ReadSingle();
- Unk26 = reader.ReadUInt32();
- Unk27 = reader.ReadBytes(12);
- Unk28 = ReadFixedString(reader, 32);
- Unk29 = reader.ReadUInt32();
- Unk30 = ReadFixedString(reader, 32);
- Unk31 = reader.ReadUInt32();
- }
-
- private void WriteFixedString(PacketWriter writer, string str, int length)
- {
- byte[] bytes = new byte[length];
- byte[] strBytes = Encoding.UTF8.GetBytes(str);
- Array.Copy(strBytes, bytes, Math.Min(strBytes.Length, length));
- writer.Write(bytes);
- }
-
- public void WriteToStream(PacketWriter writer)
- {
- writer.Write((int)Status);
- writer.WriteUtf16(Error, 0x8BA4, 0xB6);
-
- if (Player.ID == 0)
- {
- for (var i = 0; i < 0xEC; i++)
- writer.Write((byte)0);
- }
- else
- {
- //WriteFixedString(writer, Error, 32);
- Player.WriteToStream(writer);
- WriteFixedString(writer, BlockName, 32);
- writer.Write(Unk1);
- writer.Write(Unk2);
- writer.Write(LevelCap);
- writer.Write(LevelCap2);
- writer.Write(Unk5);
- writer.Write(Unk6);
- writer.Write(Unk7);
- writer.Write(Unk8);
- writer.Write(Unk9);
- writer.Write(Unk10);
- writer.Write(Unk11);
- writer.Write(Unk12);
- writer.Write(Unk13);
-
- foreach (var val in Unk14)
- {
- writer.Write(val);
- }
-
- foreach (var val in Unk15)
- {
- writer.Write(val);
- }
-
- writer.Write(Unk16);
- writer.Write(Unk17);
-
- foreach (var val in Unk18)
- {
- writer.Write(val);
- }
-
- foreach (var val in Unk19)
- {
- writer.Write(val);
- }
-
- writer.Write(Unk20);
- writer.Write(Unk21);
-
- foreach (var val in Unk22)
- {
- writer.Write(val);
- }
-
- writer.Write(Unk23);
- writer.Write(Unk24);
- writer.Write(Unk25);
- writer.Write(Unk26);
- writer.Write(Unk27);
- WriteFixedString(writer, Unk28, 32);
- writer.Write(Unk29);
- WriteFixedString(writer, Unk30, 32);
- writer.Write(Unk31);
- }
- }
+ public string BlockName = new string(' ', 0x20);
+ public float Unk1 = 0;
+ public uint Unk2 = 0;
+ public uint LevelCap = 0;
+ public uint LevelCap2 = 0;
+ public uint Unk5 = 0;
+ public float Unk6 = 0;
+ public float Unk7 = 0;
+ public uint Unk8 = 0;
+ public float Unk9 = 0;
+ public float Unk10 = 0;
+ public uint Unk11 = 0;
+ public float Unk12 = 0;
+ public uint Unk13 = 0;
+ public float[] Unk14 = new float[10]; // Length: 10
+ public float[] Unk15 = new float[21]; // Length: 21
+ public float Unk16 = 0;
+ public float Unk17 = 0;
+ public float[] Unk18 = new float[9]; // Length: 9
+ public uint[] Unk19 = new uint[2]; // Length: 2
+ public uint Unk20 = 0;
+ public uint Unk21 = 0;
+ public float[] Unk22 = new float[3]; // Length: 3
+ public uint Unk23 = 0;
+ public float Unk24 = 0;
+ public float Unk25 = 0;
+ public uint Unk26 = 0;
+ public byte[] Unk27 = new byte[12]; // Length: 12
+ public string Unk28 = new string(' ', 0x20);
+ public uint Unk29 = 0;
+ public string Unk30 = new string(' ', 0x20);
+ public uint Unk31 = 0;
public LoginDataPacket(string blockName, string error, uint userid)
{
Status = (userid == 0) ? LoginStatus.Failure : LoginStatus.Success;
Error = error;
- Player.ID = userid;
- Player.ObjectType = ObjectType.Player;
+ Player = new ObjectHeader(userid, ObjectType.Player);
BlockName = blockName;
+ Unk1 = 0x42700000;
+ Unk2 = 7;
+ LevelCap = 0x0A;
+ LevelCap2 = 1;
+ Unk5 = 0x41200000;
+ Unk6 = 0x40A00000;
+ Unk7 = 11;
+ Unk8 = 0x3F800000;
+ Unk9 = 0x42960000;
+ Unk10 = 40;
+ Unk11 = 0x41200000;
+ Unk12 = 1;
+ Unk13 = 1120403456;
+
+ //WHAT
+ for (int i = 0; i < Unk14.Length; i++)
+ {
+ Unk14[i] = 1065353216;
+ }
+
+ //ARE
+ for (int i = 0; i < Unk15.Length; i++)
+ {
+ Unk15[i] = 1120403456;
+ }
}
public override byte[] Build()
{
- var resp = new PacketWriter();
- resp.Write((uint)Status); // Status flag: 0=success, 1=error
- resp.WriteUtf16(Error, 0x8BA4, 0xB6);
+ var pkt = new PacketWriter();
+ pkt.Write((uint)Status);
+ pkt.WriteUtf16(Error, 0x8BA4, 0xB6);
if (Player.ID == 0)
{
for (var i = 0; i < 0xEC; i++)
- resp.Write((byte)0);
- return resp.ToArray();
+ pkt.Write((byte)0);
}
-
- // TODO: Explore this data! Some if it seems really important. (May contain level cap setting + more)
-
- resp.WriteStruct(Player);
- resp.WriteFixedLengthUtf16(BlockName, 0x20); // This is right
- // Set things to "default" values; Dunno these purposes yet.
- resp.Write(0x42700000); //0
- resp.Write(7); //4
- resp.Write(0xA); //8 - Level Cap!
- resp.Write(1); //C
- resp.Write(0x41200000); //10
- resp.Write(0x40A00000); //14
- resp.Write(11); //18
- resp.Write(0x3F800000); //1C (1 as a float)
- resp.Write(0x42960000); //20
- resp.Write(40); //24
- resp.Write(0x41200000); //28
- resp.Write(1); //2C?
- resp.Write(1120403456); //30
-
- //WHAT
- for (int i = 0; i < 10; i++)
+ else
{
- resp.Write(1065353216);
- }
- //ARE
- for (int i = 0; i < 21; i++)
- {
- resp.Write(1120403456);
- }
- //THESE?
- resp.Write(0x91A2B); //B0
- resp.Write(0x91A2B); //B4
+ Player.WriteObjectHeaderToStream(pkt);
+ pkt.WriteFixedLengthUtf16(BlockName, 0x20);
+ pkt.Write(Unk1);
+ pkt.Write(Unk2);
+ pkt.Write(LevelCap);
+ pkt.Write(LevelCap2);
+ pkt.Write(Unk5);
+ pkt.Write(Unk6);
+ pkt.Write(Unk7);
+ pkt.Write(Unk8);
+ pkt.Write(Unk9);
+ pkt.Write(Unk10);
+ pkt.Write(Unk11);
+ pkt.Write(Unk12);
+ pkt.Write(Unk13);
- resp.WriteBytes(0, 12);
+ foreach (var val in Unk14)
+ {
+ pkt.Write(val);
+ }
- return resp.ToArray();
+ foreach (var val in Unk15)
+ {
+ pkt.Write(val);
+ }
+
+ pkt.Write(Unk16);
+ pkt.Write(Unk17);
+
+ foreach (var val in Unk18)
+ {
+ pkt.Write(val);
+ }
+
+ foreach (var val in Unk19)
+ {
+ pkt.Write(val);
+ }
+
+ pkt.Write(Unk20);
+ pkt.Write(Unk21);
+
+ foreach (var val in Unk22)
+ {
+ pkt.Write(val);
+ }
+
+ pkt.Write(Unk23);
+ pkt.Write(Unk24);
+ pkt.Write(Unk25);
+ pkt.Write(Unk26);
+ pkt.Write(Unk27);
+ pkt.WriteFixedLengthUtf16(Unk28, 0x20);
+ pkt.Write(Unk29);
+ pkt.WriteFixedLengthUtf16(Unk30, 0x20);
+ pkt.Write(Unk31);
+ }
+
+ return pkt.ToArray();
}
public override PacketHeader GetHeader()
diff --git a/Server/Protocol/Packets/11-ClientPacket/11-03-CharacterListPacket.cs b/Server/Protocol/Packets/11-ClientPacket/11-03-CharacterListPacket.cs
index 2e823d0..cd1546c 100644
--- a/Server/Protocol/Packets/11-ClientPacket/11-03-CharacterListPacket.cs
+++ b/Server/Protocol/Packets/11-ClientPacket/11-03-CharacterListPacket.cs
@@ -12,6 +12,29 @@ namespace PSO2SERVER.Protocol.Packets
{
public class CharacterListPacket : Packet
{
+ // Available characters
+ public List Characters { get; set; } = new List();
+
+ // Equipped items (List of 10 Items for each character)
+ public List EquippedItems { get; set; } = new List();
+
+ // Character play times (30 times)
+ public uint[] PlayTimes { get; set; } = new uint[30];
+
+ // Character deletion flags (flag, deletion timestamp) (30 pairs of (u32, u32))
+ public (uint flag, uint timestamp)[] DeletionFlags { get; set; } = new (uint, uint)[30];
+
+ // Character ship transfer flags (30 pairs of (u32, u32))
+ public (uint flag, uint timestamp)[] TransferFlags { get; set; } = new (uint, uint)[30];
+
+ // Account accessory flag (unknown)
+ public ushort AccountAccessory { get; set; }
+
+ // Login survey flag
+ public uint LoginSurvey { get; set; }
+
+ // Ad flag
+ public uint Ad { get; set; }
// Ninji note: This packet may be followed by extra data,
// after a fixed-length array of character data structures.
@@ -37,8 +60,6 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
- var writer = new PacketWriter();
-
using (var db = new ServerEf())
{
var chars = db.Characters
@@ -46,43 +67,87 @@ namespace PSO2SERVER.Protocol.Packets
.OrderBy(o => o.CharacterID) // TODO: 按照最后游玩的角色排序
.Select(s => s);
- writer.Write((uint)chars.Count()); // 写入玩家数量
-
- writer.Write((uint)0);
-
foreach (var ch in chars)
{
- writer.Write((uint)0);
+ Characters.Add(ch);
- writer.Write((uint)ch.CharacterID);//4
- writer.Write((uint)ch.AccountID);//4
- writer.Write((uint)ch.Unk1);//4
- writer.Write((uint)ch.VoiceType);//4
- writer.Write((ushort)ch.Unk2);//2
- writer.Write(ch.VoicePitch);//2
- writer.WriteFixedLengthUtf16(ch.Name, 16);
- //Logger.WriteInternal("[CHR] 新增名为 {0} 的新角色.", ch.Name);
- writer.Write((uint)ch.Unk3);
- writer.WriteStruct(ch.Looks);
- JobParam jobParam = ch.Jobs;
- jobParam.mainClass = ClassType.Luster;
- jobParam.subClass = ClassType.Phantom;
- jobParam.entries.Luster.level = 100;
- writer.WriteStruct(jobParam);
- writer.WriteBytes(0, 148);
+ //// 创建一个 Consumable 类型的物品
+ //PSO2ItemConsumable consumableItem = new PSO2ItemConsumable
+ //{
+ // amount = 10,
+ //};
+ //PSO2Items[] items = new PSO2Items[10];
+
+ //for (var i = 0; i < 10; i++)
+ //{
+ // items[i] = new PSO2Items
+ // {
+ // uuid = (ulong)i,
+ // id = new ItemId
+ // {
+ // ItemType = 3,
+ // Id = 1,
+ // Subid = 0,
+ // },
+ // data = new Items { Consumable = consumableItem }
+ // };
+ //}
+
+ EquippedItems.Add(ch.EquipedItems);
}
- //for (var i = 0; i < 640; i++)
- // writer.Write((byte)1);
-
- //for (var i = 0; i < 30; i++)
- // writer.Write((uint)1);
-
- //writer.Write(new byte[240]);
- //writer.Write(new byte[240]);
}
- return writer.ToArray();
+ var pkt = new PacketWriter();
+
+ pkt.Write((uint)Characters.Count()); // 写入玩家数量
+ pkt.Write((uint)0);
+ foreach (var ch in Characters)
+ {
+ pkt.Write((uint)0);
+ pkt.Write(ch.BuildCharacterByteArray());//4
+ }
+
+ pkt.Write((uint)EquippedItems.Count()); // 写入物品数量
+ foreach (var itemsArray in EquippedItems)
+ {
+ foreach (var item in itemsArray)
+ {
+ byte[] itemBytes = item.ToByteArray(); // Assume PSO2Items has a ToByteArray method
+ pkt.Write(itemBytes); // Write the item bytes
+ }
+ }
+ // Write PlayTimes
+ foreach (var playTime in PlayTimes)
+ {
+ pkt.Write(playTime);
+ }
+ // Write DeletionFlags
+ foreach (var flag in DeletionFlags)
+ {
+ pkt.Write(flag.flag);
+ pkt.Write(flag.timestamp);
+ }
+ // Write TransferFlags
+ foreach (var flag in TransferFlags)
+ {
+ pkt.Write(flag.flag);
+ pkt.Write(flag.timestamp);
+ }
+
+ // Write AccountAccessory
+ pkt.Write(AccountAccessory);
+
+ // Write LoginSurvey
+ pkt.Write(LoginSurvey);
+
+ // Write Ad
+ pkt.Write(Ad);
+
+ pkt.WriteBytes(0, 2);
+
+
+ return pkt.ToArray();
}
public override PacketHeader GetHeader()
diff --git a/Server/Protocol/Packets/11-ClientPacket/11-2C-BlockBalancePacket.cs b/Server/Protocol/Packets/11-ClientPacket/11-2C-BlockBalancePacket.cs
index 5d10346..f3361a6 100644
--- a/Server/Protocol/Packets/11-ClientPacket/11-2C-BlockBalancePacket.cs
+++ b/Server/Protocol/Packets/11-ClientPacket/11-2C-BlockBalancePacket.cs
@@ -15,9 +15,11 @@ namespace PSO2SERVER.Protocol.Packets
public byte[] data { get; set; } = new byte[0x11A];
- public BlockBalancePacket()
+ public BlockBalancePacket(string blockname, ushort port)
{
+ this.blockname = blockname;
ip = ServerApp.BindAddress.GetAddressBytes();
+ this.port = port;
}
#region implemented abstract members of Packet
@@ -28,8 +30,8 @@ namespace PSO2SERVER.Protocol.Packets
pkt.Write(unk1);
pkt.WriteFixedLengthUtf16(blockname, 0x20);
pkt.Write(ip);
- pkt.Write((UInt16)12205);
- pkt.Write(new byte[0x90 - 0x6A]);
+ pkt.Write(port);
+ pkt.Write(new byte[0x11A]);
return pkt.ToArray();
}
diff --git a/Server/Protocol/Packets/11-ClientPacket/11-3D-ShipListPacket.cs b/Server/Protocol/Packets/11-ClientPacket/11-3D-ShipListPacket.cs
index 7d4b175..9c6ed5e 100644
--- a/Server/Protocol/Packets/11-ClientPacket/11-3D-ShipListPacket.cs
+++ b/Server/Protocol/Packets/11-ClientPacket/11-3D-ShipListPacket.cs
@@ -3,14 +3,21 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using static PSO2SERVER.Models.CharacterStruct;
namespace PSO2SERVER.Protocol.Packets
{
public class ShipListPacket : Packet
{
+ public List shipEntries { get; set; } = new List();
+ public int timestamp { get; set; }
+ public uint unk { get; set; }
- public ShipListPacket()
+ public ShipListPacket(List entries, int timestamp, uint unk)
{
+ shipEntries = entries;
+ this.timestamp = timestamp;
+ this.unk = unk;
}
#region implemented abstract members of Packet
@@ -18,12 +25,19 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.WriteMagic((uint)shipEntries.Count, 0xE418, 0x51);
+
+ foreach (var entry in shipEntries)
+ pkt.WriteStruct(entry);
+
+ pkt.Write(timestamp);
+ pkt.Write(unk);
return pkt.ToArray();
}
public override PacketHeader GetHeader()
{
- return new PacketHeader(0x11, 0x3D, PacketFlags.None);
+ return new PacketHeader(0x11, 0x3D, PacketFlags.PACKED);
}
#endregion
diff --git a/Server/Protocol/Packets/11-ClientPacket/11-42-CreateCharacterOneResponsePacket.cs b/Server/Protocol/Packets/11-ClientPacket/11-42-CreateCharacterOneResponsePacket.cs
index 50aa07a..de0f38d 100644
--- a/Server/Protocol/Packets/11-ClientPacket/11-42-CreateCharacterOneResponsePacket.cs
+++ b/Server/Protocol/Packets/11-ClientPacket/11-42-CreateCharacterOneResponsePacket.cs
@@ -1,4 +1,5 @@
-using PSO2SERVER.Models;
+using Org.BouncyCastle.Ocsp;
+using PSO2SERVER.Models;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -8,32 +9,26 @@ namespace PSO2SERVER.Protocol.Packets
{
public class CreateCharacterOneResponsePacket : Packet
{
- public struct CreateCharacter1ResponsePacket
- {
- ///
- /// Creation status.
- ///
- public uint Status;
-
- public uint Unk2;
-
- public uint UsedSmth;
-
- ///
- /// Required AC to buy a character creation pass.
- ///
- public uint ReqAc;
- public CreateCharacter1ResponsePacket(uint status, uint unk2, uint usedSmth, uint reqAc) : this()
- {
- Status = status;
- Unk2 = unk2;
- UsedSmth = usedSmth;
- ReqAc = reqAc;
- }
- }
-
- public CreateCharacterOneResponsePacket()
+ ///
+ /// Creation status.
+ ///
+ public uint Status;
+
+ public uint Unk2;
+
+ public uint UsedSmth;
+
+ ///
+ /// Required AC to buy a character creation pass.
+ ///
+ public uint ReqAc;
+
+ public CreateCharacterOneResponsePacket(uint status, uint unk2, uint usedSmth, uint reqAc)
{
+ Status = status;
+ Unk2 = unk2;
+ UsedSmth = usedSmth;
+ ReqAc = reqAc;
}
#region implemented abstract members of Packet
@@ -41,7 +36,10 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
- pkt.WriteStruct(new CreateCharacter1ResponsePacket(0, 0, 0, 0));
+ pkt.Write(Status);
+ pkt.Write(Unk2);
+ pkt.Write(UsedSmth);
+ pkt.Write(ReqAc);
return pkt.ToArray();
}
diff --git a/Server/Protocol/Packets/11-ClientPacket/11-53-CreateCharacterInviteNicknameResponsePacket.cs b/Server/Protocol/Packets/11-ClientPacket/11-53-CreateCharacterInviteNicknameResponsePacket.cs
index 6128252..8850825 100644
--- a/Server/Protocol/Packets/11-ClientPacket/11-53-CreateCharacterInviteNicknameResponsePacket.cs
+++ b/Server/Protocol/Packets/11-ClientPacket/11-53-CreateCharacterInviteNicknameResponsePacket.cs
@@ -6,34 +6,28 @@ using System.Text;
namespace PSO2SERVER.Protocol.Packets
{
- public class CreateCharacterInviteNicknameResponse : Packet
+ public class CreateCharacterInviteNicknameResponsePacket : Packet
{
- public struct CreateCharacterInviteNicknameResponsePacket
- {
- ///
- /// Creation status.
- ///
- public uint Status;
-
- public uint Unk2;
-
- public uint UsedSmth;
-
- ///
- /// Required AC to buy a character creation pass.
- ///
- public uint ReqAc;
- public CreateCharacterInviteNicknameResponsePacket(uint status, uint unk2, uint usedSmth, uint reqAc) : this()
- {
- Status = status;
- Unk2 = unk2;
- UsedSmth = usedSmth;
- ReqAc = reqAc;
- }
- }
-
- public CreateCharacterInviteNicknameResponse()
+ ///
+ /// Creation status.
+ ///
+ public uint Status;
+
+ public uint Unk2;
+
+ public uint UsedSmth;
+
+ ///
+ /// Required AC to buy a character creation pass.
+ ///
+ public uint ReqAc;
+
+ public CreateCharacterInviteNicknameResponsePacket(uint status, uint unk2, uint usedSmth, uint reqAc)
{
+ Status = status;
+ Unk2 = unk2;
+ UsedSmth = usedSmth;
+ ReqAc = reqAc;
}
#region implemented abstract members of Packet
@@ -41,7 +35,10 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
- pkt.WriteStruct(new CreateCharacterInviteNicknameResponsePacket(0, 0, 0, 0));
+ pkt.Write(Status);
+ pkt.Write(Unk2);
+ pkt.Write(UsedSmth);
+ pkt.Write(ReqAc);
return pkt.ToArray();
}
diff --git a/Server/Protocol/Packets/11-ClientPacket/11-AF-Unk11AFPacket.cs b/Server/Protocol/Packets/11-ClientPacket/11-AF-Unk11AFPacket.cs
index 556b7e0..df34ed6 100644
--- a/Server/Protocol/Packets/11-ClientPacket/11-AF-Unk11AFPacket.cs
+++ b/Server/Protocol/Packets/11-ClientPacket/11-AF-Unk11AFPacket.cs
@@ -8,9 +8,17 @@ namespace PSO2SERVER.Protocol.Packets
{
public class Unk11AFPacket : Packet
{
+ public uint unk1 { get; set; }
+ public uint unk2 { get; set; }
+ public uint unk3 { get; set; }
+ public uint unk4 { get; set; }
- public Unk11AFPacket()
+ public Unk11AFPacket(uint unk1, uint unk2, uint unk3, uint unk4)
{
+ this.unk1 = unk1;
+ this.unk2 = unk2;
+ this.unk3 = unk3;
+ this.unk4 = unk4;
}
#region implemented abstract members of Packet
@@ -18,6 +26,10 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.Write(unk1);
+ pkt.Write(unk2);
+ pkt.Write(unk3);
+ pkt.Write(unk4);
return pkt.ToArray();
}
diff --git a/Server/Protocol/Packets/19-LobbyPacket/19-08-SendSystemMessagePacket.cs b/Server/Protocol/Packets/19-LobbyPacket/19-08-SendSystemMessagePacket.cs
new file mode 100644
index 0000000..62cbe69
--- /dev/null
+++ b/Server/Protocol/Packets/19-LobbyPacket/19-08-SendSystemMessagePacket.cs
@@ -0,0 +1,43 @@
+using PSO2SERVER.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace PSO2SERVER.Protocol.Packets
+{
+ public class SendSystemMessagePacket : Packet
+ {
+ public string _msg { get; set; }
+ public uint unk1 { get; set; }
+ public uint unk2 { get; set; }
+ public uint unk3 { get; set; }
+
+ public SendSystemMessagePacket(string msg, uint unk1, uint unk2, uint unk3)
+ {
+ _msg = msg;
+ this.unk1 = 0x00002196;
+ this.unk2 = unk2;
+ this.unk3 = unk3;
+ }
+
+ #region implemented abstract members of Packet
+
+ public override byte[] Build()
+ {
+ var pkt = new PacketWriter();
+ pkt.WriteUtf16(_msg, 0x2126, 0xB0);
+ pkt.Write(unk1);
+ pkt.Write(unk2);
+ pkt.Write(unk3);
+ return pkt.ToArray();
+ }
+
+ public override PacketHeader GetHeader()
+ {
+ return new PacketHeader(0x19, 0x08, PacketFlags.PACKED);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Server/Protocol/Packets/19-LobbyPacket/19-09-SetLobbyEventPacket.cs b/Server/Protocol/Packets/19-LobbyPacket/19-09-SetLobbyEventPacket.cs
index be1fc23..701f680 100644
--- a/Server/Protocol/Packets/19-LobbyPacket/19-09-SetLobbyEventPacket.cs
+++ b/Server/Protocol/Packets/19-LobbyPacket/19-09-SetLobbyEventPacket.cs
@@ -8,9 +8,25 @@ namespace PSO2SERVER.Protocol.Packets
{
public class SetLobbyEventPacket : Packet
{
+ /// Event string ID.
+ public string event_name { get; set; } //Ascii
+ /// Voice line string ID.
+ public string voice_line { get; set; } //Ascii
+ /// Event start timestamp.
+ public Duration start_time { get; set; }
+ /// Event end timestamp.
+ public Duration end_time { get; set; }
+ public uint repeat_secs { get; set; }
+ public ulong unk4 { get; set; }
- public SetLobbyEventPacket()
+ public SetLobbyEventPacket(string eventname, string voiceline, Duration starttime, Duration endtime, uint repeatsecs, ulong unk4)
{
+ this.event_name = eventname;
+ this.voice_line = voiceline;
+ this.start_time = starttime;
+ this.end_time = endtime;
+ this.repeat_secs = repeatsecs;
+ this.unk4 = unk4;
}
#region implemented abstract members of Packet
@@ -18,12 +34,18 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.WriteAscii(event_name, 0xA6E4, 0xFB);
+ pkt.WriteAscii(voice_line, 0xA6E4, 0xFB);
+ pkt.WriteStruct(start_time);
+ pkt.WriteStruct(end_time);
+ pkt.Write(repeat_secs);
+ pkt.Write(unk4);
return pkt.ToArray();
}
public override PacketHeader GetHeader()
{
- return new PacketHeader(0x19, 0x09, PacketFlags.None);
+ return new PacketHeader(0x19, 0x09, PacketFlags.PACKED);
}
#endregion
diff --git a/Server/Protocol/Packets/1E-UnkPacket/1E-0C-Unk1E0CPacket.cs b/Server/Protocol/Packets/1E-UnkPacket/1E-0C-Unk1E0CPacket.cs
new file mode 100644
index 0000000..0cc7a77
--- /dev/null
+++ b/Server/Protocol/Packets/1E-UnkPacket/1E-0C-Unk1E0CPacket.cs
@@ -0,0 +1,34 @@
+using PSO2SERVER.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace PSO2SERVER.Protocol.Packets
+{
+ public class Unk1E0CPacket : Packet
+ {
+ public uint unk { get; set; }
+
+ public Unk1E0CPacket(uint unk)
+ {
+ this.unk = unk;
+ }
+
+ #region implemented abstract members of Packet
+
+ public override byte[] Build()
+ {
+ var pkt = new PacketWriter();
+ pkt.Write(unk);
+ return pkt.ToArray();
+ }
+
+ public override PacketHeader GetHeader()
+ {
+ return new PacketHeader(0x1E, 0x0C, PacketFlags.None);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Server/Protocol/Packets/1F-DailyOrderPacket/1F-03-OrderListPacket.cs b/Server/Protocol/Packets/1F-DailyOrderPacket/1F-03-OrderListPacket.cs
index cde8ce1..dfa3d4f 100644
--- a/Server/Protocol/Packets/1F-DailyOrderPacket/1F-03-OrderListPacket.cs
+++ b/Server/Protocol/Packets/1F-DailyOrderPacket/1F-03-OrderListPacket.cs
@@ -3,11 +3,16 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using static PSO2SERVER.Models.Orders;
namespace PSO2SERVER.Protocol.Packets
{
public class OrderListPacket : Packet
{
+ public ObjectHeader user { get; set; } = new ObjectHeader();
+ public ClientOrder[] orders { get; set; } = new ClientOrder[100];
+ public uint unk1 { get; set; }
+ public uint unk2 { get; set; }
public OrderListPacket()
{
@@ -18,6 +23,10 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.WriteObjectHeader(user);
+ pkt.WriteStructArray(orders);
+ pkt.Write(unk1);
+ pkt.Write(unk2);
return pkt.ToArray();
}
diff --git a/Server/Protocol/Packets/1F-DailyOrderPacket/1F-08-TakenOrdersPacket.cs b/Server/Protocol/Packets/1F-DailyOrderPacket/1F-08-TakenOrdersPacket.cs
index 797b7c0..b69f027 100644
--- a/Server/Protocol/Packets/1F-DailyOrderPacket/1F-08-TakenOrdersPacket.cs
+++ b/Server/Protocol/Packets/1F-DailyOrderPacket/1F-08-TakenOrdersPacket.cs
@@ -3,11 +3,18 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using static PSO2SERVER.Models.Orders;
namespace PSO2SERVER.Protocol.Packets
{
public class TakenOrdersPacket : Packet
{
+ public ObjectHeader user { get; set; } = new ObjectHeader();
+ public ClientOrder[] orders { get; set; } = new ClientOrder[50];
+ public OrderStatus[] statues { get; set; } = new OrderStatus[50];
+ public uint unk1 { get; set; }
+ public uint unk2 { get; set; }
+ public uint unk3 { get; set; }
public TakenOrdersPacket()
{
@@ -18,6 +25,12 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.WriteObjectHeader(user);
+ pkt.WriteStructArray(orders);
+ pkt.WriteStructArray(statues);
+ pkt.Write(unk1);
+ pkt.Write(unk2);
+ pkt.Write(unk3);
return pkt.ToArray();
}
diff --git a/Server/Protocol/Packets/23-FlagPackets/23-0E-Unk230EPacket.cs b/Server/Protocol/Packets/23-FlagPackets/23-0E-Unk230EPacket.cs
index 4886578..b510cd0 100644
--- a/Server/Protocol/Packets/23-FlagPackets/23-0E-Unk230EPacket.cs
+++ b/Server/Protocol/Packets/23-FlagPackets/23-0E-Unk230EPacket.cs
@@ -8,6 +8,15 @@ namespace PSO2SERVER.Protocol.Packets
{
public class Unk230EPacket : Packet
{
+ public struct Unk230EThing
+ {
+ public ushort unk1;
+ public ushort unk2;
+ public uint unk3;
+ public ObjectHeader unk4;
+ }
+
+ public List unk = new List();
public Unk230EPacket()
{
@@ -18,6 +27,11 @@ namespace PSO2SERVER.Protocol.Packets
public override byte[] Build()
{
var pkt = new PacketWriter();
+ pkt.WriteMagic((uint)unk.Count, 0xAC40, 0x99);
+
+ foreach (var entry in unk)
+ pkt.WriteStruct(entry);
+
return pkt.ToArray();
}
diff --git a/Server/QueryServer.cs b/Server/QueryServer.cs
index 627928e..b8d4c9d 100644
--- a/Server/QueryServer.cs
+++ b/Server/QueryServer.cs
@@ -9,6 +9,7 @@ using System.Threading;
using PSO2SERVER.Models;
using PSO2SERVER.Protocol;
using System.Threading.Tasks;
+using PSO2SERVER.Protocol.Packets;
namespace PSO2SERVER
{
@@ -78,7 +79,6 @@ namespace PSO2SERVER
private async Task DoShipListAsync(Socket socket)
{
- var writer = new PacketWriter();
var entries = new List();
for (var i = 1; i <= 10; i++)
@@ -95,25 +95,10 @@ namespace PSO2SERVER
entries.Add(entry);
}
- // Assuming header size: 8 bytes + (size of ShipEntry * number of entries) + 12 bytes
- int headerSize = 8;
- int shipEntrySize = Marshal.SizeOf(typeof(ShipEntry));
- int totalSize = headerSize + (shipEntrySize * entries.Count) + 12;
- PacketHeader header = new PacketHeader(totalSize, 0x11, 0x3D, 0x04, 0x00);
-
- writer.WriteStruct(header);
- writer.WriteMagic((uint)entries.Count, 0xE418, 81);
-
- foreach (var entry in entries)
- writer.WriteStruct(entry);
-
- writer.Write((Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds);
- writer.Write(1);
-
- var buffer = writer.ToArray();
+ var shiplistpacket = new ShipListPacket(entries, (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds, 1).GetPacketBytes();
await Task.Factory.FromAsync(
- (cb, state) => socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, cb, state),
+ (cb, state) => socket.BeginSend(shiplistpacket, 0, shiplistpacket.Length, SocketFlags.None, cb, state),
socket.EndSend,
null);
socket.Close();
@@ -121,17 +106,10 @@ namespace PSO2SERVER
private async Task DoBlockBalanceAsync(Socket socket)
{
- var writer = new PacketWriter();
- writer.WriteStruct(new PacketHeader(0x90, 0x11, 0x2C, 0x0, 0x0));
- writer.Write(new byte[0x20]);
- writer.Write(new byte[0x40]);
- writer.Write(ServerApp.BindAddress.GetAddressBytes());
- writer.Write((UInt16)12205);
- writer.Write(new byte[0x11A]);
+ var balancepacket = new BlockBalancePacket("test", 12205).GetPacketBytes();
- var buffer = writer.ToArray();
await Task.Factory.FromAsync(
- (cb, state) => socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, cb, state),
+ (cb, state) => socket.BeginSend(balancepacket, 0, balancepacket.Length, SocketFlags.None, cb, state),
socket.EndSend,
null);
socket.Close();
diff --git a/Server/Server.csproj b/Server/Server.csproj
index 214b2d2..474641a 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,19 +168,31 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -186,7 +201,10 @@
-
+
+
+
+
@@ -206,7 +224,7 @@
-
+
@@ -241,10 +259,15 @@
+
+
+
+
+
@@ -367,10 +390,10 @@
-
+
-
+
diff --git a/Server/Zone/Map.cs b/Server/Zone/Map.cs
index 7883bf8..09f762b 100644
--- a/Server/Zone/Map.cs
+++ b/Server/Zone/Map.cs
@@ -109,6 +109,21 @@ namespace PSO2SERVER.Zone
return location;
}
+ public struct ZoneSettings
+ {
+ public uint WorldId;
+ public uint Unk1;
+ public uint ZoneId;
+ public uint MapId; // Map layout id.
+ public uint ZoneType;
+ public uint Seed;
+ public uint Args;
+ public uint SizeX;
+ public uint SizeY;
+ public uint Unk2;
+ public uint AreaIndex;
+ public uint SubArea;
+ public uint Unk3;
///
/// Spawns a client into a map at a given location
@@ -133,25 +148,6 @@ namespace PSO2SERVER.Zone
}
else
{
- //PacketWriter writer = new PacketWriter();
- //writer.WriteStruct(new ObjectHeader(3, ObjectType.Map));
- //writer.WriteStruct(new ObjectHeader((uint)c._account.AccountId, ObjectType.Accounts));
- //writer.Write(0x1); // 8 Zeros
- //writer.Write(0); // 8 Zeros
- //writer.Write(~(uint)Type); // F4 FF FF FF
- //writer.Write(MapID); // Map ID maybe
- //writer.Write((uint)Flags);
- //writer.Write(GenerationArgs.seed); // 81 8F E6 19 (Maybe seed)
- //writer.Write(VariantID); // Randomgen enable / disable maybe
- //writer.Write(GenerationArgs.xsize); // X Size
- //writer.Write(GenerationArgs.ysize); // Y Size
- //writer.Write(1);
- //writer.Write(1);
- //writer.Write(~0); // FF FF FF FF FF FF FF FF
- //writer.Write(0x301);
-
- //c.SendPacket(0x3, 0x0, 0x0, writer.ToArray());
-
var _map = new Map("", MapID, VariantID, Type, Flags);
_map.GenerationArgs.seed = GenerationArgs.seed;
_map.GenerationArgs.xsize = GenerationArgs.xsize;
@@ -169,7 +165,7 @@ namespace PSO2SERVER.Zone
c.SendPacket(new SetAccountIDPacket(c._account.AccountId));
// Spawn Character
- c.SendPacket(new CharacterSpawnPacket(c.Character, location, true, 1));
+ c.SendPacket(new CharacterSpawnPacket(c.Character, location, true, true));
c.CurrentLocation = location;
c.CurrentZone = this;
@@ -186,11 +182,11 @@ namespace PSO2SERVER.Zone
}
// Spawn for others, Spawn others for me
- CharacterSpawnPacket spawnMe = new CharacterSpawnPacket(c.Character, location, false, 1);
+ CharacterSpawnPacket spawnMe = new CharacterSpawnPacket(c.Character, location, false, true);
foreach (Client other in Clients)
{
other.SendPacket(spawnMe);
- c.SendPacket(new CharacterSpawnPacket(other.Character, other.CurrentLocation, false, 1));
+ c.SendPacket(new CharacterSpawnPacket(other.Character, other.CurrentLocation, false, true));
}
Clients.Add(c);
@@ -214,10 +210,6 @@ namespace PSO2SERVER.Zone
foreach (Client other in Clients)
{
- //PacketWriter writer = new PacketWriter();
- //writer.WriteStruct(new ObjectHeader((uint)other._account.AccountId, ObjectType.Accounts));
- //writer.WriteStruct(new ObjectHeader((uint)c._account.AccountId, ObjectType.Accounts));
- //other.SendPacket(0x4, 0x3B, 0x40, writer.ToArray());
other.SendPacket(new DespawnPlayerPacket(other._account.AccountId, c._account.AccountId));
}
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 @@
+