1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-03 01:07: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_322 = 0x0209, // updated 4.1
ActorGauge = 0x249
PerformNote = 0x0252,
};

View file

@ -731,16 +731,16 @@ struct FFXIVIpcInitUI : FFXIVIpcBasePacket<InitUI>
uint8_t preNamePadding;
char name[32];
uint8_t unknown_54[16];
uint8_t unknown55;
uint8_t unknown55[4];
uint16_t levels[25];
uint32_t exp[25];
uint8_t unlockBitmask[64];
uint8_t aetheryte[16];
uint8_t discovery[420];
uint8_t howto[33];
uint8_t minions[33];
uint8_t minions[35];
uint8_t chocoboTaxiMask[8];
uint8_t contentClearMask[104];
uint8_t contentClearMask[105];
uint8_t companionBardingMask[8];
uint8_t companionEquippedHead;
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>
{
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_RELWITHDEBINFO "${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)

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].index = 0;
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 );

View file

@ -17,12 +17,17 @@ set_target_properties(server_rest PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${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)
target_link_libraries (server_rest Common xivdat pthread mysqlclient mysqlConnector dl z)
else()
# ignore unchecked iterators warnings from msvc
add_definitions(-D_SCL_SECURE_NO_WARNINGS)
target_link_libraries (server_rest Common xivdat libmysql mysqlConnector zlib1)
endif()
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
{
auto stmtItemGlobal = g_charaDb.getPreparedStatement(Db::CHARA_ITEMGLOBAL_INS );
stmtItemGlobal->setInt(1, m_id);
auto stmtItemGlobal = g_charaDb.getPreparedStatement( Db::CHARA_ITEMGLOBAL_INS );
stmtItemGlobal->setInt( 1, m_id );
stmtItemGlobal->setInt64( 2, uniqueId );
stmtItemGlobal->setInt( 3, weaponId );
g_charaDb.directExecute( stmtItemGlobal );
@ -362,10 +362,10 @@ namespace Core {
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();" );
if( !res )
if( !res->next() )
return 0;
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
auto pQR = g_charaDb.query( "SELECT account_id FROM accounts WHERE account_name = '" + username + "';" );
// found?
if( !pQR->next() )
if( pQR->next() )
return false;
// 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;
// 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 ) + ", '" +
username + "', '" +
pass + "', " +

View file

@ -70,7 +70,8 @@ Core::Entity::Player::Player() :
m_bLoadingComplete( false ),
m_bMarkedForZoning( false ),
m_zoningType( Common::ZoneingType::None ),
m_bAutoattack( false )
m_bAutoattack( false ),
m_markedForRemoval( false )
{
m_id = 0;
m_type = ActorType::Player;
@ -90,7 +91,7 @@ Core::Entity::Player::Player() :
Core::Entity::Player::~Player()
{
g_log.debug( "PlayerObj destroyed" );
}
// 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;
}
void Core::Entity::Player::setMarkedForRemoval()
{
m_markedForRemoval = true;
}
bool Core::Entity::Player::isMarkedForRemoval() const
{
return m_markedForRemoval;
}
Core::Common::OnlineStatus Core::Entity::Player::getOnlineStatus()
{
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;
}
void Core::Entity::Player::setVoiceId( uint8_t voiceId )
{
m_voice = voiceId;
@ -1396,7 +1406,7 @@ uint8_t * Core::Entity::Player::getTitleList()
uint16_t Core::Entity::Player::getTitle() const
{
return m_title;
return m_activeTitle;
}
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
return;
m_title = titleId;
m_activeTitle = titleId;
sendToInRangeSet( ActorControlPacket142( getId(), SetTitle, titleId ), true );
}

View file

@ -530,6 +530,9 @@ public:
void updateDbSearchInfo() const;
void updateDbClass() const;
void setMarkedForRemoval();
bool isMarkedForRemoval() const;
private:
uint32_t m_lastWrite;
uint32_t m_lastPing;
@ -540,6 +543,8 @@ private:
uint8_t m_mode;
bool m_markedForRemoval;
private:
uint8_t m_voice;
@ -566,7 +571,7 @@ private:
uint8_t status;
} m_retainerInfo[8];
uint16_t m_title;
uint16_t m_activeTitle;
uint8_t m_titleList[48];
uint8_t m_howTo[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 );
}
// Stats
m_hp = res->getUInt( "Hp" );
m_mp = res->getUInt( "Mp" );
m_tp = 0;
// Position
m_pos.x = res->getFloat( "PosX" );
m_pos.y = res->getFloat( "PosY" );
m_pos.z = res->getFloat( "PosZ" );
setRotation( res->getFloat( "PosR" ) );
// Model
auto custom = res->getBlobVector( "Customize" );
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" );
memcpy( reinterpret_cast< char* >( m_modelEquip ), modelEq.data(), modelEq.size() );
// Minimal info
m_guardianDeity = res->getUInt8( "GuardianDeity" );
m_birthDay = res->getUInt8( "BirthDay" );
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_homePoint = res->getUInt8( "Homepoint" );
auto howTo = res->getBlobVector( "HowTo" );
memcpy( reinterpret_cast< char* >( m_howTo ), howTo.data(), howTo.size() );
// Additional data
m_contentId = res->getUInt64( "ContentId" );
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" );
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" );
memcpy( reinterpret_cast< char* >( m_questTracking ), questTracking.data(), questTracking.size() );
m_bNewGame = res->getBoolean( "IsNewGame" );
auto aetheryte = res->getBlobVector( "Aetheryte" );
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" );
memcpy( reinterpret_cast< char* >( m_discovery ), discovery.data(), discovery.size() );
m_startTown = res->getUInt8( "StartTown" );
m_playTime = res->getUInt( "TotalPlayTime" );
m_bNewAdventurer = res->getBoolean( "IsNewAdventurer" );
m_gc = res->getUInt8( "GrandCompany" );
auto titleList = res->getBlobVector( "TitleList" );
memcpy( reinterpret_cast< char* >( m_titleList ), titleList.data(), titleList.size() );
auto gcRank = res->getBlobVector( "GrandCompanyRank" );
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();
m_pCell = nullptr;
@ -338,7 +350,7 @@ void Core::Entity::Player::updateSql()
stmt->setInt( 35, 0 ); // RestPoint
stmt->setInt( 36, 0 ); // ActiveTitle
std::vector< uint8_t > titleListVec( 32 );
std::vector< uint8_t > titleListVec( sizeof ( m_titleList ) );
stmt->setBinary( 37, titleListVec );
std::vector< uint8_t > achievementVec( 16 );
@ -402,6 +414,8 @@ void Core::Entity::Player::updateSql()
////// Class
updateDbClass();
memset( m_orchestrion, 0, sizeof( m_orchestrion ) );
}
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( 12, 0 );
g_charaDb.execute( stmt );
}
}

View file

@ -4,7 +4,21 @@ cmake_policy(SET CMP0014 OLD)
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}
*.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_RELWITHDEBINFO "${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)
@ -41,4 +56,4 @@ else()
target_link_libraries ( server_zone Common xivdat libmysql zlib1 mysqlConnector )
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 <cinttypes>
extern Core::Scripting::ScriptManager g_scriptMgr;
extern Core::Data::ExdData g_exdData;
extern Core::Logger g_log;
@ -256,7 +258,7 @@ void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlaye
else if ( subCommand == "eorzeatime" )
{
uint64_t timestamp;
sscanf( params.c_str(), "%llu", &timestamp );
sscanf( params.c_str(), "%" SCNu64, &timestamp );
pPlayer->setEorzeaTimeOffset( timestamp );
pPlayer->sendNotice( "Eorzea time offset: " + std::to_string( timestamp ) );

View file

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

View file

@ -330,7 +330,11 @@ void Core::Network::GameConnection::injectPacket( const std::string& packetpath,
fseek( fp, 0, SEEK_END );
int32_t size = ftell( 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 );
// 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 );
}
if( !session->isValid() ) //TODO: Catch more things in lobby and send real errors
else if( !session->isValid() || ( session->getPlayer() && session->getPlayer()->getLastPing() != 0 ) ) //TODO: Catch more things in lobby and send real errors
{
g_log.error( "[" + std::string(id) + "] Session INVALID, disconnecting" );
Disconnect();

View file

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

View file

@ -51,7 +51,7 @@ private:
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 ) );

View file

@ -1,7 +1,7 @@
#include <src/servers/Server_Common/Logging/Logger.h>
#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"
@ -15,11 +15,6 @@
#include "src/servers/Server_Zone/StatusEffect/StatusEffect.h"
#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;

View file

@ -1,35 +1,29 @@
#include <thread>
#include <chrono>
#include <boost/lexical_cast.hpp>
#include "ServerZone.h"
#include <src/servers/Server_Common/Version.h>
#include <src/servers/Server_Common/Logging/Logger.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 <MySqlBase.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 <src/servers/Server_Common/Network/Hive.h>
#include <src/servers/Server_Common/Network/Acceptor.h>
#include <Server_Common/Network/Connection.h>
#include <Server_Common/Network/Hive.h>
#include <src/servers/Server_Common/Exd/ExdData.h>
#include <src/servers/Server_Common/Network/PacketContainer.h>
#include <Server_Common/Exd/ExdData.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 "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 "Linkshell/LinkshellMgr.h"
@ -38,15 +32,9 @@
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#include <boost/algorithm/string.hpp>
#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 <thread>
Core::Logger g_log;
Core::Db::Database g_database;
Core::DebugCommandHandler g_gameCommandMgr;
Core::Scripting::ScriptManager g_scriptMgr;
Core::Data::ExdData g_exdData;
@ -54,7 +42,7 @@ Core::ZoneMgr g_zoneMgr;
Core::LinkshellMgr g_linkshellMgr;
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_bRunning( true )
{
@ -95,7 +83,7 @@ bool Core::ServerZone::registerBnpcTemplate( std::string templateName, uint32_t
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())
return nullptr;
@ -190,131 +178,8 @@ bool Core::ServerZone::loadSettings( int32_t argc, char* argv[] )
if( !loader.initDbs() )
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_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;
}
@ -398,10 +263,12 @@ void Core::ServerZone::mainLoop()
auto session = sessionIt.second;
if( session && session->getPlayer() )
{
// if the player is in a zone, let the zone handler take care of his updates
// else do it here.
if( !session->getPlayer()->getCurrentZone() )
session->update();
}
}
@ -413,6 +280,19 @@ void Core::ServerZone::mainLoop()
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 )
{
g_log.info("[" + std::to_string(it->second->getId() ) + "] Session time out" );

View file

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

View file

@ -125,7 +125,7 @@ void Core::Session::update()
m_pZoneConnection->processOutQueue();
}
if( m_pZoneConnection )
if( m_pChatConnection )
{
m_pChatConnection->processInQueue();
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 <cstdint>
#include <string>
#include <iostream>
#include <chrono>
#include <fstream>
#include "pcb.h"
#include "lgb.h"
#include "sgb.h"
#ifndef STANDALONE
#include <GameData.h>
#include <File.h>
#include <DatCat.h>
#include <ExdData.h>
#include <ExdCat.h>
#include <Exd.h>
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <chrono>
//#include <boost/algorithm/string.hpp>
#endif
using namespace std::chrono_literals;
@ -86,7 +88,7 @@ std::string zoneNameToPath( const std::string& name )
char region = name[1];
char type = name[2];
char zone = name[3];
static std::map<char, std::string> teriMap
static std::map< char, std::string > teriMap
{
{ 'r', "roc" },
{ 'w', "wil" },
@ -98,7 +100,7 @@ std::string zoneNameToPath( const std::string& name )
{ 'z', "zon" }
};
static std::map<char, std::string> typeMap
static std::map< char, std::string > typeMap
{
{ 'f', "fld" },
{ 't', "twn" },
@ -117,6 +119,24 @@ std::string zoneNameToPath( const std::string& name )
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[] )
{
auto startTime = std::chrono::system_clock::now();
@ -136,21 +156,35 @@ int main( int argc, char* argv[] )
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::exd::ExdData eData( data1 );
const xiv::dat::Cat& test = data1.get_category( "bg" );
auto test_file = data1.get_file( "bg/ffxiv/" + zonePath + "/level/bg.lgb" );
auto section = test_file->access_data_sections().at( 0 );
int32_t list_offset = *(uint32_t*)&section[0x18];
int32_t size = *(uint32_t*)&section[4];
auto test_file = data1.get_file( bgLgbPath );
section = test_file->access_data_sections().at( 0 );
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;
for( ; ; )
{
@ -158,7 +192,7 @@ int main( int argc, char* argv[] )
uint16_t trId = *(uint16_t*)&section1[offset1];
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 ) );
//std::cout << someString << "\n";
offset1 += 0x20;
@ -191,18 +225,24 @@ int main( int argc, char* argv[] )
fp_out = fopen( ( zoneName + ".obj" ).c_str(), "ab+" );
if( fp_out )
{
std::map<std::string, PCB_FILE> pcbFiles;
std::map<std::string, SGB_FILE> sgbFiles;
std::map<std::string, uint32_t> objCount;
std::map< std::string, PCB_FILE > pcbFiles;
std::map< std::string, SGB_FILE > sgbFiles;
std::map< std::string, uint32_t > objCount;
auto loadPcbFile = [&]( const std::string& fileName ) -> bool
{
try
{
char* dataSection = nullptr;
//std::cout << fileName << " ";
#ifndef STANDALONE
auto file = data1.get_file( fileName );
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";
uint32_t offset = 0;
@ -244,9 +284,17 @@ int main( int argc, char* argv[] )
SGB_FILE sgbFile;
try
{
char* dataSection = nullptr;
//std::cout << fileName << " ";
#ifndef STANDALONE
auto file = data1.get_file( fileName );
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] );
sgbFiles.insert( std::make_pair( fileName, sgbFile ) );
return true;
@ -356,8 +404,8 @@ int main( int argc, char* argv[] )
totalGroups++;
for( const auto& pEntry : group.entries )
{
auto pGimmick = dynamic_cast<LGB_GIMMICK_ENTRY*>( pEntry.get() );
auto pBgParts = dynamic_cast<LGB_BGPARTS_ENTRY*>( pEntry.get() );
auto pGimmick = dynamic_cast< LGB_GIMMICK_ENTRY* >( pEntry.get() );
auto pBgParts = dynamic_cast< LGB_BGPARTS_ENTRY* >( pEntry.get() );
std::string fileName( "" );
fileName.resize( 256 );
@ -410,7 +458,7 @@ int main( int argc, char* argv[] )
{
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;
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 << "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 )
{

View file

@ -303,7 +303,11 @@ int main()
outputFile1.write(&section[0], section.size());
outputFile1.close();
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( ; ; )
{