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 );