// Copyright © 2017-2018 Atomic Software, LLC. All Rights Reserved. // See LICENSE.md for full license information. using Atom.Core.Diagnostics; using Atom.Core.Extensions; using Atom.Core.Game.GameObjects.Characters; using Atom.Core.Game.GameObjects.Items; using Atom.Core.Serialization; using System; using System.IO; using System.Text; namespace Atom.Core.Networking.Messages { public class NetworkMessage : IDisposable { public const int MaxMessageSize = ushort.MaxValue; public MessageProtocol Protocol { get; set; } public ushort Department { get; set; } public ushort Command { get; set; } public Client Client { get; set; } private readonly MemoryStream Stream; private readonly BinaryReader Reader; private readonly BinaryWriter Writer; public NetworkMessage(byte[] bytes) { Stream = new MemoryStream(bytes); Reader = new BinaryReader(Stream); Protocol = (MessageProtocol) ReadUInt16(); Department = (ushort) ((ushort) Protocol >> 10); Command = (ushort) ((ushort) Protocol & 1023); } public NetworkMessage(MessageProtocol protocol) : this((ushort) protocol) { } public NetworkMessage(ushort protocol) { Stream = new MemoryStream(); Writer = new BinaryWriter(Stream); Protocol = (MessageProtocol) protocol; Department = (ushort) (protocol >> 10); Command = (ushort) (protocol & 1023); Write(protocol); } public void Send(Client client) { client.SendBytes(ToArray()); Log.Info($"Sent: {this}"); Dispose(); } public void SendLocal(Client client) { client.SendBytes(ToArray(client.EncryptionTable)); Log.Info($"Sent: {this}"); Dispose(); } public bool ReadBoolean() => Reader.ReadBoolean(); public byte ReadByte() => Reader.ReadByte(); public byte[] ReadBytes(int count) => Reader.ReadBytes(count); public byte[] ReadBytesAndSize() { var BufferLength = Reader.ReadByte(); return Reader.ReadBytes(BufferLength); } public char ReadChar() => Reader.ReadChar(); public decimal ReadDecimal() => Reader.ReadDecimal(); public double ReadDouble() => Reader.ReadDouble(); public short ReadInt16() => Reader.ReadInt16(); public int ReadInt32() => Reader.ReadInt32(); public long ReadInt64() => Reader.ReadInt64(); public sbyte ReadSByte() => Reader.ReadSByte(); public float ReadSingle() => Reader.ReadSingle(); public string ReadString() { var length = ReadByte(); return ReadString(length); } public string ReadString(int length) { var ReturnValue = 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) { ReturnValue = Encoding.ASCII.GetString(Buffer, 0, Count); } return ReturnValue; } public ushort ReadUInt16() => Reader.ReadUInt16(); public uint ReadUInt32() => Reader.ReadUInt32(); public ulong ReadUInt64() => Reader.ReadUInt64(); public void Write(char value) => Writer?.Write(value); public void Write(byte value) => Writer?.Write(value); public void Write(sbyte value) => Writer?.Write(value); public void Write(ushort value) => Writer?.Write(value); public void Write(short value) => Writer?.Write(value); public void Write(uint value) => Writer?.Write(value); public void Write(int value) => Writer?.Write(value); public void Write(ulong value) => Writer?.Write(value); public void Write(long value) => Writer?.Write(value); public void Write(float value) => Writer?.Write(value); public void Write(double value) => Writer?.Write(value); public void Write(decimal value) => Writer?.Write(value); public void Write(bool value) => Writer?.Write(value); public void Write(byte[] value) => Writer?.Write(value); public void Write(string value) => Writer?.Write(Encoding.ASCII.GetBytes(value)); public void Write(string value, int length) { var StringBytes = Encoding.ASCII.GetBytes(value); Write(StringBytes); for (var i = 0; i < length - StringBytes.Length; i++) { Write((byte)0); } } public void Write(string value, bool writeLength) { if (!writeLength) { Write(value); return; } Write((byte)value.Length); Write(Encoding.ASCII.GetBytes(value)); } public void WriteTerminate(string value) { value += char.MinValue; Write((byte)value.Length); Write(Encoding.ASCII.GetBytes(value)); } // CUSTOM WRITE METHODS public void WriteLogin(SimpleCharacter character) { Write(character.CharNo); Write(character.Name, 20); Write((ushort) character.Level); Write(character.Slot); Write(character.MapIndx, 12); WriteDeleteInfo(character); WriteShape(character); WriteEquipment(character); Write(character.KQHandle); Write(character.KQMapIndx, 12); Write(character.KQPosition.X); Write(character.KQPosition.Y); Write(character.KQMapIndx != "" ? character.KQDate.Shift() : 0); WriteNameChangeData(character); Write((int) character.TutorialState); Write(character.TutorialStep); Write((byte) 0x00); } public void WriteDeleteInfo(SimpleCharacter character) { if (!character.IsDeleted) { Fill(5, 0); return; } Write((byte)character.DeletedDate.Year); Write((byte)character.DeletedDate.Month); Write((byte)character.DeletedDate.Day); Write((byte)character.DeletedDate.Hour); Write((byte)character.DeletedDate.Minute); } public void WriteShape(SimpleCharacter character) { Write(Convert.ToByte(0x01 | (byte)character.Class << 2 | (byte)character.Gender << 7)); Write(character.Hair); Write(character.HairColor); Write(character.Face); } public void WriteEquipment(SimpleCharacter character) { Write(character.GetEquippedItem(ItemSlot.HEAD)); Write(character.GetEquippedItem(ItemSlot.MOUTH)); Write(character.GetEquippedItem(ItemSlot.RIGHTHAND)); Write(character.GetEquippedItem(ItemSlot.BODY)); Write(character.GetEquippedItem(ItemSlot.LEFTHAND)); Write(character.GetEquippedItem(ItemSlot.PANT)); Write(character.GetEquippedItem(ItemSlot.BOOT)); Write(character.GetEquippedItem(ItemSlot.ACCBOOT)); Write(character.GetEquippedItem(ItemSlot.ACCPANT)); Write(character.GetEquippedItem(ItemSlot.ACCBODY)); Write(character.GetEquippedItem(ItemSlot.ACCHEAD)); Write(character.GetEquippedItem(ItemSlot.MINIMON_R)); Write(character.GetEquippedItem(ItemSlot.EYE)); Write(character.GetEquippedItem(ItemSlot.ACCLEFTHAND)); Write(character.GetEquippedItem(ItemSlot.ACCRIGHTHAND)); Write(character.GetEquippedItem(ItemSlot.ACCBACK)); Write(character.GetEquippedItem(ItemSlot.COSEFF)); Write(character.GetEquippedItem(ItemSlot.ACCHIP)); Write(character.GetEquippedItem(ItemSlot.MINIMON)); Write(character.GetEquippedItem(ItemSlot.ACCSHIELD)); Write((byte) 0xA0); Write((byte) 0x00); Write((byte) 0xF0); } public void WriteNameChangeData(SimpleCharacter character) { Write(character.ShouldChangeName()); if (!character.ShouldChangeName()) { Fill(4, 0); return; } Write(true); Write(0); } public void Write(ISerializable value, SerializeType type = SerializeType.Default) => value.Serialize(this, type); public void Fill(int length, byte value) { for (var i = 0; i < length; i++) { Write(value); } } public override string ToString() => $"Protocol=0x{Protocol:X} ({Protocol}), Length={Stream.Length - 2}"; public byte[] ToArray(EncryptionTable encryption = null) { byte[] ReturnValue; var MessageBytes = Stream.ToArray(); encryption?.CryptBytes(MessageBytes , 0, MessageBytes .Length); if (MessageBytes .Length <= 0xff) { ReturnValue = new byte[MessageBytes .Length + 1]; Buffer.BlockCopy(MessageBytes , 0, ReturnValue, 1, MessageBytes .Length); ReturnValue[0] = (byte)MessageBytes .Length; } else { ReturnValue = new byte[MessageBytes .Length + 3]; Buffer.BlockCopy(MessageBytes , 0, ReturnValue, 3, MessageBytes .Length); Buffer.BlockCopy(BitConverter.GetBytes((ushort)MessageBytes .Length), 0, ReturnValue, 1, 2); } return ReturnValue; } public void Dispose() { if (Stream.CanRead || Stream.CanWrite) { Stream.Close(); } Writer?.Close(); Reader?.Close(); Stream?.Dispose(); Writer?.Dispose(); Reader?.Dispose(); } } }