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/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index e5cb10dd..13ba1fd7 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)) @@ -89,268 +43,272 @@ namespace FFXIVClassic_Lobby_Server if (packet.header.isEncrypted == 0x01) BasePacket.decryptPacket(client.blowfish, ref packet); - 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 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 + if (player == null) + { + player = new Player(actorID); + mPlayers[actorID] = player; + client.owner = actorID; + client.connType = 0; + player.setConnection1(client); + Log.debug(String.Format("Got actorID {0} for conn {1}.", actorID, client.getAddress())); + } + else + { + client.owner = actorID; + client.connType = 1; + player.setConnection2(client); + } - 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)); + //Get Character info + //Create player actor + client.queuePacket(reply2); + break; + } + 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) + { + 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(reply5); + 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(AddActorPacket.buildPacket(player.actorID, player.actorID, 0), true, false)); + BasePacket keyitems = new BasePacket("./packets/login/keyitems.bin"); + BasePacket currancy = new BasePacket("./packets/login/currancy.bin"); - client.queuePacket(reply6); + #region replaceid + currancy.replaceActorID(player.actorID); + keyitems.replaceActorID(player.actorID); - 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)); + 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 - client.queuePacket(BasePacket.createPacket(player.getActor().createNamePacket(player.actorID), true, false)); - client.queuePacket(BasePacket.createPacket(player.getActor().createAppearancePacket(player.actorID), true, false)); + 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)); - ////////ITEMS//////// - client.queuePacket(BasePacket.createPacket(InventoryBeginChangePacket.buildPacket(player.actorID), true, false)); + client.queuePacket(reply5); -#region itemsetup + client.queuePacket(BasePacket.createPacket(AddActorPacket.buildPacket(player.actorID, player.actorID, 0), 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(reply6); - 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(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)); - //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(BasePacket.createPacket(player.getActor().createNamePacket(player.actorID), true, false)); + client.queuePacket(BasePacket.createPacket(player.getActor().createAppearancePacket(player.actorID), true, false)); - List setinvPackets = new List(); - setinvPackets.Add(beginInventory); - setinvPackets.Add(setInventory); - setinvPackets.Add(endInventory); -#endregion + ////////ITEMS//////// + client.queuePacket(BasePacket.createPacket(InventoryBeginChangePacket.buildPacket(player.actorID), true, false)); - client.queuePacket(BasePacket.createPacket(setinvPackets, true, false)); - - //client.queuePacket(currancy); - //client.queuePacket(keyitems); + #region itemsetup -#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 + //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 - //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)); + int count = 0; - client.queuePacket(BasePacket.createPacket(InventoryEndChangePacket.buildPacket(player.actorID), true, false)); - ////////ITEMS//////// + 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; - //The rest of hardcode - client.queuePacket(reply7); - client.queuePacket(reply8); - client.queuePacket(reply9); - client.queuePacket(reply10); - //client.queuePacket(reply11); - client.queuePacket(reply12); + //Reused + SubPacket endInventory = InventorySetEndPacket.buildPacket(player.actorID); + SubPacket beginInventory = InventorySetBeginPacket.buildPacket(player.actorID, 200, 00); + SubPacket setInventory = InventoryItemPacket.buildPacket(player.actorID, items, ref count); - inn.addActorToZone(player.getActor()); + List setinvPackets = new List(); + setinvPackets.Add(beginInventory); + setinvPackets.Add(setInventory); + setinvPackets.Add(endInventory); + #endregion - break; - //Chat Received - case 0x0003: - subpacket.debugPrintSubPacket(); - break; - //Update Position - case 0x00CA: + 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/Server.cs b/FFXIVClassic Map Server/Server.cs index 8c268bc6..e658c834 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 { @@ -62,8 +63,7 @@ namespace FFXIVClassic_Lobby_Server 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 +117,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 +130,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, bytesRead); + //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 +181,35 @@ 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, int bytesRead) + { + BasePacket newPacket = null; + //Too small to even get length + if (bytesRead <= offset) + return null; + + ushort packetSize = BitConverter.ToUInt16(buffer, offset); + + //Too small to whole packet + if (bytesRead < offset + packetSize) + return null; + + if (buffer.Length < offset + packetSize) + return null; + + newPacket = new BasePacket(buffer, ref offset); + + return newPacket; + } + + #endregion public void sendPacket(string path, int conn) { diff --git a/FFXIVClassic Map Server/packets/SubPacket.cs b/FFXIVClassic Map Server/packets/SubPacket.cs index b443ce91..8ef5c83d 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..38b8f033 100644 --- a/FFXIVClassic Map Server/packets/send/login/InitPacket.cs +++ b/FFXIVClassic Map Server/packets/send/login/InitPacket.cs @@ -10,9 +10,9 @@ 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[18]; + byte[] data = new byte[0x18]; using (MemoryStream mem = new MemoryStream(data)) { @@ -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)