using System;
using System.IO;
using System.Linq;
using Vision.Core.Extensions;
using Vision.Core.Logging.Loggers;
using Vision.Core.Streams;
namespace Vision.Core.Networking.Packet
{
///
/// Class that represents a network message sent to and from a
///
///
public class NetPacket : VisionObject
{
private static readonly SocketLog Logger = new SocketLog(typeof(NetPacket));
public static readonly NetCommand[] DebugSkipCommands =
{
NetCommand.NC_MISC_HEARTBEAT_REQ,
NetCommand.NC_MISC_HEARTBEAT_ACK,
NetCommand.NC_BRIEFINFO_ABSTATE_CHANGE_CMD,
NetCommand.NC_ACT_SOMEONEMOVERUN_CMD,
NetCommand.NC_ACT_SOMEONEMOVEWALK_CMD,
NetCommand.NC_ACT_SOMEONESTOP_CMD,
NetCommand.NC_BAT_ABSTATESET_CMD,
NetCommand.NC_BAT_CEASE_FIRE_CMD,
};
///
/// The type of the message.
///
public NetCommand Command { get; set; }
///
/// Object to read data from the message.
///
public ReaderStream Reader { get; }
///
/// The stream that contains the message data.
///
private readonly MemoryStream _stream;
///
/// Object to write data to the message.
///
public WriterStream Writer { get; }
public int Size { get; protected set; }
///
/// Creates a new instance of the class.
///
/// The message data.
public NetPacket(byte[] buffer)
{
_stream = new MemoryStream(buffer);
Reader = new ReaderStream(ref _stream);
Writer = new WriterStream(ref _stream);
Command = (NetCommand)Reader.ReadUInt16();
Size = Reader.RemainingBytes;
}
///
/// Creates a new instance of the class.
///
/// The type of message.
public NetPacket(NetCommand command)
{
_stream = new MemoryStream();
Writer = new WriterStream(ref _stream);
Command = command;
Writer.Write((ushort)command);
}
///
/// Sends multiple messages as a chunk.
///
public static void SendChunk(T connection, params NetPacket[] packets) where T : NetConnectionBase
{
connection.SendChunk = true;
foreach (var pkt in packets)
{
pkt.Send(connection);
}
connection.SendChunk = false;
}
///
/// Sends the message to the connection.
///
/// The connection to send the message to.
public void Send(T connection) where T : NetConnectionBase
{
connection?.SendData(ToArray(connection));
if (!DebugSkipCommands.Contains(Command))
{
var endpointStr = connection == null ? "Connection Null" : connection.RemoteEndPoint.ToSimpleString();
Logger.Debug($"Sent {Command} packet to endpoint {endpointStr}, Size: {Size}");
}
Destroy(this);
}
///
/// Returns a byte array representing the message.
///
///
public byte[] ToArray(T connection = null) where T : NetConnectionBase
{
byte[] ret;
var buffer = _stream.ToArray();
if (connection != null)
{
// TODO: fix??
var isClient = connection.ReceiveDestinationType.IsClient();
var isToClient = connection.TransmitDestinationType.IsClient();
while (!connection.Crypto.WasSeedSet()) { }
if (isClient && !isToClient && connection.IsEstablished)
{
connection.Crypto.XorBuffer(buffer, 0, buffer.Length);
}
}
if (buffer.Length <= 0xff)
{
ret = new byte[buffer.Length + 1];
Buffer.BlockCopy(buffer, 0, ret, 1, buffer.Length);
ret[0] = (byte)buffer.Length;
}
else
{
ret = new byte[buffer.Length + 3];
Buffer.BlockCopy(buffer, 0, ret, 3, buffer.Length);
Buffer.BlockCopy(BitConverter.GetBytes((ushort)buffer.Length), 0, ret, 1, 2);
}
return ret;
}
///
/// Returns a string representing the message.
///
public override string ToString()
{
return $"Command=0x{Command:X} ({Command}), Length={_stream.Length - 2}";
}
///
/// Destroys the instance.
///
protected override void Destroy()
{
Destroy(Reader);
Destroy(Writer);
_stream?.Close();
}
}
}