//======================================================================== // // GameScriptObject.cpp - responsible for managing/executing scripts. // // copyright 2001 Sony Online Entertainment // //======================================================================== #include "serverScript/FirstServerScript.h" #include "serverScript/GameScriptObject.h" #include "ScriptMessage.h" #include "serverGame/ConfigServerGame.h" #include "serverGame/ServerController.h" #include "serverNetworkMessages/MessageQueueScriptTrigger.h" #include "serverScript/ConfigServerScript.h" #include "serverScript/ScriptFunctionTable.h" #include "serverScript/JavaLibrary.h" #include "serverScript/ScriptParameters.h" #include "sharedDebug/Profiler.h" #include "sharedFoundation/Scheduler.h" #include "sharedNetworkMessages/MessageQueueString.h" #include "sharedFoundation/GameControllerMessage.h" #include "sharedLog/Log.h" #include "sharedObject/NetworkIdManager.h" #include "sharedObject/Object.h" #include "serverGame/ServerObject.h" #include #include #ifdef _WIN32 #pragma warning (disable: 4514 4786) #endif //======================================================================== // class GameScriptObject static members //======================================================================== GameScriptObject::ScriptDataMap * GameScriptObject::ms_scriptDataMap = nullptr; bool GameScriptObject::m_pauseScripting = false; //======================================================================== // class GameScriptObject methods //======================================================================== /** * Class constructor. */ GameScriptObject::GameScriptObject(void) : m_owner(nullptr), m_scriptList(), m_scriptListInitialized(false), m_scriptListValid(false), m_lockScriptList(false), m_attachingScript(0) { m_synchronizedScriptList.setSourceObject (this); } /** * Class destructor. */ GameScriptObject::~GameScriptObject() { { PROFILER_AUTO_BLOCK_DEFINE("GameScriptObject::~GameScriptObject removeJavaId\n"); if (JavaLibrary::instance() != nullptr /*&& m_javaId != nullptr*/) { NOT_NULL(m_owner); JavaLibrary::removeJavaId(m_owner->getNetworkId()); // m_javaId = nullptr; } } { PROFILER_AUTO_BLOCK_DEFINE("GameScriptObject::removeAll\n"); removeAll(); } m_scriptList.clear(); m_owner = nullptr; } // GameScriptObject::~GameScriptObject /** * Creates the script engine. * * @return true on success, false on fail */ bool GameScriptObject::installScriptEngine(void) { if (JavaLibrary::instance() != nullptr) { DEBUG_WARNING(true, ("Trying to install script engine more than once")); return true; } ms_scriptDataMap = new GameScriptObject::ScriptDataMap; Scripting::InitScriptFuncHashMap(); JavaLibrary::install(); if (JavaLibrary::instance() == nullptr) return false; enableNewJediTracking(ConfigServerGame::getEnableNewJedi()); return true; } // GameScriptObject::installScriptEngine /** * Destroys the script engine. */ void GameScriptObject::removeScriptEngine(void) { JavaLibrary::remove(); delete ms_scriptDataMap; ms_scriptDataMap = nullptr; Scripting::RemoveScriptFuncHashMap(); } // GameScriptObject::removeScriptEngine // ------------------------------------------------------------------------ void GameScriptObject::addToPackage(Archive::AutoDeltaByteStream & bs) { bs.addVariable(m_synchronizedScriptList); } // ------------------------------------------------------------------------ /** * Called during game alter phase. * * @param time time since last frame */ void GameScriptObject::alter(real time) { UNREF(time); } //lint !e1762 Do not make const /** * Tells the scripting engine that my owner is authoritative on this server. * * @param authoritative flag if my owner is authoritative or not */ void GameScriptObject::setOwnerIsAuthoritative(bool authoritative, uint32 pid) { if (JavaLibrary::instance() == nullptr) return; if (m_owner != nullptr && m_owner->getNetworkId() != NetworkId::cms_invalid) { JavaLibrary::instance()->setObjIdAuthoritative(m_owner->getNetworkId(), authoritative, pid); // clear out old data m_scriptListValid = false; if (authoritative && m_scriptListInitialized) { initScriptInstances(); } } else { DEBUG_WARNING(true, ("Error setting Java obj_id authority, no owner!")); } } // GameScriptObject::setOwnerIsAuthoritative /** * Tells the scripting engine that my owner is loaded on this server. You cannot * unload an object, it is marked unloaded when it is deleted. */ void GameScriptObject::setOwnerIsLoaded(void) { if (JavaLibrary::instance() == nullptr) return; if (m_owner != nullptr && m_owner->getNetworkId() != NetworkId::cms_invalid) { JavaLibrary::instance()->setObjIdLoaded(m_owner->getNetworkId()); } else { DEBUG_WARNING(true, ("Error setting Java obj_id loaded, no owner!")); } } //lint !e1762 Do not make const /** * Tells the scripting engine that my owner is initialized on this server. You cannot * uninitialize an object, it is marked uninitialized when it is deleted. */ void GameScriptObject::setOwnerIsInitialized(void) { if (JavaLibrary::instance() == nullptr) return; if (m_owner != nullptr && m_owner->getNetworkId() != NetworkId::cms_invalid) { JavaLibrary::instance()->setObjIdInitialized(m_owner->getNetworkId()); if (m_owner->isAuthoritative()) { ScriptParams params; IGNORE_RETURN(trigAllScripts(Scripting::TRIG_INITIALIZE, params)); } } else { DEBUG_WARNING(true, ("Error setting Java obj_id loaded, no owner!")); } } //lint !e1762 Do not make const /** * Flag the object in script as going to be destroyed, but hasn't actually been removed from the game yet. */ void GameScriptObject::setOwnerDestroyed(void) { if (JavaLibrary::instance() == nullptr) return; if (m_owner != nullptr && m_owner->getNetworkId() != NetworkId::cms_invalid && m_owner->isAuthoritative()) JavaLibrary::flagDestroyed(m_owner->getNetworkId()); } //lint !e1762 Do not make const /** * Adds a script to the start of the script list. * * @param scriptName script to add * @param runTrigger flag to run the TRIG_ATTACH trigger * * @return SCRIPT_CONTINUE on success, SCRIPT_OVERRIDE on fail */ int GameScriptObject::attachScript(const std::string& scriptName, bool runTrigger) { if (!m_owner) { WARNING_STRICT_FATAL(true, ("Attach script %s called with no owner", scriptName.c_str())); return SCRIPT_OVERRIDE; } if (scriptName.empty()) { DEBUG_WARNING(true, ("ERROR in GameScriptObject::AttachScript: no script name")); return SCRIPT_OVERRIDE; } if (scriptName.size() >= MAX_SCRIPT_NAME_LEN) { DEBUG_WARNING(true, ("ERROR in GameScriptObject::AttachScript: name too long " "(%s)", scriptName.c_str())); return SCRIPT_OVERRIDE; } if(!m_owner->isAuthoritative()) { MessageQueueString * data = new MessageQueueString(scriptName); m_owner->getController()->appendMessage( static_cast(CM_attachScript), 0.0f, data, GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_AUTH_SERVER); return SCRIPT_CONTINUE; } if (m_lockScriptList) { DEBUG_WARNING(true, ("Trying to attach script %s to object %s while the " "script list is locked.", scriptName.c_str(), m_owner->getNetworkId().getValueString().c_str())); return SCRIPT_OVERRIDE; } if (hasScript(scriptName)) { DEBUG_WARNING(true, ("WARNING in GameScriptObject::AttachScript: dup script " "(%s)", scriptName.c_str())); return SCRIPT_OVERRIDE; } if (ConfigServerScript::getAllowBuildClusterScriptAttach()) { //@todo check for file existance. std::pair result = ms_scriptDataMap->insert(std::make_pair(scriptName, ScriptData())); m_scriptList.push_back(ScriptListEntry(&(*result.first))); } else { if (ms_scriptDataMap == nullptr || JavaLibrary::instance() == nullptr) return SCRIPT_CONTINUE; if (ms_scriptDataMap->find(scriptName) == ms_scriptDataMap->end()) { // if this is the first time the script has been attached, query it to get a // list of triggers, messageHandlers, and pathFindHandlers it has if (!JavaLibrary::instance()->queryScriptFunctions(scriptName)) { WARNING(true, ("Error while attaching script %s. Does it exist?", scriptName.c_str())); return SCRIPT_OVERRIDE; } } ScriptListEntry entry = GameScriptObject::getScriptListEntry(scriptName); // put a pointer to the pair of scriptName and ScriptData in the script list m_scriptList.push_back(entry); if (runTrigger) { // flag that we are attaching a script to ourselves so we can warn if // we get a messageTo during this time ++m_attachingScript; // Make sure to call the attach trigger on this one script if one exists. ScriptParams params; IGNORE_RETURN(trigOneScript(scriptName, Scripting::TRIG_ATTACH, params)); --m_attachingScript; } // if the script list hasn't been initialized, wait until it has, // else go ahead and initialize the script if (m_scriptListValid && entry.isValid()) { JavaLibrary::instance()->attachScriptToObjId(m_owner->getNetworkId(), scriptName); entry.getScriptData().owners.insert(m_owner->getNetworkId()); } } packScriptList(); return SCRIPT_CONTINUE; } // GameScriptObject::attachScript /** * Removes a script from the script list. * * @param scriptName script to remove * * @return SCRIPT_CONTINUE on success, SCRIPT_OVERRIDE on fail */ int GameScriptObject::detachScript(const std::string& scriptName) { if (!m_owner) { WARNING_STRICT_FATAL(true, ("Detach script %s called with no owner", scriptName.c_str())); return SCRIPT_OVERRIDE; } if (!m_owner->isAuthoritative()) { MessageQueueString * data = new MessageQueueString(scriptName); m_owner->getController()->appendMessage( CM_detachScript, 0.0f, data, GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_AUTH_SERVER); return SCRIPT_CONTINUE; } if (m_lockScriptList) { DEBUG_WARNING(true, ("Trying to detach script %s to object %s while the script list is locked.", scriptName.c_str(), m_owner->getNetworkId().getValueString().c_str())); return SCRIPT_OVERRIDE; } ScriptList::iterator i; for (i = m_scriptList.begin(); i != m_scriptList.end(); ++i) if ((*i).isValid() && (*i).getScriptName() == scriptName) break; if (i==m_scriptList.end()) { return SCRIPT_OVERRIDE; } int retval = SCRIPT_CONTINUE; if (ConfigServerScript::getAllowBuildClusterScriptAttach()) { m_scriptList.erase(i); } else { if (JavaLibrary::instance() == nullptr) return SCRIPT_CONTINUE; // Make sure to call the detach trigger on this one script if one exists. ScriptParams params; retval = trigOneScript(scriptName, Scripting::TRIG_DETACH, params); // Do not remove the script on a return of "override" if (retval == SCRIPT_CONTINUE) { ScriptDataMap::iterator scriptIter = ms_scriptDataMap->find(scriptName); if (scriptIter != ms_scriptDataMap->end()) { // remove ourself from the script's owners (*scriptIter).second.owners.erase(m_owner->getNetworkId()); } // the OnDetach triggers may have removed other scripts, added new scripts, etc., so we can't // depend on our iterator still being valid. Find the script again: for (i = m_scriptList.begin(); i != m_scriptList.end(); ++i) if ((*i).isValid() && (*i).getScriptName() == scriptName) { m_scriptList.erase(i); break; } JavaLibrary::instance()->detachScriptFromObjId(m_owner->getNetworkId(), scriptName); } } packScriptList(); return retval; } // GameScriptObject::detachScript /** * Initialize script instance map with all de-persisted * scripts */ void GameScriptObject::initScriptInstances() { m_scriptListInitialized = true; if (JavaLibrary::instance() == nullptr) return; if (!m_owner) { WARNING_STRICT_FATAL(true, ("Use of nullptr m_owner in ::initScriptInstances()")); return; } if (m_scriptListValid || !m_owner->isAuthoritative()) return; // while we are in this function, scripts may not be attached or detached on the object m_lockScriptList = true; // reset the script list on the Java side JavaLibrary::instance()->detachAllScriptsFromObjId(m_owner->getNetworkId()); JavaLibrary::instance()->attachScriptsToObjId(m_owner->getNetworkId(), m_scriptList); for (ScriptList::const_iterator i = m_scriptList.begin(); i != m_scriptList.end(); ++i) { if ((*i).isValid()) (*i).getScriptData().owners.insert(m_owner->getNetworkId()); } m_lockScriptList = false; m_scriptListValid = true; } // GameScriptObject::initScriptInstances /** * Clears the script list. */ void GameScriptObject::removeAll(void) { if (JavaLibrary::instance() == nullptr) return; // removeAll() cannot be overriden by scripts // remove ourself from all scripts for (ScriptList::const_iterator i = m_scriptList.begin(); i != m_scriptList.end(); ++i) { if ((*i).isValid()) (*i).getScriptData().owners.erase(m_owner->getNetworkId()); } // clear our lists m_scriptList.clear(); packScriptList(); } // GameScriptObject::removeAll /** * Runs all the scripts in the script list. * * @param trigId script function id to call * @param params params to pass to the script functions * * @return SCRIPT_CONTINUE on success, SCRIPT_OVERRIDE on fail */ int GameScriptObject::trigAllScripts(Scripting::TrigId trigId, ScriptParams ¶ms) { NOT_NULL(m_owner); if (JavaLibrary::instance() == nullptr) return SCRIPT_CONTINUE; // if (m_javaId == nullptr && m_owner != nullptr && m_owner->getNetworkId().getValue() != 0) // { // if (JavaLibrary::instance() != nullptr) // m_javaId = JavaLibrary::getObjId(m_owner->getNetworkId()); // } if (m_pauseScripting) return SCRIPT_CONTINUE; //Authoritative check temporarily removed because some triggers aren't being called //because the setAuth message hasn't come in yet from Central on load. if (m_owner == nullptr) //@todo FIX THIS HACK || !m_owner->isAuthoritative()) return SCRIPT_CONTINUE; if(!m_owner->isAuthoritative()) { Archive::ByteStream paramArchive; Archive::put(paramArchive, params); MessageQueueScriptTrigger * data = new MessageQueueScriptTrigger(static_cast(trigId), paramArchive); ServerController * controller = dynamic_cast(m_owner->getController()); if(controller != nullptr) { controller->appendMessage( CM_scriptTrigger, 0.0f, data, GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_AUTH_SERVER ); } return SCRIPT_CONTINUE; } // handle special login/logout needs if (trigId == Scripting::TRIG_LOGIN) JavaLibrary::instance()->setObjIdLoggedIn(m_owner->getNetworkId(), true); else if (trigId == Scripting::TRIG_LOGOUT || trigId == Scripting::TRIG_IMMEDIATE_LOGOUT) JavaLibrary::instance()->setObjIdLoggedIn(m_owner->getNetworkId(), false); if (m_scriptList.size() == 0) return SCRIPT_CONTINUE; Scripting::_ScriptFuncHashMap::const_iterator funcIter = Scripting::ScriptFuncHashMap->find(static_cast(trigId)); if (funcIter == Scripting::ScriptFuncHashMap->end()) { WARNING_STRICT_FATAL(true, ("Got bad trigger id\n")); return SCRIPT_CONTINUE; } const Scripting::ScriptFuncTable *func = (*funcIter).second; // If any script has a handler for the trigger, invoke the script library's // run all scripts function. if (hasFunction(func->name)) return JavaLibrary::instance()->runScripts(m_owner->getNetworkId(), func->name, func->argList, params); return SCRIPT_CONTINUE; } // GameScriptObject::trigAllScripts /** * Runs one script. * * @param scriptName script to run * @param trigId script function id to call * @param params params to pass to the script function * * @return SCRIPT_CONTINUE on success, SCRIPT_OVERRIDE on fail or the script * does not exist */ int GameScriptObject::trigOneScript(const std::string& scriptName, Scripting::TrigId trigId, ScriptParams ¶ms) const { if (JavaLibrary::instance() == nullptr) return SCRIPT_CONTINUE; if (m_pauseScripting) return SCRIPT_CONTINUE; if (m_owner == nullptr /*|| !m_owner->isAuthoritative()*/) return SCRIPT_OVERRIDE; if (!m_owner->isAuthoritative() && (trigId != Scripting::TRIG_ATTACH && trigId != Scripting::TRIG_DETACH && trigId != Scripting::TRIG_INITIALIZE)) { DEBUG_WARNING(true, ("Called trigOneScript on invalid trigger id %d", trigId)); return SCRIPT_OVERRIDE; } if (!hasScript(scriptName) && trigId != Scripting::TRIG_ATTACH) return SCRIPT_OVERRIDE; Scripting::_ScriptFuncHashMap::const_iterator funcIter = Scripting::ScriptFuncHashMap->find(static_cast(trigId)); if (funcIter == Scripting::ScriptFuncHashMap->end()) return SCRIPT_OVERRIDE; const Scripting::ScriptFuncTable *func = (*funcIter).second; NOT_NULL(m_owner); // check to see if the script supports the trigger ScriptDataMap::iterator scriptFuncs = ms_scriptDataMap->find(scriptName); if (scriptFuncs != ms_scriptDataMap->end()) { std::set::const_iterator result = (*scriptFuncs).second.functions.find(func->name); if (result == (*scriptFuncs).second.functions.end()) { return SCRIPT_CONTINUE; } } return JavaLibrary::instance()->runScript(m_owner->getNetworkId(), scriptName, func->name, func->argList, params); } // GameScriptObject::trigOneScript /** * Runs one or all script(s), as invoked from the console. * * @param trigId script function id to call * @param scriptName script to run; if 0 length, runs all scripts * @param args params to send to the script (as a string vector) */ int GameScriptObject::trigScriptFromConsole(const Scripting::TrigId trigId, const std::string &scriptName, const StringVector_t &args) const { if (JavaLibrary::instance() == nullptr) { LOG("ScriptInvestigation", ("Returning script continue from console trigger request because there is no JavaLibrary instance\n")); return SCRIPT_CONTINUE; } if (m_pauseScripting) { LOG("ScriptInvestigation", ("Returning script continue from console trigger request because scripting is paused\n")); return SCRIPT_CONTINUE; } if (m_owner == nullptr ) { LOG("ScriptInvestigation", ("Returning script override from console trigger request becuase m_owner is nullptr\n")); return SCRIPT_OVERRIDE; } if (!m_owner->isAuthoritative()) { LOG("ScriptInvestigation", ("Returning script override from console trigger request becuase owner is not authoritative\n")); return SCRIPT_OVERRIDE; } Scripting::_ScriptFuncHashMap::const_iterator funcIter = Scripting::ScriptFuncHashMap->find(static_cast(trigId)); if (funcIter == Scripting::ScriptFuncHashMap->end()) { LOG("ScriptInvestigation", ("Returning script override from console trigger request becuase trigger id %i was not found\n", trigId)); return SCRIPT_OVERRIDE; } const Scripting::ScriptFuncTable *func = (*funcIter).second; NOT_NULL(m_owner); int retval = SCRIPT_CONTINUE; if (scriptName.size() == 0) { // trigger all scripts if (hasFunction(func->name)) retval = JavaLibrary::instance()->runScripts(m_owner->getNetworkId(), func->name, func->argList, args); } else { if (!hasScript(scriptName)) return SCRIPT_OVERRIDE; retval = JavaLibrary::instance()->runScript(m_owner->getNetworkId(), scriptName, func->name, func->argList, args); } return retval; } // GameScriptObject::trigScriptFromConsole bool GameScriptObject::handleMessage(const std::string &messageName, const ScriptDictionaryPtr & data) { if (JavaLibrary::instance() == nullptr) { WARNING_STRICT_FATAL(true, ("(messageToFailure) GameScriptObject::handleMessage got message " "when Java not running")); return true; } if (getOwner() == nullptr) { WARNING_STRICT_FATAL(true, ("(messageToFailure) GameScriptObject::handleMessage got message " "with no owner")); return true; } if (m_pauseScripting) { LOG("messageToFailure", ("GameScriptObject::handleMessage received " "message %s for object %s while scripting is paused", messageName.c_str(), getOwner()->getNetworkId().getValueString().c_str())); return false; } if (!m_scriptListValid) { LOG("messageToFailure", ("GameScriptObject::handleMessage received " "message %s for object %s while the script list is invalid", messageName.c_str(), getOwner()->getNetworkId().getValueString().c_str())); return false; } WARNING (m_attachingScript > 0, ("(messageToFailure) Object %s received messageTo while attaching " "a script, the message will not get to the script's handler", getOwner()->getNetworkId().getValueString().c_str())); if (hasFunction(messageName)) IGNORE_RETURN(JavaLibrary::instance()->callMessages(m_owner->getNetworkId(), messageName, data)); return true; } // GameScriptObject::handleMessage bool GameScriptObject::handleMessage(const std::string &messageName, const std::vector &packedData) { ScriptDictionaryPtr dictionary; if (JavaLibrary::instance() == nullptr) return true; if (m_pauseScripting) return false; bool result = false; if (JavaLibrary::instance()->unpackDictionary(packedData, dictionary)) { result = handleMessage(messageName, dictionary); } else { WARNING(true, ("Failed to unpack dictionary for message %s. This message will not be handled.", messageName.c_str())); // prevent the message from being resent result = true; } return result; } // GameScriptObject::handleMessage /** * Execute any applicable command handlers in the script list. * * @param funcName name of the command handler to call * @param params parameters to pass to the command handler * * @return SCRIPT_CONTINUE or SCRIPT_OVERRIDE */ int GameScriptObject::callScriptCommandHandler(std::string const &funcName, ScriptParams ¶ms) const { if (JavaLibrary::instance() == nullptr) return SCRIPT_CONTINUE; if (m_pauseScripting) return SCRIPT_CONTINUE; NOT_NULL(m_owner); if (hasFunction(funcName)) return JavaLibrary::instance()->runScripts(m_owner->getNetworkId(), funcName, "Ouf", params); return SCRIPT_CONTINUE; } /** * Execute any applicable buff handlers in the script list. * * @param funcName name of the buff handler to call * @param params parameters to pass to the buff handler * * @return SCRIPT_CONTINUE or SCRIPT_OVERRIDE */ int GameScriptObject::callScriptBuffHandler(std::string const &funcName, ScriptParams ¶ms) const { if (JavaLibrary::instance() == nullptr) return SCRIPT_CONTINUE; if (m_pauseScripting) return SCRIPT_CONTINUE; NOT_NULL(m_owner); if (hasFunction(funcName)) return JavaLibrary::instance()->runScripts(m_owner->getNetworkId(), funcName, "ssffsO", params); return SCRIPT_CONTINUE; } /** * Determines if a script is in the script list. * * @param scriptName script to look for * * @return true if found, false if not */ bool GameScriptObject::hasScript(const std::string& scriptName) const { for (ScriptList::const_iterator i = m_scriptList.begin(); i != m_scriptList.end(); ++i) if ((*i).isValid() && (*i).getScriptName() == scriptName) return true; return false; } // GameScriptObject::hasScript /** * Determines if any script implements a given trigger. * * @param trigId the trigger to check for * * @return true if found, false if not */ bool GameScriptObject::hasTrigger(const Scripting::TrigId trigId) const { // get the trigger fuction description Scripting::_ScriptFuncHashMap::const_iterator funcIter = Scripting::ScriptFuncHashMap->find(static_cast(trigId)); if (funcIter == Scripting::ScriptFuncHashMap->end()) return false; const Scripting::ScriptFuncTable *func = (*funcIter).second; return hasFunction(func->name); } // GameScriptObject::hasTrigger /** * Fills in a vector with a copy of the current script list. */ void GameScriptObject::enumerateScripts(std::vector &scriptNames) const { scriptNames.clear(); for (ScriptList::const_iterator i = m_scriptList.begin(); i != m_scriptList.end(); ++i) { if ((*i).isValid()) scriptNames.push_back((*i).getScriptName()); } } // GameScriptObject::EnumerateScripts /** * Converts ScriptParams into a ScriptDictionary. * * @param params the ScriptParams to convert * @param dictionary pointer that will be set to the dictionary. The caller * is responsible for deleting the pointer. */ void GameScriptObject::makeScriptDictionary(const ScriptParams & params, ScriptDictionaryPtr & dictionary) { if (JavaLibrary::instance() != nullptr) { JavaDictionaryPtr jdp; JavaLibrary::instance()->convert(params, jdp); dictionary = jdp; } } /** * Tests if the scripting engine is running. * * @return true if the engine is running, false if not */ bool GameScriptObject::isScriptingEnabled(void) { if (JavaLibrary::instance() != nullptr) return true; return false; } // GameScriptObject::isScriptingEnabled /** * Tells the script to reload a script file. * * @param scriptName the script name * * @return true on success, false if the script wasn't found */ bool GameScriptObject::reloadScript(const std::string& scriptName) { if (JavaLibrary::instance() == nullptr) return false; bool result = false; ScriptDataMap::iterator scriptIter; scriptIter = ms_scriptDataMap->find(scriptName); if (scriptIter != ms_scriptDataMap->end()) (*scriptIter).second.functions.clear(); result = JavaLibrary::instance()->reloadScript(scriptName); // query the script to get a list of triggers, messageHandlers, and // pathFindHandlers it has if (result) { if (!JavaLibrary::instance()->queryScriptFunctions(scriptName)) { WARNING_STRICT_FATAL(true, ("Script query during reload of %s failed\n", scriptName.c_str())); } for(scriptIter = ms_scriptDataMap->begin(); scriptIter != ms_scriptDataMap->end(); ++scriptIter) { result = JavaLibrary::instance()->reloadScript(scriptIter->first); if(result) { if (!JavaLibrary::instance()->queryScriptFunctions(scriptIter->first)) { WARNING_STRICT_FATAL(true, ("Script query during reload of %s failed\n", scriptIter->first.c_str())); } } else { return false; } } } return result; } // GameScriptObject::reloadScript //----------------------------------------------------------------------- /** * Turns script logging on or off. * * @param enable flag to turn logging on or off */ void GameScriptObject::enableLogging(bool enable) { if (JavaLibrary::instance() != nullptr) JavaLibrary::instance()->enableLogging(enable); } // GameScriptObject::enableLogging //----------------------------------------------------------------------- /** * Turns tracking of players to see if they can become Jedi on or off. * * @param enableTracking flag to turn tracking on or off */ void GameScriptObject::enableNewJediTracking(bool enableTracking) { if (JavaLibrary::instance() != nullptr) JavaLibrary::instance()->enableNewJediTracking(enableTracking); } // GameScriptObject::enableNewJediTracking //----------------------------------------------------------------------- /** * Returns the amount of free memory left to the script system. */ int GameScriptObject::getScriptFreeMem() { int64 freemem = JavaLibrary::getFreeJavaMemory(); if (freemem > INT_MAX) return INT_MAX; return static_cast(freemem); } //----------------------------------------------------------------------- Scheduler & GameScriptObject::getScriptScheduler() { static Scheduler scriptScheduler; return scriptScheduler; } //----------------------------------------------------------------------- void GameScriptObject::onStopWatching(ServerObject & subject) { if (JavaLibrary::instance() == nullptr) return; if (m_owner) { JavaLibrary::onStopWatching(*m_owner, subject); } } //----------------------------------------------------------------------- void GameScriptObject::onWatching(ServerObject & subject) { if (JavaLibrary::instance() == nullptr) return; if (m_owner) { JavaLibrary::onWatching(*m_owner, subject); } } //----------------------------------------------------------------------- void GameScriptObject::runOneScript(const std::string & scriptName, const std::string & methodName, const std::string & argTypes, ScriptParams & args) { if (JavaLibrary::instance() != nullptr) IGNORE_RETURN( JavaLibrary::instance()->runScript(NetworkId(), scriptName, methodName, argTypes, args) ); } //----------------------------------------------------------------------- /** * Calls a special function on a script that returns a string instead of an * integer. The script function has no "self" associated with it. * * @param scriptName the name of the script to call * @param methodName the function name in the script * @param argTypes the argument types that are being passed to the script * @param args the arguments that are being passed to the script * * @return the string returned from the script function */ std::string GameScriptObject::callScriptConsoleHandler(const std::string & scriptName, const std::string & methodName, const std::string & argTypes, ScriptParams & args) { static const std::string errorReturnString; if (JavaLibrary::instance() != nullptr) { return JavaLibrary::instance()->callScriptConsoleHandler(scriptName, methodName, argTypes, args); } return errorReturnString; } // GameScriptObject::callScriptConsoleHandler //----------------------------------------------------------------------- void GameScriptObject::callSpaceMakeOvert(const NetworkId &player) { JavaLibrary::spaceMakeOvert(player); } //----------------------------------------------------------------------- void GameScriptObject::callSpaceClearOvert(const NetworkId &ship) { JavaLibrary::spaceClearOvert(ship); } //----------------------------------------------------------------------- std::string GameScriptObject::callDumpTargetInfo( NetworkId &id ) { if( JavaLibrary::instance() != nullptr ) { return JavaLibrary::instance()->getObjectDumpInfo( id ); } return ""; } //----------------------------------------------------------------------- void GameScriptObject::setScriptVar(const std::string & name, int value) { if (JavaLibrary::instance() == nullptr || m_owner == nullptr) return; JavaLibrary::setScriptVar(*m_owner, name, value); } //----------------------------------------------------------------------- void GameScriptObject::setScriptVar(const std::string & name, float value) { if (JavaLibrary::instance() == nullptr || m_owner == nullptr) return; JavaLibrary::setScriptVar(*m_owner, name, value); } //----------------------------------------------------------------------- void GameScriptObject::setScriptVar(const std::string & name, const std::string & value) { if (JavaLibrary::instance() == nullptr || m_owner == nullptr) return; JavaLibrary::setScriptVar(*m_owner, name, value); } //----------------------------------------------------------------------- void GameScriptObject::packAllScriptVarDeltas() { if (JavaLibrary::instance() == nullptr) return; JavaLibrary::packAllDeltaScriptVars(); } //----------------------------------------------------------------------- void GameScriptObject::clearScriptVars() { if (JavaLibrary::instance() == nullptr || m_owner == nullptr) return; JavaLibrary::clearScriptVars(*m_owner); } //----------------------------------------------------------------------- void GameScriptObject::packScriptVars(std::vector & target) const { if (JavaLibrary::instance() == nullptr || m_owner == nullptr) return; JavaLibrary::packScriptVars(*m_owner, target); } //----------------------------------------------------------------------- void GameScriptObject::unpackScriptVars(const std::vector & source) const { if (JavaLibrary::instance() == nullptr || m_owner == nullptr) return; JavaLibrary::unpackScriptVars(*m_owner, source); } //----------------------------------------------------------------------- void GameScriptObject::unpackDeltaScriptVars(const std::vector & data) const { if (JavaLibrary::instance() == nullptr || m_owner == nullptr) return; DEBUG_REPORT_LOG(! m_owner, ("A game script object received a request to unpack script var synchronization data, but it has no owner object!!! All GameScriptObjects MUST have owners!\n")); JavaLibrary::unpackDeltaScriptVars(*m_owner, data); } //----------------------------------------------------------------------- bool GameScriptObject::hasFunction(std::string const &funcName) const { for (ScriptList::const_iterator i = m_scriptList.begin(); i != m_scriptList.end(); ++i) { if ((*i).isValid()) { std::set const &functions = (*i).getScriptData().functions; if (functions.find(funcName) != functions.end()) return true; } } return false; } // ---------------------------------------------------------------------- ScriptListEntry GameScriptObject::getScriptListEntry(std::string const &scriptName) // static { // This function gets a valid script list entry for the script name // specified, regardless of whether the script has been previously // loaded, or exists at all, and if the script was not previously // loaded, looks up the accessible functions from the script. std::pair result = ms_scriptDataMap->insert(std::make_pair(scriptName, ScriptData())); ScriptListEntry entry(&(*result.first)); if (result.second) { // if the script was not already in the map, then we need to look up the functions in it if (!JavaLibrary::instance()->queryScriptFunctions(scriptName)) WARNING(true, ("Error while attaching script %s. Does it exist?", scriptName.c_str())); } return entry; } // ---------------------------------------------------------------------- /** * Pack the script list vector into the synchronized variable */ void GameScriptObject::packScriptList() { std::string newPackedList; for (ScriptList::const_iterator i=m_scriptList.begin(); i!=m_scriptList.end(); ++i) { newPackedList+=i->getScriptName()+':'; } if (newPackedList.size() > 2000) { if (m_owner) FATAL(true, ("Object %s has a script list that exceeds 2000 characters. The list: %s",m_owner->getNetworkId().getValueString().c_str(), newPackedList.c_str())); else FATAL(true, ("Object has a script list that exceeds 2000 characters, but the object id could not be determined. The list: %s", newPackedList.c_str())); } m_synchronizedScriptList=newPackedList; } // ---------------------------------------------------------------------- /** * Unpack the script list from the synchronized variable */ void GameScriptObject::unpackScriptList() { const std::string &packedList = m_synchronizedScriptList.get(); m_scriptList.clear(); std::string scriptName; for (std::string::const_iterator i=packedList.begin(); i!=packedList.end(); ++i) { if (*i==':') { m_scriptList.push_back(getScriptListEntry(scriptName)); scriptName.clear(); } else scriptName+=*i; } } // ---------------------------------------------------------------------- /** * Called when the script list synchronized variable is modified. Updates * the script list on non-authoritative objects. Updates the data in * the Java obj_id object for both authoritative and non-authoritative objects. */ void GameScriptObject::SyncScriptListCallback::modified(GameScriptObject & target, const std::string & oldValue, const std::string & newValue, bool isLocal) const { if (!isLocal) { target.unpackScriptList(); if (!ConfigServerScript::getAllowBuildClusterScriptAttach()) { JavaLibrary::instance()->detachAllScriptsFromObjId(target.m_owner->getNetworkId()); JavaLibrary::instance()->attachScriptsToObjId(target.m_owner->getNetworkId(),target.m_scriptList); } } } // ====================================================================== namespace Archive { void get(ReadIterator &source, ScriptListEntry &target) { std::string scriptName; get(source, scriptName); target = GameScriptObject::getScriptListEntry(scriptName); } // ---------------------------------------------------------------------- void put(ByteStream &target, ScriptListEntry const &source) { if (source.isValid()) put(target, source.getScriptName()); } // ---------------------------------------------------------------------- } // ---------------------------------------------------------------------- GameScriptObject * GameScriptObject::asGameScriptObject(Object * const object) { ServerObject * const serverObject = (object != nullptr) ? object->asServerObject() : nullptr; return (serverObject != nullptr) ? serverObject->getScriptObject() : nullptr; } // ---------------------------------------------------------------------- GameScriptObject const * GameScriptObject::asGameScriptObject(Object const * const object) { ServerObject const * const serverObject = (object != nullptr) ? object->asServerObject() : nullptr; return (serverObject != nullptr) ? serverObject->getScriptObject() : nullptr; } // ======================================================================