2015-08-26 13:38:58 -04:00
using FFXIVClassic_Lobby_Server.common ;
2015-09-02 14:07:45 -04:00
using FFXIVClassic_Lobby_Server.dataobjects ;
2015-08-26 13:38:58 -04:00
using FFXIVClassic_Lobby_Server.packets ;
2016-02-18 23:55:03 -05:00
using FFXIVClassic_Lobby_Server.packets.receive ;
2016-02-20 00:11:51 -05:00
using FFXIVClassic_Lobby_Server.utils ;
2015-08-26 13:38:58 -04:00
using MySql.Data.MySqlClient ;
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.IO ;
using System.Linq ;
2016-03-05 18:02:37 -05:00
using System.Security.Cryptography ;
2015-08-26 13:38:58 -04:00
using System.Text ;
2015-09-08 00:52:19 -04:00
using System.Threading ;
2015-08-26 13:38:58 -04:00
using System.Threading.Tasks ;
namespace FFXIVClassic_Lobby_Server
{
class PacketProcessor
{
2016-02-16 23:35:21 -05:00
public void processPacket ( ClientConnection client , BasePacket packet )
2015-08-26 13:38:58 -04:00
{
if ( ( packet . header . packetSize = = 0x288 ) & & ( packet . data [ 0x34 ] = = 'T' ) ) //Test Ticket Data
{
2016-03-05 18:02:37 -05:00
packet . debugPrintPacket ( ) ;
2015-08-26 13:38:58 -04:00
//Crypto handshake
ProcessStartSession ( client , packet ) ;
return ;
}
2016-02-16 23:35:21 -05:00
2015-08-26 13:38:58 -04:00
BasePacket . decryptPacket ( client . blowfish , ref packet ) ;
2016-02-16 23:35:21 -05:00
packet . debugPrintPacket ( ) ;
2015-08-26 13:38:58 -04:00
List < SubPacket > subPackets = packet . getSubpackets ( ) ;
foreach ( SubPacket subpacket in subPackets )
2015-09-13 18:21:28 -04:00
{
subpacket . debugPrintSubPacket ( ) ;
2016-02-16 23:35:21 -05:00
if ( subpacket . header . type = = 3 )
2015-08-26 13:38:58 -04:00
{
2016-02-16 23:35:21 -05:00
switch ( subpacket . gameMessage . opcode )
{
case 0x03 :
ProcessGetCharacters ( client , subpacket ) ;
break ;
case 0x04 :
ProcessSelectCharacter ( client , subpacket ) ;
break ;
case 0x05 :
ProcessSessionAcknowledgement ( client , subpacket ) ;
break ;
case 0x0B :
ProcessModifyCharacter ( client , subpacket ) ;
break ;
case 0x0F :
2015-09-02 14:07:45 -04:00
//Mod Retainers
2016-02-16 23:35:21 -05:00
default :
Log . debug ( String . Format ( "Unknown command 0x{0:X} received." , subpacket . gameMessage . opcode ) ) ;
break ;
}
2015-08-26 13:38:58 -04:00
}
}
}
private void ProcessStartSession ( ClientConnection client , BasePacket packet )
{
2016-03-05 18:02:37 -05:00
SecurityHandshakePacket securityHandshake = new SecurityHandshakePacket ( packet . data ) ;
2015-08-26 13:38:58 -04:00
2016-03-05 18:02:37 -05:00
byte [ ] blowfishKey = GenerateKey ( securityHandshake . ticketPhrase , securityHandshake . clientNumber ) ;
2015-08-26 13:38:58 -04:00
client . blowfish = new Blowfish ( blowfishKey ) ;
2016-03-05 18:02:37 -05:00
Log . info ( String . Format ( "SecCNum: 0x{0:X}" , securityHandshake . clientNumber ) ) ;
2015-08-26 13:38:58 -04:00
//Respond with acknowledgment
BasePacket outgoingPacket = new BasePacket ( HardCoded_Packets . g_secureConnectionAcknowledgment ) ;
BasePacket . encryptPacket ( client . blowfish , outgoingPacket ) ;
client . queuePacket ( outgoingPacket ) ;
}
private void ProcessSessionAcknowledgement ( ClientConnection client , SubPacket packet )
{
2016-02-20 00:11:51 -05:00
packet . debugPrintSubPacket ( ) ;
2016-02-18 23:55:03 -05:00
SessionPacket sessionPacket = new SessionPacket ( packet . data ) ;
2015-08-27 10:19:00 -04:00
String clientVersion = sessionPacket . version ;
2015-08-26 13:38:58 -04:00
2015-09-13 18:21:28 -04:00
Log . info ( String . Format ( "Got acknowledgment for secure session." ) ) ;
2015-09-11 18:57:22 -04:00
Log . info ( String . Format ( "CLIENT VERSION: {0}" , clientVersion ) ) ;
2015-08-26 13:38:58 -04:00
2015-09-13 18:21:28 -04:00
uint userId = Database . getUserIdFromSession ( sessionPacket . session ) ;
2015-08-27 10:19:00 -04:00
client . currentUserId = userId ;
2015-09-13 18:21:28 -04:00
client . currentSessionToken = sessionPacket . session ; ;
2015-08-26 13:38:58 -04:00
2015-08-27 10:19:00 -04:00
if ( userId = = 0 )
{
2015-09-13 18:21:28 -04:00
ErrorPacket errorPacket = new ErrorPacket ( sessionPacket . sequence , 0 , 0 , 13001 , "Your session has expired, please login again." ) ;
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 ;
2015-08-27 10:19:00 -04:00
}
2015-08-26 13:38:58 -04:00
2015-09-11 18:57:22 -04:00
Log . info ( String . Format ( "USER ID: {0}" , userId ) ) ;
2015-09-13 18:21:28 -04:00
List < Account > accountList = new List < Account > ( ) ;
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 ) ;
2015-08-26 13:38:58 -04:00
}
private void ProcessGetCharacters ( ClientConnection client , SubPacket packet )
{
2015-09-11 18:57:22 -04:00
Log . info ( String . Format ( "{0} => Get characters" , client . currentUserId = = 0 ? client . getAddress ( ) : "User " + client . currentUserId ) ) ;
2015-09-02 14:07:45 -04:00
2015-09-09 00:08:46 -04:00
sendWorldList ( client , packet ) ;
2015-09-10 00:52:31 -04:00
sendImportList ( client , packet ) ;
sendRetainerList ( client , packet ) ;
2015-09-13 14:12:41 -04:00
sendCharacterList ( client , packet ) ;
2015-09-10 00:52:31 -04:00
2015-08-26 13:38:58 -04:00
}
private void ProcessSelectCharacter ( ClientConnection client , SubPacket packet )
{
2016-02-18 23:55:03 -05:00
SelectCharacterPacket selectCharRequest = new SelectCharacterPacket ( packet . data ) ;
2015-09-13 18:21:28 -04:00
Log . info ( String . Format ( "{0} => Select character id {1}" , client . currentUserId = = 0 ? client . getAddress ( ) : "User " + client . currentUserId , selectCharRequest . characterId ) ) ;
2015-08-26 13:38:58 -04:00
2015-09-13 18:21:28 -04:00
Character chara = Database . getCharacter ( client . currentUserId , selectCharRequest . characterId ) ;
World world = null ;
2015-08-26 13:38:58 -04:00
2015-09-13 18:21:28 -04:00
if ( chara ! = null )
world = Database . getServer ( chara . serverId ) ;
2015-08-26 13:38:58 -04:00
2015-09-13 18:21:28 -04:00
if ( world = = null )
2015-08-26 13:38:58 -04:00
{
2015-09-13 18:21:28 -04:00
ErrorPacket errorPacket = new ErrorPacket ( selectCharRequest . sequence , 0 , 0 , 13001 , "World does not exist or is inactive." ) ;
SubPacket subpacket = errorPacket . buildPacket ( ) ;
BasePacket basePacket = BasePacket . createPacket ( subpacket , true , false ) ;
BasePacket . encryptPacket ( client . blowfish , basePacket ) ;
client . queuePacket ( basePacket ) ;
return ;
2015-08-26 13:38:58 -04:00
}
2015-09-13 18:21:28 -04:00
SelectCharacterConfirmPacket connectCharacter = new SelectCharacterConfirmPacket ( selectCharRequest . sequence , selectCharRequest . characterId , client . currentSessionToken , world . address , world . port , selectCharRequest . ticket ) ;
BasePacket outgoingPacket = BasePacket . createPacket ( connectCharacter . buildPackets ( ) , true , false ) ;
2015-08-26 13:38:58 -04:00
BasePacket . encryptPacket ( client . blowfish , outgoingPacket ) ;
client . queuePacket ( outgoingPacket ) ;
}
private void ProcessModifyCharacter ( ClientConnection client , SubPacket packet )
{
2016-02-18 23:55:03 -05:00
CharacterModifyPacket charaReq = new CharacterModifyPacket ( packet . data ) ;
2015-08-26 13:38:58 -04:00
var slot = charaReq . slot ;
var name = charaReq . characterName ;
var worldId = charaReq . worldId ;
2015-09-08 00:42:02 -04:00
uint pid = 0 , cid = 0 ;
2015-09-13 11:30:33 -04:00
//Get world from new char instance
2015-09-09 00:08:46 -04:00
if ( worldId = = 0 )
worldId = client . newCharaWorldId ;
2015-09-08 00:42:02 -04:00
2015-09-13 11:30:33 -04:00
//Check if this character exists, get world from there
if ( worldId = = 0 & & charaReq . characterId ! = 0 )
{
Character chara = Database . getCharacter ( client . currentUserId , charaReq . characterId ) ;
if ( chara ! = null )
worldId = chara . serverId ;
}
2015-09-09 00:08:46 -04:00
string worldName = null ;
World world = Database . getServer ( worldId ) ;
2015-09-08 00:42:02 -04:00
if ( world ! = null )
worldName = world . name ;
if ( worldName = = null )
{
ErrorPacket errorPacket = new ErrorPacket ( charaReq . sequence , 0 , 0 , 13001 , "World does not exist or is inactive." ) ;
SubPacket subpacket = errorPacket . buildPacket ( ) ;
BasePacket basePacket = BasePacket . createPacket ( subpacket , true , false ) ;
BasePacket . encryptPacket ( client . blowfish , basePacket ) ;
client . queuePacket ( basePacket ) ;
2015-09-11 18:57:22 -04:00
Log . info ( String . Format ( "User {0} => Error; invalid server id: \"{1}\"" , client . currentUserId , worldId ) ) ;
2015-09-08 00:42:02 -04:00
return ;
}
2015-09-13 11:30:33 -04:00
bool alreadyTaken ;
2016-02-20 00:11:51 -05:00
switch ( charaReq . command )
2015-08-26 13:38:58 -04:00
{
case 0x01 : //Reserve
2015-09-08 00:42:02 -04:00
2015-09-13 11:30:33 -04:00
alreadyTaken = Database . reserveCharacter ( client . currentUserId , slot , worldId , name , out pid , out cid ) ;
2015-08-27 10:19:00 -04:00
if ( alreadyTaken )
2015-09-02 14:07:45 -04:00
{
2015-09-09 00:08:46 -04:00
ErrorPacket errorPacket = new ErrorPacket ( charaReq . sequence , 1003 , 0 , 13005 , "" ) ; //BDB - Chara Name Used, //1003 - Bad Word
2015-09-03 22:20:53 -04:00
SubPacket subpacket = errorPacket . buildPacket ( ) ;
2015-09-02 14:07:45 -04:00
BasePacket basePacket = BasePacket . createPacket ( subpacket , true , false ) ;
BasePacket . encryptPacket ( client . blowfish , basePacket ) ;
client . queuePacket ( basePacket ) ;
2015-09-08 19:39:52 -04:00
Log . info ( String . Format ( "User {0} => Error; name taken: \"{1}\"" , client . currentUserId , charaReq . characterName ) ) ;
2015-09-02 14:07:45 -04:00
return ;
2015-09-08 19:39:52 -04:00
}
2015-09-09 00:08:46 -04:00
else
{
pid = 0 ;
client . newCharaCid = cid ;
client . newCharaSlot = slot ;
client . newCharaWorldId = worldId ;
client . newCharaName = name ;
}
2015-08-26 13:38:58 -04:00
2015-09-13 11:30:33 -04:00
Log . info ( String . Format ( "User {0} => Character reserved \"{1}\"" , client . currentUserId , name ) ) ;
2015-08-26 13:38:58 -04:00
break ;
case 0x02 : //Make
2015-09-13 11:30:33 -04:00
CharaInfo info = CharaInfo . getFromNewCharRequest ( charaReq . characterInfoEncoded ) ;
2015-09-09 00:08:46 -04:00
2016-02-20 11:20:59 -05:00
//Set Initial Appearance (items will be loaded in by map server)
2016-02-20 00:11:51 -05:00
uint [ ] classAppearance = CharacterCreatorUtils . getEquipmentForClass ( info . currentClass ) ;
info . weapon1 = classAppearance [ 0 ] ;
info . weapon2 = classAppearance [ 1 ] ;
2016-02-20 11:20:59 -05:00
info . head = classAppearance [ 7 ] ;
info . body = classAppearance [ 8 ] ;
info . legs = classAppearance [ 9 ] ;
info . hands = classAppearance [ 10 ] ;
info . feet = classAppearance [ 11 ] ;
info . belt = classAppearance [ 12 ] ;
2016-02-20 00:11:51 -05:00
//Set Initial Position
switch ( info . initialTown )
{
2016-03-05 18:02:37 -05:00
case 1 : //ocn0Battle02 (Limsa)
2016-02-20 00:11:51 -05:00
info . zoneId = 193 ;
info . x = 0.016f ;
info . y = 10.35f ;
info . z = - 36.91f ;
info . rot = 0.025f ;
break ;
2016-03-05 18:02:37 -05:00
case 2 : //fst0Battle03 (Gridania)
2016-02-20 00:11:51 -05:00
info . zoneId = 166 ;
info . x = 356.09f ;
info . y = 3.74f ;
info . z = - 701.62f ;
info . rot = - 1.4f ;
break ;
2016-03-05 18:02:37 -05:00
case 3 : //wil0Battle01 (Ul'dah)
2016-02-20 00:11:51 -05:00
info . zoneId = 184 ;
info . x = 12.63f ;
info . y = 196.05f ;
info . z = 131.01f ;
info . rot = - 1.34f ;
break ;
}
2015-09-09 00:08:46 -04:00
Database . makeCharacter ( client . currentUserId , client . newCharaCid , info ) ;
2015-09-07 23:43:23 -04:00
2015-09-09 00:08:46 -04:00
pid = 1 ;
cid = client . newCharaCid ;
2015-09-13 11:30:33 -04:00
name = client . newCharaName ;
2015-08-26 13:38:58 -04:00
2015-09-13 11:30:33 -04:00
Log . info ( String . Format ( "User {0} => Character created \"{1}\"" , client . currentUserId , name ) ) ;
2015-08-26 13:38:58 -04:00
break ;
case 0x03 : //Rename
2015-09-13 11:30:33 -04:00
alreadyTaken = Database . renameCharacter ( client . currentUserId , charaReq . characterId , worldId , charaReq . characterName ) ;
if ( alreadyTaken )
{
ErrorPacket errorPacket = new ErrorPacket ( charaReq . sequence , 1003 , 0 , 13005 , "" ) ; //BDB - Chara Name Used, //1003 - Bad Word
SubPacket subpacket = errorPacket . buildPacket ( ) ;
BasePacket basePacket = BasePacket . createPacket ( subpacket , true , false ) ;
BasePacket . encryptPacket ( client . blowfish , basePacket ) ;
client . queuePacket ( basePacket ) ;
Log . info ( String . Format ( "User {0} => Error; name taken: \"{1}\"" , client . currentUserId , charaReq . characterName ) ) ;
return ;
}
Log . info ( String . Format ( "User {0} => Character renamed \"{1}\"" , client . currentUserId , name ) ) ;
2015-08-26 13:38:58 -04:00
break ;
case 0x04 : //Delete
Database . deleteCharacter ( charaReq . characterId , charaReq . characterName ) ;
2015-09-13 11:30:33 -04:00
Log . info ( String . Format ( "User {0} => Character deleted \"{1}\"" , client . currentUserId , name ) ) ;
2015-08-26 13:38:58 -04:00
break ;
case 0x06 : //Rename Retainer
2015-09-07 23:43:23 -04:00
2015-09-13 11:30:33 -04:00
Log . info ( String . Format ( "User {0} => Retainer renamed \"{1}\"" , client . currentUserId , name ) ) ;
2015-08-26 13:38:58 -04:00
break ;
2015-09-08 00:42:02 -04:00
}
2016-02-20 00:11:51 -05:00
CharaCreatorPacket charaCreator = new CharaCreatorPacket ( charaReq . sequence , charaReq . command , pid , cid , 1 , name , worldName ) ;
2015-09-13 14:12:41 -04:00
BasePacket charaCreatorPacket = BasePacket . createPacket ( charaCreator . buildPacket ( ) , true , false ) ;
2015-09-08 00:42:02 -04:00
BasePacket . encryptPacket ( client . blowfish , charaCreatorPacket ) ;
client . queuePacket ( charaCreatorPacket ) ;
2015-09-03 22:20:53 -04:00
}
private void sendWorldList ( ClientConnection client , SubPacket packet )
{
List < World > serverList = Database . getServers ( ) ;
2015-09-13 11:30:33 -04:00
WorldListPacket worldlistPacket = new WorldListPacket ( 0 , serverList ) ;
2015-09-03 22:20:53 -04:00
List < SubPacket > subPackets = worldlistPacket . buildPackets ( ) ;
BasePacket basePacket = BasePacket . createPacket ( subPackets , true , false ) ;
BasePacket . encryptPacket ( client . blowfish , basePacket ) ;
2015-09-03 23:07:56 -04:00
client . queuePacket ( basePacket ) ;
2015-08-26 13:38:58 -04:00
}
2015-09-08 00:42:02 -04:00
private void sendImportList ( ClientConnection client , SubPacket packet )
2015-09-02 14:07:45 -04:00
{
2015-09-10 00:52:31 -04:00
List < String > names = Database . getReservedNames ( client . currentUserId ) ;
2015-09-02 14:07:45 -04:00
2015-09-13 11:30:33 -04:00
ImportListPacket importListPacket = new ImportListPacket ( 0 , names ) ;
2015-09-10 00:52:31 -04:00
List < SubPacket > subPackets = importListPacket . buildPackets ( ) ;
BasePacket basePacket = BasePacket . createPacket ( subPackets , true , false ) ;
BasePacket . encryptPacket ( client . blowfish , basePacket ) ;
client . queuePacket ( basePacket ) ;
2015-09-03 22:20:53 -04:00
}
private void sendRetainerList ( ClientConnection client , SubPacket packet )
{
2015-09-10 00:52:31 -04:00
List < Retainer > retainers = Database . getRetainers ( client . currentUserId ) ;
2015-09-13 11:30:33 -04:00
RetainerListPacket retainerListPacket = new RetainerListPacket ( 0 , retainers ) ;
2015-09-10 00:52:31 -04:00
List < SubPacket > subPackets = retainerListPacket . buildPackets ( ) ;
BasePacket basePacket = BasePacket . createPacket ( subPackets , true , false ) ;
BasePacket . encryptPacket ( client . blowfish , basePacket ) ;
client . queuePacket ( basePacket ) ;
2015-09-03 22:20:53 -04:00
}
2015-09-03 01:02:55 -04:00
2015-09-03 22:20:53 -04:00
private void sendCharacterList ( ClientConnection client , SubPacket packet )
{
2015-09-09 00:08:46 -04:00
List < Character > characterList = Database . getCharacters ( client . currentUserId ) ;
2015-09-07 12:12:08 -04:00
2015-09-13 11:30:33 -04:00
if ( characterList . Count > 8 )
Log . error ( "Warning, got more than 8 characters. List truncated, check DB for issues." ) ;
CharacterListPacket characterlistPacket = new CharacterListPacket ( 0 , characterList ) ;
2015-09-07 12:12:08 -04:00
List < SubPacket > subPackets = characterlistPacket . buildPackets ( ) ;
BasePacket basePacket = BasePacket . createPacket ( subPackets , true , false ) ;
BasePacket . encryptPacket ( client . blowfish , basePacket ) ;
client . queuePacket ( basePacket ) ;
2015-09-02 14:07:45 -04:00
}
2016-03-05 18:02:37 -05:00
private byte [ ] GenerateKey ( string ticketPhrase , uint clientNumber )
{
byte [ ] key ;
using ( MemoryStream memStream = new MemoryStream ( 0x2C ) )
{
using ( BinaryWriter binWriter = new BinaryWriter ( memStream ) )
{
binWriter . Write ( ( Byte ) 0x78 ) ;
binWriter . Write ( ( Byte ) 0x56 ) ;
binWriter . Write ( ( Byte ) 0x34 ) ;
binWriter . Write ( ( Byte ) 0x12 ) ;
binWriter . Write ( ( UInt32 ) clientNumber ) ;
binWriter . Write ( ( Byte ) 0xE8 ) ;
binWriter . Write ( ( Byte ) 0x03 ) ;
binWriter . Write ( ( Byte ) 0x00 ) ;
binWriter . Write ( ( Byte ) 0x00 ) ;
binWriter . Write ( Encoding . ASCII . GetBytes ( ticketPhrase ) , 0 , Encoding . ASCII . GetByteCount ( ticketPhrase ) > = 0x20 ? 0x20 : Encoding . ASCII . GetByteCount ( ticketPhrase ) ) ;
}
byte [ ] nonMD5edKey = memStream . GetBuffer ( ) ;
using ( MD5 md5Hash = MD5 . Create ( ) )
{
key = md5Hash . ComputeHash ( nonMD5edKey ) ;
}
}
return key ;
}
2015-08-26 13:38:58 -04:00
}
}