// Copyright 2018 RED Software, LLC. All Rights Reserved.
using System;
using System.IO;
using System.Text;
namespace IgniteEngine.Networking
{
///
/// 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);
}
}
///
/// 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);
}
///
/// 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();
}
}
}