PSO2SERVER/Server/Client.cs
2024-12-21 03:15:29 +08:00

364 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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