From a1ca96054317d638ce8ee775484f1522c0c4f501 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Tue, 23 Aug 2016 16:57:24 -0400 Subject: [PATCH] More work on the world server. --- FFXIVClassic Common Class Lib/SubPacket.cs | 32 +++-- .../DataObjects/ZoneServer.cs | 18 +++ .../FFXIVClassic World Server.csproj | 4 + FFXIVClassic Proxy Server/PacketProcessor.cs | 133 ++++++++++++++++++ .../Packets/Send/0x7ResponsePacket.cs | 31 ++++ FFXIVClassic Proxy Server/Server.cs | 41 +++++- FFXIVClassic Proxy Server/WorldMaster.cs | 4 +- 7 files changed, 248 insertions(+), 15 deletions(-) create mode 100644 FFXIVClassic Proxy Server/PacketProcessor.cs create mode 100644 FFXIVClassic Proxy Server/Packets/Send/0x7ResponsePacket.cs diff --git a/FFXIVClassic Common Class Lib/SubPacket.cs b/FFXIVClassic Common Class Lib/SubPacket.cs index 199199d0..6cd3ff27 100644 --- a/FFXIVClassic Common Class Lib/SubPacket.cs +++ b/FFXIVClassic Common Class Lib/SubPacket.cs @@ -72,26 +72,38 @@ namespace FFXIVClassic.Common offset += header.subpacketSize; } - public SubPacket(ushort opcode, uint sourceId, uint targetId, byte[] data) + public SubPacket(ushort opcode, uint sourceId, uint targetId, byte[] data) : this(true, opcode, sourceId, targetId, data) { } + + public SubPacket(bool isGameMessage, ushort opcode, uint sourceId, uint targetId, byte[] data) { header = new SubPacketHeader(); - gameMessage = new GameMessageHeader(); - gameMessage.opcode = opcode; + if (isGameMessage) + { + gameMessage = new GameMessageHeader(); + gameMessage.opcode = opcode; + gameMessage.timestamp = Utils.UnixTimeStampUTC(); + gameMessage.unknown4 = 0x14; + gameMessage.unknown5 = 0x00; + gameMessage.unknown6 = 0x00; + } + header.sourceId = sourceId; header.targetId = targetId; - gameMessage.timestamp = Utils.UnixTimeStampUTC(); + if (isGameMessage) + header.type = 0x03; + else + header.type = opcode; - header.type = 0x03; header.unknown1 = 0x00; - gameMessage.unknown4 = 0x14; - gameMessage.unknown5 = 0x00; - gameMessage.unknown6 = 0x00; this.data = data; - header.subpacketSize = (ushort) (SUBPACKET_SIZE + GAMEMESSAGE_SIZE + data.Length); + header.subpacketSize = (ushort) (SUBPACKET_SIZE + data.Length); + + if (isGameMessage) + header.subpacketSize += GAMEMESSAGE_SIZE; } public SubPacket(SubPacket original, uint newTargetId) @@ -158,6 +170,6 @@ namespace FFXIVClassic.Common ConsoleOutputColor.DarkMagenta); } #endif - } + } } } \ No newline at end of file diff --git a/FFXIVClassic Proxy Server/DataObjects/ZoneServer.cs b/FFXIVClassic Proxy Server/DataObjects/ZoneServer.cs index eafe5dae..b3cf09e1 100644 --- a/FFXIVClassic Proxy Server/DataObjects/ZoneServer.cs +++ b/FFXIVClassic Proxy Server/DataObjects/ZoneServer.cs @@ -5,6 +5,7 @@ using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; +using FFXIVClassic.Common; namespace FFXIVClassic_World_Server.DataObjects { @@ -12,6 +13,7 @@ namespace FFXIVClassic_World_Server.DataObjects { public readonly string zoneServerIp; public readonly int zoneServerPort; + public readonly int[] ownedZoneIds; public bool isConnected = false; public Socket zoneServerConnection; @@ -36,5 +38,21 @@ namespace FFXIVClassic_World_Server.DataObjects catch (Exception e) { Program.Log.Error("Failed to connect"); return; } } + + public void SendPacket(SubPacket subpacket) + { + if (isConnected) + { + byte[] packetBytes = subpacket.GetBytes(); + + try + { + zoneServerConnection.Send(packetBytes); + } + catch (Exception e) + { Program.Log.Error("Weird case, socket was d/ced: {0}", e); } + } + } + } } diff --git a/FFXIVClassic Proxy Server/FFXIVClassic World Server.csproj b/FFXIVClassic Proxy Server/FFXIVClassic World Server.csproj index 8ef6fac2..403e4f77 100644 --- a/FFXIVClassic Proxy Server/FFXIVClassic World Server.csproj +++ b/FFXIVClassic Proxy Server/FFXIVClassic World Server.csproj @@ -49,6 +49,10 @@ ..\packages\NLog.4.3.5\lib\net45\NLog.dll True + + ..\packages\RabbitMQ.Client.4.0.0\lib\net451\RabbitMQ.Client.dll + True + diff --git a/FFXIVClassic Proxy Server/PacketProcessor.cs b/FFXIVClassic Proxy Server/PacketProcessor.cs new file mode 100644 index 00000000..d50c10ed --- /dev/null +++ b/FFXIVClassic Proxy Server/PacketProcessor.cs @@ -0,0 +1,133 @@ +using FFXIVClassic.Common; +using FFXIVClassic_World_Server.Packets.Send.Login; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace FFXIVClassic_World_Server +{ + class PacketProcessor + { + /* + Session Creation: + + Get 0x1 from server + Send 0x7 + Send 0x2 + + Zone Change: + + Send 0x7 + Get 0x8 - Wait?? + Send 0x2 + */ + + + Server mServer; + List mConnections; + + public PacketProcessor(Server server) + { + mServer = server; + } + + public void ProcessPacket(ClientConnection client, BasePacket packet) + { + //if (packet.header.isCompressed == 0x01) + // BasePacket.DecryptPacket(client.blowfish, ref packet); + + List subPackets = packet.GetSubpackets(); + foreach (SubPacket subpacket in subPackets) + { + //Initial Connect Packet, Create session + if (subpacket.header.type == 0x01) + { + packet.DebugPrintPacket(); + byte[] reply1Data = { + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFD, 0xFF, 0xFF, + 0xE5, 0x6E, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x0 + }; + + byte[] reply2Data = { + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x2B, 0x5F, 0x26, + 0x66, 0x00, 0x00, 0x00, 0xC8, 0xD6, 0xAF, 0x2B, 0x38, 0x2B, 0x5F, 0x26, 0xB8, 0x8D, 0xF0, 0x2B, + 0xC8, 0xFD, 0x85, 0xFE, 0xA8, 0x7C, 0x5B, 0x09, 0x38, 0x2B, 0x5F, 0x26, 0xC8, 0xD6, 0xAF, 0x2B, + 0xB8, 0x8D, 0xF0, 0x2B, 0x88, 0xAF, 0x5E, 0x26 + }; + + BasePacket reply1 = new BasePacket(reply1Data); + BasePacket reply2 = new BasePacket(reply2Data); + + //Write Timestamp into Reply1 + using (MemoryStream mem = new MemoryStream(reply1.data)) + { + using (BinaryWriter binReader = new BinaryWriter(mem)) + { + binReader.BaseStream.Seek(0x14, SeekOrigin.Begin); + binReader.Write((UInt32)Utils.UnixTimeStampUTC()); + } + } + + //Read in Actor Id that owns this connection + 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) + { } + } + } + + mServer.AddSession(actorID); + + if (packet.header.connectionType == BasePacket.TYPE_ZONE) + Program.Log.Info("Got {0} connection for ActorID {1} @ {2}.", "zone", actorID, client.GetAddress()); + else if (packet.header.connectionType == BasePacket.TYPE_CHAT) + Program.Log.Info("Got {0} connection for ActorID {1} @ {2}.", "chat", actorID, client.GetAddress()); + + break; + } + //Ping from World Server + else if (subpacket.header.type == 0x07) + { + SubPacket init = Login0x7ResponsePacket.BuildPacket(0x50); + client.QueuePacket(BasePacket.CreatePacket(init, true, false)); + } + //Zoning Related + else if (subpacket.header.type == 0x08) + { + //Response, client's current [actorID][time] + //BasePacket init = Login0x7ResponsePacket.BuildPacket(BitConverter.ToUInt32(packet.data, 0x10), Utils.UnixTimeStampUTC(), 0x07); + //client.QueuePacket(init); + packet.DebugPrintPacket(); + } + //Game Message + else if (subpacket.header.type == 0x03) + { + //Send to the correct zone server + uint targetSession = subpacket.header.targetId; + + if (mServer.GetSession(targetSession).routing1 != null) + mServer.GetSession(targetSession).routing1.SendPacket(subpacket); + + if (mServer.GetSession(targetSession).routing2 != null) + mServer.GetSession(targetSession).routing2.SendPacket(subpacket); + } + else + packet.DebugPrintPacket(); + } + } + + } +} diff --git a/FFXIVClassic Proxy Server/Packets/Send/0x7ResponsePacket.cs b/FFXIVClassic Proxy Server/Packets/Send/0x7ResponsePacket.cs new file mode 100644 index 00000000..8d5a92df --- /dev/null +++ b/FFXIVClassic Proxy Server/Packets/Send/0x7ResponsePacket.cs @@ -0,0 +1,31 @@ +using FFXIVClassic.Common; +using System; +using System.IO; + +namespace FFXIVClassic_World_Server.Packets.Send.Login +{ + class Login0x7ResponsePacket + { + public static SubPacket BuildPacket(uint actorID) + { + byte[] data = new byte[0x18]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + try + { + binWriter.Write((UInt32)actorID); + binWriter.Write((UInt32)type); + } + catch (Exception) + { + } + } + } + + return BasePacket.CreatePacket(data, false, false); + } + } +} diff --git a/FFXIVClassic Proxy Server/Server.cs b/FFXIVClassic Proxy Server/Server.cs index d02d8312..a24606f0 100644 --- a/FFXIVClassic Proxy Server/Server.cs +++ b/FFXIVClassic Proxy Server/Server.cs @@ -21,7 +21,8 @@ namespace FFXIVClassic_World_Server WorldManager worldManager; private List mConnectionList = new List(); - private Dictionary mSessionList = new Dictionary(); + private Dictionary mZoneSessionList = new Dictionary(); + private Dictionary mChatSessionList = new Dictionary(); public Server() { @@ -120,7 +121,36 @@ namespace FFXIVClassic_World_Server } mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket); } - } + } + + public void AddSession(uint id) + { + throw new NotImplementedException(); + } + + public void RemoveSession(uint id) + { + if (mChatSessionList.ContainsKey(id)) { + mChatSessionList[id].clientSocket.Disconnect(); + mConnectionList.Remove(mChatSessionList[id].clientSocket); + mChatSessionList.Remove(id); + } + + if (mZoneSessionList.ContainsKey(id)) + { + mZoneSessionList[id].clientSocket.Disconnect(); + mConnectionList.Remove(mZoneSessionList[id].clientSocket); + mZoneSessionList.Remove(id); + } + } + + public Session GetSession(uint targetSession) + { + if (mZoneSessionList.ContainsKey(targetSession)) + return mZoneSessionList[targetSession]; + else + return null; + } /// /// 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. @@ -202,7 +232,7 @@ namespace FFXIVClassic_World_Server } } } - } + } /// /// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null. @@ -241,6 +271,11 @@ namespace FFXIVClassic_World_Server #endregion + + public WorldManager GetWorldManager() + { + return worldManager; + } } } diff --git a/FFXIVClassic Proxy Server/WorldMaster.cs b/FFXIVClassic Proxy Server/WorldMaster.cs index 40c47d60..b5f2ce12 100644 --- a/FFXIVClassic Proxy Server/WorldMaster.cs +++ b/FFXIVClassic Proxy Server/WorldMaster.cs @@ -88,10 +88,10 @@ namespace FFXIVClassic_World_Server public void DoZoneServerChange(Session session, uint zoneEntrance) { /* - ->Tell old server to save session info and remove + ->Tell old server to save session info and remove session. Start zone packets. ->Update the position to zoneEntrance ->Update routing - ->Tell new server to load session info and add + ->Tell new server to load session info and add session. Send end zone packets. */ }