Added account and select character packet creators. Fixed wrong field being read for session id. Most of the server is now NOT hardcoded and customizable from the DB. Only hardcoded packet left is the initial handshake.

This commit is contained in:
Filip Maj 2015-09-13 18:21:28 -04:00
parent caf3968e5b
commit ddf1d2d1a3
10 changed files with 259 additions and 77 deletions

View File

@ -26,6 +26,7 @@ namespace FFXIVClassic_Lobby_Server
//Instance Stuff //Instance Stuff
public uint currentUserId = 0; public uint currentUserId = 0;
public uint currentAccount; public uint currentAccount;
public string currentSessionToken;
//Chara Creation //Chara Creation
public string newCharaName; public string newCharaName;

View File

@ -7,10 +7,11 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using FFXIVClassic_Lobby_Server.common;
namespace FFXIVClassic_Lobby_Server namespace FFXIVClassic_Lobby_Server
{ {
//charState: 0 - Reserved, 1 - Deleted, 2 - Inactive, 3 - Active //charState: 0 - Reserved, 1 - Inactive, 2 - Active
class Database class Database
{ {
@ -37,7 +38,7 @@ namespace FFXIVClassic_Lobby_Server
finally finally
{ {
conn.Dispose(); conn.Dispose();
} }
} }
return id; return id;
} }
@ -93,6 +94,8 @@ namespace FFXIVClassic_Lobby_Server
{ {
conn.Dispose(); conn.Dispose();
} }
Log.database(String.Format("CID={0} created on 'characters' table.", cid));
} }
return alreadyExists; return alreadyExists;
@ -107,7 +110,7 @@ namespace FFXIVClassic_Lobby_Server
conn.Open(); conn.Open();
MySqlCommand cmd = new MySqlCommand(); MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn; cmd.Connection = conn;
cmd.CommandText = "UPDATE characters SET state=3, charaInfo=@encodedInfo WHERE userId=@userId AND id=@cid"; cmd.CommandText = "UPDATE characters SET state=2, charaInfo=@encodedInfo WHERE userId=@userId AND id=@cid";
cmd.Prepare(); cmd.Prepare();
cmd.Parameters.AddWithValue("@userId", accountId); cmd.Parameters.AddWithValue("@userId", accountId);
@ -125,6 +128,8 @@ namespace FFXIVClassic_Lobby_Server
{ {
conn.Dispose(); conn.Dispose();
} }
Log.database(String.Format("CID={0} state updated to active(2).", cid));
} }
} }
@ -167,6 +172,8 @@ namespace FFXIVClassic_Lobby_Server
conn.Dispose(); conn.Dispose();
} }
Log.database(String.Format("CID={0} name updated to \"{1}\".", characterId, newName));
return false; return false;
} }
} }
@ -196,6 +203,8 @@ namespace FFXIVClassic_Lobby_Server
conn.Dispose(); conn.Dispose();
} }
} }
Log.database(String.Format("CID={0} deleted.", characterId));
} }
public static List<World> getServers() public static List<World> getServers()

View File

@ -58,6 +58,7 @@
<Compile Include="common\Bitfield.cs" /> <Compile Include="common\Bitfield.cs" />
<Compile Include="common\Log.cs" /> <Compile Include="common\Log.cs" />
<Compile Include="common\STA_INIFile.cs" /> <Compile Include="common\STA_INIFile.cs" />
<Compile Include="dataobjects\Account.cs" />
<Compile Include="dataobjects\CharaInfo.cs" /> <Compile Include="dataobjects\CharaInfo.cs" />
<Compile Include="dataobjects\Retainer.cs" /> <Compile Include="dataobjects\Retainer.cs" />
<Compile Include="dataobjects\Character.cs" /> <Compile Include="dataobjects\Character.cs" />
@ -74,9 +75,11 @@
<Compile Include="packets\ErrorPacket.cs" /> <Compile Include="packets\ErrorPacket.cs" />
<Compile Include="packets\HardCoded_Packets.cs" /> <Compile Include="packets\HardCoded_Packets.cs" />
<Compile Include="packets\PacketStructs.cs" /> <Compile Include="packets\PacketStructs.cs" />
<Compile Include="packets\SelectCharacterConfirmPacket.cs" />
<Compile Include="packets\SubPacket.cs" /> <Compile Include="packets\SubPacket.cs" />
<Compile Include="packets\CharacterListPacket.cs" /> <Compile Include="packets\CharacterListPacket.cs" />
<Compile Include="packets\ImportListPacket.cs" /> <Compile Include="packets\ImportListPacket.cs" />
<Compile Include="packets\AccountListPacket.cs" />
<Compile Include="packets\WorldListPacket.cs" /> <Compile Include="packets\WorldListPacket.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

View File

@ -84,8 +84,8 @@ namespace FFXIVClassic_Lobby_Server
List<SubPacket> subPackets = packet.getSubpackets(); List<SubPacket> subPackets = packet.getSubpackets();
foreach (SubPacket subpacket in subPackets) foreach (SubPacket subpacket in subPackets)
{ {
subpacket.debugPrintSubPacket();
switch (subpacket.header.opcode) switch (subpacket.header.opcode)
{ {
case 0x03: case 0x03:
@ -127,27 +127,39 @@ namespace FFXIVClassic_Lobby_Server
private void ProcessSessionAcknowledgement(ClientConnection client, SubPacket packet) private void ProcessSessionAcknowledgement(ClientConnection client, SubPacket packet)
{ {
PacketStructs.SessionPacket sessionPacket = PacketStructs.toSessionStruct(packet.data); PacketStructs.SessionPacket sessionPacket = PacketStructs.toSessionStruct(packet.data);
String sessionId = sessionPacket.session;
String clientVersion = sessionPacket.version; String clientVersion = sessionPacket.version;
Log.info(String.Format("Got acknowledgment for secure session.")); Log.info(String.Format("Got acknowledgment for secure session."));
Log.info(String.Format("SESSION ID: {0}", sessionId));
Log.info(String.Format("CLIENT VERSION: {0}", clientVersion)); Log.info(String.Format("CLIENT VERSION: {0}", clientVersion));
uint userId = Database.getUserIdFromSession(sessionId); uint userId = Database.getUserIdFromSession(sessionPacket.session);
client.currentUserId = userId; client.currentUserId = userId;
client.currentSessionToken = sessionPacket.session; ;
if (userId == 0) if (userId == 0)
{ {
//client.disconnect(); ErrorPacket errorPacket = new ErrorPacket(sessionPacket.sequence, 0, 0, 13001, "Your session has expired, please login again.");
Log.info(String.Format("Invalid session, kicking...")); SubPacket subpacket = errorPacket.buildPacket();
BasePacket errorBasePacket = BasePacket.createPacket(subpacket, true, false);
BasePacket.encryptPacket(client.blowfish, errorBasePacket);
client.queuePacket(errorBasePacket);
Log.info(String.Format("Invalid session, kicking..."));
return;
} }
Log.info(String.Format("USER ID: {0}", userId)); Log.info(String.Format("USER ID: {0}", userId));
BasePacket outgoingPacket = new BasePacket("./packets/loginAck.bin");
BasePacket.encryptPacket(client.blowfish, outgoingPacket); List<Account> accountList = new List<Account>();
client.queuePacket(outgoingPacket); Account defaultAccount = new Account();
defaultAccount.id = 1;
defaultAccount.name = "FINAL FANTASY XIV";
accountList.Add(defaultAccount);
AccountListPacket listPacket = new AccountListPacket(1, accountList);
BasePacket basePacket = BasePacket.createPacket(listPacket.buildPackets(), true, false);
BasePacket.encryptPacket(client.blowfish, basePacket);
client.queuePacket(basePacket);
} }
private void ProcessGetCharacters(ClientConnection client, SubPacket packet) private void ProcessGetCharacters(ClientConnection client, SubPacket packet)
@ -163,33 +175,29 @@ namespace FFXIVClassic_Lobby_Server
private void ProcessSelectCharacter(ClientConnection client, SubPacket packet) private void ProcessSelectCharacter(ClientConnection client, SubPacket packet)
{ {
uint characterId = 0; FFXIVClassic_Lobby_Server.packets.PacketStructs.SelectCharRequestPacket selectCharRequest = PacketStructs.toSelectCharRequestStruct(packet.data);
using (BinaryReader binReader = new BinaryReader(new MemoryStream(packet.data)))
Log.info(String.Format("{0} => Select character id {1}", client.currentUserId == 0 ? client.getAddress() : "User " + client.currentUserId, selectCharRequest.characterId));
Character chara = Database.getCharacter(client.currentUserId, selectCharRequest.characterId);
World world = null;
if (chara != null)
world = Database.getServer(chara.serverId);
if (world == null)
{ {
binReader.BaseStream.Seek(0x8, SeekOrigin.Begin); ErrorPacket errorPacket = new ErrorPacket(selectCharRequest.sequence, 0, 0, 13001, "World does not exist or is inactive.");
characterId = binReader.ReadUInt32(); SubPacket subpacket = errorPacket.buildPacket();
binReader.Close(); BasePacket basePacket = BasePacket.createPacket(subpacket, true, false);
BasePacket.encryptPacket(client.blowfish, basePacket);
client.queuePacket(basePacket);
return;
} }
Log.info(String.Format("{0} => Select character id {1}", client.currentUserId == 0 ? client.getAddress() : "User " + client.currentUserId, characterId)); SelectCharacterConfirmPacket connectCharacter = new SelectCharacterConfirmPacket(selectCharRequest.sequence, selectCharRequest.characterId, client.currentSessionToken, world.address, world.port, selectCharRequest.ticket);
String serverIp = "141.117.161.40";
ushort port = 54992;
BitConverter.GetBytes(port);
BasePacket outgoingPacket = new BasePacket("./packets/selectChar.bin");
//Write Character ID and Server info
using (BinaryWriter binWriter = new BinaryWriter(new MemoryStream(outgoingPacket.data)))
{
binWriter.Seek(0x28, SeekOrigin.Begin);
binWriter.Write(characterId);
binWriter.Seek(0x78, SeekOrigin.Begin);
binWriter.Write(System.Text.Encoding.ASCII.GetBytes(serverIp));
binWriter.Seek(0x76, SeekOrigin.Begin);
binWriter.Write(port);
binWriter.Close();
}
BasePacket outgoingPacket = BasePacket.createPacket(connectCharacter.buildPackets(), true, false);
BasePacket.encryptPacket(client.blowfish, outgoingPacket); BasePacket.encryptPacket(client.blowfish, outgoingPacket);
client.queuePacket(outgoingPacket); client.queuePacket(outgoingPacket);
} }

View File

@ -8,11 +8,11 @@ namespace FFXIVClassic_Lobby_Server.common
public class Blowfish public class Blowfish
{ {
[DllImport("../../../Debug/Blowfish.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport("./Blowfish.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern short initializeBlowfish(byte[] key, short keySize, uint[] P, uint[,] S); private static extern short initializeBlowfish(byte[] key, short keySize, uint[] P, uint[,] S);
[DllImport("../../../Debug/Blowfish.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport("./Blowfish.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void blowfish_encipher(ref int xl, ref int xr, uint[] P); private static extern void blowfish_encipher(ref int xl, ref int xr, uint[] P);
[DllImport("../../../Debug/Blowfish.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport("./Blowfish.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void blowfish_decipher(ref int xl, ref int xr, uint[] P); private static extern void blowfish_decipher(ref int xl, ref int xr, uint[] P);
private uint[] P = new uint[16+2]; private uint[] P = new uint[16+2];

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Lobby_Server.dataobjects
{
class Account
{
public UInt32 id;
public string name;
}
}

View File

@ -141,6 +141,14 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
{ {
byte[] data; byte[] data;
mainHand = 79707136;
offHand = 32509954;
headGear = 43008;
bodyGear = 43008;
legsGear = 43008;
handsGear = 43008;
feetGear = 43008;
using (MemoryStream stream = new MemoryStream()) using (MemoryStream stream = new MemoryStream())
{ {
using (BinaryWriter writer = new BinaryWriter(stream)) using (BinaryWriter writer = new BinaryWriter(stream))
@ -240,8 +248,6 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
} }
data = stream.GetBuffer(); data = stream.GetBuffer();
File.WriteAllBytes("./packets/out.bin",data);
} }
return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_'); return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_');

View File

@ -0,0 +1,99 @@
using FFXIVClassic_Lobby_Server.dataobjects;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Lobby_Server.packets
{
class AccountListPacket
{
public const ushort OPCODE = 0x0C;
public const ushort MAXPERPACKET = 8;
private UInt64 sequence;
private List<Account> accountList;
public AccountListPacket(UInt64 sequence, List<Account> accountList)
{
this.sequence = sequence;
this.accountList = accountList;
}
public List<SubPacket> buildPackets()
{
List<SubPacket> subPackets = new List<SubPacket>();
int accountCount = 0;
int totalCount = 0;
MemoryStream memStream = null;
BinaryWriter binWriter = null;
foreach (Account account in accountList)
{
if (totalCount == 0 || accountCount % MAXPERPACKET == 0)
{
memStream = new MemoryStream(0x280);
binWriter = new BinaryWriter(memStream);
//Write List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (UInt32)(accountList.Count - totalCount) : (UInt32)MAXPERPACKET);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
}
//Write Entries
binWriter.Write((UInt32)account.id);
binWriter.Write((UInt32)0);
binWriter.Write(Encoding.ASCII.GetBytes(account.name.PadRight(0x40, '\0')));
accountCount++;
totalCount++;
//Send this chunk of world list
if (accountCount >= MAXPERPACKET)
{
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
subPackets.Add(subpacket);
accountCount = 0;
}
}
//If there is anything left that was missed or the list is empty
if (accountCount > 0 || accountList.Count == 0)
{
if (accountList.Count == 0)
{
memStream = new MemoryStream(0x210);
binWriter = new BinaryWriter(memStream);
//Write Empty List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write((UInt32)0);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
}
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
subPackets.Add(subpacket);
}
return subPackets;
}
}
}

View File

@ -13,13 +13,13 @@ namespace FFXIVClassic_Lobby_Server.packets
public unsafe struct SessionPacket public unsafe struct SessionPacket
{ {
[FieldOffset(0)] [FieldOffset(0)]
public UInt64 sequence; public UInt64 sequence;
[FieldOffset(0x10)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x40)]
public String session;
[FieldOffset(0x50)] [FieldOffset(0x50)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public String version; public String version;
[FieldOffset(0x70)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public String session;
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
@ -37,38 +37,13 @@ namespace FFXIVClassic_Lobby_Server.packets
public String characterInfoEncoded; public String characterInfoEncoded;
} }
//Response Packets
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public unsafe struct ReserveCharaResponse public unsafe struct SelectCharRequestPacket
{ {
public UInt64 sequence; public UInt64 sequence;
public uint errorCode; public uint characterId;
public uint statusCode; public uint unknownId;
public uint errorId; public UInt64 ticket;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x2BB)]
public String errorMessage;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct MakeCharaResponse
{
public UInt64 sequence;
public uint errorCode;
public uint statusCode;
public uint errorId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x2BB)]
public String errorMessage;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct DeleteCharaResponse
{
public UInt64 sequence;
public uint errorCode;
public uint statusCode;
public uint errorId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x2BB)]
public String errorMessage;
} }
public static unsafe CharacterRequestPacket toCharacterRequestStruct(byte[] bytes) public static unsafe CharacterRequestPacket toCharacterRequestStruct(byte[] bytes)
@ -87,6 +62,14 @@ namespace FFXIVClassic_Lobby_Server.packets
} }
} }
public static unsafe SelectCharRequestPacket toSelectCharRequestStruct(byte[] bytes)
{
fixed (byte* pdata = &bytes[0])
{
return (SelectCharRequestPacket)Marshal.PtrToStructure(new IntPtr(pdata), typeof(SelectCharRequestPacket));
}
}
public static byte[] StructureToByteArray(object obj) public static byte[] StructureToByteArray(object obj)
{ {
int len = Marshal.SizeOf(obj); int len = Marshal.SizeOf(obj);

View File

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Lobby_Server.packets
{
class SelectCharacterConfirmPacket
{
public const ushort OPCODE = 0x0F;
private UInt64 sequence;
private UInt32 characterId;
private string sessionToken;
private string worldIp;
private UInt16 worldPort;
private UInt64 selectCharTicket;
public SelectCharacterConfirmPacket(UInt64 sequence, UInt32 characterId, string sessionToken, string worldIp, ushort worldPort, UInt64 selectCharTicket)
{
this.sequence = sequence;
this.characterId = characterId;
this.sessionToken = sessionToken;
this.worldIp = worldIp;
this.worldPort = worldPort;
this.selectCharTicket = selectCharTicket;
}
public List<SubPacket> buildPackets()
{
List<SubPacket> subPackets = new List<SubPacket>();
byte[] data;
using (MemoryStream memStream = new MemoryStream(0x98))
{
using (BinaryWriter binWriter = new BinaryWriter(memStream))
{
binWriter.Write((UInt64)sequence);
binWriter.Write((UInt32)characterId); //ActorId
binWriter.Write((UInt32)characterId); //CharacterId
binWriter.Write((UInt32)0);
binWriter.Write(Encoding.ASCII.GetBytes(sessionToken.PadRight(0x42, '\0'))); //Session Token
binWriter.Write((UInt16)worldPort); //World Port
binWriter.Write(Encoding.ASCII.GetBytes(worldIp.PadRight(0x38, '\0'))); //World Hostname/IP
binWriter.Write((UInt64)selectCharTicket); //Ticket or Handshake of somekind
}
data = memStream.GetBuffer();
}
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
subPackets.Add(subpacket);
return subPackets;
}
}
}