合并数据
This commit is contained in:
commit
61df2b8f27
@ -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,
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
98
Server/Models/BattleStats.cs
Normal file
98
Server/Models/BattleStats.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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; }
|
180
Server/Models/CharacterAddtionStruct.cs
Normal file
180
Server/Models/CharacterAddtionStruct.cs
Normal 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,
|
||||
}
|
||||
|
||||
// 每个枚举成员的值是通过位移操作计算的:
|
||||
|
||||
//Hunter:1 << 0,即 1(0x0001)
|
||||
//Ranger:1 << 1,即 2(0x0002)
|
||||
//Force:1 << 2,即 4(0x0004)
|
||||
//Fighter:1 << 3,即 8(0x0008)
|
||||
//Gunner:1 << 4,即 16(0x0010)
|
||||
//Techer:1 << 5,即 32(0x0020)
|
||||
//Braver:1 << 6,即 64(0x0040)
|
||||
//Bouncer:1 << 7,即 128(0x0080)
|
||||
//Challenger:1 << 8,即 256(0x0100)
|
||||
//Summoner:1 << 9,即 512(0x0200)
|
||||
//BattleWarrior:1 << 10,即 1024(0x0400)
|
||||
//Hero:1 << 11,即 2048(0x0800)
|
||||
//Phantom:1 << 12,即 4096(0x1000)
|
||||
//Etole:1 << 13,即 8192(0x2000)
|
||||
//Luster:1 << 14,即 16384(0x4000)
|
||||
|
||||
[Flags]
|
||||
public enum ClassTypeField : ushort
|
||||
{
|
||||
Hunter = 1 << 0,
|
||||
Ranger = 1 << 1,
|
||||
Force = 1 << 2,
|
||||
Fighter = 1 << 3,
|
||||
Gunner = 1 << 4,
|
||||
Techer = 1 << 5,
|
||||
Braver = 1 << 6,
|
||||
Bouncer = 1 << 7,
|
||||
Challenger = 1 << 8,
|
||||
Summoner = 1 << 9,
|
||||
BattleWarrior = 1 << 10,
|
||||
Hero = 1 << 11,
|
||||
Phantom = 1 << 12,
|
||||
Etole = 1 << 13,
|
||||
Luster = 1 << 14
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct JobEntry
|
||||
{
|
||||
/// Main level.
|
||||
public ushort level;
|
||||
public ushort level2; // SubClass level
|
||||
/// Current EXP.
|
||||
public uint exp;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Entries
|
||||
{
|
||||
public JobEntry
|
||||
hunter
|
||||
, ranger
|
||||
, force
|
||||
, fighter
|
||||
, gunner
|
||||
, techer
|
||||
, braver
|
||||
, bouncer
|
||||
, Challenger
|
||||
, Summoner
|
||||
, BattleWarrior
|
||||
, Hero
|
||||
, Phantom
|
||||
, Etole
|
||||
, Luster
|
||||
, unk16
|
||||
, unk17
|
||||
, unk18
|
||||
, unk19
|
||||
, unk20
|
||||
, unk21
|
||||
, unk22
|
||||
, unk23
|
||||
, unk24
|
||||
;
|
||||
}
|
||||
|
||||
public enum RunAnimation : ushort
|
||||
{
|
||||
Walking = 9,
|
||||
Hovering = 11
|
||||
}
|
||||
|
||||
public enum Race : ushort
|
||||
{
|
||||
Unknown = 0xFFFF,
|
||||
Human = 0,
|
||||
Newman,
|
||||
Cast,
|
||||
Dewman,
|
||||
}
|
||||
|
||||
public enum Gender : ushort
|
||||
{
|
||||
Unknown = 0xFFFF,
|
||||
Male = 0,
|
||||
Female,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Figure
|
||||
{
|
||||
public ushort x, y, z; // Great naming, SEGA
|
||||
}
|
||||
|
||||
public struct AccessoryData
|
||||
{
|
||||
public sbyte Value1;
|
||||
public sbyte Value2;
|
||||
public sbyte Value3;
|
||||
}
|
||||
|
||||
public enum SkinColor : byte
|
||||
{
|
||||
RaceDefined,
|
||||
Human,
|
||||
Deuman,
|
||||
Cast
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HSVColor
|
||||
{
|
||||
public ushort hue, saturation, value;
|
||||
}
|
||||
}
|
@ -9,172 +9,6 @@ namespace PSO2SERVER.Models
|
||||
{
|
||||
public class CharacterStruct
|
||||
{
|
||||
public enum ShortLanguage : byte
|
||||
{
|
||||
Japanese,
|
||||
English,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct CharParam
|
||||
{
|
||||
public int CharacterID;
|
||||
public int player_id;
|
||||
public uint unk1;
|
||||
public uint voice_type;
|
||||
public ushort unk2;
|
||||
public short voice_pitch;
|
||||
}
|
||||
|
||||
public enum ClassType : byte
|
||||
{
|
||||
Unknown = 0xFF,
|
||||
Hunter = 0,
|
||||
Ranger,
|
||||
Force,
|
||||
Fighter,
|
||||
Gunner,
|
||||
Techer,
|
||||
Braver,
|
||||
Bouncer,
|
||||
Challenger,
|
||||
Summoner,
|
||||
BattleWarrior,
|
||||
Hero,
|
||||
Phantom,
|
||||
Etole,
|
||||
Luster,
|
||||
}
|
||||
|
||||
// 每个枚举成员的值是通过位移操作计算的:
|
||||
|
||||
//Hunter:1 << 0,即 1(0x0001)
|
||||
//Ranger:1 << 1,即 2(0x0002)
|
||||
//Force:1 << 2,即 4(0x0004)
|
||||
//Fighter:1 << 3,即 8(0x0008)
|
||||
//Gunner:1 << 4,即 16(0x0010)
|
||||
//Techer:1 << 5,即 32(0x0020)
|
||||
//Braver:1 << 6,即 64(0x0040)
|
||||
//Bouncer:1 << 7,即 128(0x0080)
|
||||
//Challenger:1 << 8,即 256(0x0100)
|
||||
//Summoner:1 << 9,即 512(0x0200)
|
||||
//BattleWarrior:1 << 10,即 1024(0x0400)
|
||||
//Hero:1 << 11,即 2048(0x0800)
|
||||
//Phantom:1 << 12,即 4096(0x1000)
|
||||
//Etole:1 << 13,即 8192(0x2000)
|
||||
//Luster:1 << 14,即 16384(0x4000)
|
||||
|
||||
[Flags]
|
||||
public enum ClassTypeField : ushort
|
||||
{
|
||||
Hunter = 1 << 0,
|
||||
Ranger = 1 << 1,
|
||||
Force = 1 << 2,
|
||||
Fighter = 1 << 3,
|
||||
Gunner = 1 << 4,
|
||||
Techer = 1 << 5,
|
||||
Braver = 1 << 6,
|
||||
Bouncer = 1 << 7,
|
||||
Challenger = 1 << 8,
|
||||
Summoner = 1 << 9,
|
||||
BattleWarrior = 1 << 10,
|
||||
Hero = 1 << 11,
|
||||
Phantom = 1 << 12,
|
||||
Etole = 1 << 13,
|
||||
Luster = 1 << 14
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct JobEntry
|
||||
{
|
||||
/// Main level.
|
||||
public ushort level;
|
||||
public ushort level2; // SubClass level
|
||||
/// Current EXP.
|
||||
public uint exp;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Entries
|
||||
{
|
||||
public JobEntry
|
||||
hunter
|
||||
, ranger
|
||||
, force
|
||||
, fighter
|
||||
, gunner
|
||||
, techer
|
||||
, braver
|
||||
, bouncer
|
||||
, Challenger
|
||||
, Summoner
|
||||
, BattleWarrior
|
||||
, Hero
|
||||
, Phantom
|
||||
, Etole
|
||||
, Luster
|
||||
, unk16
|
||||
, unk17
|
||||
, unk18
|
||||
, unk19
|
||||
, unk20
|
||||
, unk21
|
||||
, unk22
|
||||
, unk23
|
||||
, unk24
|
||||
;
|
||||
}
|
||||
|
||||
public enum RunAnimation : ushort
|
||||
{
|
||||
Walking = 9,
|
||||
Hovering = 11
|
||||
}
|
||||
|
||||
public enum Race : ushort
|
||||
{
|
||||
Unknown = 0xFFFF,
|
||||
Human = 0,
|
||||
Newman,
|
||||
Cast,
|
||||
Dewman,
|
||||
}
|
||||
|
||||
public enum Gender : ushort
|
||||
{
|
||||
Unknown = 0xFFFF,
|
||||
Male = 0,
|
||||
Female,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Figure
|
||||
{
|
||||
public ushort x, y, z; // Great naming, SEGA
|
||||
}
|
||||
|
||||
public struct AccessoryData
|
||||
{
|
||||
public sbyte Value1;
|
||||
public sbyte Value2;
|
||||
public sbyte Value3;
|
||||
}
|
||||
|
||||
public enum SkinColor : byte
|
||||
{
|
||||
RaceDefined,
|
||||
Human,
|
||||
Deuman,
|
||||
Cast
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HSVColor
|
||||
{
|
||||
public ushort hue, saturation, value;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct LooksParam
|
||||
{
|
||||
|
@ -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
399
Server/Models/FixedTypes.cs
Normal 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))}]";
|
||||
}
|
||||
}
|
||||
}
|
54
Server/Models/NetInterface.cs
Normal file
54
Server/Models/NetInterface.cs
Normal 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
29
Server/Models/Orders.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
|
@ -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); // 写入填充部分
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
207
Server/Models/RevealedRegions.cs
Normal file
207
Server/Models/RevealedRegions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
241
Server/Models/Stats.cs
Normal 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
100
Server/Models/Time.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
76
Server/Network/Ipv4Addr.cs
Normal file
76
Server/Network/Ipv4Addr.cs
Normal 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);
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 初始化完成.");
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
52
Server/Protocol/Handlers/04-ObjectHandler/04-75-ActionEnd.cs
Normal file
52
Server/Protocol/Handlers/04-ObjectHandler/04-75-ActionEnd.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
// {
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
27
Server/Protocol/Handlers/1F-ClientOrderHandler/1F-0F-UNK.cs
Normal file
27
Server/Protocol/Handlers/1F-ClientOrderHandler/1F-0F-UNK.cs
Normal 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}");
|
||||
}
|
||||
}
|
||||
}
|
@ -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(); // 返回整个数据包字节数组
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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 个字节
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -9,7 +9,7 @@ using PSO2SERVER.Zone;
|
||||
|
||||
namespace PSO2SERVER.Protocol.Packets
|
||||
{
|
||||
class MovementPacket : Packet
|
||||
public class MovementPacket : Packet
|
||||
{
|
||||
public struct PackedVec4
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
|
34
Server/Protocol/Packets/1E-UnkPacket/1E-0C-Unk1E0CPacket.cs
Normal file
34
Server/Protocol/Packets/1E-UnkPacket/1E-0C-Unk1E0CPacket.cs
Normal 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
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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" />
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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" />
|
||||
|
Loading…
Reference in New Issue
Block a user