1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-03 17:27:47 +00:00

Merge remote-tracking branch 'upstream/SQL_REWRITE_OWN' into SQL_REWRITE_OWN

This commit is contained in:
Perize 2017-11-22 00:34:03 +09:00
commit 70668727df
27 changed files with 330 additions and 794 deletions

63
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,63 @@
# Contributing
Thanks for contributing to Sapphire!
First, we'd like to mention that a lot of discussion regarding the project happens in our Discord server.
We value research and discussion as to how we should tackle our issues as well as improving what is already in.
Of course we also value testing - many things tend to break due to patches, or mistakes/edge cases.
Regardless of how you plan on contributing, your thoughts are appreciated and you're welcome to join our Discord (link in README.md).
## Research
Care in implementating features should be taken. It tends to be end up weird, and replicating the expected behavior
is always preferred. Avoid assumptions and guesswork whenever possible.
As much research possible should be done before writing it out - on game data, testing with retail,
and even common practices in server development (emulators or not).
## Pull Requests
When making a PR, please make sure that it follows our style guidelines and good practices.
### Coding style
Indentations are Allman-style based, 4-space, no tabs.
Space between arguments in function calls, as well as for types.
Example (shortened from ActionHandler.cpp):
```cpp
switch( commandId )
{
case 0x01: // Toggle sheathe
{
if ( param11 == 1 )
pPlayer->setStance( Entity::Actor::Stance::Active );
else
{
pPlayer->setStance( Entity::Actor::Stance::Passive );
pPlayer->setAutoattack( false );
}
pPlayer->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), 0, param11, 1 ) );
break;
}
case 0x03: // Change target
{
uint64_t targetId = inPacket.getValAt< uint64_t >( 0x24 );
pPlayer->changeTarget( targetId );
break;
}
default:
{
break;
}
}
```
### Feature implementation
Please make sure edge cases have been tested, behavior is aligned with retail and (if applicable) your queries make sense.
Any changes to the SQL base should be noted (and reflected in the update.sql file in rootDirectory/sql).

View file

@ -1,379 +0,0 @@
#include "Database.h"
#include <stdio.h>
#include <stdarg.h>
#include <thread>
#include <chrono>
#include <sstream>
#include "src/servers/Server_Common/Logging/Logger.h"
extern Core::Logger g_log;
namespace Core {
namespace Db {
QueryResult::QueryResult( MYSQL_RES *res, uint32_t fields, uint32_t rows )
: m_result( res ),
m_fieldCount( fields ),
m_rowCount( rows )
{
m_currentRow = new Field[fields];
}
QueryResult::~QueryResult()
{
mysql_free_result( m_result );
delete[] m_currentRow;
}
bool QueryResult::nextRow()
{
MYSQL_ROW row = mysql_fetch_row( m_result );
auto length = mysql_fetch_lengths( m_result );
if( row == nullptr )
return false;
for( uint32_t i = 0; i < m_fieldCount; ++i )
{
m_currentRow[i].setValue( row[i] );
m_currentRow[i].setLength( 0 );
if( length )
m_currentRow[i].setLength( length[i] );
}
return true;
}
Field *QueryResult::fetch()
{
return m_currentRow;
}
uint32_t QueryResult::getFieldCount() const
{
return m_fieldCount;
}
uint32_t QueryResult::getRowCount() const
{
return m_rowCount;
}
Database::Database()
{
m_port = 0;
m_counter = 0;
m_pConnections = nullptr;
m_connectionCount = -1; // Not connected.
}
Database::~Database()
{
for( int32_t i = 0; i < m_connectionCount; ++i )
{
if( m_pConnections[i].conn != nullptr )
mysql_close( m_pConnections[i].conn );
}
delete[] m_pConnections;
}
bool Database::initialize( const DatabaseParams& params )
{
uint32_t i;
MYSQL * temp;
MYSQL * temp2;
my_bool my_true = true;
g_log.info( "Database: Connecting to " + params.hostname + ", database " + params.databaseName + "..." );
m_pConnections = new DatabaseConnection[params.connectionCount];
for( i = 0; i < params.connectionCount; ++i )
{
temp = mysql_init( nullptr );
if( mysql_options( temp, MYSQL_SET_CHARSET_NAME, "utf8" ) )
g_log.error( "Database: Could not set utf8 character set." );
if( mysql_options( temp, MYSQL_OPT_RECONNECT, &my_true ) )
g_log.error( "Database: MYSQL_OPT_RECONNECT could not be set, "
"connection drops may occur but will be counteracted." );
temp2 = mysql_real_connect( temp,
params.hostname.c_str(),
params.username.c_str(),
params.password.c_str(),
params.databaseName.c_str(),
params.port,
nullptr,
0 );
if( temp2 == nullptr )
{
g_log.fatal( "Database: Connection failed due to: `%s`" + std::string( mysql_error( temp ) ) );
return false;
}
m_pConnections[i].conn = temp2;
}
return true;
}
uint64_t Database::getNextUId()
{
execute( std::string( "INSERT INTO uniqueiddata( IdName ) VALUES( 'NOT_SET' );" ) );
auto res = query( "SELECT LAST_INSERT_ID();" );
if( !res )
return 0;
Db::Field *field = res->fetch();
return field[0].get< uint64_t >();
}
DatabaseConnection * Database::getFreeConnection()
{
uint32_t i = 0;
while( true )
{
DatabaseConnection * con = &m_pConnections[( ( i++ ) % m_connectionCount )];
if( con->lock.try_lock() )
return con;
// sleep every 20 iterations, otherwise this can cause 100% cpu if the db link goes dead
if( !( i % 20 ) )
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
}
}
boost::shared_ptr< QueryResult > Database::query( const std::string& QueryString )
{
// Send the query
boost::shared_ptr< QueryResult > qResult( nullptr );
DatabaseConnection * con = getFreeConnection();
if( sendQuery( con, QueryString.c_str(), false ) )
qResult = boost::shared_ptr< QueryResult >( storeQueryResult( con ) );
con->lock.unlock();
return qResult;
}
bool Database::execute( const std::string& QueryString )
{
DatabaseConnection * con = getFreeConnection();
bool Result = sendQuery( con, QueryString, false );
con->lock.unlock();
return Result;
}
void Database::freeQueryResult( QueryResult * p )
{
delete p;
}
std::string Database::escapeString( std::string Escape )
{
char a2[16384] = { 0 };
DatabaseConnection * con = getFreeConnection();
const char * ret;
if( mysql_real_escape_string( con->conn, a2, Escape.c_str(), ( uint32_t ) Escape.length() ) == 0 )
ret = Escape.c_str();
else
ret = a2;
con->lock.unlock();
return std::string( ret );
}
void Database::escapeLongString( const char * str, uint32_t len, std::stringstream& out )
{
char a2[65536 * 3] = { 0 };
DatabaseConnection * con = getFreeConnection();
mysql_real_escape_string( con->conn, a2, str, ( uint32_t ) len );
out.write( a2, ( std::streamsize )strlen( a2 ) );
con->lock.unlock();
}
std::string Database::escapeString( const char * esc, DatabaseConnection * con )
{
char a2[16384] = { 0 };
const char * ret;
if( mysql_real_escape_string( con->conn, a2, ( char* ) esc, ( uint32_t ) strlen( esc ) ) == 0 )
ret = esc;
else
ret = a2;
return std::string( ret );
}
bool Database::sendQuery( DatabaseConnection *con, const std::string &sql, bool Self )
{
int32_t result = mysql_query( con->conn, sql.c_str() );
if( result > 0 )
{
if( Self == false && handleError( con, mysql_errno( con->conn ) ) )
{
// Re-send the query, the connection was successful.
// The true on the end will prevent an endless loop here, as it will
// stop after sending the query twice.
result = sendQuery(con, sql, true);
}
else
{
g_log.error( "Database: query failed " + std::string( mysql_error( con->conn ) ) );
g_log.error( "\t" + std::string( sql ) );
}
}
return ( result == 0 ? true : false );
}
bool Database::handleError( DatabaseConnection *con, uint32_t ErrorNumber )
{
// Handle errors that should cause a reconnect to the CDatabase.
switch( ErrorNumber ) {
case 2006: // Mysql server has gone away
case 2008: // Client ran out of memory
case 2013: // Lost connection to sql server during query
case 2055: // Lost connection to sql server - system error
{
// Let's instruct a reconnect to the db when we encounter these errors.
return reconnect( con );
}
}
return false;
}
QueryResult * Database::storeQueryResult( DatabaseConnection * con )
{
QueryResult* res;
MYSQL_RES* pRes = mysql_store_result( con->conn );
auto uRows = mysql_affected_rows( con->conn );
auto uFields = mysql_field_count( con->conn );
if( uRows == 0 || uFields == 0 || pRes == 0 )
{
if( pRes != nullptr )
mysql_free_result( pRes );
return nullptr;
}
res = new QueryResult( pRes,
static_cast< uint32_t >( uFields ),
static_cast< uint32_t >( uRows ) );
res->nextRow();
return res;
}
bool Database::reconnect( DatabaseConnection * conn )
{
MYSQL * temp;
MYSQL * temp2;
temp = mysql_init( nullptr );
temp2 = mysql_real_connect( temp,
m_hostname.c_str(),
m_username.c_str(),
m_password.c_str(),
m_databaseName.c_str(),
m_port,
nullptr,
0 );
if( temp2 == nullptr )
{
g_log.error( "Database: Could not reconnect to database -> " + std::string( mysql_error( temp ) ) );
mysql_close( temp );
return false;
}
if( conn->conn != nullptr )
mysql_close( conn->conn );
conn->conn = temp;
return true;
}
void Database::cleanupLibs()
{
mysql_library_end();
}
void Database::shutdown()
{
for( int32_t i = 0; i < m_connectionCount; ++i )
{
if( m_pConnections[i].conn != nullptr )
{
mysql_close( m_pConnections[i].conn );
m_pConnections[i].conn = nullptr;
}
}
}
const std::string &Database::getHostName()
{
return m_hostname;
}
const std::string &Database::getDatabaseName()
{
return m_databaseName;
}
void Field::setValue( char *value )
{
m_pValue = value;
}
void Field::setLength( uint32_t value )
{
m_size = value;
}
std::string Field::getString() const
{
if( !m_pValue )
return "";
return std::string( m_pValue );
}
void Field::getBinary( char *dstBuf, uint16_t size ) const
{
if( m_pValue )
memcpy( dstBuf, m_pValue, size );
else
dstBuf = nullptr;
}
float Field::getFloat() const
{
return m_pValue ? static_cast< float >( atof( m_pValue ) ) : 0;
}
bool Field::getBool() const
{
return m_pValue ? atoi( m_pValue ) > 0 : false;
}
uint32_t Field::getLength() const
{
return m_size;
}
}
}

View file

@ -1,160 +0,0 @@
#ifndef _DATABASE_H
#define _DATABASE_H
#include <mutex>
#include <stdio.h>
#include <mysql.h>
#include <boost/shared_ptr.hpp>
#include <string.h>
namespace Core {
namespace Db {
class Field
{
public:
// set value
void setValue( char* value );
void setLength( uint32_t value );
std::string getString() const;
void getBinary( char* dstBuf, uint16_t size ) const;
float getFloat() const;
bool getBool() const;
template< class T >
__inline T get() const
{
if( !m_pValue )
return 0;
return static_cast< T >( atol( m_pValue ) );
}
uint64_t getUInt64()
{
if( m_pValue )
{
#ifdef _WIN32
uint64_t value;
sscanf( m_pValue, "%I64d", &value );
return value;
#else
uint64_t value;
sscanf( m_pValue, "%Lu", &value );
return value;
#endif
}
else
return 0;
}
uint32_t getLength() const;
private:
char *m_pValue;
uint32_t m_size;
};
class QueryResult
{
public:
QueryResult( MYSQL_RES *res, uint32_t fields, uint32_t rows );
~QueryResult();
bool nextRow();
Field* fetch();
uint32_t getFieldCount() const;
uint32_t getRowCount() const;
protected:
uint32_t m_fieldCount;
uint32_t m_rowCount;
Field *m_currentRow;
MYSQL_RES *m_result;
};
struct DatabaseConnection
{
std::mutex lock;
MYSQL *conn;
};
struct DatabaseParams
{
std::string hostname;
std::string username;
std::string password;
std::string databaseName;
uint16_t port;
uint32_t bufferSize;
uint32_t connectionCount;
};
class Database
{
public:
Database();
virtual ~Database();
bool initialize( const DatabaseParams& params );
void shutdown();
boost::shared_ptr< QueryResult > query( const std::string& QueryString );
bool waitExecuteNA( const char* QueryString );//Wait For Request Completion
bool execute( const char* QueryString, ... );
bool execute( const std::string& QueryString );
const std::string& getHostName();
const std::string& getDatabaseName();
std::string escapeString( std::string Escape );
void escapeLongString( const char * str, uint32_t len, std::stringstream& out );
std::string escapeString( const char * esc, DatabaseConnection *con );
void freeQueryResult( QueryResult * p );
DatabaseConnection *getFreeConnection();
void cleanupLibs();
/* database is killed off manually. */
void onShutdown() {}
uint64_t getNextUId();
protected:
// actual query function
bool sendQuery( DatabaseConnection *con, const std::string &sql, bool Self );
QueryResult * storeQueryResult( DatabaseConnection * con );
bool handleError( DatabaseConnection *conn, uint32_t ErrorNumber );
bool reconnect( DatabaseConnection *conn );
DatabaseConnection *m_pConnections;
uint32_t m_counter;
///////////////////////////////
int32_t m_connectionCount;
// For reconnecting a broken connection
std::string m_hostname;
std::string m_username;
std::string m_password;
std::string m_databaseName;
uint32_t m_port;
};
}
}
#endif

View file

@ -136,6 +136,7 @@ namespace Packets {
IPCTYPE_UNK_320 = 0x0207, // updated 4.1 IPCTYPE_UNK_320 = 0x0207, // updated 4.1
IPCTYPE_UNK_322 = 0x0209, // updated 4.1 IPCTYPE_UNK_322 = 0x0209, // updated 4.1
ActorGauge = 0x249
PerformNote = 0x0252, PerformNote = 0x0252,
}; };

View file

@ -731,16 +731,16 @@ struct FFXIVIpcInitUI : FFXIVIpcBasePacket<InitUI>
uint8_t preNamePadding; uint8_t preNamePadding;
char name[32]; char name[32];
uint8_t unknown_54[16]; uint8_t unknown_54[16];
uint8_t unknown55; uint8_t unknown55[4];
uint16_t levels[25]; uint16_t levels[25];
uint32_t exp[25]; uint32_t exp[25];
uint8_t unlockBitmask[64]; uint8_t unlockBitmask[64];
uint8_t aetheryte[16]; uint8_t aetheryte[16];
uint8_t discovery[420]; uint8_t discovery[420];
uint8_t howto[33]; uint8_t howto[33];
uint8_t minions[33]; uint8_t minions[35];
uint8_t chocoboTaxiMask[8]; uint8_t chocoboTaxiMask[8];
uint8_t contentClearMask[104]; uint8_t contentClearMask[105];
uint8_t companionBardingMask[8]; uint8_t companionBardingMask[8];
uint8_t companionEquippedHead; uint8_t companionEquippedHead;
uint8_t companionEquippedBody; uint8_t companionEquippedBody;
@ -1314,6 +1314,12 @@ struct FFXIVIpcMount : FFXIVIpcBasePacket<Mount>
}; };
struct FFXIVIpcActorGauge : FFXIVIpcBasePacket<ActorGauge>
{
uint8_t classJobId;
uint8_t data[15]; // depends on classJobId
};
struct FFXIVIpcPerformNote : FFXIVIpcBasePacket<PerformNote> struct FFXIVIpcPerformNote : FFXIVIpcBasePacket<PerformNote>
{ {
uint8_t data[32]; uint8_t data[32];

View file

@ -18,6 +18,7 @@ set_target_properties(server_lobby PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
) )
if (UNIX) if (UNIX)

View file

@ -139,7 +139,7 @@ void Core::Network::GameConnection::getCharList( FFXIVARR_PACKET_RAW& packet, ui
serverListPacket.data().server[0].id = g_serverLobby.getConfig()->getValue<uint16_t>( "Settings.Parameters.WorldID", 1 ); serverListPacket.data().server[0].id = g_serverLobby.getConfig()->getValue<uint16_t>( "Settings.Parameters.WorldID", 1 );
serverListPacket.data().server[0].index = 0; serverListPacket.data().server[0].index = 0;
serverListPacket.data().final = 1; serverListPacket.data().final = 1;
sprintf( serverListPacket.data().server[0].name, g_serverLobby.getConfig()->getValue< std::string >( "Settings.Parameters.WorldName", "Sapphire" ).c_str() ); strcpy( serverListPacket.data().server[0].name, g_serverLobby.getConfig()->getValue< std::string >( "Settings.Parameters.WorldName", "Sapphire" ).c_str() );
pRP.addPacket( serverListPacket ); pRP.addPacket( serverListPacket );

View file

@ -17,12 +17,17 @@ set_target_properties(server_rest PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
) )
if (UNIX) if (UNIX)
target_link_libraries (server_rest Common xivdat pthread mysqlclient mysqlConnector dl z) target_link_libraries (server_rest Common xivdat pthread mysqlclient mysqlConnector dl z)
else() else()
# ignore unchecked iterators warnings from msvc
add_definitions(-D_SCL_SECURE_NO_WARNINGS)
target_link_libraries (server_rest Common xivdat libmysql mysqlConnector zlib1) target_link_libraries (server_rest Common xivdat libmysql mysqlConnector zlib1)
endif() endif()
target_link_libraries( server_rest ${Boost_LIBRARIES} ${Boost_LIBRARIES} ) target_link_libraries( server_rest ${Boost_LIBRARIES} ${Boost_LIBRARIES} )

View file

@ -345,8 +345,8 @@ namespace Core {
void PlayerMinimal::insertDbGlobalItem( uint32_t weaponId, uint64_t uniqueId ) const void PlayerMinimal::insertDbGlobalItem( uint32_t weaponId, uint64_t uniqueId ) const
{ {
auto stmtItemGlobal = g_charaDb.getPreparedStatement(Db::CHARA_ITEMGLOBAL_INS ); auto stmtItemGlobal = g_charaDb.getPreparedStatement( Db::CHARA_ITEMGLOBAL_INS );
stmtItemGlobal->setInt(1, m_id); stmtItemGlobal->setInt( 1, m_id );
stmtItemGlobal->setInt64( 2, uniqueId ); stmtItemGlobal->setInt64( 2, uniqueId );
stmtItemGlobal->setInt( 3, weaponId ); stmtItemGlobal->setInt( 3, weaponId );
g_charaDb.directExecute( stmtItemGlobal ); g_charaDb.directExecute( stmtItemGlobal );
@ -362,10 +362,10 @@ namespace Core {
uint64_t PlayerMinimal::getNextUId64() const uint64_t PlayerMinimal::getNextUId64() const
{ {
g_charaDb.execute( std::string( "INSERT INTO uniqueiddata( IdName ) VALUES( 'NOT_SET' );" ) ); g_charaDb.directExecute( std::string( "INSERT INTO uniqueiddata( IdName ) VALUES( 'NOT_SET' );" ) );
auto res = g_charaDb.query( "SELECT LAST_INSERT_ID();" ); auto res = g_charaDb.query( "SELECT LAST_INSERT_ID();" );
if( !res ) if( !res->next() )
return 0; return 0;
return res->getUInt64( 1 ); return res->getUInt64( 1 );

View file

@ -88,7 +88,7 @@ bool Core::Network::SapphireAPI::createAccount( const std::string& username, con
// get account from login name // get account from login name
auto pQR = g_charaDb.query( "SELECT account_id FROM accounts WHERE account_name = '" + username + "';" ); auto pQR = g_charaDb.query( "SELECT account_id FROM accounts WHERE account_name = '" + username + "';" );
// found? // found?
if( !pQR->next() ) if( pQR->next() )
return false; return false;
// we are clear and can create a new account // we are clear and can create a new account
@ -99,7 +99,7 @@ bool Core::Network::SapphireAPI::createAccount( const std::string& username, con
uint32_t accountId = pQR->getUInt( 1 ) + 1; uint32_t accountId = pQR->getUInt( 1 ) + 1;
// store the account to the db // store the account to the db
g_charaDb.execute( "INSERT INTO accounts (account_Id, account_name, account_pass, account_created) VALUE( " + g_charaDb.directExecute( "INSERT INTO accounts (account_Id, account_name, account_pass, account_created) VALUE( " +
std::to_string( accountId ) + ", '" + std::to_string( accountId ) + ", '" +
username + "', '" + username + "', '" +
pass + "', " + pass + "', " +

View file

@ -70,7 +70,8 @@ Core::Entity::Player::Player() :
m_bLoadingComplete( false ), m_bLoadingComplete( false ),
m_bMarkedForZoning( false ), m_bMarkedForZoning( false ),
m_zoningType( Common::ZoneingType::None ), m_zoningType( Common::ZoneingType::None ),
m_bAutoattack( false ) m_bAutoattack( false ),
m_markedForRemoval( false )
{ {
m_id = 0; m_id = 0;
m_type = ActorType::Player; m_type = ActorType::Player;
@ -90,7 +91,7 @@ Core::Entity::Player::Player() :
Core::Entity::Player::~Player() Core::Entity::Player::~Player()
{ {
g_log.debug( "PlayerObj destroyed" );
} }
// TODO: add a proper calculation based on race / job / level / gear // TODO: add a proper calculation based on race / job / level / gear
@ -134,6 +135,16 @@ uint8_t Core::Entity::Player::getStartTown() const
return m_startTown; return m_startTown;
} }
void Core::Entity::Player::setMarkedForRemoval()
{
m_markedForRemoval = true;
}
bool Core::Entity::Player::isMarkedForRemoval() const
{
return m_markedForRemoval;
}
Core::Common::OnlineStatus Core::Entity::Player::getOnlineStatus() Core::Common::OnlineStatus Core::Entity::Player::getOnlineStatus()
{ {
uint64_t newMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::NewAdventurer ); uint64_t newMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::NewAdventurer );
@ -899,7 +910,6 @@ uint32_t Core::Entity::Player::getLastPing() const
return m_lastPing; return m_lastPing;
} }
void Core::Entity::Player::setVoiceId( uint8_t voiceId ) void Core::Entity::Player::setVoiceId( uint8_t voiceId )
{ {
m_voice = voiceId; m_voice = voiceId;
@ -1396,7 +1406,7 @@ uint8_t * Core::Entity::Player::getTitleList()
uint16_t Core::Entity::Player::getTitle() const uint16_t Core::Entity::Player::getTitle() const
{ {
return m_title; return m_activeTitle;
} }
void Core::Entity::Player::addTitle( uint16_t titleId ) void Core::Entity::Player::addTitle( uint16_t titleId )
@ -1417,7 +1427,7 @@ void Core::Entity::Player::setTitle( uint16_t titleId )
if ( ( m_titleList[index] & value ) == 0 ) // Player doesn't have title - bail if ( ( m_titleList[index] & value ) == 0 ) // Player doesn't have title - bail
return; return;
m_title = titleId; m_activeTitle = titleId;
sendToInRangeSet( ActorControlPacket142( getId(), SetTitle, titleId ), true ); sendToInRangeSet( ActorControlPacket142( getId(), SetTitle, titleId ), true );
} }

View file

@ -530,6 +530,9 @@ public:
void updateDbSearchInfo() const; void updateDbSearchInfo() const;
void updateDbClass() const; void updateDbClass() const;
void setMarkedForRemoval();
bool isMarkedForRemoval() const;
private: private:
uint32_t m_lastWrite; uint32_t m_lastWrite;
uint32_t m_lastPing; uint32_t m_lastPing;
@ -540,6 +543,8 @@ private:
uint8_t m_mode; uint8_t m_mode;
bool m_markedForRemoval;
private: private:
uint8_t m_voice; uint8_t m_voice;
@ -566,7 +571,7 @@ private:
uint8_t status; uint8_t status;
} m_retainerInfo[8]; } m_retainerInfo[8];
uint16_t m_title; uint16_t m_activeTitle;
uint8_t m_titleList[48]; uint8_t m_titleList[48];
uint8_t m_howTo[33]; uint8_t m_howTo[33];
uint8_t m_minions[33]; uint8_t m_minions[33];

View file

@ -81,15 +81,21 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession )
setRotation( 0.0f ); setRotation( 0.0f );
} }
// Stats
m_hp = res->getUInt( "Hp" ); m_hp = res->getUInt( "Hp" );
m_mp = res->getUInt( "Mp" ); m_mp = res->getUInt( "Mp" );
m_tp = 0; m_tp = 0;
// Position
m_pos.x = res->getFloat( "PosX" ); m_pos.x = res->getFloat( "PosX" );
m_pos.y = res->getFloat( "PosY" ); m_pos.y = res->getFloat( "PosY" );
m_pos.z = res->getFloat( "PosZ" ); m_pos.z = res->getFloat( "PosZ" );
setRotation( res->getFloat( "PosR" ) ); setRotation( res->getFloat( "PosR" ) );
// Model
auto custom = res->getBlobVector( "Customize" ); auto custom = res->getBlobVector( "Customize" );
memcpy( reinterpret_cast< char* >( m_customize ), custom.data(), custom.size() ); memcpy( reinterpret_cast< char* >( m_customize ), custom.data(), custom.size() );
@ -98,6 +104,8 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession )
auto modelEq = res->getBlobVector( "ModelEquip" ); auto modelEq = res->getBlobVector( "ModelEquip" );
memcpy( reinterpret_cast< char* >( m_modelEquip ), modelEq.data(), modelEq.size() ); memcpy( reinterpret_cast< char* >( m_modelEquip ), modelEq.data(), modelEq.size() );
// Minimal info
m_guardianDeity = res->getUInt8( "GuardianDeity" ); m_guardianDeity = res->getUInt8( "GuardianDeity" );
m_birthDay = res->getUInt8( "BirthDay" ); m_birthDay = res->getUInt8( "BirthDay" );
m_birthMonth = res->getUInt8( "BirthMonth" ); m_birthMonth = res->getUInt8( "BirthMonth" );
@ -105,12 +113,27 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession )
m_class = static_cast< ClassJob >( res->getUInt( "Class" ) ); m_class = static_cast< ClassJob >( res->getUInt( "Class" ) );
m_homePoint = res->getUInt8( "Homepoint" ); m_homePoint = res->getUInt8( "Homepoint" );
auto howTo = res->getBlobVector( "HowTo" ); // Additional data
memcpy( reinterpret_cast< char* >( m_howTo ), howTo.data(), howTo.size() );
m_contentId = res->getUInt64( "ContentId" ); m_contentId = res->getUInt64( "ContentId" );
m_voice = res->getUInt8( "Voice" ); m_voice = res->getUInt8( "Voice" );
m_startTown = res->getUInt8( "StartTown" );
m_playTime = res->getUInt( "TotalPlayTime" );
m_bNewGame = res->getBoolean( "IsNewGame" );
m_bNewAdventurer = res->getBoolean( "IsNewAdventurer" );
m_openingSequence = res->getUInt8( "OpeningSequence" );
m_gc = res->getUInt8( "GrandCompany" );
m_cfPenaltyUntil = res->getUInt( "CFPenaltyUntil" );
m_activeTitle = res->getUInt16( "ActiveTitle" );
m_gmRank = res->getUInt8( "GMRank" );
// Blobs
auto howTo = res->getBlobVector( "HowTo" );
memcpy( reinterpret_cast< char* >( m_howTo ), howTo.data(), howTo.size() );
auto questCompleteFlags = res->getBlobVector( "QuestCompleteFlags" ); auto questCompleteFlags = res->getBlobVector( "QuestCompleteFlags" );
memcpy( reinterpret_cast< char* >( m_questCompleteFlags ), questCompleteFlags.data(), questCompleteFlags.size() ); memcpy( reinterpret_cast< char* >( m_questCompleteFlags ), questCompleteFlags.data(), questCompleteFlags.size() );
@ -118,8 +141,6 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession )
auto questTracking = res->getBlobVector( "QuestTracking" ); auto questTracking = res->getBlobVector( "QuestTracking" );
memcpy( reinterpret_cast< char* >( m_questTracking ), questTracking.data(), questTracking.size() ); memcpy( reinterpret_cast< char* >( m_questTracking ), questTracking.data(), questTracking.size() );
m_bNewGame = res->getBoolean( "IsNewGame" );
auto aetheryte = res->getBlobVector( "Aetheryte" ); auto aetheryte = res->getBlobVector( "Aetheryte" );
memcpy( reinterpret_cast< char* >( m_aetheryte ), aetheryte.data(), aetheryte.size() ); memcpy( reinterpret_cast< char* >( m_aetheryte ), aetheryte.data(), aetheryte.size() );
@ -129,21 +150,12 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession )
auto discovery = res->getBlobVector( "Discovery" ); auto discovery = res->getBlobVector( "Discovery" );
memcpy( reinterpret_cast< char* >( m_discovery ), discovery.data(), discovery.size() ); memcpy( reinterpret_cast< char* >( m_discovery ), discovery.data(), discovery.size() );
m_startTown = res->getUInt8( "StartTown" ); auto titleList = res->getBlobVector( "TitleList" );
m_playTime = res->getUInt( "TotalPlayTime" ); memcpy( reinterpret_cast< char* >( m_titleList ), titleList.data(), titleList.size() );
m_bNewAdventurer = res->getBoolean( "IsNewAdventurer" );
m_gc = res->getUInt8( "GrandCompany" );
auto gcRank = res->getBlobVector( "GrandCompanyRank" ); auto gcRank = res->getBlobVector( "GrandCompanyRank" );
memcpy( reinterpret_cast< char* >( m_gcRank ), gcRank.data(), gcRank.size() ); memcpy( reinterpret_cast< char* >( m_gcRank ), gcRank.data(), gcRank.size() );
m_cfPenaltyUntil = res->getUInt( "CFPenaltyUntil" );
m_openingSequence = res->getUInt8( "OpeningSequence" );
m_gmRank = res->getUInt8( "GMRank" );
res->free(); res->free();
m_pCell = nullptr; m_pCell = nullptr;
@ -338,7 +350,7 @@ void Core::Entity::Player::updateSql()
stmt->setInt( 35, 0 ); // RestPoint stmt->setInt( 35, 0 ); // RestPoint
stmt->setInt( 36, 0 ); // ActiveTitle stmt->setInt( 36, 0 ); // ActiveTitle
std::vector< uint8_t > titleListVec( 32 ); std::vector< uint8_t > titleListVec( sizeof ( m_titleList ) );
stmt->setBinary( 37, titleListVec ); stmt->setBinary( 37, titleListVec );
std::vector< uint8_t > achievementVec( 16 ); std::vector< uint8_t > achievementVec( 16 );
@ -402,6 +414,8 @@ void Core::Entity::Player::updateSql()
////// Class ////// Class
updateDbClass(); updateDbClass();
memset( m_orchestrion, 0, sizeof( m_orchestrion ) );
} }
void Core::Entity::Player::updateDbClass() const void Core::Entity::Player::updateDbClass() const
@ -484,6 +498,4 @@ void Core::Entity::Player::insertQuest( uint16_t questId, uint8_t index, uint8_t
stmt->setInt( 11, 0 ); stmt->setInt( 11, 0 );
stmt->setInt( 12, 0 ); stmt->setInt( 12, 0 );
g_charaDb.execute( stmt ); g_charaDb.execute( stmt );
} }

View file

@ -4,7 +4,21 @@ cmake_policy(SET CMP0014 OLD)
project(Sapphire_Zone) project(Sapphire_Zone)
file(GLOB SERVER_PUBLIC_INCLUDE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/* ${CMAKE_CURRENT_SOURCE_DIR}/Script/*) file(GLOB SERVER_PUBLIC_INCLUDE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
/*.h
Actor/*.h
Action/*.h
DebugCommand/*.h
Event/*.h
Inventory/*.h
Linkshell/*.h
Network/*.h
Network/Handlers/*.h
Network/PacketWrappers/*.h
Script/*.h
StatusEffect/*.h
Zone/*.h)
file(GLOB SERVER_SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} file(GLOB SERVER_SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
*.c* *.c*
Actor/*.c* Actor/*.c*
@ -33,6 +47,7 @@ set_target_properties(server_zone PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
) )
if (UNIX) if (UNIX)
@ -41,4 +56,4 @@ else()
target_link_libraries ( server_zone Common xivdat libmysql zlib1 mysqlConnector ) target_link_libraries ( server_zone Common xivdat libmysql zlib1 mysqlConnector )
endif() endif()
target_link_libraries(server_zone ${Boost_LIBRARIES} ${Boost_LIBRARIES} ) target_link_libraries(server_zone ${Boost_LIBRARIES} )

View file

@ -32,6 +32,8 @@
#include <Server_Common/Database/DatabaseDef.h> #include <Server_Common/Database/DatabaseDef.h>
#include <cinttypes>
extern Core::Scripting::ScriptManager g_scriptMgr; extern Core::Scripting::ScriptManager g_scriptMgr;
extern Core::Data::ExdData g_exdData; extern Core::Data::ExdData g_exdData;
extern Core::Logger g_log; extern Core::Logger g_log;
@ -256,7 +258,7 @@ void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlaye
else if ( subCommand == "eorzeatime" ) else if ( subCommand == "eorzeatime" )
{ {
uint64_t timestamp; uint64_t timestamp;
sscanf( params.c_str(), "%llu", &timestamp ); sscanf( params.c_str(), "%" SCNu64, &timestamp );
pPlayer->setEorzeaTimeOffset( timestamp ); pPlayer->setEorzeaTimeOffset( timestamp );
pPlayer->sendNotice( "Eorzea time offset: " + std::to_string( timestamp ) ); pPlayer->sendNotice( "Eorzea time offset: " + std::to_string( timestamp ) );

View file

@ -1,12 +1,11 @@
#include "LinkshellMgr.h" #include "LinkshellMgr.h"
#include <Server_Common/Database/Database.h>
#include <Server_Common/Logging/Logger.h> #include <Server_Common/Logging/Logger.h>
#include <boost/make_shared.hpp> #include <boost/make_shared.hpp>
#include <servers/Server_Common/Database/DatabaseDef.h>
#include "Linkshell.h" #include "Linkshell.h"
extern Core::Logger g_log; extern Core::Logger g_log;
extern Core::Db::Database g_database;
Core::LinkshellMgr::LinkshellMgr() Core::LinkshellMgr::LinkshellMgr()
{ {
@ -16,47 +15,47 @@ Core::LinkshellMgr::LinkshellMgr()
bool Core::LinkshellMgr::loadLinkshells() bool Core::LinkshellMgr::loadLinkshells()
{ {
auto res = g_database.query( "SELECT LinkshellId, MasterCharacterId, CharacterIdList, " auto res = g_charaDb.query( "SELECT LinkshellId, MasterCharacterId, CharacterIdList, "
"LinkshellName, LeaderIdList, InviteIdList " "LinkshellName, LeaderIdList, InviteIdList "
"FROM infolinkshell " "FROM infolinkshell "
"ORDER BY LinkshellId ASC;" ); "ORDER BY LinkshellId ASC;" );
// we do not really need linkshells to function...
if( !res )
return true;
Db::Field *field = res->fetch(); while( res->next() )
do
{ {
uint64_t linkshellId = field[0].get< uint64_t >(); uint64_t linkshellId = res->getUInt64( 1 );
uint32_t masterId = field[1].get< uint32_t >(); uint32_t masterId = res->getUInt( 2 );
std::string name = field[3].getString(); std::string name = res->getString( 4 );
auto func = []( std::set< uint64_t >& outList, Db::Field * pField ) auto func = []( std::set< uint64_t >& outList, std::vector< char >& inData )
{ {
if( pField->getLength() ) if( inData.size() )
{ {
std::vector< uint64_t > list( pField->getLength() / 8 ); std::vector< uint64_t > list( inData.size() / 8 );
pField->getBinary( reinterpret_cast< char * >( &list[0] ), pField->getLength() );
outList.insert( list.begin(), list.end() ); outList.insert( list.begin(), list.end() );
} }
}; };
std::set< uint64_t > members; std::set< uint64_t > members;
func( members, &field[2] ); std::vector< char > membersBin;
membersBin = res->getBlobVector( 3 );
func( members, membersBin );
std::set< uint64_t > leaders; std::set< uint64_t > leaders;
func( members, &field[4] ); std::vector< char > leadersBin;
leadersBin = res->getBlobVector( 5 );
func( members, leadersBin );
std::set< uint64_t > invites; std::set< uint64_t > invites;
func( members, &field[5] ); std::vector< char > invitesBin;
invitesBin = res->getBlobVector( 6 );
func( members, invitesBin );
auto lsPtr = boost::make_shared< Linkshell >( linkshellId, name, masterId, members, leaders, invites ); auto lsPtr = boost::make_shared< Linkshell >( linkshellId, name, masterId, members, leaders, invites );
m_linkshellIdMap[linkshellId] = lsPtr; m_linkshellIdMap[linkshellId] = lsPtr;
m_linkshellNameMap[name] = lsPtr; m_linkshellNameMap[name] = lsPtr;
} while( res->nextRow() ); }
return true; return true;

View file

@ -330,7 +330,11 @@ void Core::Network::GameConnection::injectPacket( const std::string& packetpath,
fseek( fp, 0, SEEK_END ); fseek( fp, 0, SEEK_END );
int32_t size = ftell( fp ); int32_t size = ftell( fp );
rewind( fp ); rewind( fp );
fread( packet, sizeof( char ), size, fp ); if ( fread( packet, sizeof( char ), size, fp ) != size )
{
g_log.error( "Packet " + packetpath + " did not read full size: " + std::to_string( size ) );
return;
}
fclose( fp ); fclose( fp );
// cycle through the packet entries and queue each one // cycle through the packet entries and queue each one
@ -389,8 +393,7 @@ void Core::Network::GameConnection::handlePackets( const Core::Network::Packets:
} }
session = g_serverZone.getSession( playerId ); session = g_serverZone.getSession( playerId );
} }
else if( !session->isValid() || ( session->getPlayer() && session->getPlayer()->getLastPing() != 0 ) ) //TODO: Catch more things in lobby and send real errors
if( !session->isValid() ) //TODO: Catch more things in lobby and send real errors
{ {
g_log.error( "[" + std::string(id) + "] Session INVALID, disconnecting" ); g_log.error( "[" + std::string(id) + "] Session INVALID, disconnecting" );
Disconnect(); Disconnect();

View file

@ -333,7 +333,7 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac
GamePacketNew< FFXIVIpcSetSearchInfo, ServerZoneIpcType > searchInfoPacket( targetPlayer->getId() ); GamePacketNew< FFXIVIpcSetSearchInfo, ServerZoneIpcType > searchInfoPacket( targetPlayer->getId() );
searchInfoPacket.data().onlineStatusFlags = param1; searchInfoPacket.data().onlineStatusFlags = param1;
searchInfoPacket.data().selectRegion = targetPlayer->getSearchSelectRegion(); searchInfoPacket.data().selectRegion = targetPlayer->getSearchSelectRegion();
sprintf( searchInfoPacket.data().searchMessage, targetPlayer->getSearchMessage() ); strcpy( searchInfoPacket.data().searchMessage, targetPlayer->getSearchMessage() );
targetPlayer->queuePacket( searchInfoPacket ); targetPlayer->queuePacket( searchInfoPacket );
targetPlayer->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), SetStatusIcon, targetPlayer->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), SetStatusIcon,

View file

@ -84,7 +84,7 @@ void Core::Network::GameConnection::setSearchInfoHandler( const Packets::GamePac
GamePacketNew< FFXIVIpcSetSearchInfo, ServerZoneIpcType > searchInfoPacket( pPlayer->getId() ); GamePacketNew< FFXIVIpcSetSearchInfo, ServerZoneIpcType > searchInfoPacket( pPlayer->getId() );
searchInfoPacket.data().onlineStatusFlags = status; searchInfoPacket.data().onlineStatusFlags = status;
searchInfoPacket.data().selectRegion = pPlayer->getSearchSelectRegion(); searchInfoPacket.data().selectRegion = pPlayer->getSearchSelectRegion();
sprintf( searchInfoPacket.data().searchMessage, pPlayer->getSearchMessage() ); strcpy( searchInfoPacket.data().searchMessage, pPlayer->getSearchMessage() );
queueOutPacket( searchInfoPacket ); queueOutPacket( searchInfoPacket );
pPlayer->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), SetStatusIcon, pPlayer->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), SetStatusIcon,
@ -98,7 +98,7 @@ void Core::Network::GameConnection::reqSearchInfoHandler( const Packets::GamePac
GamePacketNew< FFXIVIpcInitSearchInfo, ServerZoneIpcType > searchInfoPacket( pPlayer->getId() ); GamePacketNew< FFXIVIpcInitSearchInfo, ServerZoneIpcType > searchInfoPacket( pPlayer->getId() );
searchInfoPacket.data().onlineStatusFlags = pPlayer->getOnlineStatusMask(); searchInfoPacket.data().onlineStatusFlags = pPlayer->getOnlineStatusMask();
searchInfoPacket.data().selectRegion = pPlayer->getSearchSelectRegion(); searchInfoPacket.data().selectRegion = pPlayer->getSearchSelectRegion();
sprintf( searchInfoPacket.data().searchMessage, pPlayer->getSearchMessage() ); strcpy( searchInfoPacket.data().searchMessage, pPlayer->getSearchMessage() );
queueOutPacket( searchInfoPacket ); queueOutPacket( searchInfoPacket );
} }
@ -540,6 +540,8 @@ void Core::Network::GameConnection::logoutHandler( const Packets::GamePacket& in
logoutPacket.data().flags1 = 0x02; logoutPacket.data().flags1 = 0x02;
logoutPacket.data().flags2 = 0x2000; logoutPacket.data().flags2 = 0x2000;
queueOutPacket( logoutPacket ); queueOutPacket( logoutPacket );
pPlayer->setMarkedForRemoval();
} }

View file

@ -51,7 +51,7 @@ private:
memset( &m_data.name[0], 0, sizeof( m_data.name ) ); memset( &m_data.name[0], 0, sizeof( m_data.name ) );
sprintf( &m_data.name[0], player->getName().c_str() ); strcpy( &m_data.name[0], player->getName().c_str() );
memcpy( m_data.aetheryte, player->getAetheryteArray(), sizeof ( m_data.aetheryte ) ); memcpy( m_data.aetheryte, player->getAetheryteArray(), sizeof ( m_data.aetheryte ) );

View file

@ -1,7 +1,7 @@
#include <src/servers/Server_Common/Logging/Logger.h>
#include <chaiscript/chaiscript.hpp> #include <chaiscript/chaiscript.hpp>
#include <src/servers/Server_Common/Script/ChaiscriptStdLib.h> #include <Server_Common/Logging/Logger.h>
#include <Server_Common/Script/ChaiscriptStdLib.h>
#include "src/servers/Server_Zone/Script/ScriptManager.h" #include "src/servers/Server_Zone/Script/ScriptManager.h"
@ -15,11 +15,6 @@
#include "src/servers/Server_Zone/StatusEffect/StatusEffect.h" #include "src/servers/Server_Zone/StatusEffect/StatusEffect.h"
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/format.hpp>
#include <boost/foreach.hpp>
extern Core::Logger g_log; extern Core::Logger g_log;

View file

@ -1,35 +1,29 @@
#include <thread>
#include <chrono>
#include <boost/lexical_cast.hpp>
#include "ServerZone.h" #include "ServerZone.h"
#include <src/servers/Server_Common/Version.h> #include <src/servers/Server_Common/Version.h>
#include <src/servers/Server_Common/Logging/Logger.h> #include <src/servers/Server_Common/Logging/Logger.h>
#include <src/servers/Server_Common/Config/XMLConfig.h> #include <src/servers/Server_Common/Config/XMLConfig.h>
#include <src/servers/Server_Common/Database/Database.h>
#include <src/servers/Server_Common/Version.h> #include <src/servers/Server_Common/Version.h>
#include <MySqlBase.h> #include <MySqlBase.h>
#include <Connection.h> #include <Connection.h>
#include <Statement.h>
#include <ResultSet.h>
#include <PreparedStatement.h>
#include <PreparedResultSet.h>
#include <src/servers/Server_Common/Network/Connection.h> #include <Server_Common/Network/Connection.h>
#include <src/servers/Server_Common/Network/Hive.h> #include <Server_Common/Network/Hive.h>
#include <src/servers/Server_Common/Network/Acceptor.h>
#include <src/servers/Server_Common/Exd/ExdData.h> #include <Server_Common/Exd/ExdData.h>
#include <src/servers/Server_Common/Network/PacketContainer.h> #include <Server_Common/Network/PacketContainer.h>
#include <Server_Common/Database/DbLoader.h>
#include <Server_Common/Database/CharaDbConnection.h>
#include <Server_Common/Database/DbWorkerPool.h>
#include <Server_Common/Database/PreparedStatement.h>
#include "src/servers/Server_Zone/Network/GameConnection.h" #include "Network/GameConnection.h"
#include "Session.h" #include "Session.h"
#include "src/servers/Server_Zone/Zone/ZoneMgr.h" #include "Zone/ZoneMgr.h"
#include "src/servers/Server_Zone/DebugCommand/DebugCommandHandler.h" #include "DebugCommand/DebugCommandHandler.h"
#include "Script/ScriptManager.h" #include "Script/ScriptManager.h"
#include "Linkshell/LinkshellMgr.h" #include "Linkshell/LinkshellMgr.h"
@ -38,15 +32,9 @@
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/make_shared.hpp> #include <boost/make_shared.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <thread>
#include <Server_Common/Database/DbLoader.h>
#include <Server_Common/Database/CharaDbConnection.h>
#include <Server_Common/Database/DbWorkerPool.h>
#include <Server_Common/Database/PreparedStatement.h>
Core::Logger g_log; Core::Logger g_log;
Core::Db::Database g_database;
Core::DebugCommandHandler g_gameCommandMgr; Core::DebugCommandHandler g_gameCommandMgr;
Core::Scripting::ScriptManager g_scriptMgr; Core::Scripting::ScriptManager g_scriptMgr;
Core::Data::ExdData g_exdData; Core::Data::ExdData g_exdData;
@ -54,7 +42,7 @@ Core::ZoneMgr g_zoneMgr;
Core::LinkshellMgr g_linkshellMgr; Core::LinkshellMgr g_linkshellMgr;
Core::Db::DbWorkerPool< Core::Db::CharaDbConnection > g_charaDb; Core::Db::DbWorkerPool< Core::Db::CharaDbConnection > g_charaDb;
Core::ServerZone::ServerZone( const std::string& configPath, uint16_t serverId ) Core::ServerZone::ServerZone( const std::string& configPath )
: m_configPath( configPath ), : m_configPath( configPath ),
m_bRunning( true ) m_bRunning( true )
{ {
@ -95,7 +83,7 @@ bool Core::ServerZone::registerBnpcTemplate( std::string templateName, uint32_t
Core::Entity::BattleNpcTemplatePtr Core::ServerZone::getBnpcTemplate( std::string templateName ) Core::Entity::BattleNpcTemplatePtr Core::ServerZone::getBnpcTemplate( std::string templateName )
{ {
auto it = m_bnpcTemplates.find(templateName); auto it = m_bnpcTemplates.find( templateName );
if (it != m_bnpcTemplates.end()) if (it != m_bnpcTemplates.end())
return nullptr; return nullptr;
@ -190,131 +178,8 @@ bool Core::ServerZone::loadSettings( int32_t argc, char* argv[] )
if( !loader.initDbs() ) if( !loader.initDbs() )
return false; return false;
// execute() runs asynchronous
g_charaDb.execute( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( 101, '127.0.0.1', 54555);" );
g_charaDb.execute( "DELETE FROM zoneservers WHERE id = 101" );
// query runs synchronous
auto res = g_charaDb.query( "SELECT id,ip,port FROM zoneservers" );
while( res->next() )
{
g_log.info( "id: " + std::to_string( res->getUInt( "id" ) ) );
g_log.info( "ip: " + res->getString( "ip" ) );
g_log.info( "port: " + std::to_string( res->getUInt( "port" ) ) );
}
//stmt->setUInt( 1, 245 );
//stmt->setString( 2, "12.12.12.12" );
//stmt->setUInt( 3, 3306 );
//CharacterDatabase.execute( stmt );
try
{
// // bunch of test cases for db wrapper
boost::shared_ptr< Mysql::MySqlBase > base( new Mysql::MySqlBase() );
g_log.info( base->getVersionInfo() );
Mysql::optionMap options;
options[ MYSQL_OPT_RECONNECT ] = "1";
auto con = base->connect( "127.0.0.1", "root", "", options, 3306 );
// if( con->getAutoCommit() )
// g_log.info( "autocommit active" );
// con->setAutoCommit( false );
// if( !con->getAutoCommit() )
// g_log.info( "autocommit inactive" );
// con->setAutoCommit( true );
// if( con->getAutoCommit() )
// g_log.info( "autocommit active" );
con->setSchema( "sapphire" );
// boost::scoped_ptr< Mysql::Statement > stmt( con->createStatement() );
// bool t1 = stmt->execute( "DELETE FROM zoneservers WHERE id = 101" );
// t1 = stmt->execute( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( 101, '127.0.0.1', 54555);" );
// // t1 = stmt->execute( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( 101, '127.0.0.1', 54555);" ); // throws duplicate entry
// t1 = stmt->execute( "DELETE FROM zoneservers WHERE id = 101" );
// t1 = stmt->execute( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( 101, '127.0.0.1', 54555);" );
// //t1 = stmt->execute( "DELETE FROM zoneservers WHERE id = 101" );
// //boost::scoped_ptr< Mysql::Statement > stmt1( con->createStatement() );
// //bool t2 = stmt1->execute( "INSERT INTO BLARGH!" ); // throws error
// boost::scoped_ptr< Mysql::Statement > stmt2( con->createStatement() );
// boost::scoped_ptr< Mysql::ResultSet > res( stmt2->executeQuery( "SELECT id,ip,port FROM zoneservers" ) );
// while( res->next() )
// {
// g_log.info( "id: " + std::to_string( res->getUInt( "id" ) ) );
// g_log.info( "ip: " + res->getString( "ip" ) );
// g_log.info( "port: " + std::to_string( res->getUInt( "port" ) ) );
// // alternatively ( slightly faster )
// // g_log.info( "id: " + std::to_string( res->getUInt( 1 ) ) );
// // g_log.info( "ip: " + res->getString( 2 ) );
// // g_log.info( "port: " + std::to_string( res->getUInt( 3 ) ) );
// }
// // binary data test
// boost::scoped_ptr< Mysql::Statement > stmt3( con->createStatement() );
// boost::scoped_ptr< Mysql::ResultSet > res1( stmt3->executeQuery( "SELECT * FROM charabase" ) );
// while( res1->next() )
// {
// auto blob = res1->getBlobVector( "Customize" );
// }
// pstmt2->setInt( 1, 1001 );
// pstmt2->execute();
// boost::scoped_ptr< Mysql::PreparedStatement > pstmt( con->prepareStatement( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( ?, ?, ?);" ) );
// pstmt->setInt( 1, 1001 );
// pstmt->setString( 2, "123.123.123.123" );
// pstmt->setInt( 3, 5454 );
// pstmt->execute();
// pstmt->setInt( 1, 1021 );
// pstmt->setString( 2, "173.173.173.173" );
// pstmt->setInt( 3, 5151 );
// pstmt->execute();
// boost::scoped_ptr< Mysql::PreparedStatement > pstmt1( con->prepareStatement( "DELETE FROM zoneservers WHERE id = ?" ) );
// pstmt->setInt( 1, 1021 );
// pstmt->execute();
// pstmt->setInt( 1, 1001 );
// pstmt->execute();
}
catch( std::runtime_error e )
{
g_log.error( e.what() );
}
Db::DatabaseParams params;
params.bufferSize = 16384;
params.connectionCount = 3;
params.databaseName = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Database", "sapphire" );
params.hostname = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Host", "127.0.0.1" );
params.password = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Pass", "" );
params.port = m_pConfig->getValue< uint16_t >( "Settings.General.Mysql.Port", 3306 );
params.username = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Username", "root" );
if( !g_database.initialize( params ) )
{
std::this_thread::sleep_for( std::chrono::milliseconds( 5000 ) );
return false;
}
m_port = m_pConfig->getValue< uint16_t >( "Settings.General.ListenPort", 54992 ); m_port = m_pConfig->getValue< uint16_t >( "Settings.General.ListenPort", 54992 );
m_ip = m_pConfig->getValue< std::string >( "Settings.General.ListenIp", "0.0.0.0" );; m_ip = m_pConfig->getValue< std::string >( "Settings.General.ListenIp", "0.0.0.0" );
return true; return true;
} }
@ -398,10 +263,12 @@ void Core::ServerZone::mainLoop()
auto session = sessionIt.second; auto session = sessionIt.second;
if( session && session->getPlayer() ) if( session && session->getPlayer() )
{ {
// if the player is in a zone, let the zone handler take care of his updates // if the player is in a zone, let the zone handler take care of his updates
// else do it here. // else do it here.
if( !session->getPlayer()->getCurrentZone() ) if( !session->getPlayer()->getCurrentZone() )
session->update(); session->update();
} }
} }
@ -413,6 +280,19 @@ void Core::ServerZone::mainLoop()
auto pPlayer = it->second->getPlayer(); auto pPlayer = it->second->getPlayer();
// remove session of players marked for removel ( logoff / kick )
if( pPlayer->isMarkedForRemoval() && diff > 1 )
{
it->second->close();
// if( it->second.unique() )
{
g_log.info("[" + std::to_string(it->second->getId() ) + "] Session removal" );
it = this->m_sessionMap.erase( it );
continue;
}
}
// remove sessions that simply timed out
if( diff > 20 ) if( diff > 20 )
{ {
g_log.info("[" + std::to_string(it->second->getId() ) + "] Session time out" ); g_log.info("[" + std::to_string(it->second->getId() ) + "] Session time out" );

View file

@ -15,7 +15,7 @@ namespace Core {
class ServerZone class ServerZone
{ {
public: public:
ServerZone( const std::string& configPath, uint16_t serverId = 0 ); ServerZone( const std::string& configPath );
~ServerZone(); ~ServerZone();
void run( int32_t argc, char* argv[] ); void run( int32_t argc, char* argv[] );

View file

@ -125,7 +125,7 @@ void Core::Session::update()
m_pZoneConnection->processOutQueue(); m_pZoneConnection->processOutQueue();
} }
if( m_pZoneConnection ) if( m_pChatConnection )
{ {
m_pChatConnection->processInQueue(); m_pChatConnection->processInQueue();
m_pChatConnection->processOutQueue(); m_pChatConnection->processOutQueue();

View file

@ -0,0 +1,24 @@
collision data exporter for sapphire
compile with STANDALONE defined to compile without boost and sapphire dependencies
usage:
- regular
- compile with root sapphire dir cmakelists
- sapphire/src/tools/bin/pcb_reader2 "<path/to/game/sqpack/ffxiv>" <territory>
- standalone
- compile main.cpp with STANDALONE defined in build arg
- download ffxivexplorer <http://ffxivexplorer.fragmenterworks.com/>
- ffxivexplorer > path/to/ffxiv's/game/sqpack/ffxiv/020000.dat
- ctrl click the following:
- `bg/ffxiv/[REGION]/common/collision`
- `bg/ffxiv/[REGION]/[dun|fld|twn|etc..]/common/collision/`
- `bg/ffxiv/[REGION]/[dun|fld|twn|etc..]/collision/`
- `bg/ffxiv/region/shared/[for_bg|for_hou]/`
- `bg/ffxiv/[REGION]/[dun|fld|twn|etc..]/ZONE/level/`
- `bg/ffxiv/[REGION]/[dun|fld|twn|etc..]/ZONE/collision/`
- `bgcommon/world/sys/shared/for_bg/`
and `File > Export Raw` to pcb_reader exe dir (common and shared files are optional but you will be missing a lot of objects if you skip them)
- note: at this time ffxivexplorer is still missing some hashes, though any tool which can export the exds should work fine
- main "" <territory>

View file

@ -1,21 +1,23 @@
#include <stdio.h> #include <stdio.h>
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <iostream>
#include <chrono>
#include <fstream>
#include "pcb.h" #include "pcb.h"
#include "lgb.h" #include "lgb.h"
#include "sgb.h" #include "sgb.h"
#ifndef STANDALONE
#include <GameData.h> #include <GameData.h>
#include <File.h> #include <File.h>
#include <DatCat.h> #include <DatCat.h>
#include <ExdData.h> #include <ExdData.h>
#include <ExdCat.h> #include <ExdCat.h>
#include <Exd.h> #include <Exd.h>
//#include <boost/algorithm/string.hpp>
#include <iostream> #endif
#include <boost/algorithm/string.hpp>
#include <chrono>
using namespace std::chrono_literals; using namespace std::chrono_literals;
@ -86,7 +88,7 @@ std::string zoneNameToPath( const std::string& name )
char region = name[1]; char region = name[1];
char type = name[2]; char type = name[2];
char zone = name[3]; char zone = name[3];
static std::map<char, std::string> teriMap static std::map< char, std::string > teriMap
{ {
{ 'r', "roc" }, { 'r', "roc" },
{ 'w', "wil" }, { 'w', "wil" },
@ -98,7 +100,7 @@ std::string zoneNameToPath( const std::string& name )
{ 'z', "zon" } { 'z', "zon" }
}; };
static std::map<char, std::string> typeMap static std::map< char, std::string > typeMap
{ {
{ 'f', "fld" }, { 'f', "fld" },
{ 't', "twn" }, { 't', "twn" },
@ -117,6 +119,24 @@ std::string zoneNameToPath( const std::string& name )
return ret; return ret;
} }
void readFileToBuffer( const std::string& path, std::vector< char >& buf )
{
auto inFile = std::ifstream( path, std::ios::binary );
if( inFile.good() )
{
inFile.seekg( 0, inFile.end );
int32_t fileSize = inFile.tellg();
buf.resize( fileSize );
inFile.seekg( 0, inFile.beg );
inFile.read( &buf[0], fileSize );
inFile.close();
}
else
{
throw std::runtime_error( "Unable to open " + path );
}
}
int main( int argc, char* argv[] ) int main( int argc, char* argv[] )
{ {
auto startTime = std::chrono::system_clock::now(); auto startTime = std::chrono::system_clock::now();
@ -136,21 +156,35 @@ int main( int argc, char* argv[] )
try try
{ {
std::string listPcbPath( "bg/ffxiv/" + zonePath + "/collision/list.pcb" );
std::string bgLgbPath( "bg/ffxiv/" + zonePath + "/level/bg.lgb" );
std::string collisionFilePath( "bg/ffxiv/" + zonePath + "/collision/" );
std::vector< char > section;
std::vector< char > section1;
#ifndef STANDALONE
xiv::dat::GameData data1( gamePath ); xiv::dat::GameData data1( gamePath );
xiv::exd::ExdData eData( data1 ); xiv::exd::ExdData eData( data1 );
const xiv::dat::Cat& test = data1.get_category( "bg" ); const xiv::dat::Cat& test = data1.get_category( "bg" );
auto test_file = data1.get_file( "bg/ffxiv/" + zonePath + "/level/bg.lgb" ); auto test_file = data1.get_file( bgLgbPath );
auto section = test_file->access_data_sections().at( 0 ); section = test_file->access_data_sections().at( 0 );
int32_t list_offset = *(uint32_t*)&section[0x18];
int32_t size = *(uint32_t*)&section[4];
std::vector<std::string> stringList; auto test_file1 = data1.get_file( listPcbPath );
section1 = test_file1->access_data_sections().at( 0 );
#else
{
readFileToBuffer( bgLgbPath, section );
readFileToBuffer( listPcbPath, section1 );
}
#endif
int32_t list_offset = *( uint32_t* )&section[0x18];
int32_t size = *( uint32_t* )&section[4];
std::vector< std::string > stringList;
auto test_file1 = data1.get_file( "bg/ffxiv/" + zonePath + "/collision/list.pcb" );
auto section1 = test_file1->access_data_sections().at( 0 );
std::string path = "bg/ffxiv/" + zonePath + "/collision/";
uint32_t offset1 = 0x20; uint32_t offset1 = 0x20;
for( ; ; ) for( ; ; )
{ {
@ -158,7 +192,7 @@ int main( int argc, char* argv[] )
uint16_t trId = *(uint16_t*)&section1[offset1]; uint16_t trId = *(uint16_t*)&section1[offset1];
char someString[200]; char someString[200];
sprintf( someString, "%str%04d.pcb", path.c_str(), trId ); sprintf( someString, "%str%04d.pcb", collisionFilePath.c_str(), trId );
stringList.push_back( std::string( someString ) ); stringList.push_back( std::string( someString ) );
//std::cout << someString << "\n"; //std::cout << someString << "\n";
offset1 += 0x20; offset1 += 0x20;
@ -191,18 +225,24 @@ int main( int argc, char* argv[] )
fp_out = fopen( ( zoneName + ".obj" ).c_str(), "ab+" ); fp_out = fopen( ( zoneName + ".obj" ).c_str(), "ab+" );
if( fp_out ) if( fp_out )
{ {
std::map<std::string, PCB_FILE> pcbFiles; std::map< std::string, PCB_FILE > pcbFiles;
std::map<std::string, SGB_FILE> sgbFiles; std::map< std::string, SGB_FILE > sgbFiles;
std::map<std::string, uint32_t> objCount; std::map< std::string, uint32_t > objCount;
auto loadPcbFile = [&]( const std::string& fileName ) -> bool auto loadPcbFile = [&]( const std::string& fileName ) -> bool
{ {
try try
{ {
char* dataSection = nullptr;
//std::cout << fileName << " "; //std::cout << fileName << " ";
#ifndef STANDALONE
auto file = data1.get_file( fileName ); auto file = data1.get_file( fileName );
auto sections = file->get_data_sections(); auto sections = file->get_data_sections();
auto dataSection = &sections.at( 0 )[0]; dataSection = &sections.at( 0 )[0];
#else
std::vector< char > buf;
readFileToBuffer( fileName, buf );
dataSection = &buf[0];
#endif
//std::cout << sections.size() << "\n"; //std::cout << sections.size() << "\n";
uint32_t offset = 0; uint32_t offset = 0;
@ -244,9 +284,17 @@ int main( int argc, char* argv[] )
SGB_FILE sgbFile; SGB_FILE sgbFile;
try try
{ {
char* dataSection = nullptr;
//std::cout << fileName << " ";
#ifndef STANDALONE
auto file = data1.get_file( fileName ); auto file = data1.get_file( fileName );
auto sections = file->get_data_sections(); auto sections = file->get_data_sections();
auto dataSection = &sections.at( 0 )[0]; dataSection = &sections.at( 0 )[0];
#else
std::vector< char > buf;
readFileToBuffer( fileName, buf );
dataSection = &buf[0];
#endif
sgbFile = SGB_FILE( &dataSection[0] ); sgbFile = SGB_FILE( &dataSection[0] );
sgbFiles.insert( std::make_pair( fileName, sgbFile ) ); sgbFiles.insert( std::make_pair( fileName, sgbFile ) );
return true; return true;
@ -356,8 +404,8 @@ int main( int argc, char* argv[] )
totalGroups++; totalGroups++;
for( const auto& pEntry : group.entries ) for( const auto& pEntry : group.entries )
{ {
auto pGimmick = dynamic_cast<LGB_GIMMICK_ENTRY*>( pEntry.get() ); auto pGimmick = dynamic_cast< LGB_GIMMICK_ENTRY* >( pEntry.get() );
auto pBgParts = dynamic_cast<LGB_BGPARTS_ENTRY*>( pEntry.get() ); auto pBgParts = dynamic_cast< LGB_BGPARTS_ENTRY* >( pEntry.get() );
std::string fileName( "" ); std::string fileName( "" );
fileName.resize( 256 ); fileName.resize( 256 );
@ -410,7 +458,7 @@ int main( int argc, char* argv[] )
{ {
for( const auto& pEntry : group.entries ) for( const auto& pEntry : group.entries )
{ {
auto pModel = dynamic_cast<SGB_MODEL_ENTRY*>( pEntry.get() ); auto pModel = dynamic_cast< SGB_MODEL_ENTRY* >( pEntry.get() );
fileName = pModel->collisionFileName; fileName = pModel->collisionFileName;
writeOutput( fileName, &pGimmick->header.scale, &pGimmick->header.rotation, &pGimmick->header.translation, pModel ); writeOutput( fileName, &pGimmick->header.scale, &pGimmick->header.rotation, &pGimmick->header.translation, pModel );
} }
@ -423,7 +471,7 @@ int main( int argc, char* argv[] )
std::cout << "Total Groups " << totalGroups << " Total entries " << totalGroupEntries << "\n"; std::cout << "Total Groups " << totalGroups << " Total entries " << totalGroupEntries << "\n";
} }
std::cout << "Finished exporting " << zoneName << " in " << std::cout << "Finished exporting " << zoneName << " in " <<
std::chrono::duration_cast<std::chrono::seconds>( std::chrono::system_clock::now() - startTime ).count() << " seconds\n"; std::chrono::duration_cast< std::chrono::seconds >( std::chrono::system_clock::now() - startTime ).count() << " seconds\n";
} }
catch( std::exception& e ) catch( std::exception& e )
{ {

View file

@ -303,7 +303,11 @@ int main()
outputFile1.write(&section[0], section.size()); outputFile1.write(&section[0], section.size());
outputFile1.close(); outputFile1.close();
std::string command= std::string("java -jar unluac_2015_06_13.jar ") + "generated/" + questInfo->name_intern + ".luab" + ">> " + "generated/" + questInfo->name_intern + ".lua"; std::string command= std::string("java -jar unluac_2015_06_13.jar ") + "generated/" + questInfo->name_intern + ".luab" + ">> " + "generated/" + questInfo->name_intern + ".lua";
system(command.c_str()); if ( system( command.c_str() ) == -1 )
{
g_log.error( "Error executing java command:\n" + command + "\nerrno: " + std::strerror( errno ) );
return errno;
}
for( ; ; ) for( ; ; )
{ {