using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using DFEngine.Content.GameObjects;
namespace DFEngine.Network
{
///
/// Class that represents a network message sent to and from a
/// .
///
public class NetworkMessage : Object
{
///
/// The type of the message.
///
public NetworkCommand Command { get; set; }
///
/// Object to read data from the message.
///
private readonly BinaryReader reader;
///
/// The stream that contains the message data.
///
private readonly MemoryStream stream;
///
/// Object to write data to the message.
///
private readonly BinaryWriter writer;
///
/// Creates a new instance of the class.
///
/// The message data.
public NetworkMessage(byte[] buffer)
{
stream = new MemoryStream(buffer);
reader = new BinaryReader(stream);
Command = (NetworkCommand)ReadUInt16();
}
///
/// Creates a new instance of the class.
///
/// The type of message.
public NetworkMessage(NetworkCommand command)
{
stream = new MemoryStream();
writer = new BinaryWriter(stream);
Command = command;
Write((ushort)command);
}
///
/// Sends multiple messages as a chunk.
///
public static void SendChunk(NetworkConnection connection, params NetworkMessage[] messages)
{
connection.SendChunk = true;
for (var i = 0; i < messages.Length; i++)
{
messages[i].Send(connection);
}
connection.SendChunk = false;
}
///
/// Fills the number of bytes with the value.
///
/// The number of bytes to fill.
/// The value to fill with.
public void Fill(int count, byte value)
{
for (var i = 0; i < count; i++)
{
Write(value);
}
}
///
/// The number of unread bytes in the message.
///
public int RemainingBytes
{
get
{
var length = stream?.Length;
var position = stream?.Position;
return (int)((length.HasValue & position.HasValue ? length.GetValueOrDefault() - position.GetValueOrDefault() : new long?()) ?? 0L);
}
}
///
/// Reads a boolean value from the current stream.
///
public bool ReadBoolean()
{
return reader.ReadBoolean();
}
///
/// Reads a byte value from the current stream.
///
public byte ReadByte()
{
return reader.ReadByte();
}
///
/// Reads an array of bytes from the current stream.
///
/// The number of bytes to read.
public byte[] ReadBytes(int count)
{
return reader.ReadBytes(count);
}
///
/// Reads a character value from the current stream.
///
public char ReadChar()
{
return reader.ReadChar();
}
///
/// Reads a decimal value from the current stream.
///
public decimal ReadDecimal()
{
return reader.ReadDecimal();
}
///
/// Reads a double value from the current stream.
///
public double ReadDouble()
{
return reader.ReadDouble();
}
///
/// Reads a 16-bit integer value from the current stream.
///
public short ReadInt16()
{
return reader.ReadInt16();
}
///
/// Reads a 32-bit integer value from the current stream.
///
public int ReadInt32()
{
return reader.ReadInt32();
}
///
/// Reads a 64-bit integer value from the current stream.
///
///
public long ReadInt64()
{
return reader.ReadInt64();
}
///
/// Reads a float value from the current stream.
///
///
public float ReadSingle()
{
return reader.ReadSingle();
}
///
/// Reads a string value from the current stream.
///
public string ReadString()
{
return ReadString(ReadByte());
}
///
/// Reads a string value from the current stream.
///
/// The length of the stream.
public string ReadString(int length)
{
var ret = string.Empty;
var buffer = new byte[length];
var count = 0;
stream.Read(buffer, 0, buffer.Length);
if (buffer[length - 1] != 0)
{
count = length;
}
else
{
while (buffer[count] != 0 && count < length)
{
count++;
}
}
if (count > 0)
{
ret = Encoding.ASCII.GetString(buffer, 0, count);
}
return ret;
}
///
/// Reads an unsigned 16-bit integer value from the current stream.
///
public ushort ReadUInt16()
{
return reader.ReadUInt16();
}
///
/// Reads an unsigned 32-bit integer value from the current stream.
///
public uint ReadUInt32()
{
return reader.ReadUInt32();
}
///
/// Reads an unsigned 64-bit integer value from the current stream.
///
public ulong ReadUInt64()
{
return reader.ReadUInt64();
}
///
/// Sends the message to the connection.
///
/// The connection to send the message to.
public void Send(NetworkConnection connection)
{
connection?.SendData(ToArray(connection));
Destroy(this);
}
public void Send(Character character)
{
Send(character?.Client);
}
public void Broadcast(GameObject from, params NetworkConnection[] except)
{
if (from == null)
{
return;
}
if (except == null)
{
except = new NetworkConnection[0];
}
Character character;
if ((character = from as Character) != null && !except.Contains(character.Client))
{
Send(character);
}
for (var upperBound = from.VisibleObjects.GetUpperBound(); upperBound >= 0; --upperBound)
{
var visibleCharacter = from.VisibleCharacters[upperBound];
if (!except.Contains(visibleCharacter.Client))
{
Send(visibleCharacter);
}
}
}
///
/// Returns a byte array representing the message.
///
///
public byte[] ToArray(NetworkConnection connection = null)
{
byte[] ret;
var buffer = stream.ToArray();
if (connection != null)
{
if (connection.Type != NetworkConnectionType.NCT_CLIENT && connection.IsEstablished)
{
connection.DecryptBuffer(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}";
}
///
/// Writes a boolean value to the current stream.
///
/// The value to write.
public void Write(bool value)
{
writer.Write(value);
}
///
/// Writes a byte value to the current stream.
///
/// The value to write.
public void Write(byte value)
{
writer.Write(value);
}
///
/// Writes a signed byte value to the current stream.
///
/// The value to write.
public void Write(sbyte value)
{
writer.Write(value);
}
///
/// Writes a byte array to the current stream.
///
/// The value to write.
public void Write(byte[] value)
{
writer.Write(value);
}
///
/// Writes a 16-bit integer value to the current stream.
///
/// The value to write.
public void Write(short value)
{
writer.Write(value);
}
///
/// Writes a 32-bit integer value to the current stream.
///
/// The value to write.
public void Write(int value)
{
writer.Write(value);
}
///
/// Writes a 64-bit integer value to the current stream.
///
/// The value to write.
public void Write(long value)
{
writer.Write(value);
}
///
/// Writes an unsigned 16-bit integer value to the current stream.
///
/// The value to write.
public void Write(ushort value)
{
writer.Write(value);
}
///
/// Writes an unsigned 32-bit integer value to the current stream.
///
/// The value to write.
public void Write(uint value)
{
writer.Write(value);
}
///
/// Writes an unsigned 64-bit integer value to the current stream.
///
/// The value to write.
public void Write(ulong value)
{
writer.Write(value);
}
///
/// Writes a double value to the current stream.
///
/// The value to write.
public void Write(double value)
{
writer.Write(value);
}
///
/// Writes a decimal value to the current stream.
///
/// The value to write.
public void Write(decimal value)
{
writer.Write(value);
}
///
/// Writes a float value to the current stream.
///
/// The value to write.
public void Write(float value)
{
writer.Write(value);
}
///
/// Writes a string value to the current stream.
///
/// The value to write.
public void Write(string value)
{
Write(value, value.Length);
}
///
/// Writes a string value to the current stream.
///
/// The value to write.
/// The length of the string.
public void Write(string value, int length)
{
var buffer = Encoding.ASCII.GetBytes(value);
Write(buffer);
for (var i = 0; i < length - buffer.Length; i++)
{
Write((byte)0);
}
}
///
/// Destroys the instance.
///
protected override void Destroy()
{
// Reader and writer are not always initialized, so we need to check
// for null before attempting to close them.
reader?.Close();
writer?.Close();
stream.Close();
}
}
}