diff --git a/.travis.yml b/.travis.yml index d8158ae9..99dc3656 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,7 @@ before_install: - sudo add-apt-repository -y ppa:rexut/recoil - sudo apt-get update - sudo apt-get install -y libboost1.63-dev libboost1.63-all-dev + - sudo apt-get install -y libmysqlclient-dev # Build steps script: diff --git a/bin/config/settings_zone.xml b/bin/config/settings_zone.xml index a0d56a0a..a16b685d 100644 --- a/bin/config/settings_zone.xml +++ b/bin/config/settings_zone.xml @@ -8,15 +8,16 @@ H:\\SteamLibrary\\steamapps\\common\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack\\ffxiv - - ./compiledscripts/ - ./cache/ + + ./compiledscripts/ + ./cache/ - - 1 - ../cmake-build-debug/ - cmake --build %1% --target %2% - + + 1 + ../scripts/native/ + ../cmake-build-debug/ + cmake --build %1% --target %2% + @@ -28,13 +29,13 @@ root sapphire - 2 - 2 + 2 + 2 - + <<<Welcome to Sapphire>>> This is a very good server diff --git a/scripts/native/ScriptObject.h b/scripts/native/ScriptObject.h index 108b8df0..6691f474 100644 --- a/scripts/native/ScriptObject.h +++ b/scripts/native/ScriptObject.h @@ -5,6 +5,7 @@ #include #include #include +#include #endif //SAPPHIRE_SCRIPTOBJECT_H diff --git a/scripts/native/opening/OpeningGridania.cpp b/scripts/native/opening/OpeningGridania.cpp index 2a9e5e6b..5e4374c1 100644 --- a/scripts/native/opening/OpeningGridania.cpp +++ b/scripts/native/opening/OpeningGridania.cpp @@ -63,6 +63,8 @@ public: Scene00030( player ); } }; + + player.eventPlay( getId(), 40, 1, 2, 1, callback ); } /////////////////////////////// diff --git a/src/libraries b/src/libraries index 8a26ae78..f4d3c5b3 160000 --- a/src/libraries +++ b/src/libraries @@ -1 +1 @@ -Subproject commit 8a26ae78e37701a9b66e7200f1e0ad3387da17c3 +Subproject commit f4d3c5b38d13a15efc42686c9bac768c2be28be1 diff --git a/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp b/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp index 9f6a39dd..dceb3432 100644 --- a/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp +++ b/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp @@ -589,15 +589,15 @@ void Core::DebugCommandHandler::script( char* data, Entity::Player &player, boos } } - else if( subCommand == "reload" || subCommand == "rl" ) + else if( subCommand == "queuereload" || subCommand == "qrl" ) { if( subCommand == params ) player.sendDebug( "Command failed: requires name of script to reload" ); else - if( g_scriptMgr.getNativeScriptHandler().reloadScript( params ) ) - player.sendDebug( "Reloaded '" + params + "' successfully" ); - else - player.sendDebug( "Failed to reload '" + params + "'" ); + { + g_scriptMgr.getNativeScriptHandler().queueScriptReload( params ); + player.sendDebug( "Queued script reload for script: " + params ); + } } else if( subCommand == "build" || subCommand == "b" ) { diff --git a/src/servers/Server_Zone/Script/NativeScriptManager.cpp b/src/servers/Server_Zone/Script/NativeScriptManager.cpp index 2441816e..a28a823f 100644 --- a/src/servers/Server_Zone/Script/NativeScriptManager.cpp +++ b/src/servers/Server_Zone/Script/NativeScriptManager.cpp @@ -137,19 +137,41 @@ namespace Core { return m_loader.unloadScript( info ); } - bool NativeScriptManager::reloadScript( const std::string& name ) + void NativeScriptManager::queueScriptReload( const std::string &name ) { auto info = m_loader.getScriptInfo( name ); if( !info ) - return false; + return; // backup actual lib path std::string libPath( info->library_path ); if( !unloadScript( info ) ) - return false; + return; - return loadScript( libPath ); + m_scriptLoadQueue.push( libPath ); + } + + void NativeScriptManager::processLoadQueue() + { + std::vector< std::string > deferredLoads; + + while( !m_scriptLoadQueue.empty() ) + { + auto item = m_scriptLoadQueue.front(); + + // if it fails, we defer the loading to the next tick + if( !loadScript( item ) ) + deferredLoads.push_back( item ); + + m_scriptLoadQueue.pop(); + } + + if( !deferredLoads.empty() ) + { + for( auto& item : deferredLoads ) + m_scriptLoadQueue.push( item ); + } } void NativeScriptManager::findScripts( std::set< Core::Scripting::ScriptInfo* >& scripts, const std::string& search ) @@ -157,6 +179,11 @@ namespace Core { return m_loader.findScripts( scripts, search ); } + bool NativeScriptManager::isModuleLoaded( const std::string &name ) + { + return m_loader.isModuleLoaded( name ); + } + boost::shared_ptr< NativeScriptManager > createNativeScriptMgr() diff --git a/src/servers/Server_Zone/Script/NativeScriptManager.h b/src/servers/Server_Zone/Script/NativeScriptManager.h index 42c4a4ed..43db0d76 100644 --- a/src/servers/Server_Zone/Script/NativeScriptManager.h +++ b/src/servers/Server_Zone/Script/NativeScriptManager.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -26,6 +27,8 @@ namespace Scripting { ScriptLoader m_loader; + std::queue< std::string > m_scriptLoadQueue; + bool unloadScript( ScriptInfo* info ); public: @@ -39,10 +42,13 @@ namespace Scripting { bool loadScript( const std::string& path ); bool unloadScript( const std::string& name ); - bool reloadScript( const std::string& name ); + void queueScriptReload( const std::string& name ); void findScripts( std::set< Core::Scripting::ScriptInfo* >& scripts, const std::string& search ); + void processLoadQueue(); + const std::string getModuleExtension(); + bool isModuleLoaded( const std::string& name ); template< typename key, typename val > bool removeValueFromMap( ScriptObject* ptr, std::unordered_map< key, val >& map ) diff --git a/src/servers/Server_Zone/Script/ScriptLoader.cpp b/src/servers/Server_Zone/Script/ScriptLoader.cpp index 7900a91e..b480e06e 100644 --- a/src/servers/Server_Zone/Script/ScriptLoader.cpp +++ b/src/servers/Server_Zone/Script/ScriptLoader.cpp @@ -62,12 +62,17 @@ Core::Scripting::ScriptInfo* Core::Scripting::ScriptLoader::loadModule( const st fs::create_directories( cacheDir ); fs::path dest( cacheDir /= f.filename().string() ); - if ( fs::exists( dest ) ) + try { - fs::remove( dest ); + fs::copy_file( f, dest, fs::copy_option::overwrite_if_exists ); + } + catch( const boost::filesystem::filesystem_error& err ) + { + g_log.error( "Error copying file to cache: " + err.code().message() ); + + return nullptr; } - fs::copy_file( f, dest ); #ifdef _WIN32 ModuleHandle handle = LoadLibrary( dest.string().c_str() ); @@ -82,7 +87,7 @@ Core::Scripting::ScriptInfo* Core::Scripting::ScriptLoader::loadModule( const st return nullptr; } - g_log.info( "Loaded module '" + f.filename().string() + "' @ 0x" + boost::str( boost::format( "%|08X|" ) % handle ) ); + g_log.debug( "Loaded module '" + f.filename().string() + "' @ 0x" + boost::str( boost::format( "%|08X|" ) % handle ) ); auto info = new ScriptInfo; info->handle = handle; diff --git a/src/servers/Server_Zone/Script/ScriptManager.cpp b/src/servers/Server_Zone/Script/ScriptManager.cpp index 2680618e..039c59be 100644 --- a/src/servers/Server_Zone/Script/ScriptManager.cpp +++ b/src/servers/Server_Zone/Script/ScriptManager.cpp @@ -21,18 +21,28 @@ #include +// enable the ambiguity fix for every platform to avoid #define nonsense +#define WIN_AMBIGUITY_FIX +#include + extern Core::Logger g_log; extern Core::Data::ExdData g_exdData; extern Core::ServerZone g_serverZone; -Core::Scripting::ScriptManager::ScriptManager() +Core::Scripting::ScriptManager::ScriptManager() : + m_firstScriptChangeNotificiation( false ) { m_nativeScriptManager = createNativeScriptMgr(); } Core::Scripting::ScriptManager::~ScriptManager() { + Watchdog::unwatchAll(); +} +void Core::Scripting::ScriptManager::update() +{ + m_nativeScriptManager->processLoadQueue(); } bool Core::Scripting::ScriptManager::init() @@ -42,24 +52,66 @@ bool Core::Scripting::ScriptManager::init() loadDir( g_serverZone.getConfig()->getValue< std::string >( "Settings.General.Scripts.Path", "./compiledscripts/" ), files, m_nativeScriptManager->getModuleExtension() ); + uint32_t scriptsFound = 0; + uint32_t scriptsLoaded = 0; + for( auto itr = files.begin(); itr != files.end(); ++itr ) { auto& path = *itr; - m_nativeScriptManager->loadScript( path ); + scriptsFound++; + + if( m_nativeScriptManager->loadScript( path ) ) + scriptsLoaded++; } + g_log.info( "ScriptManager: Loaded " + std::to_string( scriptsLoaded ) + "/" + std::to_string( scriptsFound ) + " scripts successfully" ); + + watchDirectories(); + return true; } -void Core::Scripting::ScriptManager::loadDir( std::string dirname, std::set& files, std::string ext ) +void Core::Scripting::ScriptManager::watchDirectories() +{ + Watchdog::watchMany( g_serverZone.getConfig()->getValue< std::string >( "Settings.General.Scripts.Path", "./compiledscripts/" ) + "*" + m_nativeScriptManager->getModuleExtension(), + [ this ]( const std::vector< ci::fs::path >& paths ) + { + if( !m_firstScriptChangeNotificiation ) + { + // for whatever reason, the first time this runs, it detects every file as changed + // so we're always going to ignore the first notification + m_firstScriptChangeNotificiation = true; + return; + } + + for( auto path : paths ) + { + if( m_nativeScriptManager->isModuleLoaded( path.stem().string() ) ) + { + g_log.debug( "Reloading changed script: " + path.stem().string() ); + + m_nativeScriptManager->queueScriptReload( path.stem( ).string( )); + } + else + { + g_log.debug( "Loading new script: " + path.stem().string() ); + + m_nativeScriptManager->loadScript( path.string() ); + } + } + }); +} + +void Core::Scripting::ScriptManager::loadDir( const std::string& dirname, std::set &files, const std::string& ext ) { g_log.info( "ScriptEngine: loading scripts from " + dirname ); boost::filesystem::path targetDir( dirname ); - boost::filesystem::recursive_directory_iterator iter( targetDir ), eod; + boost::filesystem::directory_iterator iter( targetDir ); + boost::filesystem::directory_iterator eod; BOOST_FOREACH( boost::filesystem::path const& i, make_pair( iter, eod ) ) { diff --git a/src/servers/Server_Zone/Script/ScriptManager.h b/src/servers/Server_Zone/Script/ScriptManager.h index 5df46660..f87d434f 100644 --- a/src/servers/Server_Zone/Script/ScriptManager.h +++ b/src/servers/Server_Zone/Script/ScriptManager.h @@ -22,6 +22,8 @@ namespace Core std::function< std::string( Entity::Player& ) > m_onFirstEnterWorld; // auto fn = m_pChaiHandler->eval< std::function >( "onFirstEnterWorld" ); + bool m_firstScriptChangeNotificiation; + public: ScriptManager(); ~ScriptManager(); @@ -29,6 +31,10 @@ namespace Core bool init(); void reload(); + void update(); + + void watchDirectories(); + void onPlayerFirstEnterWorld( Entity::Player& player ); static bool registerBnpcTemplate( std::string templateName, uint32_t bnpcBaseId, uint32_t bnpcNameId, uint32_t modelId, std::string aiName ); @@ -54,7 +60,7 @@ namespace Core bool onEventHandlerTradeReturn( Entity::Player& player, uint32_t eventId, uint16_t subEvent, uint16_t param, uint32_t catalogId ); - void loadDir( std::string dirname, std::set& files, std::string ext ); + void loadDir( const std::string& dirname, std::set &files, const std::string& ext ); NativeScriptManager& getNativeScriptHandler(); }; diff --git a/src/servers/Server_Zone/ServerZone.cpp b/src/servers/Server_Zone/ServerZone.cpp index 8044f7ec..eccb7ea4 100644 --- a/src/servers/Server_Zone/ServerZone.cpp +++ b/src/servers/Server_Zone/ServerZone.cpp @@ -260,6 +260,8 @@ void Core::ServerZone::mainLoop() g_zoneMgr.updateZones(); + g_scriptMgr.update(); + auto currTime = static_cast< uint32_t >( time( nullptr ) ); lock_guard< std::mutex > lock( this->m_sessionMutex );