| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- #include "Base.h"
- #include "ScriptController.h"
- #include "ScriptTarget.h"
- namespace gameplay
- {
- // TODO: Handle reloading scripts and then case where EventRegistries have events added AFTER a script has been loaded (i.e. need to reload callbacks for the script).
- extern void splitURL(const std::string& url, std::string* file, std::string* id);
- const char* ScriptTarget::Event::getName() const
- {
- return name.c_str();
- }
- const char* ScriptTarget::Event::getArgs() const
- {
- return args.c_str();
- }
- ScriptTarget::EventRegistry::EventRegistry()
- {
- }
- ScriptTarget::EventRegistry::~EventRegistry()
- {
- for (size_t i = 0, count = _events.size(); i < count; ++i)
- {
- SAFE_DELETE(_events[i]);
- }
- }
- const ScriptTarget::Event* ScriptTarget::EventRegistry::addEvent(const char* name, const char* args)
- {
- GP_ASSERT(name);
- Event* evt = new Event;
- evt->name = name;
- evt->args = args ? args : "";
- _events.push_back(evt);
- return evt;
- }
- unsigned int ScriptTarget::EventRegistry::getEventCount() const
- {
- return _events.size();
- }
- const ScriptTarget::Event* ScriptTarget::EventRegistry::getEvent(unsigned int index) const
- {
- GP_ASSERT(index < _events.size());
- return _events[index];
- }
- const ScriptTarget::Event* ScriptTarget::EventRegistry::getEvent(const char* name) const
- {
- GP_ASSERT(name);
- for (size_t i = 0, count = _events.size(); i < count; ++i)
- {
- if (_events[i]->name == name)
- return _events[i];
- }
- return NULL;
- }
- ScriptTarget::ScriptTarget() : _scriptRegistries(NULL), _scripts(NULL), _scriptCallbacks(NULL)
- {
- }
- ScriptTarget::~ScriptTarget()
- {
- // Free callbacks
- SAFE_DELETE(_scriptCallbacks);
- // Free scripts
- ScriptEntry* se = _scripts;
- while (se)
- {
- ScriptEntry* tmp = se;
- se = se->next;
- SAFE_RELEASE(tmp->script);
- SAFE_DELETE(tmp);
- }
- // Free registry entries
- RegistryEntry* re = _scriptRegistries;
- while (re)
- {
- RegistryEntry* tmp = re;
- re = re->next;
- // Don't delete the actual EventRegistry, since it's shared by all
- // ScriptTargets of the same type
- SAFE_DELETE(tmp);
- }
- }
- void ScriptTarget::registerEvents(EventRegistry* registry)
- {
- GP_ASSERT(registry);
- // Attach the registry
- RegistryEntry* re = new RegistryEntry(registry);
- if (_scriptRegistries)
- {
- RegistryEntry* last = _scriptRegistries;
- while (last->next)
- last = last->next;
- last->next = re;
- re->prev = last;
- }
- else
- {
- _scriptRegistries = re;
- }
- }
- Script* ScriptTarget::addScript(const char* path)
- {
- ScriptController* sc = Game::getInstance()->getScriptController();
- // Load the script
- Script* script = sc->loadScript(path, Script::PROTECTED);
- if (!script)
- return NULL;
- // Attach the script
- ScriptEntry* se = new ScriptEntry(script);
- if (_scripts)
- {
- ScriptEntry* last = _scripts;
- while (last->next)
- last = last->next;
- last->next = se;
- se->prev = last;
- }
- else
- {
- _scripts = se;
- }
- // Inspect the loaded script for event functions that are supported by this ScriptTarget.
- // TODO: We'll need to re-load eventCallbacks when EventRegistries change for this ScriptObject.
- RegistryEntry* re = _scriptRegistries;
- while (re)
- {
- std::vector<Event*>& events = re->registry->_events;
- for (size_t i = 0, count = events.size(); i < count; ++i)
- {
- const Event* event = events[i];
- if (sc->functionExists(event->name.c_str(), script))
- {
- if (!_scriptCallbacks)
- _scriptCallbacks = new std::map<const Event*, std::vector<CallbackFunction>>();
- (*_scriptCallbacks)[event].push_back(CallbackFunction(script, event->name.c_str()));
- }
- }
- re = re->next;
- }
- // Automatically call the 'attached' event if it is defined within the script
- if (sc->functionExists("attached", script))
- {
- char args[256];
- sprintf(args, "<%s>", getTypeName());
- sc->executeFunction<void>(script, "attached", args, dynamic_cast<void*>(this));
- }
- return script;
- }
- bool ScriptTarget::removeScript(const char* path)
- {
- GP_ASSERT(path);
- ScriptEntry* se = _scripts;
- while (se)
- {
- if (strcmp(se->script->getPath(), path) == 0 && se->script->getScope() == Script::PROTECTED)
- {
- removeScript(se);
- return true;
- }
- se = se->next;
- }
- return false;
- }
- void ScriptTarget::removeScript(ScriptEntry* se)
- {
- GP_ASSERT(se);
- // Link out this ScriptEntry
- if (se->prev)
- se->prev->next = se->next;
- if (se->next)
- se->next->prev = se->prev;
- if (_scripts == se)
- _scripts = se->next;
- Script* script = se->script;
- // Delete the ScriptEntry
- SAFE_DELETE(se);
- // Erase any callback functions registered for this script
- if (_scriptCallbacks)
- {
- std::map<const Event*, std::vector<CallbackFunction>>::iterator itr = _scriptCallbacks->begin();
- for ( ; itr != _scriptCallbacks->end(); ++itr)
- {
- std::vector<CallbackFunction>& callbacks = itr->second;
- std::vector<CallbackFunction>::iterator itr2 = callbacks.begin();
- while (itr2 != callbacks.end())
- {
- if (itr2->script == script)
- itr2 = callbacks.erase(itr2);
- else
- ++itr2;
- }
- }
- }
- // Free the script
- SAFE_RELEASE(script);
- }
- void ScriptTarget::addScriptCallback(const Event* event, const char* function)
- {
- GP_ASSERT(event);
- GP_ASSERT(function);
- // Parse the script name (if it exists) and function out
- std::string scriptPath, func;
- splitURL(function, &scriptPath, &func);
- if (func.length() == 0)
- {
- // The url doesn't reference a script, only a function
- func = scriptPath;
- scriptPath = "";
- }
- // Have we already loaded this global script?
- bool loaded = true;
- Script* script = NULL;
- if (!scriptPath.empty())
- {
- loaded = false;
- ScriptEntry* se = _scripts;
- while (se)
- {
- if (scriptPath == se->script->getPath() && se->script->getScope() == Script::GLOBAL)
- {
- // Script is already loaded
- script = se->script;
- loaded = true;
- break;
- }
- se = se->next;
- }
- }
- if (!loaded)
- {
- // The specified global script is not yet loaded, so do so
- script = Game::getInstance()->getScriptController()->loadScript(scriptPath.c_str(), Script::GLOBAL);
- if (script)
- {
- loaded = true;
- ScriptEntry* se = new ScriptEntry(script);
- if (_scripts)
- {
- ScriptEntry* last = _scripts;
- while (last->next)
- last = last->next;
- last->next = se;
- se->prev = last;
- }
- else
- {
- _scripts = se;
- }
- }
- else
- {
- GP_WARN("Failed to load script '%s' for script target while registering for function: %s", scriptPath.c_str(), function);
- }
- }
- if (loaded)
- {
- // Store the callback
- if (!_scriptCallbacks)
- _scriptCallbacks = new std::map<const Event*, std::vector<CallbackFunction>>();
- (*_scriptCallbacks)[event].push_back(CallbackFunction(script, func.c_str()));
- }
- }
- void ScriptTarget::removeScriptCallback(const Event* event, const char* function)
- {
- // Parse the script name (if it exists) and function out
- std::string scriptPath, func;
- splitURL(function, &scriptPath, &func);
- if (func.length() == 0)
- {
- // The url doesn't reference a script, only a function
- func = scriptPath;
- scriptPath = "";
- }
- // Find the script entry for this callback
- ScriptEntry* scriptEntry = NULL;
- if (!scriptPath.empty())
- {
- ScriptEntry* se = _scripts;
- while (se)
- {
- if (scriptPath == se->script->getPath() && se->script->getScope() == Script::GLOBAL)
- {
- scriptEntry = se;
- break;
- }
- se = se->next;
- }
- }
- Script* script = scriptEntry ? scriptEntry->script : NULL;
- // Remove any registered callback functions that match the specified one
- int removedCallbacks = 0;
- int totalCallbacks = 0;
- if (_scriptCallbacks)
- {
- std::map<const Event*, std::vector<CallbackFunction>>::iterator itr = _scriptCallbacks->begin();
- for (; itr != _scriptCallbacks->end(); ++itr)
- {
- // Erase matching callback functions for this event
- bool forEvent = itr->first == event;
- std::vector<CallbackFunction>& callbacks = itr->second;
- std::vector<CallbackFunction>::iterator itr2 = callbacks.begin();
- while (itr2 != callbacks.end())
- {
- if (itr2->script == script)
- {
- ++totalCallbacks; // sum total number of callbacks found for this script
- if (forEvent && itr2->function == func)
- {
- itr2 = callbacks.erase(itr2);
- ++removedCallbacks; // sum number of callbacks removed
- }
- else
- ++itr2;
- }
- }
- }
- }
- // Cleanup the script if there are no remaining callbacks for it
- if (scriptEntry && (totalCallbacks - removedCallbacks) <= 0)
- {
- removeScript(scriptEntry);
- }
- }
- void ScriptTarget::clearScripts()
- {
- while (_scripts)
- {
- removeScript(_scripts);
- }
- }
- bool ScriptTarget::hasScriptListener(const char* eventName) const
- {
- const Event* event = getScriptEvent(eventName);
- return event ? hasScriptListener(event) : false;
- }
- const ScriptTarget::Event* ScriptTarget::getScriptEvent(const char* eventName) const
- {
- GP_ASSERT(eventName);
- // Lookup the event for this name
- const Event* event = NULL;
- RegistryEntry* re = _scriptRegistries;
- while (re)
- {
- if ((event = re->registry->getEvent(eventName)) != NULL)
- break;
- re = re->next;
- }
- return event;
- }
- bool ScriptTarget::hasScriptListener(const Event* event) const
- {
- GP_ASSERT(event);
- if (_scriptCallbacks)
- {
- std::map<const Event*, std::vector<CallbackFunction>>::iterator itr = _scriptCallbacks->find(event);
- if (itr != _scriptCallbacks->end())
- {
- return !itr->second.empty();
- }
- }
- return false;
- }
- template<> void ScriptTarget::fireScriptEvent<void>(const Event* event, ...)
- {
- GP_ASSERT(event);
- if (!_scriptCallbacks)
- return; // no registered callbacks
- va_list list;
- va_start(list, event);
- // Lookup registered callbacks for this event and fire them
- std::map<const Event*, std::vector<CallbackFunction>>::iterator itr = _scriptCallbacks->find(event);
- if (itr != _scriptCallbacks->end())
- {
- ScriptController* sc = Game::getInstance()->getScriptController();
- std::vector<CallbackFunction>& callbacks = itr->second;
- for (size_t i = 0, count = callbacks.size(); i < count; ++i)
- {
- CallbackFunction& cb = callbacks[i];
- sc->executeFunction<void>(cb.script, cb.function.c_str(), event->args.c_str(), &list);
- }
- }
- va_end(list);
- }
- template<> bool ScriptTarget::fireScriptEvent<bool>(const Event* event, ...)
- {
- GP_ASSERT(event);
- if (!_scriptCallbacks)
- return false; // no registered callbacks
- va_list list;
- va_start(list, event);
- // Lookup registered callbacks for this event and fire them
- std::map<const Event*, std::vector<CallbackFunction>>::iterator itr = _scriptCallbacks->find(event);
- if (itr != _scriptCallbacks->end())
- {
- ScriptController* sc = Game::getInstance()->getScriptController();
- std::vector<CallbackFunction>& callbacks = itr->second;
- for (size_t i = 0, count = callbacks.size(); i < count; ++i)
- {
- CallbackFunction& cb = callbacks[i];
- if (sc->executeFunction<bool>(cb.script, cb.function.c_str(), event->args.c_str(), &list))
- {
- va_end(list);
- return true;
- }
- }
- }
- va_end(list);
- return false;
- }
- }
|