#ifndef CSCRIPTENGINE_H #define CSCRIPTENGINE_H #include #include #include #include #include #include #include #include #include #include "BabyDI.h" #include "scripting/ScriptAction.h" #include "scripting/ScriptFactory.h" #include "scripting/SourceCode.h" #include "scripting/interface/ScriptBindings.h" #ifdef V8NPCSERVER #include "scripting/v8/V8ScriptWrappers.h" #endif class IScriptEnv; class IScriptFunction; class NPC; class Server; class Weapon; class ScriptEngine { public: ScriptEngine() = default; ~ScriptEngine(); bool initialize(); void cleanup(bool shutDown = false); void runScripts(const std::chrono::high_resolution_clock::time_point& time); void scriptWatcher(); void startScriptExecution(const std::chrono::high_resolution_clock::time_point& startTime); bool stopScriptExecution(); Server* getServer() const; IScriptEnv* getScriptEnv() const; IScriptObject* getServerObject() const; bool executeNpc(NPC* npc); bool executeWeapon(Weapon* weapon); void registerNpcTimer(NPC* npc); void registerNpcUpdate(NPC* npc); void registerWeaponUpdate(Weapon* weapon); void unregisterNpcTimer(NPC* npc); void unregisterNpcUpdate(NPC* npc); void unregisterWeaponUpdate(Weapon* weapon); // callbacks IScriptFunction* getCallBack(const std::string& callback) const; void removeCallBack(const std::string& callback); void setCallBack(const std::string& callback, IScriptFunction* cbFunc); // Compile script into a ScriptFunction IScriptFunction* compileCache(const std::string& code, bool referenceCount = true); // Clear cache for code bool clearCache(const std::string& code); // Clear cache for code, with a WrapperScript of Type T template bool clearCache(const std::string& code); template bool clearCache(const std::string_view& code); template ScriptAction createAction(const std::string& action, Args... An); template void wrapScriptObject(T* obj) const; const ScriptRunError& getScriptError() const; void reportScriptException(const ScriptRunError& error); void reportScriptException(const std::string& error_message); private: void runTimers(const std::chrono::high_resolution_clock::time_point& time); BabyDI_INJECT(Server, m_server); IScriptEnv* m_env = nullptr; IScriptFunction* m_bootstrapFunction = nullptr; std::unique_ptr> m_environmentObject; std::unique_ptr> m_serverObject; std::chrono::high_resolution_clock::time_point m_lastScriptTimer = std::chrono::high_resolution_clock::now(); std::chrono::nanoseconds m_accumulator = std::chrono::nanoseconds(0); // Script watcher std::atomic m_scriptIsRunning = false; std::atomic m_scriptWatcherRunning = false; std::chrono::high_resolution_clock::time_point m_scriptStartTime; std::mutex m_scriptWatcherLock; std::thread m_scriptWatcherThread; std::unordered_map m_cachedScripts; std::unordered_map m_callbacks; std::unordered_set m_updateNpcs; std::unordered_set m_updateNpcsTimer; std::unordered_set m_updateWeapons; std::unordered_set m_deletedCallbacks; }; inline void ScriptEngine::startScriptExecution(const std::chrono::high_resolution_clock::time_point& startTime) { { std::lock_guard guard(m_scriptWatcherLock); m_scriptStartTime = startTime; } m_scriptIsRunning.store(true); } inline bool ScriptEngine::stopScriptExecution() { bool res = m_scriptIsRunning.load(); if (res) m_scriptIsRunning.store(false); return res; } // Getters inline Server* ScriptEngine::getServer() const { return m_server; } inline IScriptEnv* ScriptEngine::getScriptEnv() const { return m_env; } inline IScriptObject* ScriptEngine::getServerObject() const { return m_serverObject.get(); } inline IScriptFunction* ScriptEngine::getCallBack(const std::string& callback) const { auto it = m_callbacks.find(callback); if (it != m_callbacks.end()) return it->second; return nullptr; } inline const ScriptRunError& ScriptEngine::getScriptError() const { return m_env->getScriptError(); } // Register scripts for processing inline void ScriptEngine::registerNpcTimer(NPC* npc) { m_updateNpcsTimer.insert(npc); } inline void ScriptEngine::registerNpcUpdate(NPC* npc) { m_updateNpcs.insert(npc); } inline void ScriptEngine::registerWeaponUpdate(Weapon* weapon) { m_updateWeapons.insert(weapon); } // Unregister scripts from processing inline void ScriptEngine::unregisterWeaponUpdate(Weapon* weapon) { m_updateWeapons.erase(weapon); } inline void ScriptEngine::unregisterNpcUpdate(NPC* npc) { m_updateNpcs.erase(npc); } inline void ScriptEngine::unregisterNpcTimer(NPC* npc) { m_updateNpcsTimer.erase(npc); } // template ScriptAction ScriptEngine::createAction(const std::string& action, Args... An) { constexpr size_t Argc = (sizeof...(Args)); assert(Argc > 0); SCRIPTENV_D("Server_RegisterAction:\n"); SCRIPTENV_D("\tAction: %s\n", action.c_str()); SCRIPTENV_D("\tArguments: %zu\n", Argc); auto funcIt = m_callbacks.find(action); if (funcIt == m_callbacks.end()) { SCRIPTENV_D("Global::Server_RegisterAction: Callback not registered for %s\n", action.c_str()); return ScriptAction{}; } // Create an arguments object, and pass it to ScriptAction IScriptArguments* args = ScriptFactory::createArguments(m_env, std::forward(An)...); assert(args); return ScriptAction(funcIt->second, args, action); } template inline void ScriptEngine::wrapScriptObject(T* obj) const { SCRIPTENV_D("Begin Global::wrapScriptObject()\n"); // Wrap the object, and set the new script object on the original object auto wrappedObject = ScriptFactory::wrapObject(m_env, ScriptConstructorId::result, obj); obj->setScriptObject(std::move(wrappedObject)); SCRIPTENV_D("End Global::wrapScriptObject()\n\n"); } template inline bool ScriptEngine::clearCache(const std::string& code) { return clearCache(wrapScript(code)); } template inline bool ScriptEngine::clearCache(const std::string_view& code) { return clearCache(wrapScript(code)); } #endif