1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-26 22:37:45 +00:00
sapphire/src/servers/Server_Zone/ServerZone.cpp

468 lines
14 KiB
C++
Raw Normal View History

2017-08-08 13:53:47 +02:00
#include <thread>
#include <chrono>
#include <boost/lexical_cast.hpp>
#include "ServerZone.h"
2017-08-19 00:18:40 +02:00
#include <src/servers/Server_Common/Logging/Logger.h>
#include <src/servers/Server_Common/Config/XMLConfig.h>
#include <src/servers/Server_Common/Database/Database.h>
2017-08-08 13:53:47 +02:00
2017-09-25 14:44:15 +02:00
#include <MySqlBase.h>
#include <Connection.h>
#include <Statement.h>
#include <ResultSet.h>
#include <PreparedStatement.h>
#include <PreparedResultSet.h>
2017-08-19 00:18:40 +02:00
#include <src/servers/Server_Common/Network/Connection.h>
#include <src/servers/Server_Common/Network/Hive.h>
#include <src/servers/Server_Common/Network/Acceptor.h>
2017-08-08 13:53:47 +02:00
2017-08-19 00:18:40 +02:00
#include <src/servers/Server_Common/Exd/ExdData.h>
#include <src/servers/Server_Common/Network/PacketContainer.h>
2017-08-08 13:53:47 +02:00
#include "src/servers/Server_Zone/Network/GameConnection.h"
2017-08-08 13:53:47 +02:00
#include "Session.h"
#include "src/servers/Server_Zone/Zone/ZoneMgr.h"
2017-08-08 13:53:47 +02:00
#include "src/servers/Server_Zone/DebugCommand/DebugCommandHandler.h"
2017-08-08 13:53:47 +02:00
#include "Script/ScriptManager.h"
#include "Linkshell/LinkshellMgr.h"
2017-08-08 13:53:47 +02:00
#include "Forwards.h"
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#include <boost/algorithm/string.hpp>
2017-08-08 13:53:47 +02:00
Core::Logger g_log;
Core::Db::Database g_database;
Core::DebugCommandHandler g_gameCommandMgr;
2017-08-08 13:53:47 +02:00
Core::Scripting::ScriptManager g_scriptMgr;
Core::Data::ExdData g_exdData;
Core::ZoneMgr g_zoneMgr;
Core::LinkshellMgr g_linkshellMgr;
2017-08-08 13:53:47 +02:00
Core::ServerZone::ServerZone( const std::string& configPath, uint16_t serverId )
: m_configPath( configPath )
2017-08-08 13:53:47 +02:00
{
m_pConfig = XMLConfigPtr( new XMLConfig );
}
Core::ServerZone::~ServerZone()
{
}
Core::XMLConfigPtr Core::ServerZone::getConfig() const
{
return m_pConfig;
}
2017-09-14 17:07:58 +02:00
size_t Core::ServerZone::getSessionCount() const
{
return m_sessionMap.size();
}
2017-08-08 13:53:47 +02:00
bool Core::ServerZone::registerBnpcTemplate( std::string templateName, uint32_t bnpcBaseId,
uint32_t bnpcNameId, uint32_t modelId, std::string aiName )
{
auto it = m_bnpcTemplates.find( templateName );
if( it != m_bnpcTemplates.end() )
{
g_log.error( templateName + " already registered, skipping..." );
return false;
}
Entity::BattleNpcTemplatePtr pNpcTemplate( new Entity::BattleNpcTemplate( templateName, bnpcBaseId, bnpcNameId, modelId, aiName ) );
m_bnpcTemplates[templateName] = pNpcTemplate;
return true;
}
Core::Entity::BattleNpcTemplatePtr Core::ServerZone::getBnpcTemplate( std::string templateName )
{
auto it = m_bnpcTemplates.find(templateName);
if (it != m_bnpcTemplates.end())
return nullptr;
return it->second;
}
bool Core::ServerZone::loadSettings( int32_t argc, char* argv[] )
2017-08-08 13:53:47 +02:00
{
g_log.info( "Loading config " + m_configPath );
if( !m_pConfig->loadConfig( m_configPath ) )
{
g_log.fatal( "Error loading config " + m_configPath );
return false;
}
std::vector<std::string> args( argv + 1, argv + argc );
for( uint32_t i = 0; i + 1 < args.size(); i += 2 )
2017-08-08 13:53:47 +02:00
{
std::string arg( "" );
std::string val( "" );
try
{
2017-08-11 23:15:55 +01:00
arg = boost::to_lower_copy( std::string( args[i] ) );
2017-08-08 13:53:47 +02:00
val = std::string( args[i + 1] );
// trim '-' from start of arg
arg = arg.erase( 0, arg.find_first_not_of( '-' ) );
if( arg == "ip" )
{
// todo: ip addr in config
m_pConfig->setValue< std::string >( "Settings.General.ListenIP", val );
}
else if( arg == "p" || arg == "port" )
{
m_pConfig->setValue< std::string >( "Settings.General.ListenPort", val );
}
else if( arg == "exdpath" || arg == "datapath" )
2017-08-08 13:53:47 +02:00
{
m_pConfig->setValue< std::string >( "Settings.General.DataPath", val );
}
else if( arg == "h" || arg == "dbhost" )
{
m_pConfig->setValue< std::string >( "Settings.General.Mysql.Host", val );
}
else if( arg == "dbport" )
{
m_pConfig->setValue< std::string >( "Settings.General.Mysql.Port", val );
}
else if( arg == "u" || arg == "user" || arg == "dbuser" )
{
m_pConfig->setValue< std::string >( "Settings.General.Mysql.Username", val );
}
else if( arg == "pass" || arg == "dbpass" )
{
m_pConfig->setValue< std::string >( "Settings.General.Mysql.Pass", val );
}
else if( arg == "d" || arg == "db" || arg == "database" )
{
m_pConfig->setValue< std::string >( "Settings.General.Mysql.Database", val );
}
}
catch( ... )
{
g_log.error( "Error parsing argument: " + arg + " " + "value: " + val + "\n" );
g_log.error( "Usage: <arg> <val> \n" );
}
}
g_log.info( "Setting up EXD data" );
if( !g_exdData.init( m_pConfig->getValue< std::string >( "Settings.General.DataPath", "" ) ) )
{
g_log.fatal( "Error setting up EXD data " );
return false;
}
try
{
2017-09-19 00:02:14 +02:00
// bunch of test cases for db wrapper
Mysql::MySqlBase base;
g_log.info( base.getVersionInfo() );
Mysql::optionMap options;
options[ MYSQL_OPT_RECONNECT ] = "1";
boost::scoped_ptr< Mysql::Connection > con( base.connect( "127.0.0.1", "root", "", options ) );
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" );
2017-09-19 00:02:14 +02:00
con->setSchema( "sapphire" );
boost::scoped_ptr< Mysql::Statement > stmt( con->createStatement() );
2017-09-19 00:02:14 +02:00
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() );
2017-09-19 00:02:14 +02:00
//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" ) );
2017-09-19 00:02:14 +02:00
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 ) ) );
}
2017-09-23 23:26:23 +02:00
// 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" );
}
boost::scoped_ptr< Mysql::PreparedStatement > pstmt2( con->prepareStatement( "DELETE FROM zoneservers WHERE id = ?" ) );
2017-09-23 23:26:23 +02:00
pstmt2->setInt( 1, 1021 );
pstmt2->execute();
pstmt2->setInt( 1, 1001 );
pstmt2->execute();
boost::scoped_ptr< Mysql::PreparedStatement > pstmt( con->prepareStatement( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( ?, ?, ?);" ) );
2017-09-23 23:26:23 +02:00
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 = ?" ) );
2017-09-23 23:26:23 +02:00
pstmt->setInt( 1, 1021 );
pstmt->execute();
pstmt->setInt( 1, 1001 );
pstmt->execute();
}
catch( std::runtime_error e )
{
g_log.error( e.what() );
}
2017-08-08 13:53:47 +02:00
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" );;
2017-08-08 13:53:47 +02:00
return true;
}
void Core::ServerZone::run( int32_t argc, char* argv[] )
2017-08-08 13:53:47 +02:00
{
// TODO: add more error checks for the entire initialisation
g_log.setLogPath( "log\\SapphireZone_" );
2017-08-08 13:53:47 +02:00
g_log.init();
g_log.info( "===========================================================" );
g_log.info( "Sapphire Server Project " );
g_log.info( "Version: x.y.z" );
g_log.info( "Compiled: " __DATE__ " " __TIME__ );
g_log.info( "===========================================================" );
if( !loadSettings( argc, argv ) )
{
g_log.fatal( "Unable to load settings!" );
return;
}
g_exdData.loadZoneInfo();
g_exdData.loadClassJobInfo();
g_exdData.loadParamGrowInfo();
g_exdData.loadEventActionInfo();
g_exdData.loadActionInfo();
g_exdData.loadStatusEffectInfo();
g_exdData.loadAetheryteInfo();
g_exdData.loadTribeInfo();
g_log.info( "LinkshellMgr: Caching linkshells" );
if( !g_linkshellMgr.loadLinkshells() )
{
g_log.fatal( "Unable to load linkshells!" );
return;
}
2017-08-08 13:53:47 +02:00
Network::HivePtr hive( new Network::Hive() );
Network::addServerToHive< Network::GameConnection >( m_ip, m_port, hive );
g_scriptMgr.init();
g_log.info( "ZoneMgr: Setting up zones" );
2017-08-08 13:53:47 +02:00
g_zoneMgr.createZones();
std::vector< std::thread > thread_list;
thread_list.push_back( std::thread( std::bind( &Network::Hive::Run, hive.get() ) ) );
g_log.info( "Server listening on port: " + std::to_string( m_port ) );
g_log.info( "Ready for connections..." );
while( true )
{
std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) );
g_zoneMgr.updateZones();
std::lock_guard<std::mutex> lock( m_sessionMutex );
for( auto sessionIt : m_sessionMap )
{
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();
}
}
uint32_t currTime = static_cast< uint32_t >( time( nullptr ) );
auto it = m_sessionMap.begin();
for( ; it != m_sessionMap.end(); )
{
uint32_t diff = currTime - it->second->getLastDataTime();
auto pPlayer = it->second->getPlayer();
if( diff > 20 )
{
g_log.info( "[" + std::to_string( it->second->getId() ) + "] Session time out" );
it->second->close();
// if( it->second.unique() )
{
it = m_sessionMap.erase( it );
}
}
else
{
++it;
}
}
}
// currently never reached, need a "stopServer" variable to break out of the above while loop
/*for( auto& thread_entry : thread_list )
{
thread_entry.join();
}*/
}
bool Core::ServerZone::createSession( uint32_t sessionId )
{
std::lock_guard< std::mutex > lock( m_sessionMutex );
const std::string session_id_str = std::to_string( sessionId );
auto it = m_sessionMap.find( sessionId );
if( it != m_sessionMap.end() )
{
g_log.error( "[" + session_id_str + "] Error creating session" );
return false;
}
g_log.info( "[" + session_id_str + "] Creating new session" );
boost::shared_ptr<Session> newSession( new Session( sessionId ) );
m_sessionMap[sessionId] = newSession;
if( !newSession->loadPlayer() )
{
g_log.error( "[" + session_id_str + "] Error loading player " + session_id_str );
return false;
}
m_playerSessionMap[newSession->getPlayer()->getName()] = newSession;
return true;
}
void Core::ServerZone::removeSession( uint32_t sessionId )
{
m_sessionMap.erase( sessionId );
}
void Core::ServerZone::updateSession( uint32_t id )
{
std::lock_guard< std::mutex > lock( m_sessionMutex );
auto it = m_sessionMap.find( id );
if( it != m_sessionMap.end() )
it->second->loadPlayer();
}
Core::SessionPtr Core::ServerZone::getSession( uint32_t id )
{
//std::lock_guard<std::mutex> lock( m_sessionMutex );
auto it = m_sessionMap.find( id );
if( it != m_sessionMap.end() )
return ( it->second );
return nullptr;
}
Core::SessionPtr Core::ServerZone::getSession( std::string playerName )
{
//std::lock_guard<std::mutex> lock( m_sessionMutex );
auto it = m_playerSessionMap.find( playerName );
if (it != m_playerSessionMap.end())
return (it->second);
return nullptr;
}
void Core::ServerZone::removeSession( std::string playerName )
{
m_playerSessionMap.erase( playerName );
}
void Core::ServerZone::updateSession( std::string playerName )
{
std::lock_guard< std::mutex > lock( m_sessionMutex );
auto it = m_playerSessionMap.find( playerName );
if( it != m_playerSessionMap.end() )
it->second->loadPlayer();
}