364 lines
12 KiB
C#
364 lines
12 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
using PSO2SERVER.Database;
|
||
using PSO2SERVER.Models;
|
||
using PSO2SERVER.Network;
|
||
using PSO2SERVER.Protocol;
|
||
using PSO2SERVER.Protocol.Handlers;
|
||
using PSO2SERVER.Protocol.Packets;
|
||
using PSO2SERVER.Zone;
|
||
using static Google.Protobuf.Reflection.UninterpretedOption.Types;
|
||
|
||
namespace PSO2SERVER
|
||
{
|
||
public class Client
|
||
{
|
||
public Client()
|
||
{
|
||
IsClosed = false;
|
||
|
||
_readBuffer = new byte[1024 * 64];
|
||
_readBufferSize = 0;
|
||
|
||
InputArc4 = null;
|
||
OutputArc4 = null;
|
||
|
||
Palette = new PSOPalette();
|
||
battle_stats = new PlayerStats();
|
||
|
||
SendPacket(new ServerHelloPacket(0x03, 100, 68833280));
|
||
}
|
||
|
||
public Client(Server server, SocketClient socket)
|
||
{
|
||
IsClosed = false;
|
||
_server = server;
|
||
Socket = socket;
|
||
|
||
socket.DataReceived += HandleDataReceived;
|
||
socket.ConnectionLost += HandleConnectionLost;
|
||
|
||
_readBuffer = new byte[1024 * 64];
|
||
_readBufferSize = 0;
|
||
|
||
InputArc4 = null;
|
||
OutputArc4 = null;
|
||
|
||
Palette = new PSOPalette();
|
||
battle_stats = new PlayerStats();
|
||
|
||
SendPacket(new ServerHelloPacket(0x03, 100, 68833280));
|
||
}
|
||
|
||
public enum PacketType : byte
|
||
{
|
||
/// NGS packet.
|
||
NGS,
|
||
/// Classic packet. (i.e. NA, JP and Vita)
|
||
Classic,
|
||
/// NA packet.
|
||
NA,
|
||
/// JP packet.
|
||
JP,
|
||
/// Vita packet.
|
||
Vita,
|
||
/// Raw packet. (i.e. don't parse the packet)
|
||
Raw,
|
||
}
|
||
|
||
internal static RSACryptoServiceProvider RsaCsp = null;
|
||
private readonly byte[] _readBuffer;
|
||
private readonly Server _server;
|
||
internal ICryptoTransform InputArc4, OutputArc4;
|
||
//private int _packetId;
|
||
private uint _readBufferSize;
|
||
|
||
/// <summary>
|
||
/// Server
|
||
/// </summary>
|
||
public bool IsClosed { get; private set; }
|
||
public SocketClient Socket { get; private set; }
|
||
|
||
/// <summary>
|
||
/// Game
|
||
/// </summary>
|
||
// TODO Consider moving these somewhere else
|
||
public Account _account { get; set; }
|
||
public Character Character { get; set; }
|
||
public uint MovementTimestamp { get; internal set; }
|
||
public Flags _flags { get; set; }
|
||
public PSOPalette Palette { get; set; }
|
||
public PlayerStats battle_stats { get; set; }
|
||
public UserState UserState { get; set; }
|
||
|
||
/// <summary>
|
||
/// Party
|
||
/// </summary>
|
||
public Party.Party currentParty;
|
||
|
||
/// <summary>
|
||
/// Map
|
||
/// </summary>
|
||
public Map CurrentMap;
|
||
public PSOLocation CurrentLocation;
|
||
public PSOLocation LastLocation;
|
||
|
||
private void HandleDataReceived(byte[] data, int size)
|
||
{
|
||
if ((_readBufferSize + size) > _readBuffer.Length)
|
||
{
|
||
Logger.WriteError("[<--] 接收到 {0} 字节大于预设buf长度", size);
|
||
// Buffer overrun
|
||
// TODO: Drop the connection when this occurs?
|
||
return;
|
||
}
|
||
|
||
Logger.Write("[<--] 接收到 {0} 字节", size);
|
||
|
||
Array.Copy(data, 0, _readBuffer, _readBufferSize, size);
|
||
|
||
InputArc4?.TransformBlock(_readBuffer, (int)_readBufferSize, size, _readBuffer, (int)_readBufferSize);
|
||
|
||
_readBufferSize += (uint)size;
|
||
|
||
// Process ALL the packets
|
||
uint position = 0;
|
||
|
||
while ((position + 8) <= _readBufferSize)
|
||
{
|
||
var packetSize =
|
||
_readBuffer[position] |
|
||
((uint)_readBuffer[position + 1] << 8) |
|
||
((uint)_readBuffer[position + 2] << 16) |
|
||
((uint)_readBuffer[position + 3] << 24);
|
||
|
||
// Minimum size, just to avoid possible infinite loops etc
|
||
if (packetSize < 8)
|
||
packetSize = 8;
|
||
|
||
// If we don't have enough data for this one...
|
||
if (packetSize > 0x1000000 || (packetSize + position) > _readBufferSize)
|
||
break;
|
||
|
||
// Now handle this one
|
||
HandlePacket(
|
||
_readBuffer[position + 4], _readBuffer[position + 5],
|
||
_readBuffer[position + 6], _readBuffer[position + 7],
|
||
_readBuffer, position + 8, packetSize - 8);
|
||
|
||
// If the connection was closed, we have no more business here
|
||
if (IsClosed)
|
||
break;
|
||
|
||
position += packetSize;
|
||
}
|
||
|
||
// Wherever 'position' is up to, is what was successfully processed
|
||
if (position > 0)
|
||
{
|
||
if (position >= _readBufferSize)
|
||
_readBufferSize = 0;
|
||
else
|
||
{
|
||
Array.Copy(_readBuffer, position, _readBuffer, 0, _readBufferSize - position);
|
||
_readBufferSize -= position;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void HandleConnectionLost()
|
||
{
|
||
// :(
|
||
Logger.Write("[BYE] 连接丢失. :(");
|
||
CurrentMap?.RemoveClient(this);
|
||
IsClosed = true;
|
||
}
|
||
|
||
public void SendPacket(byte[] blob, string name = null)
|
||
{
|
||
var typeA = blob[4];
|
||
var typeB = blob[5];
|
||
var flags1 = blob[6];
|
||
var flags2 = blob[7];
|
||
string sendName;
|
||
|
||
if (name != null)
|
||
{
|
||
sendName = $"0x{typeA:X2} - 0x{typeB:X2} ({name})";
|
||
}
|
||
else
|
||
{
|
||
sendName = $"0x{typeA:X2} - 0x{typeB:X2}";
|
||
}
|
||
|
||
if ((typeA != 0x08 && typeB != 0x0B) && (typeA != 0x08 && typeB != 0x0C) && (typeA != 0x04))
|
||
Logger.WriteSend($"[-->] {sendName} (Flags {(PacketFlags)flags1}) ({blob.Length} 字节)");
|
||
|
||
if (Logger.VerbosePackets)
|
||
{
|
||
var info = string.Format("[-->] 0x{0:X2} - 0x{1:X2} 数据包:", typeA, typeB);
|
||
Logger.WriteHex(info, blob);
|
||
}
|
||
|
||
LogPacket(false, typeA, typeB, flags1, flags2, blob);
|
||
|
||
OutputArc4?.TransformBlock(blob, 0, blob.Length, blob, 0);
|
||
|
||
try
|
||
{
|
||
Socket.Write(blob);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.WriteException("发送数据包错误", ex);
|
||
}
|
||
}
|
||
|
||
public void SendPacket(byte typeA, byte typeB, byte flags, byte[] data, string name = null)
|
||
{
|
||
if (data == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(data), "Data array cannot be null.");
|
||
}
|
||
|
||
using (var memoryStream = new MemoryStream())
|
||
using (var binaryWriter = new BinaryWriter(memoryStream))
|
||
{
|
||
// 写入数据长度(总长度为头部4字节 + typeA(1) + typeB(1) + flags(1) + 额外字节的长度))
|
||
uint dataLen = (uint)data.Length + 8;
|
||
binaryWriter.Write(dataLen); // 自动处理为小端序
|
||
|
||
// 写入类型A、类型B和标志
|
||
binaryWriter.Write(typeA);
|
||
binaryWriter.Write(typeB);
|
||
binaryWriter.Write(flags);
|
||
binaryWriter.Write((byte)0); // 额外的字节
|
||
|
||
// 写入数据
|
||
binaryWriter.Write(data);
|
||
|
||
// 调用实际发送数据的方法
|
||
SendPacket(memoryStream.ToArray(), name);
|
||
}
|
||
}
|
||
|
||
public void SendPacket(Packet packet)
|
||
{
|
||
var h = packet.GetHeader();
|
||
SendPacket(h.Type, h.Subtype, h.Flags1, packet.Build(), packet.GetType().Name);
|
||
}
|
||
|
||
private void HandlePacket(byte typeA, byte typeB, byte flags1, byte flags2, byte[] data, uint position, uint size)
|
||
{
|
||
var handler = PacketHandlers.GetHandlerFor(typeA, typeB);
|
||
string packetName;
|
||
if (handler != null)
|
||
{
|
||
packetName = $"0x{typeA:X2} - 0x{typeB:X2} ({handler.GetType().Name})";
|
||
}
|
||
else
|
||
{
|
||
packetName = $"0x{typeA:X2} - 0x{typeB:X2}";
|
||
}
|
||
|
||
if ((typeA != 0x04))
|
||
Logger.WriteRecv($"[<--] {packetName} (Flags {(PacketFlags)flags1}) ({size + 8} 字节)");
|
||
|
||
var packet = new byte[size];
|
||
Array.Copy(data, position, packet, 0, size);
|
||
|
||
if (Logger.VerbosePackets && size > 0) // TODO: This is trimming too far?
|
||
{
|
||
var dataTrimmed = new byte[size];
|
||
for (var i = 0; i < size; i++)
|
||
dataTrimmed[i] = data[i];
|
||
|
||
var info = string.Format("[<--] 0x{0:X2} - 0x{1:X2} 数据包:", typeA, typeB);
|
||
Logger.WriteHex(info, dataTrimmed);
|
||
}
|
||
|
||
LogPacket(true, typeA, typeB, flags1, flags2, packet);
|
||
|
||
if (handler != null)
|
||
handler.HandlePacket(this, flags1, packet, 0, size);
|
||
else
|
||
{
|
||
//Logger.WriteWarning("[<--未解析] 0x{0:X2} - 0x{1:X2} (UNK) (Flags {2}) ({3} 字节)", typeA,
|
||
// typeB, (PacketFlags)flags1, size);
|
||
var info = string.Format("[<--未解析] 0x{0:X2} - 0x{1:X2} (UNK) (Flags {2}) ({3} 字节)", typeA,
|
||
typeB, (PacketFlags)flags1, size);
|
||
Logger.WriteUnkHex(info, packet);
|
||
LogUnkClientPacket(typeA, typeB, flags1, flags2, packet);
|
||
}
|
||
|
||
// throw new NotImplementedException();
|
||
}
|
||
|
||
private void LogPacket(bool fromClient, byte typeA, byte typeB, byte flags1, byte flags2, byte[] packet)
|
||
{
|
||
// Check for and create packets directory if it doesn't exist
|
||
var packetPath = Path.Combine("packets", fromClient ? "客户端" : "服务端");
|
||
|
||
// 记录数据包
|
||
LogPacketInternal(packetPath, fromClient, typeA, typeB, flags1, flags2, packet);
|
||
}
|
||
|
||
private void LogUnkClientPacket(byte typeA, byte typeB, byte flags1, byte flags2, byte[] packet)
|
||
{
|
||
// Check for and create packets directory if it doesn't exist
|
||
var packetPath = Path.Combine("UnkClientPackets", "客户端");
|
||
|
||
// 记录未知数据包
|
||
LogPacketInternal(packetPath, true, typeA, typeB, flags1, flags2, packet, "C-unk");
|
||
}
|
||
|
||
private void LogPacketInternal(string directory, bool fromClient, byte typeA, byte typeB, byte flags1, byte flags2, byte[] packet, string customPrefix = null)
|
||
{
|
||
// 生成格式化日期和时间
|
||
var datePart = _server.StartTime.ToString("yyyy-MM-dd");
|
||
var timePart = _server.StartTime.ToString("HH-mm-ss-fff");
|
||
|
||
// Check for and create packets directory if it doesn't exist
|
||
var packetPath = Path.Combine(directory, $"0x{typeA:X2}-0x{typeB:X2}", datePart);
|
||
|
||
// 创建目录
|
||
if (!Directory.Exists(packetPath))
|
||
{
|
||
Directory.CreateDirectory(packetPath);
|
||
}
|
||
|
||
// 构造文件名
|
||
var prefix = string.IsNullOrEmpty(customPrefix) ? (fromClient ? "客户端" : "服务端") : customPrefix;
|
||
|
||
// 确保文件名不包含不允许的字符
|
||
var safePrefix = string.Join("_", prefix.Split(Path.GetInvalidFileNameChars()));
|
||
var filename = Path.Combine(packetPath, $"{safePrefix}-0x{typeA:X2}-0x{typeB:X2}-{timePart}-{packet.Length}字节.bin");
|
||
|
||
try
|
||
{
|
||
using (var stream = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.None))
|
||
{
|
||
if (fromClient || !string.IsNullOrEmpty(customPrefix))
|
||
{
|
||
stream.Write(new byte[] { typeA, typeB, flags1, flags2 }, 0, 4);
|
||
}
|
||
stream.Write(packet, 0, packet.Length);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"记录数据包时出错: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
public enum Language : uint
|
||
{
|
||
Japanese = 0,
|
||
English = 1
|
||
}
|
||
} |