From 235a5adae3d86f542096d70fa038d4b3f9743d47 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Wed, 14 Oct 2015 23:48:49 -0400 Subject: [PATCH 1/5] Fixed bugs on how subpackets handled. Reorganized subpackets and split header with game header. --- FFXIVClassic Map Server/PacketProcessor.cs | 446 +++++++++--------- FFXIVClassic Map Server/packets/SubPacket.cs | 71 ++- .../packets/send/login/InitPacket.cs | 2 +- 3 files changed, 286 insertions(+), 233 deletions(-) diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index e5cb10dd..1c71ebe9 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -89,268 +89,276 @@ namespace FFXIVClassic_Lobby_Server if (packet.header.isEncrypted == 0x01) BasePacket.decryptPacket(client.blowfish, ref packet); + packet.debugPrintPacket(); List subPackets = packet.getSubpackets(); foreach (SubPacket subpacket in subPackets) { - //Console.WriteLine(client.getAddress()); - switch (subpacket.header.opcode) - { - //Initial - case 0x0000: - BasePacket init = InitPacket.buildPacket(0, Utils.UnixTimeStampUTC()); - BasePacket reply2 = new BasePacket("./packets/login/login2.bin"); - - //Already Handshaked - if (client.owner != 0) - { - using (MemoryStream mem = new MemoryStream(reply2.data)) - { - using (BinaryWriter binReader = new BinaryWriter(mem)) - { - binReader.BaseStream.Seek(0x10, SeekOrigin.Begin); - binReader.Write(player.actorID); - } - } - - client.queuePacket(init); - client.queuePacket(reply2); - break; - } - - uint actorID = 0; - using (MemoryStream mem = new MemoryStream(packet.data)) - { - using (BinaryReader binReader = new BinaryReader(mem)) - { - try - { - byte[] readIn = new byte[12]; - binReader.BaseStream.Seek(0x14, SeekOrigin.Begin); - binReader.Read(readIn, 0, 12); - actorID = UInt32.Parse(Encoding.ASCII.GetString(readIn)); - } - catch (Exception) - {} - } - } - - if (actorID == 0) - break; - - //Second connection - if (mPlayers.ContainsKey(actorID)) - player = mPlayers[actorID]; + if (subpacket.header.type == 0x01) + { + BasePacket init = InitPacket.buildPacket(0, Utils.UnixTimeStampUTC()); + BasePacket reply2 = new BasePacket("./packets/login/login2.bin"); + //Already Handshaked + if (client.owner != 0) + { using (MemoryStream mem = new MemoryStream(reply2.data)) { using (BinaryWriter binReader = new BinaryWriter(mem)) { binReader.BaseStream.Seek(0x10, SeekOrigin.Begin); - binReader.Write(actorID); + binReader.Write(player.actorID); } } - Log.debug(String.Format("Got actorID {0} for conn {1}.", actorID, client.getAddress())); - - if (player == null) - { - player = new Player(actorID); - mPlayers[actorID] = player; - client.owner = actorID; - client.connType = 0; - player.setConnection1(client); - } - else - { - client.owner = actorID; - client.connType = 1; - player.setConnection2(client); - } - - //Get Character info - //Create player actor client.queuePacket(init); client.queuePacket(reply2); break; - //Ping - case 0x0001: - //subpacket.debugPrintSubPacket(); - PingPacket pingPacket = new PingPacket(subpacket.data); - client.queuePacket(BasePacket.createPacket(PongPacket.buildPacket(player.actorID, pingPacket.time), true, false)); + } + + uint actorID = 0; + using (MemoryStream mem = new MemoryStream(packet.data)) + { + using (BinaryReader binReader = new BinaryReader(mem)) + { + try + { + byte[] readIn = new byte[12]; + binReader.BaseStream.Seek(0x14, SeekOrigin.Begin); + binReader.Read(readIn, 0, 12); + actorID = UInt32.Parse(Encoding.ASCII.GetString(readIn)); + } + catch (Exception) + { } + } + } + + if (actorID == 0) break; - //Unknown - case 0x0002: - subpacket.debugPrintSubPacket(); - - BasePacket reply5 = new BasePacket("./packets/login/login5.bin"); - BasePacket reply6 = new BasePacket("./packets/login/login6_data.bin"); - BasePacket reply7 = new BasePacket("./packets/login/login7_data.bin"); - BasePacket reply8 = new BasePacket("./packets/login/login8_data.bin"); - BasePacket reply9 = new BasePacket("./packets/login/login9_zonesetup.bin"); - BasePacket reply10 = new BasePacket("./packets/login/login10.bin"); - BasePacket reply11 = new BasePacket("./packets/login/login11.bin"); - BasePacket reply12 = new BasePacket("./packets/login/login12.bin"); - BasePacket keyitems = new BasePacket("./packets/login/keyitems.bin"); - BasePacket currancy = new BasePacket("./packets/login/currancy.bin"); + //Second connection + if (mPlayers.ContainsKey(actorID)) + player = mPlayers[actorID]; -#region replaceid - currancy.replaceActorID(player.actorID); - keyitems.replaceActorID(player.actorID); + using (MemoryStream mem = new MemoryStream(reply2.data)) + { + using (BinaryWriter binReader = new BinaryWriter(mem)) + { + binReader.BaseStream.Seek(0x10, SeekOrigin.Begin); + binReader.Write(actorID); + } + } - reply5.replaceActorID(player.actorID); - reply6.replaceActorID(player.actorID); - reply7.replaceActorID(player.actorID); - reply8.replaceActorID(player.actorID); - reply9.replaceActorID(player.actorID); - reply10.replaceActorID(player.actorID); - reply11.replaceActorID(player.actorID); - reply12.replaceActorID(player.actorID); -#endregion + Log.debug(String.Format("Got actorID {0} for conn {1}.", actorID, client.getAddress())); - client.queuePacket(BasePacket.createPacket(SetMapPacket.buildPacket(player.actorID, 0xD1), true, false)); - client.queuePacket(BasePacket.createPacket(SetMusicPacket.buildPacket(player.actorID, 0x3D, 0x01), true, false)); - client.queuePacket(BasePacket.createPacket(_0x2Packet.buildPacket(player.actorID), true, false)); + if (player == null) + { + player = new Player(actorID); + mPlayers[actorID] = player; + client.owner = actorID; + client.connType = 0; + player.setConnection1(client); + } + else + { + client.owner = actorID; + client.connType = 1; + player.setConnection2(client); + } - client.queuePacket(reply5); + //Get Character info + //Create player actor + client.queuePacket(init); + client.queuePacket(reply2); + break; + } + else if (subpacket.header.type == 0x08) + { - client.queuePacket(BasePacket.createPacket(AddActorPacket.buildPacket(player.actorID, player.actorID, 0), true, false)); + } + else if (subpacket.header.type == 0x03) + { + switch (subpacket.gameMessage.opcode) + { + //Ping + case 0x0001: + //subpacket.debugPrintSubPacket(); + PingPacket pingPacket = new PingPacket(subpacket.data); + client.queuePacket(BasePacket.createPacket(PongPacket.buildPacket(player.actorID, pingPacket.time), true, false)); + break; + //Unknown + case 0x0002: + subpacket.debugPrintSubPacket(); - client.queuePacket(reply6); + BasePacket reply5 = new BasePacket("./packets/login/login5.bin"); + BasePacket reply6 = new BasePacket("./packets/login/login6_data.bin"); + BasePacket reply7 = new BasePacket("./packets/login/login7_data.bin"); + BasePacket reply8 = new BasePacket("./packets/login/login8_data.bin"); + BasePacket reply9 = new BasePacket("./packets/login/login9_zonesetup.bin"); + BasePacket reply10 = new BasePacket("./packets/login/login10.bin"); + BasePacket reply11 = new BasePacket("./packets/login/login11.bin"); + BasePacket reply12 = new BasePacket("./packets/login/login12.bin"); - client.queuePacket(BasePacket.createPacket(SetActorPositionPacket.buildPacket(player.actorID, player.actorID, SetActorPositionPacket.INNPOS_X, SetActorPositionPacket.INNPOS_Y, SetActorPositionPacket.INNPOS_Z, SetActorPositionPacket.INNPOS_ROT, SetActorPositionPacket.SPAWNTYPE_PLAYERWAKE), true, false)); - client.queuePacket(BasePacket.createPacket(player.getActor().createSpeedPacket(player.actorID), true, false)); - client.queuePacket(BasePacket.createPacket(player.getActor().createStatePacket(player.actorID), true, false)); + BasePacket keyitems = new BasePacket("./packets/login/keyitems.bin"); + BasePacket currancy = new BasePacket("./packets/login/currancy.bin"); - client.queuePacket(BasePacket.createPacket(player.getActor().createNamePacket(player.actorID), true, false)); - client.queuePacket(BasePacket.createPacket(player.getActor().createAppearancePacket(player.actorID), true, false)); + #region replaceid + currancy.replaceActorID(player.actorID); + keyitems.replaceActorID(player.actorID); - ////////ITEMS//////// - client.queuePacket(BasePacket.createPacket(InventoryBeginChangePacket.buildPacket(player.actorID), true, false)); + reply5.replaceActorID(player.actorID); + reply6.replaceActorID(player.actorID); + reply7.replaceActorID(player.actorID); + reply8.replaceActorID(player.actorID); + reply9.replaceActorID(player.actorID); + reply10.replaceActorID(player.actorID); + reply11.replaceActorID(player.actorID); + reply12.replaceActorID(player.actorID); + #endregion -#region itemsetup + client.queuePacket(BasePacket.createPacket(SetMapPacket.buildPacket(player.actorID, 0xD1), true, false)); + client.queuePacket(BasePacket.createPacket(_0x2Packet.buildPacket(player.actorID), true, false)); + client.queuePacket(BasePacket.createPacket(SetMusicPacket.buildPacket(player.actorID, 0x3D, 0x01), true, false)); - //TEST - List items = new List(); - items.Add(new Item(1337, 8030920, 5)); //Leather Jacket - items.Add(new Item(1338, 8013626, 1)); //Chocobo Mask - items.Add(new Item(1339, 5030402, 2)); //Thyrus - items.Add(new Item(1340, 8013635, 3)); //Dalamud Horn - items.Add(new Item(1341, 10100132, 4)); //Savage Might 4 - items.Add(new Item(1342, 8032407, 6)); //Green Summer Halter (Female) - items.Add(new Item(1343, 8051307, 7)); //Green Summer Tanga (Female) - items.Add(new Item(1344, 8050766, 8)); //Flame Private's Saroul + client.queuePacket(reply5); - int count = 0; - - items[2].isHighQuality = true; - items[0].durability = 9999; - items[0].spiritbind = 10000; - items[0].materia1 = 6; - items[0].materia2 = 7; - items[0].materia3 = 8; - items[0].materia4 = 9; - items[0].materia5 = 10; - items[1].durability = 9999; - items[2].durability = 0xFFFFFFF; - items[3].durability = 9999; - items[4].quantity = 99; + client.queuePacket(BasePacket.createPacket(AddActorPacket.buildPacket(player.actorID, player.actorID, 0), true, false)); - //Reused - SubPacket endInventory = InventorySetEndPacket.buildPacket(player.actorID); - SubPacket beginInventory = InventorySetBeginPacket.buildPacket(player.actorID, 200, 00); - SubPacket setInventory = InventoryItemPacket.buildPacket(player.actorID, items, ref count); + client.queuePacket(reply6); - List setinvPackets = new List(); - setinvPackets.Add(beginInventory); - setinvPackets.Add(setInventory); - setinvPackets.Add(endInventory); -#endregion + client.queuePacket(BasePacket.createPacket(SetActorPositionPacket.buildPacket(player.actorID, player.actorID, SetActorPositionPacket.INNPOS_X, SetActorPositionPacket.INNPOS_Y, SetActorPositionPacket.INNPOS_Z, SetActorPositionPacket.INNPOS_ROT, SetActorPositionPacket.SPAWNTYPE_PLAYERWAKE), true, false)); + client.queuePacket(BasePacket.createPacket(player.getActor().createSpeedPacket(player.actorID), true, false)); + client.queuePacket(BasePacket.createPacket(player.getActor().createStatePacket(player.actorID), true, false)); - client.queuePacket(BasePacket.createPacket(setinvPackets, true, false)); - - //client.queuePacket(currancy); - //client.queuePacket(keyitems); + client.queuePacket(BasePacket.createPacket(player.getActor().createNamePacket(player.actorID), true, false)); + client.queuePacket(BasePacket.createPacket(player.getActor().createAppearancePacket(player.actorID), true, false)); -#region equipsetup - EquipmentSetupPacket initialEqupmentPacket = new EquipmentSetupPacket(); - initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_BODY, 5); - initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_HEAD, 3); - initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_UNDERSHIRT, 6); - initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_UNDERGARMENT, 7); - initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_MAINHAND, 2); - initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_LEGS, 8); -#endregion + ////////ITEMS//////// + client.queuePacket(BasePacket.createPacket(InventoryBeginChangePacket.buildPacket(player.actorID), true, false)); - //Equip Init - client.queuePacket(BasePacket.createPacket(InventorySetBeginPacket.buildPacket(player.actorID, 0x23, InventorySetBeginPacket.CODE_EQUIPMENT), true, false)); - client.queuePacket(BasePacket.createPacket(initialEqupmentPacket.buildPackets(player.actorID), true, false)); - client.queuePacket(BasePacket.createPacket(InventorySetEndPacket.buildPacket(player.actorID), true, false)); + #region itemsetup - client.queuePacket(BasePacket.createPacket(InventoryEndChangePacket.buildPacket(player.actorID), true, false)); - ////////ITEMS//////// + //TEST + List items = new List(); + items.Add(new Item(1337, 8030920, 5)); //Leather Jacket + items.Add(new Item(1338, 8013626, 1)); //Chocobo Mask + items.Add(new Item(1339, 5030402, 2)); //Thyrus + items.Add(new Item(1340, 8013635, 3)); //Dalamud Horn + items.Add(new Item(1341, 10100132, 4)); //Savage Might 4 + items.Add(new Item(1342, 8032407, 6)); //Green Summer Halter (Female) + items.Add(new Item(1343, 8051307, 7)); //Green Summer Tanga (Female) + items.Add(new Item(1344, 8050766, 8)); //Flame Private's Saroul - //The rest of hardcode - client.queuePacket(reply7); - client.queuePacket(reply8); - client.queuePacket(reply9); - client.queuePacket(reply10); - //client.queuePacket(reply11); - client.queuePacket(reply12); + int count = 0; - inn.addActorToZone(player.getActor()); + items[2].isHighQuality = true; + items[0].durability = 9999; + items[0].spiritbind = 10000; + items[0].materia1 = 6; + items[0].materia2 = 7; + items[0].materia3 = 8; + items[0].materia4 = 9; + items[0].materia5 = 10; + items[1].durability = 9999; + items[2].durability = 0xFFFFFFF; + items[3].durability = 9999; + items[4].quantity = 99; - break; - //Chat Received - case 0x0003: - subpacket.debugPrintSubPacket(); - break; - //Update Position - case 0x00CA: + //Reused + SubPacket endInventory = InventorySetEndPacket.buildPacket(player.actorID); + SubPacket beginInventory = InventorySetBeginPacket.buildPacket(player.actorID, 200, 00); + SubPacket setInventory = InventoryItemPacket.buildPacket(player.actorID, items, ref count); + + List setinvPackets = new List(); + setinvPackets.Add(beginInventory); + setinvPackets.Add(setInventory); + setinvPackets.Add(endInventory); + #endregion + + client.queuePacket(BasePacket.createPacket(setinvPackets, true, false)); + + //client.queuePacket(currancy); + //client.queuePacket(keyitems); + + #region equipsetup + EquipmentSetupPacket initialEqupmentPacket = new EquipmentSetupPacket(); + initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_BODY, 5); + initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_HEAD, 3); + initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_UNDERSHIRT, 6); + initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_UNDERGARMENT, 7); + initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_MAINHAND, 2); + initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_LEGS, 8); + #endregion + + //Equip Init + client.queuePacket(BasePacket.createPacket(InventorySetBeginPacket.buildPacket(player.actorID, 0x23, InventorySetBeginPacket.CODE_EQUIPMENT), true, false)); + client.queuePacket(BasePacket.createPacket(initialEqupmentPacket.buildPackets(player.actorID), true, false)); + client.queuePacket(BasePacket.createPacket(InventorySetEndPacket.buildPacket(player.actorID), true, false)); + + client.queuePacket(BasePacket.createPacket(InventoryEndChangePacket.buildPacket(player.actorID), true, false)); + ////////ITEMS//////// + + //The rest of hardcode + client.queuePacket(reply7); + client.queuePacket(reply8); + client.queuePacket(reply9); + client.queuePacket(reply10); + //client.queuePacket(reply11); + client.queuePacket(reply12); + + inn.addActorToZone(player.getActor()); + + break; + //Chat Received + case 0x0003: + subpacket.debugPrintSubPacket(); + break; //Update Position - UpdatePlayerPositionPacket posUpdate = new UpdatePlayerPositionPacket(subpacket.data); - player.updatePlayerActorPosition(posUpdate.x, posUpdate.y, posUpdate.z, posUpdate.rot, posUpdate.moveState); + case 0x00CA: + //Update Position + UpdatePlayerPositionPacket posUpdate = new UpdatePlayerPositionPacket(subpacket.data); + player.updatePlayerActorPosition(posUpdate.x, posUpdate.y, posUpdate.z, posUpdate.rot, posUpdate.moveState); - //Update Instance - List instanceUpdatePackets = player.updateInstance(inn.getActorsAroundActor(player.getActor(), 50)); - foreach (BasePacket bp in instanceUpdatePackets) - client.queuePacket(bp); + //Update Instance + List instanceUpdatePackets = player.updateInstance(inn.getActorsAroundActor(player.getActor(), 50)); + foreach (BasePacket bp in instanceUpdatePackets) + client.queuePacket(bp); - break; - //Set Target - case 0x00CD: - subpacket.debugPrintSubPacket(); + break; + //Set Target + case 0x00CD: + subpacket.debugPrintSubPacket(); - SetTargetPacket setTarget = new SetTargetPacket(subpacket.data); - player.getActor().currentTarget = setTarget.actorID; - client.queuePacket(BasePacket.createPacket(SetActorTargetAnimatedPacket.buildPacket(player.actorID, player.actorID, setTarget.actorID), true, false)); - break; - //Lock Target - case 0x00CC: - LockTargetPacket lockTarget = new LockTargetPacket(subpacket.data); - player.getActor().currentLockedTarget = lockTarget.actorID; - break; - //Start Script - case 0x012D: - subpacket.debugPrintSubPacket(); - //StartScriptPacket startScript = new StartScriptPacket(subpacket.data); - //client.queuePacket(new BasePacket("./packets/script/bed.bin")); - client.queuePacket(BasePacket.createPacket(ActorDoEmotePacket.buildPacket(player.actorID, player.getActor().currentTarget, 137), true, false)); - break; - //Script Result - case 0x012E: - subpacket.debugPrintSubPacket(); - processScriptResult(subpacket); - break; - case 0x012F: - subpacket.debugPrintSubPacket(); - - break; - default: - Log.debug(String.Format("Unknown command 0x{0:X} received.", subpacket.header.opcode)); - subpacket.debugPrintSubPacket(); - break; + SetTargetPacket setTarget = new SetTargetPacket(subpacket.data); + player.getActor().currentTarget = setTarget.actorID; + client.queuePacket(BasePacket.createPacket(SetActorTargetAnimatedPacket.buildPacket(player.actorID, player.actorID, setTarget.actorID), true, false)); + break; + //Lock Target + case 0x00CC: + LockTargetPacket lockTarget = new LockTargetPacket(subpacket.data); + player.getActor().currentLockedTarget = lockTarget.actorID; + break; + //Start Script + case 0x012D: + subpacket.debugPrintSubPacket(); + //StartScriptPacket startScript = new StartScriptPacket(subpacket.data); + //client.queuePacket(new BasePacket("./packets/script/bed.bin")); + client.queuePacket(BasePacket.createPacket(ActorDoEmotePacket.buildPacket(player.actorID, player.getActor().currentTarget, 137), true, false)); + break; + //Script Result + case 0x012E: + subpacket.debugPrintSubPacket(); + processScriptResult(subpacket); + break; + case 0x012F: + subpacket.debugPrintSubPacket(); + + break; + default: + Log.debug(String.Format("Unknown command 0x{0:X} received.", subpacket.gameMessage.opcode)); + subpacket.debugPrintSubPacket(); + break; + } } } } diff --git a/FFXIVClassic Map Server/packets/SubPacket.cs b/FFXIVClassic Map Server/packets/SubPacket.cs index b443ce91..40ff4094 100644 --- a/FFXIVClassic Map Server/packets/SubPacket.cs +++ b/FFXIVClassic Map Server/packets/SubPacket.cs @@ -13,10 +13,15 @@ namespace FFXIVClassic_Lobby_Server.packets public struct SubPacketHeader { public ushort subpacketSize; - public ushort unknown0; //Always 0x03 + public ushort type; public uint sourceId; public uint targetId; public uint unknown1; + } + + [StructLayout(LayoutKind.Sequential)] + public struct GameMessageHeader + { public ushort unknown4; //Always 0x14 public ushort opcode; public uint unknown5; @@ -26,9 +31,11 @@ namespace FFXIVClassic_Lobby_Server.packets public class SubPacket { - public const int SUBPACKET_SIZE = 0x20; + public const int SUBPACKET_SIZE = 0x10; + public const int GAMEMESSAGE_SIZE = 0x10; public SubPacketHeader header; + public GameMessageHeader gameMessage; public byte[] data; public unsafe SubPacket(byte[] bytes, ref int offset) @@ -41,11 +48,27 @@ namespace FFXIVClassic_Lobby_Server.packets header = (SubPacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader)); } + if (header.type == 0x3) + { + fixed (byte* pdata = &bytes[offset + SUBPACKET_SIZE]) + { + gameMessage = (GameMessageHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(GameMessageHeader)); + } + } + if (bytes.Length < offset + header.subpacketSize) throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data"); - data = new byte[header.subpacketSize - SUBPACKET_SIZE]; - Array.Copy(bytes, offset + SUBPACKET_SIZE, data, 0, data.Length); + if (header.type == 0x3) + { + data = new byte[header.subpacketSize - SUBPACKET_SIZE - GAMEMESSAGE_SIZE]; + Array.Copy(bytes, offset + SUBPACKET_SIZE + GAMEMESSAGE_SIZE, data, 0, data.Length); + } + else + { + data = new byte[header.subpacketSize - SUBPACKET_SIZE]; + Array.Copy(bytes, offset + SUBPACKET_SIZE, data, 0, data.Length); + } offset += header.subpacketSize; } @@ -53,21 +76,23 @@ namespace FFXIVClassic_Lobby_Server.packets public SubPacket(ushort opcode, uint sourceId, uint targetId, byte[] data) { this.header = new SubPacketHeader(); - header.opcode = opcode; + this.gameMessage = new GameMessageHeader(); + + gameMessage.opcode = opcode; header.sourceId = sourceId; header.targetId = targetId; - header.timestamp = Utils.UnixTimeStampUTC(); + gameMessage.timestamp = Utils.UnixTimeStampUTC(); - header.unknown0 = 0x03; + header.type = 0x03; header.unknown1 = 0x00; - header.unknown4 = 0x14; - header.unknown5 = 0x00; - header.unknown6 = 0x00; + gameMessage.unknown4 = 0x14; + gameMessage.unknown5 = 0x00; + gameMessage.unknown6 = 0x00; this.data = data; - header.subpacketSize = (ushort)(0x20 + data.Length); + header.subpacketSize = (ushort)(SUBPACKET_SIZE + GAMEMESSAGE_SIZE + data.Length); } public byte[] getHeaderBytes() @@ -82,11 +107,27 @@ namespace FFXIVClassic_Lobby_Server.packets return arr; } + public byte[] getGameMessageBytes() + { + int size = Marshal.SizeOf(gameMessage); + byte[] arr = new byte[size]; + + IntPtr ptr = Marshal.AllocHGlobal(size); + Marshal.StructureToPtr(gameMessage, ptr, true); + Marshal.Copy(ptr, arr, 0, size); + Marshal.FreeHGlobal(ptr); + return arr; + } + public byte[] getBytes() { byte[] outBytes = new byte[header.subpacketSize]; Array.Copy(getHeaderBytes(), 0, outBytes, 0, SUBPACKET_SIZE); - Array.Copy(data, 0, outBytes, SUBPACKET_SIZE, data.Length); + + if (header.type == 0x3) + Array.Copy(getGameMessageBytes(), 0, outBytes, SUBPACKET_SIZE, GAMEMESSAGE_SIZE); + + Array.Copy(data, 0, outBytes, SUBPACKET_SIZE + header.type == 0x3 ? GAMEMESSAGE_SIZE : 0, data.Length); return outBytes; } @@ -94,8 +135,12 @@ namespace FFXIVClassic_Lobby_Server.packets { #if DEBUG Console.BackgroundColor = ConsoleColor.DarkRed; - Console.WriteLine("Size: 0x{0:X}, Opcode: 0x{1:X}", header.subpacketSize, header.opcode); + Console.WriteLine("Size: 0x{0:X}", header.subpacketSize); + if (header.type == 0x03) + Console.WriteLine("Opcode: 0x{0:X}", gameMessage.opcode); Console.WriteLine("{0}", Utils.ByteArrayToHex(getHeaderBytes())); + if (header.type == 0x03) + Console.WriteLine("{0}", Utils.ByteArrayToHex(getGameMessageBytes())); Console.BackgroundColor = ConsoleColor.DarkMagenta; Console.WriteLine("{0}", Utils.ByteArrayToHex(data)); Console.BackgroundColor = ConsoleColor.Black; diff --git a/FFXIVClassic Map Server/packets/send/login/InitPacket.cs b/FFXIVClassic Map Server/packets/send/login/InitPacket.cs index 849224da..2ffa98ab 100644 --- a/FFXIVClassic Map Server/packets/send/login/InitPacket.cs +++ b/FFXIVClassic Map Server/packets/send/login/InitPacket.cs @@ -12,7 +12,7 @@ namespace FFXIVClassic_Map_Server.packets.send.login { public static BasePacket buildPacket(uint unknown, uint time) { - byte[] data = new byte[18]; + byte[] data = new byte[0x18]; using (MemoryStream mem = new MemoryStream(data)) { From c767c626a37f34748fb1f5258eff3beaa06cb119 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Thu, 15 Oct 2015 16:55:01 -0400 Subject: [PATCH 2/5] Changed how packets are received. No longer have incoming stream and just parse as they come in onAccept. --- FFXIVClassic Map Server/PacketProcessor.cs | 53 +------------ FFXIVClassic Map Server/Server.cs | 88 ++++++++++++++-------- 2 files changed, 58 insertions(+), 83 deletions(-) diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index 1c71ebe9..e0cb7421 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -25,7 +25,6 @@ namespace FFXIVClassic_Lobby_Server { Dictionary mPlayers; List mConnections; - Boolean isAlive = true; Zone inn = new Zone(); @@ -33,54 +32,9 @@ namespace FFXIVClassic_Lobby_Server { mPlayers = playerList; mConnections = connectionList; - } + } - public void update() - { - Console.WriteLine("Packet processing thread has started"); - while (isAlive) - { - lock (mConnections) - { - foreach (ClientConnection conn in mConnections) - { - //Receive conn1 packets - while (true) - { - if (conn == null || conn.incomingStream.Size < BasePacket.BASEPACKET_SIZE) - break; - - try { - if (conn.incomingStream.Size < BasePacket.BASEPACKET_SIZE) - break; - BasePacketHeader header = BasePacket.getHeader(conn.incomingStream.Peek(BasePacket.BASEPACKET_SIZE)); - - if (conn.incomingStream.Size < header.packetSize) - break; - - BasePacket packet = new BasePacket(conn.incomingStream.Get(header.packetSize)); - processPacket(conn, packet); - - } - catch(OverflowException) - { break; } - } - - //Send packets - if (conn != null) - conn.flushQueuedSendPackets(); - } - } - - //Don't waste CPU if isn't needed - if (mConnections.Count == 0) - Thread.Sleep(2000); - else - Thread.Sleep(100); - } - } - - private void processPacket(ClientConnection client, BasePacket packet) + public void processPacket(ClientConnection client, BasePacket packet) { Player player = null; if (client.owner != 0 && mPlayers.ContainsKey(client.owner)) @@ -149,8 +103,6 @@ namespace FFXIVClassic_Lobby_Server } } - Log.debug(String.Format("Got actorID {0} for conn {1}.", actorID, client.getAddress())); - if (player == null) { player = new Player(actorID); @@ -158,6 +110,7 @@ namespace FFXIVClassic_Lobby_Server client.owner = actorID; client.connType = 0; player.setConnection1(client); + Log.debug(String.Format("Got actorID {0} for conn {1}.", actorID, client.getAddress())); } else { diff --git a/FFXIVClassic Map Server/Server.cs b/FFXIVClassic Map Server/Server.cs index 8c268bc6..009305d6 100644 --- a/FFXIVClassic Map Server/Server.cs +++ b/FFXIVClassic Map Server/Server.cs @@ -9,6 +9,7 @@ using System.Threading; using FFXIVClassic_Lobby_Server.common; using FFXIVClassic_Map_Server.dataobjects; using FFXIVClassic_Lobby_Server.packets; +using System.IO; namespace FFXIVClassic_Lobby_Server { @@ -61,9 +62,6 @@ namespace FFXIVClassic_Lobby_Server Console.WriteLine("{0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port); Console.ForegroundColor = ConsoleColor.Gray; - mProcessor = new PacketProcessor(mConnectedPlayerList, mConnectionList); - mProcessorThread = new Thread(new ThreadStart(mProcessor.update)); - mProcessorThread.Start(); //mGameThread = new Thread(new ThreadStart(mProcessor.update)); //mGameThread.Start(); return true; @@ -117,32 +115,10 @@ namespace FFXIVClassic_Lobby_Server } } - private Player findPlayerBySocket(Socket s) - { - lock (mConnectedPlayerList) - { - foreach (KeyValuePair p in mConnectedPlayerList) - { - if ((p.Value.getConnection1().socket.RemoteEndPoint as IPEndPoint).Address.Equals((s.RemoteEndPoint as IPEndPoint).Address)) - { - return p.Value; - } - - if ((p.Value.getConnection2().socket.RemoteEndPoint as IPEndPoint).Address.Equals((s.RemoteEndPoint as IPEndPoint).Address)) - { - return p.Value; - } - } - } - - return null; - } - - private Player findPlayerByClientConnection(ClientConnection conn) - { - throw new NotImplementedException(); - } - + /// + /// Receive Callback. Reads in incoming data, converting them to base packets. Base packets are sent to be parsed. If not enough data at the end to build a basepacket, move to the beginning and prepend. + /// + /// private void receiveCallback(IAsyncResult result) { ClientConnection conn = (ClientConnection)result.AsyncState; @@ -152,10 +128,32 @@ namespace FFXIVClassic_Lobby_Server int bytesRead = conn.socket.EndReceive(result); if (bytesRead > 0) { - conn.processIncoming(bytesRead); + int offset = 0; - //Queue the next receive - conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), conn); + //Build packets until can no longer or out of data + while(true) + { + BasePacket basePacket = buildPacket(ref offset, conn.buffer); + //If can't build packet, break, else process another + if (basePacket == null) + break; + else + mProcessor.processPacket(conn, basePacket); + } + + //Not all bytes consumed, transfer leftover to beginning + if (offset <= bytesRead) + Array.Copy(conn.buffer, offset, conn.buffer, 0, bytesRead - offset); + + //Build any queued subpackets into basepackets and send + conn.flushQueuedSendPackets(); + + if (offset <= bytesRead) + //Need offset since not all bytes consumed + conn.socket.BeginReceive(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset), SocketFlags.None, new AsyncCallback(receiveCallback), conn); + else + //All bytes consumed, full buffer available + conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), conn); } else { @@ -181,8 +179,32 @@ namespace FFXIVClassic_Lobby_Server } } - #endregion + /// + /// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null. + /// + /// Current offset in buffer. + /// Incoming buffer. + /// Returns either a BasePacket or null if not enough data. + public BasePacket buildPacket(ref int offset, byte[] buffer) + { + BasePacket newPacket = null; + //Too small to even get length + if (buffer.Length <= offset + 1) + return null; + + ushort packetSize = BitConverter.ToUInt16(buffer, offset); + + //Too small to whole packet + if (buffer.Length <= offset + packetSize) + return null; + + newPacket = new BasePacket(buffer, ref offset); + + return newPacket; + } + + #endregion public void sendPacket(string path, int conn) { From 7c22cece9392f9a42e317eaabdefd7f6d985d3e2 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Thu, 15 Oct 2015 17:04:04 -0400 Subject: [PATCH 3/5] Removed unused incoming stream code. --- FFXIVClassic Map Server/ClientConnection.cs | 12 +----------- .../FFXIVClassic Map Server.csproj | 1 + 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/FFXIVClassic Map Server/ClientConnection.cs b/FFXIVClassic Map Server/ClientConnection.cs index 70f86065..efb54fcf 100644 --- a/FFXIVClassic Map Server/ClientConnection.cs +++ b/FFXIVClassic Map Server/ClientConnection.cs @@ -20,22 +20,12 @@ namespace FFXIVClassic_Lobby_Server public Blowfish blowfish; public Socket socket; public byte[] buffer = new byte[0xffff]; - public CircularBuffer incomingStream = new CircularBuffer(1024); - private BlockingCollection sendPacketQueue = new BlockingCollection(100); + private BlockingCollection sendPacketQueue = new BlockingCollection(1000); //Instance Stuff public uint owner = 0; public uint connType = 0; - - public void processIncoming(int bytesIn) - { - if (bytesIn == 0) - return; - - incomingStream.Put(buffer, 0, bytesIn); - } - public void queuePacket(BasePacket packet) { sendPacketQueue.Add(packet); diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index dd3fc56e..388bdab2 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -93,6 +93,7 @@ + From 2e683892c80fe9bace22c8f9335fcb3ccbeb3477 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Thu, 15 Oct 2015 22:17:21 -0400 Subject: [PATCH 4/5] Connection working again. Fixed type 7 ping packet. --- .../FFXIVClassic Map Server.csproj | 1 - FFXIVClassic Map Server/PacketProcessor.cs | 13 +++++-------- FFXIVClassic Map Server/Server.cs | 18 ++++++++++++------ FFXIVClassic Map Server/packets/SubPacket.cs | 2 +- .../packets/send/login/InitPacket.cs | 4 ++-- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index 388bdab2..dd3fc56e 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -93,7 +93,6 @@ - diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index e0cb7421..219e2df1 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -43,14 +43,11 @@ namespace FFXIVClassic_Lobby_Server if (packet.header.isEncrypted == 0x01) BasePacket.decryptPacket(client.blowfish, ref packet); - packet.debugPrintPacket(); - List subPackets = packet.getSubpackets(); foreach (SubPacket subpacket in subPackets) { if (subpacket.header.type == 0x01) - { - BasePacket init = InitPacket.buildPacket(0, Utils.UnixTimeStampUTC()); + { BasePacket reply2 = new BasePacket("./packets/login/login2.bin"); //Already Handshaked @@ -65,7 +62,7 @@ namespace FFXIVClassic_Lobby_Server } } - client.queuePacket(init); + client.queuePacket(reply2); break; } @@ -121,13 +118,13 @@ namespace FFXIVClassic_Lobby_Server //Get Character info //Create player actor - client.queuePacket(init); client.queuePacket(reply2); break; } - else if (subpacket.header.type == 0x08) + else if (subpacket.header.type == 0x07) { - + BasePacket init = InitPacket.buildPacket(BitConverter.ToUInt32(packet.data, 0x10), Utils.UnixTimeStampUTC()); + client.queuePacket(init); } else if (subpacket.header.type == 0x03) { diff --git a/FFXIVClassic Map Server/Server.cs b/FFXIVClassic Map Server/Server.cs index 009305d6..d20bf7e5 100644 --- a/FFXIVClassic Map Server/Server.cs +++ b/FFXIVClassic Map Server/Server.cs @@ -62,6 +62,8 @@ namespace FFXIVClassic_Lobby_Server Console.WriteLine("{0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port); Console.ForegroundColor = ConsoleColor.Gray; + mProcessor = new PacketProcessor(mConnectedPlayerList, mConnectionList); + //mGameThread = new Thread(new ThreadStart(mProcessor.update)); //mGameThread.Start(); return true; @@ -69,6 +71,7 @@ namespace FFXIVClassic_Lobby_Server private void acceptCallback(IAsyncResult result) { + Log.conn("TEST."); ClientConnection conn = null; Socket socket = (System.Net.Sockets.Socket)result.AsyncState; @@ -133,7 +136,7 @@ namespace FFXIVClassic_Lobby_Server //Build packets until can no longer or out of data while(true) { - BasePacket basePacket = buildPacket(ref offset, conn.buffer); + BasePacket basePacket = buildPacket(ref offset, conn.buffer, bytesRead); //If can't build packet, break, else process another if (basePacket == null) break; @@ -142,13 +145,13 @@ namespace FFXIVClassic_Lobby_Server } //Not all bytes consumed, transfer leftover to beginning - if (offset <= bytesRead) + if (offset < bytesRead) Array.Copy(conn.buffer, offset, conn.buffer, 0, bytesRead - offset); //Build any queued subpackets into basepackets and send conn.flushQueuedSendPackets(); - if (offset <= bytesRead) + if (offset < bytesRead) //Need offset since not all bytes consumed conn.socket.BeginReceive(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset), SocketFlags.None, new AsyncCallback(receiveCallback), conn); else @@ -185,18 +188,21 @@ namespace FFXIVClassic_Lobby_Server /// Current offset in buffer. /// Incoming buffer. /// Returns either a BasePacket or null if not enough data. - public BasePacket buildPacket(ref int offset, byte[] buffer) + public BasePacket buildPacket(ref int offset, byte[] buffer, int bytesRead) { BasePacket newPacket = null; //Too small to even get length - if (buffer.Length <= offset + 1) + if (bytesRead <= offset) return null; ushort packetSize = BitConverter.ToUInt16(buffer, offset); //Too small to whole packet - if (buffer.Length <= offset + packetSize) + if (bytesRead < offset + packetSize) + return null; + + if (buffer.Length < offset + packetSize) return null; newPacket = new BasePacket(buffer, ref offset); diff --git a/FFXIVClassic Map Server/packets/SubPacket.cs b/FFXIVClassic Map Server/packets/SubPacket.cs index 40ff4094..8ef5c83d 100644 --- a/FFXIVClassic Map Server/packets/SubPacket.cs +++ b/FFXIVClassic Map Server/packets/SubPacket.cs @@ -127,7 +127,7 @@ namespace FFXIVClassic_Lobby_Server.packets if (header.type == 0x3) Array.Copy(getGameMessageBytes(), 0, outBytes, SUBPACKET_SIZE, GAMEMESSAGE_SIZE); - Array.Copy(data, 0, outBytes, SUBPACKET_SIZE + header.type == 0x3 ? GAMEMESSAGE_SIZE : 0, data.Length); + Array.Copy(data, 0, outBytes, SUBPACKET_SIZE + (header.type == 0x3 ? GAMEMESSAGE_SIZE : 0), data.Length); return outBytes; } diff --git a/FFXIVClassic Map Server/packets/send/login/InitPacket.cs b/FFXIVClassic Map Server/packets/send/login/InitPacket.cs index 2ffa98ab..38b8f033 100644 --- a/FFXIVClassic Map Server/packets/send/login/InitPacket.cs +++ b/FFXIVClassic Map Server/packets/send/login/InitPacket.cs @@ -10,7 +10,7 @@ namespace FFXIVClassic_Map_Server.packets.send.login { class InitPacket { - public static BasePacket buildPacket(uint unknown, uint time) + public static BasePacket buildPacket(uint actorID, uint time) { byte[] data = new byte[0x18]; @@ -26,7 +26,7 @@ namespace FFXIVClassic_Map_Server.packets.send.login binWriter.Write((uint)0); binWriter.Write((uint)0xFFFFFD7F); - binWriter.Write((uint)unknown); + binWriter.Write((uint)actorID); binWriter.Write((uint)time); } catch (Exception) From 498eaaf568db4e7caf4fea8d3fb8f1b30517c1c3 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Thu, 15 Oct 2015 22:34:11 -0400 Subject: [PATCH 5/5] Disabled some debug output and removed test line in Server. --- FFXIVClassic Map Server/PacketProcessor.cs | 2 +- FFXIVClassic Map Server/Server.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index 219e2df1..13ba1fd7 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -277,7 +277,7 @@ namespace FFXIVClassic_Lobby_Server break; //Set Target case 0x00CD: - subpacket.debugPrintSubPacket(); + //subpacket.debugPrintSubPacket(); SetTargetPacket setTarget = new SetTargetPacket(subpacket.data); player.getActor().currentTarget = setTarget.actorID; diff --git a/FFXIVClassic Map Server/Server.cs b/FFXIVClassic Map Server/Server.cs index d20bf7e5..e658c834 100644 --- a/FFXIVClassic Map Server/Server.cs +++ b/FFXIVClassic Map Server/Server.cs @@ -71,7 +71,6 @@ namespace FFXIVClassic_Lobby_Server private void acceptCallback(IAsyncResult result) { - Log.conn("TEST."); ClientConnection conn = null; Socket socket = (System.Net.Sockets.Socket)result.AsyncState;