mirror of
				https://bitbucket.org/Ioncannon/project-meteor-server.git
				synced 2025-05-20 08:26:59 -04:00 
			
		
		
		
	Ported over the network code from the map server. A lot of backedlogged changes are here as well.
This commit is contained in:
		| @@ -22,6 +22,7 @@ namespace FFXIVClassic_Lobby_Server | |||||||
|         public byte[] buffer = new byte[0xffff]; |         public byte[] buffer = new byte[0xffff]; | ||||||
|         public CircularBuffer<byte> incomingStream = new CircularBuffer<byte>(1024); |         public CircularBuffer<byte> incomingStream = new CircularBuffer<byte>(1024); | ||||||
|         public BlockingCollection<BasePacket> sendPacketQueue = new BlockingCollection<BasePacket>(100); |         public BlockingCollection<BasePacket> sendPacketQueue = new BlockingCollection<BasePacket>(100); | ||||||
|  |         public int lastPartialSize = 0; | ||||||
|  |  | ||||||
|         //Instance Stuff |         //Instance Stuff | ||||||
|         public uint currentUserId = 0; |         public uint currentUserId = 0; | ||||||
|   | |||||||
| @@ -146,7 +146,6 @@ namespace FFXIVClassic_Lobby_Server | |||||||
|  |  | ||||||
|                     cmd.Parameters.AddWithValue("@characterId", cid); |                     cmd.Parameters.AddWithValue("@characterId", cid); | ||||||
|                     cmd.Parameters.AddWithValue("@baseId", 0xFFFFFFFF); |                     cmd.Parameters.AddWithValue("@baseId", 0xFFFFFFFF); | ||||||
|                     cmd.Parameters.AddWithValue("@tribe", charaInfo.appearance.tribe); |  | ||||||
|                     cmd.Parameters.AddWithValue("@size", charaInfo.appearance.size); |                     cmd.Parameters.AddWithValue("@size", charaInfo.appearance.size); | ||||||
|                     cmd.Parameters.AddWithValue("@voice", charaInfo.appearance.voice); |                     cmd.Parameters.AddWithValue("@voice", charaInfo.appearance.voice); | ||||||
|                     cmd.Parameters.AddWithValue("@skinColor", charaInfo.appearance.skinColor); |                     cmd.Parameters.AddWithValue("@skinColor", charaInfo.appearance.skinColor); | ||||||
| @@ -360,7 +359,7 @@ namespace FFXIVClassic_Lobby_Server | |||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     conn.Open(); |                     conn.Open(); | ||||||
|                     appearance = conn.Query<Appearance>("SELECT * FROM characters_appearance WHERE id=@CharaId", new { CharaId = charaId }).SingleOrDefault(); |                     appearance = conn.Query<Appearance>("SELECT * FROM characters_appearance WHERE characterId=@CharaId", new { CharaId = charaId }).SingleOrDefault(); | ||||||
|                 } |                 } | ||||||
|                 catch (MySqlException e) |                 catch (MySqlException e) | ||||||
|                 { |                 { | ||||||
|   | |||||||
| @@ -15,60 +15,8 @@ namespace FFXIVClassic_Lobby_Server | |||||||
| { | { | ||||||
|     class PacketProcessor |     class PacketProcessor | ||||||
|     { |     { | ||||||
|         List<ClientConnection> mConnections; |  | ||||||
|         Boolean isAlive = true; |  | ||||||
|  |  | ||||||
|         public PacketProcessor(List<ClientConnection> connectionList) |         public void processPacket(ClientConnection client, BasePacket packet) | ||||||
|         { |  | ||||||
|             mConnections = connectionList; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public void update() |  | ||||||
|         { |  | ||||||
|             Console.WriteLine("Packet processing thread has started"); |  | ||||||
|             while (isAlive) |  | ||||||
|             { |  | ||||||
|                 lock (mConnections) |  | ||||||
|                 { |  | ||||||
|                     foreach (ClientConnection client in mConnections) |  | ||||||
|                     { |  | ||||||
|                         //Receive packets |  | ||||||
|                         while (true) |  | ||||||
|                         { |  | ||||||
|                             if (client.incomingStream.Size < BasePacket.BASEPACKET_SIZE) |  | ||||||
|                                 break; |  | ||||||
|  |  | ||||||
|                             try { |  | ||||||
|                                 if (client.incomingStream.Size < BasePacket.BASEPACKET_SIZE) |  | ||||||
|                                     break; |  | ||||||
|                                 BasePacketHeader header = BasePacket.getHeader(client.incomingStream.Peek(BasePacket.BASEPACKET_SIZE)); |  | ||||||
|  |  | ||||||
|                                 if (client.incomingStream.Size < header.packetSize) |  | ||||||
|                                     break; |  | ||||||
|  |  | ||||||
|                                 BasePacket packet = new BasePacket(client.incomingStream.Get(header.packetSize)); |  | ||||||
|                                 processPacket(client, packet); |  | ||||||
|  |  | ||||||
|                             } |  | ||||||
|                             catch(OverflowException) |  | ||||||
|                             { break; } |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         //Send packets |  | ||||||
|                         while (client.sendPacketQueue.Count != 0) |  | ||||||
|                             client.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) |  | ||||||
|         { |         { | ||||||
|  |  | ||||||
|             if ((packet.header.packetSize == 0x288) && (packet.data[0x34] == 'T'))		//Test Ticket Data |             if ((packet.header.packetSize == 0x288) && (packet.data[0x34] == 'T'))		//Test Ticket Data | ||||||
| @@ -80,13 +28,16 @@ namespace FFXIVClassic_Lobby_Server | |||||||
|  |  | ||||||
|             BasePacket.decryptPacket(client.blowfish, ref packet); |             BasePacket.decryptPacket(client.blowfish, ref packet); | ||||||
|  |  | ||||||
|             //packet.debugPrintPacket(); |             packet.debugPrintPacket(); | ||||||
|  |  | ||||||
|             List<SubPacket> subPackets = packet.getSubpackets(); |             List<SubPacket> subPackets = packet.getSubpackets(); | ||||||
|             foreach (SubPacket subpacket in subPackets) |             foreach (SubPacket subpacket in subPackets) | ||||||
|             { |             { | ||||||
|                 subpacket.debugPrintSubPacket(); |                 subpacket.debugPrintSubPacket(); | ||||||
|                 switch (subpacket.header.opcode) |  | ||||||
|  |                 if (subpacket.header.type == 3) | ||||||
|  |                 { | ||||||
|  |                     switch (subpacket.gameMessage.opcode) | ||||||
|                     { |                     { | ||||||
|                         case 0x03: |                         case 0x03: | ||||||
|                             ProcessGetCharacters(client, subpacket); |                             ProcessGetCharacters(client, subpacket); | ||||||
| @@ -103,11 +54,12 @@ namespace FFXIVClassic_Lobby_Server | |||||||
|                         case 0x0F: |                         case 0x0F: | ||||||
|                         //Mod Retainers |                         //Mod Retainers | ||||||
|                         default: |                         default: | ||||||
|                         Log.debug(String.Format("Unknown command 0x{0:X} received.", subpacket.header.opcode)); |                             Log.debug(String.Format("Unknown command 0x{0:X} received.", subpacket.gameMessage.opcode)); | ||||||
|                             break; |                             break; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         private void ProcessStartSession(ClientConnection client, BasePacket packet) |         private void ProcessStartSession(ClientConnection client, BasePacket packet) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ namespace FFXIVClassic_Lobby_Server | |||||||
|                 startServer = false; |                 startServer = false; | ||||||
|  |  | ||||||
|             //Test DB Connection |             //Test DB Connection | ||||||
|             Console.Write("Testing DB connection... "); |             Console.Write("Testing DB connection to \"{0}\"... ", ConfigConstants.DATABASE_HOST); | ||||||
|             using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) |             using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) | ||||||
|             { |             { | ||||||
|                 try |                 try | ||||||
|   | |||||||
| @@ -7,19 +7,19 @@ using System.Net.Sockets; | |||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using System.Threading; | using System.Threading; | ||||||
| using FFXIVClassic_Lobby_Server.common; | using FFXIVClassic_Lobby_Server.common; | ||||||
|  | using FFXIVClassic_Lobby_Server.packets; | ||||||
|  |  | ||||||
| namespace FFXIVClassic_Lobby_Server | namespace FFXIVClassic_Lobby_Server | ||||||
| { | { | ||||||
|     class Server |     class Server | ||||||
|     { |     { | ||||||
|         public const int FFXIV_LOBBY_PORT   = 54994; |         public const int FFXIV_LOBBY_PORT   = 54994; | ||||||
|         public const int BUFFER_SIZE        = 0x400; |         public const int BUFFER_SIZE        = 0xFFFF; | ||||||
|         public const int BACKLOG            = 100; |         public const int BACKLOG            = 100; | ||||||
|  |  | ||||||
|         private Socket mServerSocket; |         private Socket mServerSocket; | ||||||
|         private List<ClientConnection> mConnectionList = new List<ClientConnection>(); |         private List<ClientConnection> mConnectionList = new List<ClientConnection>(); | ||||||
|         private PacketProcessor mProcessor; |         private PacketProcessor mProcessor; | ||||||
|         private Thread mProcessorThread; |  | ||||||
|  |  | ||||||
|         #region Socket Handling |         #region Socket Handling | ||||||
|         public bool startServer() |         public bool startServer() | ||||||
| @@ -56,9 +56,8 @@ namespace FFXIVClassic_Lobby_Server | |||||||
|             Console.WriteLine("{0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port); |             Console.WriteLine("{0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port); | ||||||
|             Console.ForegroundColor = ConsoleColor.Gray; |             Console.ForegroundColor = ConsoleColor.Gray; | ||||||
|  |  | ||||||
|             mProcessor = new PacketProcessor(mConnectionList); |             mProcessor = new PacketProcessor(); | ||||||
|             mProcessorThread = new Thread(new ThreadStart(mProcessor.update)); |  | ||||||
|             mProcessorThread.Start(); |  | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -110,20 +109,51 @@ namespace FFXIVClassic_Lobby_Server | |||||||
|         private void receiveCallback(IAsyncResult result) |         private void receiveCallback(IAsyncResult result) | ||||||
|         { |         { | ||||||
|             ClientConnection conn = (ClientConnection)result.AsyncState;             |             ClientConnection conn = (ClientConnection)result.AsyncState;             | ||||||
|  |  | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 int bytesRead = conn.socket.EndReceive(result); |                 int bytesRead = conn.socket.EndReceive(result); | ||||||
|                 if (bytesRead > 0) |  | ||||||
|                 { |  | ||||||
|                     conn.processIncoming(bytesRead); |  | ||||||
|  |  | ||||||
|                     //Queue the next receive |                 bytesRead += conn.lastPartialSize; | ||||||
|  |  | ||||||
|  |                 if (bytesRead >= 0) | ||||||
|  |                 { | ||||||
|  |                     int offset = 0; | ||||||
|  |  | ||||||
|  |                     //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); | ||||||
|  |  | ||||||
|  |                     Array.Clear(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset)); | ||||||
|  |  | ||||||
|  |                     conn.lastPartialSize = 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); |                         conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), conn); | ||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     Log.conn(String.Format("{0} has disconnected.", conn.currentUserId == 0 ? conn.getAddress() : "User " + conn.currentUserId)); |                     Log.conn(String.Format("{0} has disconnected.", conn.currentUserId == 0 ? conn.getAddress() : "User " + conn.currentUserId)); | ||||||
|                     conn.socket.Close(); |  | ||||||
|                     lock (mConnectionList) |                     lock (mConnectionList) | ||||||
|                     { |                     { | ||||||
|                         mConnectionList.Remove(conn); |                         mConnectionList.Remove(conn); | ||||||
| @@ -134,8 +164,8 @@ namespace FFXIVClassic_Lobby_Server | |||||||
|             { |             { | ||||||
|                 if (conn.socket != null) |                 if (conn.socket != null) | ||||||
|                 { |                 { | ||||||
|                     Log.conn(String.Format("Connection @ {0} has disconnected.", conn.currentUserId == 0 ? "Unknown User " : "User " + conn.currentUserId)); |                     Log.conn(String.Format("{0} has disconnected.", conn.currentUserId == 0 ? conn.getAddress() : "User " + conn.currentUserId)); | ||||||
|                     conn.socket.Close(); |  | ||||||
|                     lock (mConnectionList) |                     lock (mConnectionList) | ||||||
|                     { |                     { | ||||||
|                         mConnectionList.Remove(conn); |                         mConnectionList.Remove(conn); | ||||||
| @@ -144,6 +174,41 @@ namespace FFXIVClassic_Lobby_Server | |||||||
|             } |             } | ||||||
|         }       |         }       | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="offset">Current offset in buffer.</param> | ||||||
|  |         /// <param name="buffer">Incoming buffer.</param> | ||||||
|  |         /// <returns>Returns either a BasePacket or null if not enough data.</returns> | ||||||
|  |         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; | ||||||
|  |  | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 newPacket = new BasePacket(buffer, ref offset); | ||||||
|  |             } | ||||||
|  |             catch (OverflowException) | ||||||
|  |             { | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return newPacket; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         #endregion |         #endregion | ||||||
|  |  | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -44,34 +44,140 @@ namespace FFXIVClassic_Lobby_Server.common | |||||||
|             } |             } | ||||||
|             return new string(result); |             return new string(result); | ||||||
|         } |         } | ||||||
|         /* |  | ||||||
|         /// <summary> |         public static UInt32 UnixTimeStampUTC() | ||||||
|         /// Grabs an instance of a class directly from the MySqlDataReader instead of manually building from GetString() etc. |  | ||||||
|         /// </summary> |  | ||||||
|         /// <typeparam name="T">Type of object to return</typeparam> |  | ||||||
|         /// <param name="reader">Reader instance</param> |  | ||||||
|         /// <returns>new T</returns> |  | ||||||
|         public static T GetSQLObject<T>(this MySql.Data.MySqlClient.MySqlDataReader reader) |  | ||||||
|         { |         { | ||||||
|             var obj = Activator.CreateInstance(typeof(T)); |             UInt32 unixTimeStamp; | ||||||
|             var fields = obj.GetType().GetFields(); |             DateTime currentTime = DateTime.Now; | ||||||
|  |             DateTime zuluTime = currentTime.ToUniversalTime(); | ||||||
|  |             DateTime unixEpoch = new DateTime(1970, 1, 1); | ||||||
|  |             unixTimeStamp = (UInt32)(zuluTime.Subtract(unixEpoch)).TotalSeconds; | ||||||
|  |  | ||||||
|             foreach (var field in obj.GetType().GetFields()) |             return unixTimeStamp; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public static UInt64 MilisUnixTimeStampUTC() | ||||||
|         { |         { | ||||||
|                 var attrs = field.GetCustomAttributes(typeof(System.Runtime.Serialization.DataMemberAttribute), true) as System.Runtime.Serialization.DataMemberAttribute[]; |             UInt64 unixTimeStamp; | ||||||
|  |             DateTime currentTime = DateTime.Now; | ||||||
|  |             DateTime zuluTime = currentTime.ToUniversalTime(); | ||||||
|  |             DateTime unixEpoch = new DateTime(1970, 1, 1); | ||||||
|  |             unixTimeStamp = (UInt64)(zuluTime.Subtract(unixEpoch)).TotalMilliseconds; | ||||||
|  |  | ||||||
|                 if (attrs.Length == 0) |             return unixTimeStamp; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public static ulong swapEndian(ulong input) | ||||||
|         { |         { | ||||||
|                     continue; |             return ((0x00000000000000FF) & (input >> 56) | | ||||||
|  |                     (0x000000000000FF00) & (input >> 40) | | ||||||
|  |                     (0x0000000000FF0000) & (input >> 24) | | ||||||
|  |                     (0x00000000FF000000) & (input >> 8) | | ||||||
|  |                     (0x000000FF00000000) & (input << 8) | | ||||||
|  |                     (0x0000FF0000000000) & (input << 24) | | ||||||
|  |                     (0x00FF000000000000) & (input << 40) | | ||||||
|  |                     (0xFF00000000000000) & (input << 56)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|                 var fieldID = attrs[0].Name; |         public static uint swapEndian(uint input) | ||||||
|                 var fieldType = field.FieldType; |         { | ||||||
|  |             return ((input >> 24) & 0xff) | | ||||||
|                 field.SetValue(obj, reader.GetValue(reader.GetOrdinal(fieldID))); |                     ((input << 8) & 0xff0000) | | ||||||
|  |                     ((input >> 8) & 0xff00) | | ||||||
|  |                     ((input << 24) & 0xff000000); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|             return (T)obj; |         public static int swapEndian(int input) | ||||||
|         }*/ |         { | ||||||
|  |             uint inputAsUint = (uint)input; | ||||||
|  |  | ||||||
|  |             input = (int) | ||||||
|  |                     (((inputAsUint >> 24) & 0xff) | | ||||||
|  |                     ((inputAsUint << 8) & 0xff0000) | | ||||||
|  |                     ((inputAsUint >> 8) & 0xff00) | | ||||||
|  |                     ((inputAsUint << 24) & 0xff000000)); | ||||||
|  |  | ||||||
|  |             return input; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public static uint MurmurHash2(string key, uint seed) | ||||||
|  |         { | ||||||
|  |             // 'm' and 'r' are mixing constants generated offline. | ||||||
|  |             // They're not really 'magic', they just happen to work well. | ||||||
|  |  | ||||||
|  |             byte[] data = Encoding.ASCII.GetBytes(key); | ||||||
|  |             const uint m = 0x5bd1e995; | ||||||
|  |             const int r = 24; | ||||||
|  |             int len = key.Length; | ||||||
|  |             int dataIndex = len - 4; | ||||||
|  |  | ||||||
|  |             // Initialize the hash to a 'random' value | ||||||
|  |  | ||||||
|  |             uint h = seed ^ (uint)len; | ||||||
|  |  | ||||||
|  |             // Mix 4 bytes at a time into the hash | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             while (len >= 4) | ||||||
|  |             { | ||||||
|  |                 h *= m; | ||||||
|  |  | ||||||
|  |                 uint k = (uint)BitConverter.ToInt32(data, dataIndex); | ||||||
|  |                 k = ((k >> 24) & 0xff) | // move byte 3 to byte 0 | ||||||
|  |                         ((k << 8) & 0xff0000) | // move byte 1 to byte 2 | ||||||
|  |                         ((k >> 8) & 0xff00) | // move byte 2 to byte 1 | ||||||
|  |                         ((k << 24) & 0xff000000); // byte 0 to byte 3 | ||||||
|  |  | ||||||
|  |                 k *= m; | ||||||
|  |                 k ^= k >> r; | ||||||
|  |                 k *= m; | ||||||
|  |  | ||||||
|  |                 h ^= k; | ||||||
|  |  | ||||||
|  |                 dataIndex -= 4; | ||||||
|  |                 len -= 4; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Handle the last few bytes of the input array | ||||||
|  |             switch (len) | ||||||
|  |             { | ||||||
|  |                 case 3: | ||||||
|  |                     h ^= (uint)data[0] << 16; goto case 2; | ||||||
|  |                 case 2: | ||||||
|  |                     h ^= (uint)data[len - 2] << 8; goto case 1; | ||||||
|  |                 case 1: | ||||||
|  |                     h ^= data[len - 1]; | ||||||
|  |                     h *= m; | ||||||
|  |                     break; | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             // Do a few final mixes of the hash to ensure the last few | ||||||
|  |             // bytes are well-incorporated. | ||||||
|  |  | ||||||
|  |             h ^= h >> 13; | ||||||
|  |             h *= m; | ||||||
|  |             h ^= h >> 15; | ||||||
|  |  | ||||||
|  |             return h; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public static byte[] ConvertBoolArrayToBinaryStream(bool[] array) | ||||||
|  |         { | ||||||
|  |             byte[] data = new byte[(array.Length / 8) + (array.Length % 8 != 0 ? 1 : 0)]; | ||||||
|  |  | ||||||
|  |             int dataCounter = 0; | ||||||
|  |             for (int i = 0; i < array.Length; i += 8) | ||||||
|  |             { | ||||||
|  |                 for (int bitCount = 0; bitCount < 8; bitCount++) | ||||||
|  |                 { | ||||||
|  |                     if (i + bitCount >= array.Length) | ||||||
|  |                         break; | ||||||
|  |                     data[dataCounter] = (byte)(((array[i + bitCount] ? 1 : 0) << 7 - bitCount) | data[dataCounter]); | ||||||
|  |                 } | ||||||
|  |                 dataCounter++; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return data; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,7 +10,6 @@ namespace FFXIVClassic_Lobby_Server.dataobjects | |||||||
|     { |     { | ||||||
|         //////////// |         //////////// | ||||||
|         //Chara Info |         //Chara Info | ||||||
|         public byte tribe = 0; |  | ||||||
|         public byte size = 0; |         public byte size = 0; | ||||||
|         public byte voice = 0; |         public byte voice = 0; | ||||||
|         public ushort skinColor = 0; |         public ushort skinColor = 0; | ||||||
|   | |||||||
| @@ -44,6 +44,7 @@ namespace FFXIVClassic_Lobby_Server.dataobjects | |||||||
|         public uint currentClass = 0; |         public uint currentClass = 0; | ||||||
|         public uint currentJob = 0; |         public uint currentJob = 0; | ||||||
|         public uint allegiance = 0; |         public uint allegiance = 0; | ||||||
|  |         public uint tribe = 0; | ||||||
|  |  | ||||||
|         public uint currentLevel = 1; |         public uint currentLevel = 1; | ||||||
|  |  | ||||||
| @@ -60,7 +61,7 @@ namespace FFXIVClassic_Lobby_Server.dataobjects | |||||||
|                 { |                 { | ||||||
|                     uint version = reader.ReadUInt32(); |                     uint version = reader.ReadUInt32(); | ||||||
|                     uint unknown1 = reader.ReadUInt32(); |                     uint unknown1 = reader.ReadUInt32(); | ||||||
|                     appearance.tribe = reader.ReadByte(); |                     info.tribe = reader.ReadByte(); | ||||||
|                     appearance.size = reader.ReadByte(); |                     appearance.size = reader.ReadByte(); | ||||||
|                     appearance.hairStyle = reader.ReadUInt16(); |                     appearance.hairStyle = reader.ReadUInt16(); | ||||||
|                     appearance.hairHighlightColor = reader.ReadUInt16(); |                     appearance.hairHighlightColor = reader.ReadUInt16(); | ||||||
| @@ -106,7 +107,7 @@ namespace FFXIVClassic_Lobby_Server.dataobjects | |||||||
|             return info; |             return info; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public String buildForCharaList(Character chara, Appearance appearance) |         public static String buildForCharaList(Character chara, Appearance appearance) | ||||||
|         { |         { | ||||||
|             byte[] data; |             byte[] data; | ||||||
|              |              | ||||||
| @@ -137,7 +138,7 @@ namespace FFXIVClassic_Lobby_Server.dataobjects | |||||||
|                     writer.Write(System.Text.Encoding.UTF8.GetBytes(chara.name + '\0')); |                     writer.Write(System.Text.Encoding.UTF8.GetBytes(chara.name + '\0')); | ||||||
|                     writer.Write((UInt32)0x1c); |                     writer.Write((UInt32)0x1c); | ||||||
|                     writer.Write((UInt32)0x04); |                     writer.Write((UInt32)0x04); | ||||||
|                     writer.Write((UInt32)getTribeModel(appearance.tribe)); |                     writer.Write((UInt32)getTribeModel(chara.tribe)); | ||||||
|                     writer.Write((UInt32)appearance.size); |                     writer.Write((UInt32)appearance.size); | ||||||
|                     uint colorVal = appearance.skinColor | (uint)(appearance.hairColor << 10) | (uint)(appearance.eyeColor << 20); |                     uint colorVal = appearance.skinColor | (uint)(appearance.hairColor << 10) | (uint)(appearance.eyeColor << 20); | ||||||
|                     writer.Write((UInt32)colorVal); |                     writer.Write((UInt32)colorVal); | ||||||
| @@ -185,7 +186,7 @@ namespace FFXIVClassic_Lobby_Server.dataobjects | |||||||
|                     writer.Write((UInt16)chara.currentLevel); |                     writer.Write((UInt16)chara.currentLevel); | ||||||
|                     writer.Write((byte)chara.currentJob); |                     writer.Write((byte)chara.currentJob); | ||||||
|                     writer.Write((UInt16)1); |                     writer.Write((UInt16)1); | ||||||
|                     writer.Write((byte)appearance.tribe); |                     writer.Write((byte)chara.tribe); | ||||||
|  |  | ||||||
|                     writer.Write((UInt32)0xe22222aa); |                     writer.Write((UInt32)0xe22222aa); | ||||||
|  |  | ||||||
| @@ -204,8 +205,8 @@ namespace FFXIVClassic_Lobby_Server.dataobjects | |||||||
|  |  | ||||||
|                     writer.BaseStream.Seek(0x10, SeekOrigin.Current); |                     writer.BaseStream.Seek(0x10, SeekOrigin.Current); | ||||||
|  |  | ||||||
|                     writer.Write((UInt32)chara.allegiance); |                     writer.Write((UInt32)chara.initialTown); | ||||||
|                     writer.Write((UInt32)chara.allegiance); |                     writer.Write((UInt32)chara.initialTown); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 data = stream.GetBuffer(); |                 data = stream.GetBuffer(); | ||||||
| @@ -223,7 +224,7 @@ namespace FFXIVClassic_Lobby_Server.dataobjects | |||||||
|             return Convert.ToBase64String(bytes).Replace('+', '-').Replace('/', '_'); |             return Convert.ToBase64String(bytes).Replace('+', '-').Replace('/', '_'); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public UInt32 getTribeModel(byte tribe) |         public static UInt32 getTribeModel(byte tribe) | ||||||
|         { |         { | ||||||
|             switch (tribe) |             switch (tribe) | ||||||
|             { |             { | ||||||
|   | |||||||
| @@ -20,12 +20,13 @@ namespace FFXIVClassic_Lobby_Server | |||||||
|         public bool doRename; |         public bool doRename; | ||||||
|         public uint currentZoneId; |         public uint currentZoneId; | ||||||
|  |  | ||||||
|         public uint guardian = 0; |         public byte guardian = 0; | ||||||
|         public uint birthMonth = 0; |         public byte birthMonth = 0; | ||||||
|         public uint birthDay = 0; |         public byte birthDay = 0; | ||||||
|         public uint currentClass = 0; |         public uint currentClass = 3; | ||||||
|         public uint currentJob = 0; |         public uint currentJob = 0; | ||||||
|         public uint allegiance = 0; |         public byte initialTown = 0; | ||||||
|  |         public byte tribe = 0; | ||||||
|  |  | ||||||
|         public uint currentLevel = 1; |         public uint currentLevel = 1; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,24 +10,29 @@ using System.IO; | |||||||
|  |  | ||||||
| namespace FFXIVClassic_Lobby_Server.packets | namespace FFXIVClassic_Lobby_Server.packets | ||||||
| { | { | ||||||
|  |  | ||||||
|     [StructLayout(LayoutKind.Sequential)] |     [StructLayout(LayoutKind.Sequential)] | ||||||
|     public struct BasePacketHeader |     public struct BasePacketHeader | ||||||
|     { |     { | ||||||
|         public byte isAuthenticated; |         public byte isAuthenticated; | ||||||
|         public byte isEncrypted; |         public byte isEncrypted; | ||||||
|         public ushort       reserved; |         public ushort connectionType; | ||||||
|         public ushort packetSize; |         public ushort packetSize; | ||||||
|         public ushort numSubpackets; |         public ushort numSubpackets; | ||||||
|         public uint         unknown1; //Id? |         public ulong timestamp; //Miliseconds | ||||||
|         public uint         unknown2; //Usually 0x13B        |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class BasePacket{ |     public class BasePacket | ||||||
|  |     { | ||||||
|  |  | ||||||
|  |         public const int TYPE_ZONE = 1; | ||||||
|  |         public const int TYPE_CHAT = 2; | ||||||
|         public const int BASEPACKET_SIZE = 0x10; |         public const int BASEPACKET_SIZE = 0x10; | ||||||
|  |  | ||||||
|         public BasePacketHeader header; |         public BasePacketHeader header; | ||||||
|         public byte[] data; |         public byte[] data; | ||||||
|  |  | ||||||
|  |         //Loads a sniffed packet from a file | ||||||
|         public unsafe BasePacket(String path) |         public unsafe BasePacket(String path) | ||||||
|         { |         { | ||||||
|             byte[] bytes = File.ReadAllBytes(path); |             byte[] bytes = File.ReadAllBytes(path); | ||||||
| @@ -45,10 +50,16 @@ namespace FFXIVClassic_Lobby_Server.packets | |||||||
|  |  | ||||||
|             int packetSize = header.packetSize; |             int packetSize = header.packetSize; | ||||||
|  |  | ||||||
|  |             if (packetSize - BASEPACKET_SIZE != 0) | ||||||
|  |             { | ||||||
|                 data = new byte[packetSize - BASEPACKET_SIZE]; |                 data = new byte[packetSize - BASEPACKET_SIZE]; | ||||||
|                 Array.Copy(bytes, BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE); |                 Array.Copy(bytes, BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE); | ||||||
|             } |             } | ||||||
|  |             else | ||||||
|  |                 data = new byte[0]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         //Loads a sniffed packet from a byte array | ||||||
|         public unsafe BasePacket(byte[] bytes) |         public unsafe BasePacket(byte[] bytes) | ||||||
|         { |         { | ||||||
|             if (bytes.Length < BASEPACKET_SIZE) |             if (bytes.Length < BASEPACKET_SIZE) | ||||||
| @@ -141,6 +152,52 @@ namespace FFXIVClassic_Lobby_Server.packets | |||||||
|             return outBytes; |             return outBytes; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         //Replaces all instances of the sniffed actorID with the given one | ||||||
|  |         public void replaceActorID(uint actorID) | ||||||
|  |         { | ||||||
|  |             using (MemoryStream mem = new MemoryStream(data)) | ||||||
|  |             { | ||||||
|  |                 using (BinaryWriter binWriter = new BinaryWriter(mem)) | ||||||
|  |                 { | ||||||
|  |                     using (BinaryReader binreader = new BinaryReader(mem)) | ||||||
|  |                     { | ||||||
|  |                         while (binreader.BaseStream.Position + 4 < data.Length) | ||||||
|  |                         { | ||||||
|  |                             uint read = binreader.ReadUInt32(); | ||||||
|  |                             if (read == 0x029B2941 || read == 0x02977DC7 || read == 0x0297D2C8 || read == 0x0230d573 || read == 0x23317df || read == 0x23344a3 || read == 0x1730bdb) //Original ID | ||||||
|  |                             { | ||||||
|  |                                 binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin); | ||||||
|  |                                 binWriter.Write(actorID); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         //Replaces all instances of the sniffed actorID with the given one | ||||||
|  |         public void replaceActorID(uint fromActorID, uint actorID) | ||||||
|  |         { | ||||||
|  |             using (MemoryStream mem = new MemoryStream(data)) | ||||||
|  |             { | ||||||
|  |                 using (BinaryWriter binWriter = new BinaryWriter(mem)) | ||||||
|  |                 { | ||||||
|  |                     using (BinaryReader binreader = new BinaryReader(mem)) | ||||||
|  |                     { | ||||||
|  |                         while (binreader.BaseStream.Position + 4 < data.Length) | ||||||
|  |                         { | ||||||
|  |                             uint read = binreader.ReadUInt32(); | ||||||
|  |                             if (read == fromActorID) //Original ID | ||||||
|  |                             { | ||||||
|  |                                 binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin); | ||||||
|  |                                 binWriter.Write(actorID); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         #region Utility Functions |         #region Utility Functions | ||||||
|         public static BasePacket createPacket(List<SubPacket> subpackets, bool isAuthed, bool isEncrypted) |         public static BasePacket createPacket(List<SubPacket> subpackets, bool isAuthed, bool isEncrypted) | ||||||
|         { |         { | ||||||
| @@ -152,6 +209,7 @@ namespace FFXIVClassic_Lobby_Server.packets | |||||||
|             header.isEncrypted = isEncrypted ? (byte)1 : (byte)0; |             header.isEncrypted = isEncrypted ? (byte)1 : (byte)0; | ||||||
|             header.numSubpackets = (ushort)subpackets.Count; |             header.numSubpackets = (ushort)subpackets.Count; | ||||||
|             header.packetSize = BASEPACKET_SIZE; |             header.packetSize = BASEPACKET_SIZE; | ||||||
|  |             header.timestamp = Utils.MilisUnixTimeStampUTC(); | ||||||
|  |  | ||||||
|             //Get packet size |             //Get packet size | ||||||
|             foreach (SubPacket subpacket in subpackets) |             foreach (SubPacket subpacket in subpackets) | ||||||
| @@ -184,6 +242,7 @@ namespace FFXIVClassic_Lobby_Server.packets | |||||||
|             header.isEncrypted = isEncrypted ? (byte)1 : (byte)0; |             header.isEncrypted = isEncrypted ? (byte)1 : (byte)0; | ||||||
|             header.numSubpackets = (ushort)1; |             header.numSubpackets = (ushort)1; | ||||||
|             header.packetSize = BASEPACKET_SIZE; |             header.packetSize = BASEPACKET_SIZE; | ||||||
|  |             header.timestamp = Utils.MilisUnixTimeStampUTC(); | ||||||
|  |  | ||||||
|             //Get packet size |             //Get packet size | ||||||
|             header.packetSize += subpacket.header.subpacketSize; |             header.packetSize += subpacket.header.subpacketSize; | ||||||
| @@ -200,13 +259,35 @@ namespace FFXIVClassic_Lobby_Server.packets | |||||||
|             return packet; |             return packet; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public static BasePacket createPacket(byte[] data, bool isAuthed, bool isEncrypted) | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             Debug.Assert(data != null); | ||||||
|  |  | ||||||
|  |             //Create Header | ||||||
|  |             BasePacketHeader header = new BasePacketHeader(); | ||||||
|  |  | ||||||
|  |             header.isAuthenticated = isAuthed ? (byte)1 : (byte)0; | ||||||
|  |             header.isEncrypted = isEncrypted ? (byte)1 : (byte)0; | ||||||
|  |             header.numSubpackets = (ushort)1; | ||||||
|  |             header.packetSize = BASEPACKET_SIZE; | ||||||
|  |             header.timestamp = Utils.MilisUnixTimeStampUTC(); | ||||||
|  |  | ||||||
|  |             //Get packet size | ||||||
|  |             header.packetSize += (ushort)data.Length; | ||||||
|  |  | ||||||
|  |             BasePacket packet = new BasePacket(header, data); | ||||||
|  |             return packet; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public static unsafe void encryptPacket(Blowfish blowfish, BasePacket packet) |         public static unsafe void encryptPacket(Blowfish blowfish, BasePacket packet) | ||||||
|         { |         { | ||||||
|             byte[] data = packet.data; |             byte[] data = packet.data; | ||||||
|             int size = packet.header.packetSize; |             int size = packet.header.packetSize; | ||||||
|  |  | ||||||
|             int offset = 0; |             int offset = 0; | ||||||
|             while (offset < data.Length) {  |             while (offset < data.Length) | ||||||
|  |             { | ||||||
|                 if (data.Length < offset + SubPacket.SUBPACKET_SIZE) |                 if (data.Length < offset + SubPacket.SUBPACKET_SIZE) | ||||||
|                     throw new OverflowException("Packet Error: Subpacket was too small"); |                     throw new OverflowException("Packet Error: Subpacket was too small"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -77,8 +77,7 @@ namespace FFXIVClassic_Lobby_Server.packets | |||||||
|                 binWriter.Write(Encoding.ASCII.GetBytes(chara.name.PadRight(0x20, '\0'))); //Name |                 binWriter.Write(Encoding.ASCII.GetBytes(chara.name.PadRight(0x20, '\0'))); //Name | ||||||
|                 binWriter.Write(Encoding.ASCII.GetBytes(worldname.PadRight(0xE, '\0'))); //World Name |                 binWriter.Write(Encoding.ASCII.GetBytes(worldname.PadRight(0xE, '\0'))); //World Name | ||||||
|  |  | ||||||
|                 CharaInfo info = JsonConvert.DeserializeObject<CharaInfo>(chara.charaInfo); |                 binWriter.Write(CharaInfo.buildForCharaList(chara, appearance)); //Appearance Data | ||||||
|                 binWriter.Write(info.buildForCharaList(chara, appearance)); //Appearance Data |  | ||||||
|                 //binWriter.Write(CharaInfo.debug()); //Appearance Data |                 //binWriter.Write(CharaInfo.debug()); //Appearance Data | ||||||
|                  |                  | ||||||
|                 characterCount++; |                 characterCount++; | ||||||
|   | |||||||
| @@ -13,10 +13,15 @@ namespace FFXIVClassic_Lobby_Server.packets | |||||||
|     public struct SubPacketHeader |     public struct SubPacketHeader | ||||||
|     { |     { | ||||||
|         public ushort subpacketSize; |         public ushort subpacketSize; | ||||||
|         public ushort       unknown0; //Always 0x03 |         public ushort type; | ||||||
|         public uint sourceId; |         public uint sourceId; | ||||||
|         public uint targetId; |         public uint targetId; | ||||||
|         public uint unknown1; |         public uint unknown1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [StructLayout(LayoutKind.Sequential)] | ||||||
|  |     public struct GameMessageHeader | ||||||
|  |     { | ||||||
|         public ushort unknown4; //Always 0x14 |         public ushort unknown4; //Always 0x14 | ||||||
|         public ushort opcode; |         public ushort opcode; | ||||||
|         public uint unknown5; |         public uint unknown5; | ||||||
| @@ -26,9 +31,11 @@ namespace FFXIVClassic_Lobby_Server.packets | |||||||
|  |  | ||||||
|     public class SubPacket |     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 SubPacketHeader header; | ||||||
|  |         public GameMessageHeader gameMessage; | ||||||
|         public byte[] data; |         public byte[] data; | ||||||
|  |  | ||||||
|         public unsafe SubPacket(byte[] bytes, ref int offset) |         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)); |                 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) |             if (bytes.Length < offset + header.subpacketSize) | ||||||
|                 throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data"); |                 throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data"); | ||||||
|  |  | ||||||
|  |             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]; |                 data = new byte[header.subpacketSize - SUBPACKET_SIZE]; | ||||||
|                 Array.Copy(bytes, offset + SUBPACKET_SIZE, data, 0, data.Length); |                 Array.Copy(bytes, offset + SUBPACKET_SIZE, data, 0, data.Length); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             offset += header.subpacketSize; |             offset += header.subpacketSize; | ||||||
|         } |         } | ||||||
| @@ -53,22 +76,34 @@ namespace FFXIVClassic_Lobby_Server.packets | |||||||
|         public SubPacket(ushort opcode, uint sourceId, uint targetId, byte[] data) |         public SubPacket(ushort opcode, uint sourceId, uint targetId, byte[] data) | ||||||
|         { |         { | ||||||
|             this.header = new SubPacketHeader(); |             this.header = new SubPacketHeader(); | ||||||
|             header.opcode = opcode; |             this.gameMessage = new GameMessageHeader(); | ||||||
|  |  | ||||||
|  |             gameMessage.opcode = opcode; | ||||||
|             header.sourceId = sourceId; |             header.sourceId = sourceId; | ||||||
|             header.targetId = targetId; |             header.targetId = targetId; | ||||||
|  |  | ||||||
|             UInt32 unixTimestamp = (UInt32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; |             gameMessage.timestamp = Utils.UnixTimeStampUTC(); | ||||||
|             header.timestamp = unixTimestamp; |  | ||||||
|  |  | ||||||
|             header.unknown0 = 0x03; |             header.type = 0x03; | ||||||
|             header.unknown1 = 0x00; |             header.unknown1 = 0x00; | ||||||
|             header.unknown4 = 0x14; |             gameMessage.unknown4 = 0x14; | ||||||
|             header.unknown5 = 0x00; |             gameMessage.unknown5 = 0x00; | ||||||
|             header.unknown6 = 0x00; |             gameMessage.unknown6 = 0x00; | ||||||
|  |  | ||||||
|             this.data = data; |             this.data = data; | ||||||
|  |  | ||||||
|             header.subpacketSize = (ushort)(0x20 + data.Length); |             header.subpacketSize = (ushort)(SUBPACKET_SIZE + GAMEMESSAGE_SIZE + data.Length); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public SubPacket(SubPacket original, uint newTargetId) | ||||||
|  |         { | ||||||
|  |             this.header = new SubPacketHeader(); | ||||||
|  |             this.gameMessage = original.gameMessage; | ||||||
|  |             header.subpacketSize = original.header.subpacketSize; | ||||||
|  |             header.type = original.header.type; | ||||||
|  |             header.sourceId = original.header.sourceId; | ||||||
|  |             header.targetId = newTargetId; | ||||||
|  |             data = original.data; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public byte[] getHeaderBytes() |         public byte[] getHeaderBytes() | ||||||
| @@ -83,11 +118,27 @@ namespace FFXIVClassic_Lobby_Server.packets | |||||||
|             return arr; |             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() |         public byte[] getBytes() | ||||||
|         { |         { | ||||||
|             byte[] outBytes = new byte[header.subpacketSize]; |             byte[] outBytes = new byte[header.subpacketSize]; | ||||||
|             Array.Copy(getHeaderBytes(), 0, outBytes, 0, SUBPACKET_SIZE); |             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; |             return outBytes; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -95,8 +146,12 @@ namespace FFXIVClassic_Lobby_Server.packets | |||||||
|         { |         { | ||||||
| #if DEBUG | #if DEBUG | ||||||
|             Console.BackgroundColor = ConsoleColor.DarkRed; |             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())); |             Console.WriteLine("{0}", Utils.ByteArrayToHex(getHeaderBytes())); | ||||||
|  |             if (header.type == 0x03) | ||||||
|  |                 Console.WriteLine("{0}", Utils.ByteArrayToHex(getGameMessageBytes())); | ||||||
|             Console.BackgroundColor = ConsoleColor.DarkMagenta; |             Console.BackgroundColor = ConsoleColor.DarkMagenta; | ||||||
|             Console.WriteLine("{0}", Utils.ByteArrayToHex(data)); |             Console.WriteLine("{0}", Utils.ByteArrayToHex(data)); | ||||||
|             Console.BackgroundColor = ConsoleColor.Black; |             Console.BackgroundColor = ConsoleColor.Black; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user