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 ;
using MySql.Data.MySqlClient ;
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.IO ;
using System.Linq ;
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
{
List < ClientConnection > mConnections ;
Boolean isAlive = true ;
public PacketProcessor ( List < ClientConnection > connectionList )
{
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 ( ) ;
}
}
2015-09-08 00:52:19 -04:00
//Don't waste CPU if isn't needed
if ( mConnections . Count = = 0 )
Thread . Sleep ( 2000 ) ;
else
Thread . Sleep ( 100 ) ;
2015-08-26 13:38:58 -04:00
}
}
private void processPacket ( ClientConnection client , BasePacket packet )
{
if ( ( packet . header . packetSize = = 0x288 ) & & ( packet . data [ 0x34 ] = = 'T' ) ) //Test Ticket Data
{
//Crypto handshake
ProcessStartSession ( client , packet ) ;
return ;
}
BasePacket . decryptPacket ( client . blowfish , ref packet ) ;
2015-09-13 14:12:41 -04: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 ( ) ;
2015-08-26 13:38:58 -04:00
switch ( subpacket . header . opcode )
{
case 0x03 :
ProcessGetCharacters ( client , subpacket ) ;
break ;
case 0x04 :
ProcessSelectCharacter ( client , subpacket ) ;
break ;
case 0x05 :
ProcessSessionAcknowledgement ( client , subpacket ) ;
break ;
case 0x0B :
ProcessModifyCharacter ( client , subpacket ) ;
2015-09-02 14:07:45 -04:00
break ;
case 0x0F :
//Mod Retainers
2015-08-26 13:38:58 -04:00
default :
2015-09-11 18:57:22 -04:00
Log . debug ( String . Format ( "Unknown command 0x{0:X} received." , subpacket . header . opcode ) ) ;
2015-08-26 13:38:58 -04:00
break ;
}
}
}
private void ProcessStartSession ( ClientConnection client , BasePacket packet )
{
UInt32 clientTime = BitConverter . ToUInt32 ( packet . data , 0x74 ) ;
//We assume clientTime is 0x50E0E812, but we need to generate a proper key later
byte [ ] blowfishKey = { 0xB4 , 0xEE , 0x3F , 0x6C , 0x01 , 0x6F , 0x5B , 0xD9 , 0x71 , 0x50 , 0x0D , 0xB1 , 0x85 , 0xA2 , 0xAB , 0x43 } ;
client . blowfish = new Blowfish ( blowfishKey ) ;
2015-09-11 18:57:22 -04:00
Log . info ( String . Format ( "Received encryption key: 0x{0:X}" , clientTime ) ) ;
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 )
{
2015-09-13 18:21:28 -04:00
PacketStructs . SessionPacket sessionPacket = PacketStructs . toSessionStruct ( 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 )
{
2015-09-13 18:21:28 -04:00
FFXIVClassic_Lobby_Server . packets . PacketStructs . SelectCharRequestPacket selectCharRequest = PacketStructs . toSelectCharRequestStruct ( packet . data ) ;
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 )
{
2015-08-27 10:19:00 -04:00
PacketStructs . CharacterRequestPacket charaReq = PacketStructs . toCharacterRequestStruct ( packet . data ) ;
2015-08-26 13:38:58 -04:00
var slot = charaReq . slot ;
var code = charaReq . command ;
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 ;
2015-08-26 13:38:58 -04:00
switch ( code )
{
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
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
}
CharaCreatorPacket charaCreator = new CharaCreatorPacket ( charaReq . sequence , code , 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
}
2015-08-26 13:38:58 -04:00
}
}