合并数据

This commit is contained in:
Longfeng Qin 2024-12-07 23:02:25 +08:00
commit 61df2b8f27
93 changed files with 3996 additions and 1425 deletions

View File

@ -321,49 +321,6 @@ namespace PSO2SERVER
}
}
public class NetInterface
{
/// <summary>
/// Interface status.
/// </summary>
public uint State { get; set; }
/// <summary>
/// Interface MAC address.
/// </summary>
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,

View File

@ -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())

View File

@ -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<PSO2Items>(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

View File

@ -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<EnemyHitbox> Hitboxes { get; set; } = new List<EnemyHitbox>();
// 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;
}
}
}

View File

@ -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; }

View File

@ -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,
}
// 每个枚举成员的值是通过位移操作计算的:
//Hunter1 << 0即 10x0001
//Ranger1 << 1即 20x0002
//Force1 << 2即 40x0004
//Fighter1 << 3即 80x0008
//Gunner1 << 4即 160x0010
//Techer1 << 5即 320x0020
//Braver1 << 6即 640x0040
//Bouncer1 << 7即 1280x0080
//Challenger1 << 8即 2560x0100
//Summoner1 << 9即 5120x0200
//BattleWarrior1 << 10即 10240x0400
//Hero1 << 11即 20480x0800
//Phantom1 << 12即 40960x1000
//Etole1 << 13即 81920x2000
//Luster1 << 14即 163840x4000
[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;
}
}

View File

@ -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,
}
// 每个枚举成员的值是通过位移操作计算的:
//Hunter1 << 0即 10x0001
//Ranger1 << 1即 20x0002
//Force1 << 2即 40x0004
//Fighter1 << 3即 80x0008
//Gunner1 << 4即 160x0010
//Techer1 << 5即 320x0020
//Braver1 << 6即 640x0040
//Bouncer1 << 7即 1280x0080
//Challenger1 << 8即 2560x0100
//Summoner1 << 9即 5120x0200
//BattleWarrior1 << 10即 10240x0400
//Hero1 << 11即 20480x0800
//Phantom1 << 12即 40960x1000
//Etole1 << 13即 81920x2000
//Luster1 << 14即 163840x4000
[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
{

View File

@ -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
}
}

399
Server/Models/FixedTypes.cs Normal file
View File

@ -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<T>
{
private List<T> _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<T>(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<T>(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<T> where T : IComparable<T>
{
private List<T> _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<T>();
_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<T>
{
private HashSet<T> _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<T>();
_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<byte> byteList = new List<byte>();
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<byte> byteList = new List<byte>();
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<byte> byteList = new List<byte>();
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))}]";
}
}
}

View File

@ -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
{
/// <summary>
/// Interface status.
/// </summary>
public uint State { get; set; }
/// <summary>
/// Interface MAC address.
/// </summary>
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();
}
}
}
}
}

29
Server/Models/Orders.cs Normal file
View File

@ -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;
}
}
}

View File

@ -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<ushort> AffixSpan
public Span<ushort> 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
{

View File

@ -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); // 写入填充部分

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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
{
/// <summary>
/// Revealed minimap regions
/// </summary>
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();
}
}
/// <summary>
/// 获取指定行列位置的位值
/// </summary>
/// <param name="row">行索引0 到 7</param>
/// <param name="col">列索引0 到 9</param>
/// <returns>该位置的位值true = 已揭示false = 未揭示)</returns>
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;
}
/// <summary>
/// 设置指定行列位置的位值
/// </summary>
/// <param name="row">行索引0 到 7</param>
/// <param name="col">列索引0 到 9</param>
/// <param name="value">设置的值true = 设置为已揭示false = 设置为未揭示)</param>
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;
}
}
}
}

View File

@ -9,7 +9,8 @@ namespace PSO2SERVER.Models
Online,
Busy,
Full,
Offline
Offline,
Undefined = 0xFFFF
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]

241
Server/Models/Stats.cs Normal file
View File

@ -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<LevelStats> Stats { get; set; } = new List<LevelStats>();
}
public class PlayerStats
{
public List<List<LevelStats>> Stats { get; set; } = new List<List<LevelStats>>();
public List<StatMultipliers> Modifiers { get; set; } = new List<StatMultipliers>();
}
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<EnemyLevelBaseStats> Levels { get; set; } = new List<EnemyLevelBaseStats>();
}
public class EnemyStats
{
public List<EnemyLevelBaseStats> Levels { get; set; } = new List<EnemyLevelBaseStats>();
public List<EnemyHitbox> Hitboxes { get; set; } = new List<EnemyHitbox>();
}
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<string, EnemyStats> Enemies { get; set; } = new Dictionary<string, EnemyStats>();
}
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<uint, float> Pa { get; set; } = new Tuple<uint, float>(0, 1.0f);
public DamageType() { }
public DamageType(float mul)
{
Mul = mul;
}
public DamageType(Tuple<uint, float> 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字节数组
}
}
}

100
Server/Models/Time.cs Normal file
View File

@ -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<Duration>, IComparable<Duration>
{
// 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);
}
}
}

View File

@ -0,0 +1,76 @@
using System;
public struct Ipv4Addr : IEquatable<Ipv4Addr>
{
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<T> 接口
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);
}
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -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<Party, string> parties = new Dictionary<Party, string>(); // Key: Party, Value: Name? (for now)
public Dictionary<Party, string> parties = new Dictionary<Party, string>(); // 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 初始化完成.");
}

View File

@ -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());

View File

@ -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());

View File

@ -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)

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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<ObjectHeader>();
performer = reader.ReadStruct<ObjectHeader>();
unk2 = reader.ReadUInt32();
unk3 = reader.ReadStruct<ObjectHeader>();
unk4 = reader.ReadStruct<ObjectHeader>();
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));
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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());
}
}
}

View File

@ -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));
}
}
}

View File

@ -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<ObjectHeader>();
// 现在可以对任务列表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)
// {
// }
//}
}
}

View File

@ -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());

View File

@ -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));
}
}
}

View File

@ -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));

View File

@ -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<LooksParam>();
packet.Jobs = reader.ReadStruct<JobParam>();
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<LooksParam>();
Unk3 = reader.ReadUInt32();
Jobs = reader.ReadStruct<JobParam>();
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

View File

@ -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<NetInterface> 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);
}
}
}

View File

@ -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

View File

@ -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));
}
}
}

View File

@ -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<BlockInfo> Blocks { get; set; } = new FixedVec<BlockInfo> (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);

View File

@ -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<TakenOrdersRequestPacket>();
Logger.Write($"unk1 = {packet.unk1},unk2 = {packet.unk2},unk3 = {packet.unk3},unk4 = {packet.unk4}");
}
}
}

View File

@ -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());
}
}
}

View File

@ -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}");
}
}
}

View File

@ -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<Unk1F0FPacket>();
Logger.Write($"unk1 = {packet.unk1}, unk2 = {packet.unk2}");
}
}
}

View File

@ -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<byte>();
totalPacket.AddRange(header.ToBytes()); // 包头转换为字节并加入
totalPacket.AddRange(packetBody); // 包体加入
return totalPacket.ToArray(); // 返回整个数据包字节数组
}
}
}

View File

@ -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);
}
}
}

View File

@ -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>(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<ObjectHeader> 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<T>
public void WriteList<T>(List<T> 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 个字节
}
}
}

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -9,7 +9,7 @@ using PSO2SERVER.Zone;
namespace PSO2SERVER.Protocol.Packets
{
class MovementPacket : Packet
public class MovementPacket : Packet
{
public struct PackedVec4
{

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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<EXPReceiver> receivers { get; set; } = new List<EXPReceiver>();
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

View File

@ -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;
/// <summary>
/// CharacterSpawnPacket Struct
/// </summary>
/// 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()

View File

@ -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();
}

View File

@ -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<uint> data { get; set; } = new List<uint>();
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();
}

View File

@ -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<uint> data { get; set; } = new List<uint>();
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()

View File

@ -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

View File

@ -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

View File

@ -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<ObjectHeader> unk2 { get; set; } = new List<ObjectHeader>();
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<ObjectHeader> 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();
}

View File

@ -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();
}

View File

@ -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);
}
}
}

View File

@ -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,
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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>(QuestDefiniton);
writer.WriteStruct<QuestDifficulty>(QuestDifficulty);
writer.WriteStruct<ObjectHeader>(Player);
writer.Write(unk1);
writer.Write(unk2);
return writer.ToArray();
}
public override PacketHeader GetHeader()
{
return new PacketHeader(0x0E, 0x31, PacketFlags.None);
}
}
}

View File

@ -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<QuestDefiniton>(data);
writer.WriteStruct<QuestDifficultyPacket.QuestDifficulty>(difficulty);
return writer.ToArray();
}
public override PacketHeader GetHeader()
{
return new PacketHeader(0x0E, 0x31);
}
}
}

View File

@ -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<EquipedItem> Items { get; set; } = new List<EquipedItem>();
// 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<EquipedItem> 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()

View File

@ -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
{
/// <summary>
/// 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()

View File

@ -12,6 +12,29 @@ namespace PSO2SERVER.Protocol.Packets
{
public class CharacterListPacket : Packet
{
// Available characters
public List<Character> Characters { get; set; } = new List<Character>();
// Equipped items (List of 10 Items for each character)
public List<PSO2Items[]> EquippedItems { get; set; } = new List<PSO2Items[]>();
// 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()

View File

@ -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();
}

View File

@ -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<ShipEntry> shipEntries { get; set; } = new List<ShipEntry>();
public int timestamp { get; set; }
public uint unk { get; set; }
public ShipListPacket()
public ShipListPacket(List<ShipEntry> 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

View File

@ -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
{
/// <summary>
/// Creation status.
/// </summary>
public uint Status;
public uint Unk2;
public uint UsedSmth;
/// <summary>
/// Required AC to buy a character creation pass.
/// </summary>
public uint ReqAc;
public CreateCharacter1ResponsePacket(uint status, uint unk2, uint usedSmth, uint reqAc) : this()
{
Status = status;
Unk2 = unk2;
UsedSmth = usedSmth;
ReqAc = reqAc;
}
}
public CreateCharacterOneResponsePacket()
/// <summary>
/// Creation status.
/// </summary>
public uint Status;
public uint Unk2;
public uint UsedSmth;
/// <summary>
/// Required AC to buy a character creation pass.
/// </summary>
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();
}

View File

@ -6,34 +6,28 @@ using System.Text;
namespace PSO2SERVER.Protocol.Packets
{
public class CreateCharacterInviteNicknameResponse : Packet
public class CreateCharacterInviteNicknameResponsePacket : Packet
{
public struct CreateCharacterInviteNicknameResponsePacket
{
/// <summary>
/// Creation status.
/// </summary>
public uint Status;
public uint Unk2;
public uint UsedSmth;
/// <summary>
/// Required AC to buy a character creation pass.
/// </summary>
public uint ReqAc;
public CreateCharacterInviteNicknameResponsePacket(uint status, uint unk2, uint usedSmth, uint reqAc) : this()
{
Status = status;
Unk2 = unk2;
UsedSmth = usedSmth;
ReqAc = reqAc;
}
}
public CreateCharacterInviteNicknameResponse()
/// <summary>
/// Creation status.
/// </summary>
public uint Status;
public uint Unk2;
public uint UsedSmth;
/// <summary>
/// Required AC to buy a character creation pass.
/// </summary>
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();
}

View File

@ -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();
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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<Unk230EThing> unk = new List<Unk230EThing>();
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();
}

View File

@ -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<ShipEntry>();
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();

View File

@ -135,6 +135,9 @@
<Reference Include="System.Diagnostics.DiagnosticSource, Version=8.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.8.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.Half, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Half.1.0.0\lib\netstandard2.0\System.Half.dll</HintPath>
</Reference>
<Reference Include="System.IO.Pipelines, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Pipelines.8.0.0\lib\net462\System.IO.Pipelines.dll</HintPath>
</Reference>
@ -165,19 +168,31 @@
<Compile Include="ConsoleSystem.cs" />
<Compile Include="Crypto\KeyLoader.cs" />
<Compile Include="Logger.cs" />
<Compile Include="Models\block.cs" />
<Compile Include="Models\BattleStats.cs" />
<Compile Include="Models\BlockInfo.cs" />
<Compile Include="Models\CharacterAddtionStruct.cs" />
<Compile Include="Models\FixedTypes.cs" />
<Compile Include="Models\Flags.cs" />
<Compile Include="Models\Mission.cs" />
<Compile Include="Models\NetInterface.cs" />
<Compile Include="Models\Orders.cs" />
<Compile Include="Models\PSO2Item.cs" />
<Compile Include="Models\PSOData.cs" />
<Compile Include="Models\PSOObject.cs" />
<Compile Include="Models\PSOPalette.cs" />
<Compile Include="Models\Quest.cs" />
<Compile Include="Models\RevealedRegions.cs" />
<Compile Include="Models\Stats.cs" />
<Compile Include="Models\Time.cs" />
<Compile Include="Network\Ipv4Addr.cs" />
<Compile Include="Network\PortChecker.cs" />
<Compile Include="Object\ObjectManager.cs" />
<Compile Include="Protocol\Handlers\04-ObjectHandler\04-75-ActionEnd.cs" />
<Compile Include="Protocol\Handlers\06-PlayerStatusHandler\06-01-DealDamage.cs" />
<Compile Include="Protocol\Handlers\0B-QuestHandler\0B-20-AcceptQuest.cs" />
<Compile Include="Protocol\Handlers\11-ClientHandler\11-14-BlockLogin.cs" />
<Compile Include="Protocol\Handlers\11-ClientHandler\11-90-CharacterUndeletionRequest.cs" />
<Compile Include="Protocol\Handlers\11-ClientHandler\11-64-AllBlocksListRequest.cs" />
<Compile Include="Protocol\Handlers\11-ClientHandler\11-DC-CharacterSurvey.cs" />
<Compile Include="Protocol\Handlers\11-ClientHandler\11-EB-NicknameCheckRequest - 复制.cs" />
<Compile Include="Protocol\Handlers\11-ClientHandler\11-52-CreateCharacterInviteNickname.cs" />
@ -186,7 +201,10 @@
<Compile Include="Protocol\Handlers\1C-CharacterInfoHandler\1C-46-UNK.cs" />
<Compile Include="Protocol\Handlers\1C-CharacterInfoHandler\1C-0E-UNK.cs" />
<Compile Include="Protocol\Handlers\1C-CharacterInfoHandler\1C-1E-CharacterInfoRequest.cs" />
<Compile Include="Protocol\Handlers\1F-UNKHandler\1F-09-UNK1F09.cs" />
<Compile Include="Protocol\Handlers\1F-ClientOrderHandler\1F-02-OrderListRequest.cs" />
<Compile Include="Protocol\Handlers\1F-ClientOrderHandler\1F-01-TakenOrdersRequest.cs" />
<Compile Include="Protocol\Handlers\1F-ClientOrderHandler\1F-0F-UNK.cs" />
<Compile Include="Protocol\Handlers\1F-ClientOrderHandler\1F-09-UNK1F09.cs" />
<Compile Include="Protocol\Handlers\23-FlagHandler\23-0B-SkitItemAddRequest.cs" />
<Compile Include="Protocol\Handlers\03-ServerHandler\03-03-InitialLoad.cs" />
<Compile Include="Protocol\Handlers\03-ServerHandler\03-0C-ServerPong.cs" />
@ -206,7 +224,7 @@
<Compile Include="Protocol\Handlers\0B-QuestHandler\0B-CD-AcceptStoryQuestHandler.cs" />
<Compile Include="Protocol\Handlers\0B-QuestHandler\0B-09-MinimapRevealRequest.cs" />
<Compile Include="Protocol\Handlers\0B-QuestHandler\0B-15-QuestCounterAvailableHander.cs" />
<Compile Include="Protocol\Handlers\0B-QuestHandler\0B-17-QuestListRequestHandler.cs" />
<Compile Include="Protocol\Handlers\0B-QuestHandler\0B-17-QuestCategoryRequest.cs" />
<Compile Include="Protocol\Handlers\0B-QuestHandler\0B-19-QuestDifficultyRequestHandler.cs" />
<Compile Include="Protocol\Handlers\0B-QuestHandler\0B-30-QuestCounterHandler.cs" />
<Compile Include="Protocol\Handlers\--UNK.cs" />
@ -241,10 +259,15 @@
<Compile Include="Protocol\Handlers\4A-ARKSMisionsHandler\4A-00-MissionListRequest.cs" />
<Compile Include="Protocol\Handlers\4D-ClassicMissionPassHandler\4D-02-MissionPassRequest.cs" />
<Compile Include="Protocol\Handlers\4D-ClassicMissionPassHandler\4D-00-MissionPassInfoRequest.cs" />
<Compile Include="Protocol\Packets\04-ObjectRelatedPacket\04-75-ActionEndPacket.cs" />
<Compile Include="Protocol\Packets\0B-QuestPacket\0B-1B-QuestCategoryStopperPacket.cs" />
<Compile Include="Protocol\Packets\19-LobbyPacket\19-08-SendSystemMessagePacket.cs" />
<Compile Include="Protocol\Packets\1E-UnkPacket\1E-0C-Unk1E0CPacket.cs" />
<Compile Include="Protocol\Packets\0B-QuestPacket\0B-1C-QuestDifficultySendFinishedPacket.cs" />
<Compile Include="Protocol\Packets\0F-ItemPacket\0F-0C-LoadEquipedPacket.cs" />
<Compile Include="Protocol\Packets\11-ClientPacket\11-42-CreateCharacterOneResponsePacket.cs" />
<Compile Include="Protocol\Packets\11-ClientPacket\11-BD-CharacterShipTransferCancelPacket.cs" />
<Compile Include="Protocol\Packets\04-ObjectRelatedPacket\04-71-MovementEndPacket.cs" />
<Compile Include="Protocol\Packets\4A-ARKSMissionPacket\4A-03-Unk4A03Packet.cs" />
<Compile Include="Protocol\Packets\4A-ARKSMissionPacket\4A-01-ARKSMissionListPacket.cs" />
<Compile Include="Protocol\Packets\07-ChatPacket\07-00-ChatPacket.cs" />
@ -367,10 +390,10 @@
<Compile Include="Protocol\Packets\21-PalettePacket\21-03-FullPaletteInfoPacket.cs" />
<Compile Include="Protocol\Packets\21-PalettePacket\21-01-LoadPalettePacket.cs" />
<Compile Include="Protocol\Packets\0E-PartyPacket\0E-02-PartyInitPacket.cs" />
<Compile Include="Protocol\Packets\0E-PartyPacket\0E-31-QuestStartPacket.cs" />
<Compile Include="Protocol\Packets\0E-PartyPacket\0E-31-PartySetQuestPacket.cs" />
<Compile Include="Protocol\Packets\0B-QuestPacket\0B-1A-QuestDifficultyPacket.cs" />
<Compile Include="Protocol\Packets\0B-QuestPacket\0B-16-QuestAvailablePacket.cs" />
<Compile Include="Protocol\Packets\0B-QuestPacket\0B-18-QuestListPacket.cs" />
<Compile Include="Protocol\Packets\0B-QuestPacket\0B-18-QuestCategoryPacket.cs" />
<Compile Include="Protocol\Packets\11-ClientPacket\11-1C-SetCurrencyPacket.cs" />
<Compile Include="Protocol\Packets\0F-ItemPacket\0F-14-SetMesetaPacket.cs" />
<Compile Include="Protocol\Packets\03-ServerPacket\03-04-LoadingScreenTransitionPacket.cs" />

View File

@ -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;
/// <summary>
/// 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));
}

View File

@ -4,6 +4,7 @@
<package id="BouncyCastle.Cryptography" version="2.4.0" targetFramework="net48" />
<package id="EntityFramework" version="6.5.1" targetFramework="net48" />
<package id="Google.Protobuf" version="3.28.1" targetFramework="net48" />
<package id="Half" version="1.0.0" targetFramework="net48" />
<package id="K4os.Compression.LZ4" version="1.3.8" targetFramework="net48" />
<package id="K4os.Compression.LZ4.Streams" version="1.3.8" targetFramework="net48" />
<package id="K4os.Hash.xxHash" version="1.0.8" targetFramework="net48" />