继续修正

This commit is contained in:
Longfeng Qin 2024-09-17 11:29:41 +08:00
parent 554a46206c
commit 86cbf43914
20 changed files with 443 additions and 164 deletions

View File

@ -239,11 +239,12 @@ namespace PSO2SERVER
if (!Directory.Exists(packetPath))
Directory.CreateDirectory(packetPath);
var filename = string.Format("{0}/0x{1:X2}-0x{2:X2}-{3}.bin"
var filename = string.Format("{0}/0x{1:X2}-0x{2:X2}-{3}-{4}.bin"
, packetPath
, typeA, typeB
//, _packetId++
, fromClient ? "C" : "S"
, _server.StartTime.ToShortTimeString().Replace('/', '-').Replace(':', '-')
);
using (var stream = File.OpenWrite(filename))
@ -269,11 +270,12 @@ namespace PSO2SERVER
if (!Directory.Exists(packetPath))
Directory.CreateDirectory(packetPath);
var filename = string.Format("{0}/0x{1:X2}-0x{2:X2}-{3}.bin"
var filename = string.Format("{0}/0x{1:X2}-0x{2:X2}-{3}-{4}.bin"
, packetPath
, typeA, typeB
//, _packetId++
, "C-unk"
, _server.StartTime.ToShortTimeString().Replace('/', '-').Replace(':', '-')
);
using (var stream = File.OpenWrite(filename))

View File

@ -9,43 +9,179 @@ namespace PSO2SERVER.Models
{
public class Character
{
public enum ClassType : byte
{
Unknown = 0xFF,
Hunter = 0,
Ranger,
Force,
Fighter,
Gunner,
Techer,
Braver,
Bouncer,
Challenger,
Summoner,
BattleWarrior,
Hero,
Phantom,
Etole,
Luster,
}
// 每个枚举成员的值是通过位移操作计算的:
//Hunter1 << 0即 10x0001
//Ranger1 << 1即 20x0002
//Force1 << 2即 40x0004
//Fighter1 << 3即 80x0008
//Gunner1 << 4即 160x0010
//Techer1 << 5即 320x0020
//Braver1 << 6即 640x0040
//Bouncer1 << 7即 1280x0080
//Challenger1 << 8即 2560x0100
//Summoner1 << 9即 5120x0200
//BattleWarrior1 << 10即 10240x0400
//Hero1 << 11即 20480x0800
//Phantom1 << 12即 40960x1000
//Etole1 << 13即 81920x2000
//Luster1 << 14即 163840x4000
[Flags]
public enum ClassTypeField : ushort
{
Unknown = 0xFF,
None = 0,
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 unsafe struct CharParam
{
public uint character_id;
public uint player_id;
public uint unk1;
public uint voice_type;
public ushort unk2;
public short voice_pitch;
}
[StructLayout(LayoutKind.Sequential)]
public struct JobEntry
{
public ushort level;
public ushort level2; // Usually the same as the above, what is this used for?
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
;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct JobParam
{
//public fixed byte unknown_0[4];
public ClassType mainClass;
public ClassType subClass;
public fixed byte uknown_6[2];
public ClassTypeField enabledClasses;
public fixed byte uknown_8[2];
//public byte padding0;
public Entries entries; //TODO: Make this a fixed array
public fixed ushort unk_maxlevel[15];
//public ushort unknown_48, unknown_4A;
//public uint unknown_4C;
//public ushort unknown_50, unknown_52;
//public uint unknown_54;
//public ushort unknown_58, unknown_5A;
//public uint unknown_5C;
//public ushort unknown_60, unknown_62;
//public uint unknown_64;
//public uint unknown_68;
//public ushort unknown_6C, unknown_6E;
//public fixed int unknown_70[4];
}
public enum RunAnimation : ushort
{
Walking = 9,
Hovering = 11
}
public enum Race : ushort
{
Unknown = 0xFFFF,
Human = 0,
Newman,
Cast,
Dewman
Dewman,
}
public enum Gender : ushort
{
Unknown = 0xFFFF,
Male = 0,
Female
Female,
}
public enum ClassType : byte
public struct AccessoryData
{
Hunter = 0,
Fighter,
Ranger,
Gunner,
Force,
Techer,
Braver,
Bouncer,
public sbyte Value1;
public sbyte Value2;
public sbyte Value3;
}
[Flags]
public enum ClassTypeField : byte
public enum SkinColor
{
Hunter = 1,
Fighter = 2,
Ranger = 4,
Gunner = 8,
Force = 16,
Techer = 32,
Braver = 64,
Bouncer = 128
RaceDefined,
Human,
Deuman,
Cast
}
[StructLayout(LayoutKind.Sequential)]
@ -60,83 +196,117 @@ namespace PSO2SERVER.Models
public ushort x, y, z; // Great naming, SEGA
}
[StructLayout(LayoutKind.Sequential)]
public struct JobEntry
{
public ushort level;
public ushort level2; // Usually the same as the above, what is this used for?
public uint exp;
}
[StructLayout(LayoutKind.Sequential)]
public struct Entries
{
public JobEntry hunter, fighter, ranger, gunner, force, techer, braver, bouncer;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct JobParam
{
public fixed byte unknown_0[4];
public ClassType mainClass;
public ClassType subClass;
public fixed byte uknown_6[2];
public ClassTypeField enabledClasses;
public fixed byte uknown_8[2];
public byte padding0;
public Entries entries; //TODO: Make this a fixed array
public ushort unknown_48, unknown_4A;
public uint unknown_4C;
public ushort unknown_50, unknown_52;
public uint unknown_54;
public ushort unknown_58, unknown_5A;
public uint unknown_5C;
public ushort unknown_60, unknown_62;
public uint unknown_64;
public uint unknown_68;
public ushort unknown_6C, unknown_6E;
public fixed int unknown_70[4];
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct LooksParam
{
public fixed byte padding[4];
public ushort height;
public fixed byte charData[80]; // Figure Data, needs more work
public ushort accessoryData1;
public ushort accessoryData2;
public ushort accessoryData3;
public ushort accessoryData4;
public HSVColor costumeColor;
public HSVColor mainColor;
public HSVColor sub1Color;
public HSVColor sub2Color;
public HSVColor sub3Color;
public HSVColor eyeColor;
public HSVColor hairColor;
public int modelID;
public ushort mainParts;
public ushort bodyPaint;
public ushort emblem;
public ushort eyePattern;
public ushort eyelashes;
public ushort eyebrows;
public ushort face;
public ushort facePaint1;
public ushort hairstyle;
public ushort accessory1;
public ushort accessory2;
public ushort accessory3;
public ushort facePaint2;
public ushort arms;
public ushort legs;
public ushort accessory4;
public ushort costume;
public RunAnimation running_animation;
public Race race;
public Gender gender;
//public fixed byte padding[4];
public ushort Muscule;
public Figure Body;
public Figure Arms;
public Figure Legs;
public Figure Chest;
public Figure FaceShape;
public Figure FaceParts;
public Figure Eyes;
public Figure NoseSize;
public Figure NoseHeight;
public Figure Mouth;
public Figure Ears;
public Figure Neck;
public Figure Waist;
public Figure Body2;
public Figure Arms2;
public Figure Legs2;
public Figure Chest2;
public Figure Neck2;
public Figure Waist2;
public fixed byte Unk1[0x20];
public fixed byte Unk2[0x0A];
public AccessoryData Acc1Location;
public AccessoryData Acc2Location;
public AccessoryData Acc3Location;
public AccessoryData Acc4Location;
//public ushort height;
//public fixed byte charData[80]; // Figure Data, needs more work
//public ushort accessoryData1;
//public ushort accessoryData2;
//public ushort accessoryData3;
//public ushort accessoryData4;
public HSVColor UnkColor;
public HSVColor CostumeColor;
public HSVColor MainColor;
public HSVColor Sub1Color;
public HSVColor Sub2Color;
public HSVColor Sub3Color;
public HSVColor EyeColor;
public HSVColor HairColor;
public fixed byte Unk3[0x20];
public fixed byte Unk4[0x10];
public ushort CostumeId;
public ushort BodyPaint1;
public ushort StickerId;
public ushort RightEyeId;
public ushort EyebrowId;
public ushort EyelashId;
public ushort FaceId1;
public ushort FaceId2;
public ushort Facemakeup1Id;
public ushort HairstyleId;
public ushort Acc1Id;
public ushort Acc2Id;
public ushort Acc3Id;
public ushort Facemakeup2Id;
public ushort LegId;
public ushort ArmId;
public ushort Acc4Id;
public fixed byte Unk5[0x04];
public ushort BodyPaint2;
public ushort LeftEyeId;
public fixed byte Unk6[0x12];
public AccessoryData Acc1Size;
public AccessoryData Acc2Size;
public AccessoryData Acc3Size;
public AccessoryData Acc4Size;
public AccessoryData Acc1Rotation;
public AccessoryData Acc2Rotation;
public AccessoryData Acc3Rotation;
public AccessoryData Acc4Rotation;
public ushort Unk7;
public fixed byte Unk8[0x08];
public SkinColor SkinColorType;
public sbyte EyebrowThickness;
//public int modelID;
//public ushort mainParts;
//public ushort bodyPaint;
//public ushort emblem;
//public ushort eyePattern;
//public ushort eyelashes;
//public ushort eyebrows;
//public ushort face;
//public ushort facePaint1;
//public ushort hairstyle;
//public ushort accessory1;
//public ushort accessory2;
//public ushort accessory3;
//public ushort facePaint2;
//public ushort arms;
//public ushort legs;
//public ushort accessory4;
//public ushort costume;
//public Race race;
//public Gender gender;
}
// Probably more info than this
@ -146,6 +316,24 @@ namespace PSO2SERVER.Models
public int CharacterId { get; set; }
public virtual Player Player { get; set; }
public byte[] CharSetBinary
{
get
{
PacketWriter w = new PacketWriter();
w.WriteStruct(CharSet);
return w.ToArray();
}
set
{
CharSet = Helper.ByteArrayToStructure<CharParam>(value);
}
}
public CharParam CharSet { get; set; }
public string Name { get; set; }
public byte[] LooksBinary
@ -164,6 +352,8 @@ namespace PSO2SERVER.Models
}
public LooksParam Looks { get; set; }
public byte[] JobsBinary
{
get
@ -180,7 +370,6 @@ namespace PSO2SERVER.Models
}
public LooksParam Looks { get; set; }
public JobParam Jobs { get; set; }
}

View File

@ -1,4 +1,5 @@
using PSO2SERVER.Database;
using PSO2SERVER.Packets.PSOPackets;
using System.Linq;
namespace PSO2SERVER.Packets.Handlers
@ -13,53 +14,7 @@ namespace PSO2SERVER.Packets.Handlers
if (context.User == null)
return;
var writer = new PacketWriter();
using (var db = new ServerEf())
{
var chars = db.Characters
.Where(w => w.Player.PlayerId == context.User.PlayerId)
.OrderBy(o => o.CharacterId) // TODO: Order by last played
.Select(s => s);
writer.Write((uint)chars.Count()); // Number of characters
for (var i = 0; i < 0x4; i++) // Whatever this is
writer.Write((byte)0);
foreach (var ch in chars)
{
writer.Write((uint)ch.CharacterId);
writer.Write((uint)context.User.PlayerId);
for (var i = 0; i < 0x10; i++)
writer.Write((byte)0);
writer.WriteFixedLengthUtf16(ch.Name, 16);
writer.Write((uint)0);
writer.WriteStruct(ch.Looks); // Note: Pre-Episode 4 created looks doesn't seem to work anymore
writer.WriteStruct(ch.Jobs);
for (var i = 0; i < 0xFC; i++)
writer.Write((byte)0);
}
}
// Ninji note: This packet may be followed by extra data,
// after a fixed-length array of character data structures.
// Needs more investigation at some point.
// ---
// CK note: Extra data is likely current equipment, playtime, etc.
// All of that data is currently unaccounted for at the moment.
//忍者注意:这个包后面可能有额外的数据,
//在固定长度的字符数据结构数组之后。
//需要更多的调查。
// ---
// CK注:额外的数据可能是当前设备,游戏时间等。
//所有这些数据目前都是未知的。
context.SendPacket(0x11, 0x03, 0, writer.ToArray());
context.SendPacket(new CharacterListPacket(context.User.PlayerId));
}
#endregion

View File

@ -5,6 +5,7 @@ using PSO2SERVER.Packets.PSOPackets;
using PSO2SERVER.Database;
using System.Linq;
using System;
using System.Runtime.InteropServices;
namespace PSO2SERVER.Packets.Handlers
{
@ -21,20 +22,22 @@ namespace PSO2SERVER.Packets.Handlers
var reader = new PacketReader(data, position, size);
//var info = string.Format("[接收] 接收到的数据 (hex): ");
//Logger.WriteHex(info, data);
var setting = reader.ReadStruct<Character.CharParam>();
//reader.ReadBytes(12); // 12 unknown bytes
//reader.ReadByte(); // VoiceType
//reader.ReadBytes(5); // 5 unknown bytes
//reader.ReadUInt16(); // VoiceData
reader.ReadBytes(12); // 12 unknown bytes
reader.ReadByte(); // VoiceType
reader.ReadBytes(5); // 5 unknown bytes
reader.ReadUInt16(); // VoiceData
var name = reader.ReadFixedLengthUtf16(16);//玩家名称 宽字符
reader.BaseStream.Seek(0x4, SeekOrigin.Current); // Padding
//reader.BaseStream.Seek(0x04, SeekOrigin.Current); // Padding
var looks = reader.ReadStruct<Character.LooksParam>();
var jobs = reader.ReadStruct<Character.JobParam>();
//Logger.WriteInternal("[CHR] {0} 创建了名为 {1} 的新角色.", context.User.Username, name);
var newCharacter = new Character
{
CharSet = setting,
Name = name,
Jobs = jobs,
Looks = looks,
@ -68,10 +71,11 @@ namespace PSO2SERVER.Packets.Handlers
context.Character = newCharacter;
// Set Player ID
var writer = new PacketWriter();
writer.Write(0);
writer.Write((uint) context.User.PlayerId);
context.SendPacket(0x11, 0x07, 0, writer.ToArray());
//var writer = new PacketWriter();
//writer.Write(0);
//writer.Write((uint) context.User.PlayerId);
//context.SendPacket(0x11, 0x07, 0, writer.ToArray());
context.SendPacket(new CharacterCreateResponsePacket(CharacterCreateResponsePacket.CharacterCreationStatus.Success, (uint)context.User.PlayerId));
// Spawn
context.SendPacket(new NoPayloadPacket(0x11, 0x3E));

View File

@ -33,6 +33,7 @@ namespace PSO2SERVER.Packets.Handlers
db.ChangeTracker.DetectChanges();
}
// 将客户端发送至默认大厅
Map lobbyMap = ZoneManager.Instance.MapFromInstance("lobby", "lobby");
lobbyMap.SpawnClient(context, lobbyMap.GetDefaultLocation(), "lobby");
@ -41,7 +42,7 @@ namespace PSO2SERVER.Packets.Handlers
//context.SendPacket(File.ReadAllBytes("testbed/237.23-7.210.189.208.30.bin"));
// Give a blank palette
// 先给一个空的 Palette
context.SendPacket(new PalettePacket());
// memset packet - Enables menus

View File

@ -8,29 +8,22 @@ namespace PSO2SERVER.Packets.PSOPackets
{
public class UnkPacket : Packet
{
private readonly byte _subtype;
private readonly byte _type;
public UnkPacket()
{
_type = 0x00;
_subtype = 0x00;
}
#region implemented abstract members of Packet
public override byte[] Build()
{
return new byte[0];
var pkt = new PacketWriter();
return pkt.ToArray();
}
public override PacketHeader GetHeader()
{
return new PacketHeader
{
Type = _type,
Subtype = _subtype
};
return new PacketHeader(0x00, 0x00, PacketFlags.None);
}
#endregion

View File

@ -25,7 +25,7 @@ namespace PSO2SERVER.Packets.PSOPackets
public override PacketHeader GetHeader()
{
return new PacketHeader(0x0B, 0x18, 0x04);
return new PacketHeader(0x0B, 0x18, PacketFlags.PACKED);
}
// Hoo boy, this is 468 bytes!

View File

@ -0,0 +1,80 @@
using PSO2SERVER.Database;
using PSO2SERVER.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
namespace PSO2SERVER.Packets.PSOPackets
{
public class CharacterListPacket : Packet
{
// Ninji note: This packet may be followed by extra data,
// after a fixed-length array of character data structures.
// Needs more investigation at some point.
// ---
// CK note: Extra data is likely current equipment, playtime, etc.
// All of that data is currently unaccounted for at the moment.
//忍者注意:这个包后面可能有额外的数据,
//在固定长度的字符数据结构数组之后。
//需要更多的调查。
// ---
// CK注:额外的数据可能是当前设备,游戏时间等。
//所有这些数据目前都是未知的。
private int _PlayerId;
public CharacterListPacket(int PlayerId)
{
_PlayerId = PlayerId;
}
#region implemented abstract members of Packet
public override byte[] Build()
{
var writer = new PacketWriter();
using (var db = new ServerEf())
{
var chars = db.Characters
.Where(w => w.Player.PlayerId == _PlayerId)
.OrderBy(o => o.CharacterId) // TODO: Order by last played
.Select(s => s);
writer.Write((uint)chars.Count()); // Number of characters
for (var i = 0; i < 0x4; i++) // Whatever this is
writer.Write((byte)0);
foreach (var ch in chars)
{
writer.Write((uint)ch.CharacterId);
writer.Write((uint)_PlayerId);
for (var i = 0; i < 0x10; i++)
writer.Write((byte)0);
writer.WriteFixedLengthUtf16(ch.Name, 16);
writer.Write((uint)0);
writer.WriteStruct(ch.Looks); // Note: 第4集之前创造的外观似乎不再有效了
writer.WriteStruct(ch.Jobs);
for (var i = 0; i < 0xFC; i++)
writer.Write((byte)0);
}
}
return writer.ToArray();
}
public override PacketHeader GetHeader()
{
return new PacketHeader(0x11, 0x03, PacketFlags.None);
}
#endregion
}
}

View File

@ -0,0 +1,53 @@
using Mysqlx;
using PSO2SERVER.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
namespace PSO2SERVER.Packets.PSOPackets
{
public class CharacterCreateResponsePacket : Packet
{
public enum CharacterCreationStatus
{
/// 角色创建成功.
Success,
/// 显示数据空白错误信息.
EmptyError,
/// 角色数量上限错误.
LimitReached,
/// AC不足无法创建角色.
NoAC,
/// 常规系统报错.
SystemError,
}
private CharacterCreationStatus status;
private uint userid;
public CharacterCreateResponsePacket(CharacterCreationStatus status, uint userid)
{
this.status = status;
this.userid = userid;
}
#region implemented abstract members of Packet
public override byte[] Build()
{
var pkt = new PacketWriter();
pkt.Write((int)status);
pkt.Write(userid);
return pkt.ToArray();
}
public override PacketHeader GetHeader()
{
return new PacketHeader(0x11, 0x07, PacketFlags.None);
}
#endregion
}
}

View File

@ -203,6 +203,8 @@
<Compile Include="Packets\Handlers\03-ServerHandler\03-34-TeleportCasinoToLobby.cs" />
<Compile Include="Packets\Handlers\11-ClientHandler\11-41-CreateCharacterOne.cs" />
<Compile Include="Packets\Handlers\2F-SymbolHandler\2F-06-SymbolArtHandler.cs" />
<Compile Include="Packets\PSOPackets\11-ClientPacket\11-03-CharacterListPacket.cs" />
<Compile Include="Packets\PSOPackets\11-ClientPacket\11-07-CharacterCreateResponsePacket.cs" />
<Compile Include="Packets\PSOPackets\04-ObjectPacket\04-06-DespawnObjectPacket.cs" />
<Compile Include="Packets\PSOPackets\00-00-UnkPacket.cs" />
<Compile Include="Packets\PSOPackets\03-ServerPacket\03-2B-UnlockControlsPacket.cs" />

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.