//======================================================================== // ScriptExports.cpp // // Part of the GameCode4 Application // // GameCode4 is the sample application that encapsulates much of the source code // discussed in "Game Coding Complete - 4th Edition" by Mike McShaffry and David // "Rez" Graham, published by Charles River Media. // ISBN-10: 1133776574 | ISBN-13: 978-1133776574 // // If this source code has found it's way to you, and you think it has helped you // in any way, do the authors a favor and buy a new copy of the book - there are // detailed explanations in it that compliment this code well. Buy a copy at Amazon.com // by clicking here: // http://www.amazon.com/gp/product/1133776574/ref=olp_product_details?ie=UTF8&me=&seller= // // There's a companion web site at http://www.mcshaffry.com/GameCode/ // // The source code is managed and maintained through Google Code: // http://code.google.com/p/gamecode4/ // // (c) Copyright 2012 Michael L. McShaffry and David Graham // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser GPL v3 // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See // http://www.gnu.org/licenses/lgpl-3.0.txt for more details. // // You should have received a copy of the GNU Lesser GPL v3 // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //======================================================================== #include "../Unix/GameCodeStd.h" #include "../ResourceCache/ResCache.h" #include "ScriptExports.h" #include "LuaStateManager.h" #include "../EventManager/Events.h" #include #include //--------------------------------------------------------------------------------------------------------------------- // This is the C++ listener proxy for script event listeners. It pairs a single event type with a Lua callback // function. Note that this event can be defined in C++ or Lua. It may also be sent from C++ or Lua. // // The Lua callback function should take in a table with the event data. The return value is ignored. // function Callback(eventData) // // Chapter 12, page 384 //--------------------------------------------------------------------------------------------------------------------- class ScriptEventListener { EventType m_eventType; sol::protected_function m_scriptCallbackFunction; public: explicit ScriptEventListener(const EventType& eventType, const sol::function scriptCallbackFunction); ~ScriptEventListener(void); EventListenerDelegate GetDelegate(void) { return MakeDelegate(this, &ScriptEventListener::ScriptEventDelegate); } void ScriptEventDelegate(IEventDataPtr pEventPtr); }; //--------------------------------------------------------------------------------------------------------------------- // This class manages the C++ ScriptListener objects needed for script event listeners. // Chapter 12, page 385 //--------------------------------------------------------------------------------------------------------------------- class ScriptEventListenerMgr { typedef std::set ScriptEventListenerSet; ScriptEventListenerSet m_listeners; public: ~ScriptEventListenerMgr(void); void AddListener(ScriptEventListener* pListener); void DestroyListener(ScriptEventListener* pListener); }; //--------------------------------------------------------------------------------------------------------------------- // Prototypes for the functions to export. - Chapter 12, page 368 //--------------------------------------------------------------------------------------------------------------------- class InternalScriptExports { static ScriptEventListenerMgr* s_pScriptEventListenerMgr; public: static bool Init(void); static void Destroy(void); // resource loading static bool LoadAndExecuteScriptResource(const char* scriptResource); // event system static unsigned long RegisterEventListener(EventType eventType, sol::function callbackFunction); static bool QueueEvent(EventType eventType, sol::object eventData); // misc static void LuaLog(sol::object text); private: static std::shared_ptr BuildEvent(EventType eventType, sol::object& eventData); }; ScriptEventListenerMgr* InternalScriptExports::s_pScriptEventListenerMgr = NULL; //--------------------------------------------------------------------------------------------------------------------- // Destructor //--------------------------------------------------------------------------------------------------------------------- ScriptEventListenerMgr::~ScriptEventListenerMgr(void) { for (auto it = m_listeners.begin(); it != m_listeners.end(); ++it) { ScriptEventListener* pListener = (*it); delete pListener; } m_listeners.clear(); } //--------------------------------------------------------------------------------------------------------------------- // Adds a new listener //--------------------------------------------------------------------------------------------------------------------- void ScriptEventListenerMgr::AddListener(ScriptEventListener* pListener) { m_listeners.insert(pListener); } //--------------------------------------------------------------------------------------------------------------------- // Event Listener //--------------------------------------------------------------------------------------------------------------------- ScriptEventListener::ScriptEventListener(const EventType& eventType, const sol::function scriptCallbackFunction) : m_scriptCallbackFunction(scriptCallbackFunction) { m_eventType = eventType; } ScriptEventListener::~ScriptEventListener(void) { IEventManager* pEventMgr = IEventManager::Get(); if (pEventMgr) pEventMgr->VRemoveListener(GetDelegate(), m_eventType); } void ScriptEventListener::ScriptEventDelegate(IEventDataPtr pEvent) { GCC_ASSERT(m_scriptCallbackFunction.valid()); // this should never happen since it's validated before even creating this object // call the lua function std::shared_ptr pScriptEvent = static_pointer_cast(pEvent); sol::function Callback = m_scriptCallbackFunction; Callback(pScriptEvent->GetEventData()); } //--------------------------------------------------------------------------------------------------------------------- // Initializes the script export system //--------------------------------------------------------------------------------------------------------------------- bool InternalScriptExports::Init(void) { GCC_ASSERT(s_pScriptEventListenerMgr == NULL); s_pScriptEventListenerMgr = GCC_NEW ScriptEventListenerMgr; return true; } //--------------------------------------------------------------------------------------------------------------------- // Destroys the script export system //--------------------------------------------------------------------------------------------------------------------- void InternalScriptExports::Destroy(void) { GCC_ASSERT(s_pScriptEventListenerMgr != NULL); SAFE_DELETE(s_pScriptEventListenerMgr); } //--------------------------------------------------------------------------------------------------------------------- // Loads a script resource then executes it. This is used by the require() function in script (defined in // PreInit.lua). //--------------------------------------------------------------------------------------------------------------------- bool InternalScriptExports::LoadAndExecuteScriptResource(const char* scriptResource) { Resource resource(scriptResource); std::shared_ptr pResourceHandle = g_pApp->m_ResCache->GetHandle(&resource); if (pResourceHandle) return true; return false; } //--------------------------------------------------------------------------------------------------------------------- // Queue's an event from the script. Returns true if the event was sent, false if not. //--------------------------------------------------------------------------------------------------------------------- bool InternalScriptExports::QueueEvent(EventType eventType, sol::object eventData) { std::shared_ptr pEvent(BuildEvent(eventType, eventData)); if (pEvent) { IEventManager::Get()->VQueueEvent(pEvent); return true; } return false; } //--------------------------------------------------------------------------------------------------------------------- // Builds the event to be sent or queued //--------------------------------------------------------------------------------------------------------------------- std::shared_ptr InternalScriptExports::BuildEvent(EventType eventType, sol::object& eventData) { // create the event from the event type std::shared_ptr pEvent(ScriptEvent::CreateEventFromScript(eventType)); if (!pEvent) return std::shared_ptr(); // set the event data that was passed in if (!pEvent->SetEventData(eventData)) { return std::shared_ptr(); } return pEvent; } //--------------------------------------------------------------------------------------------------------------------- // Instantiates a C++ ScriptListener object, inserts it into the manager, and returns a handle to it. The script // should maintain the handle if it needs to remove the listener at some point. Otherwise, the listener will be // destroyed when the program exits. //--------------------------------------------------------------------------------------------------------------------- unsigned long InternalScriptExports::RegisterEventListener(EventType eventType, sol::function callbackFunction) { GCC_ASSERT(s_pScriptEventListenerMgr); if (callbackFunction.valid()) { // create the C++ listener proxy and set it to listen for the event ScriptEventListener* pListener = GCC_NEW ScriptEventListener(eventType, callbackFunction); s_pScriptEventListenerMgr->AddListener(pListener); IEventManager::Get()->VAddListener(pListener->GetDelegate(), eventType); // convert the pointer to an unsigned long to use as the handle unsigned long handle = reinterpret_cast(pListener); return handle; } GCC_ERROR("Attempting to register script event listener with invalid callback function"); return 0; } void InternalScriptExports::LuaLog(sol::object text) { if (text.is()) { GCC_LOG("Lua", text.as()); } else { const char* typeName = lua_typename(text.lua_state(), static_cast(text.get_type())); GCC_LOG("Lua", "<" + std::string(typeName) + ">"); } } //--------------------------------------------------------------------------------------------------------------------- // This function registers all the ScriptExports functions with the scripting system. It is called in // Application::Init(). //--------------------------------------------------------------------------------------------------------------------- void ScriptExports::Register(void) { sol::state_view lua = LuaStateManager::Get()->GetLuaState(); sol::global_table globals = lua.globals(); // init InternalScriptExports::Init(); // resource loading globals["LoadAndExecuteScriptResource"] = &InternalScriptExports::LoadAndExecuteScriptResource; // actors // globals.RegisterDirect("CreateActor", &InternalScriptExports::CreateActor); // event system globals["RegisterEventListener"] = &InternalScriptExports::RegisterEventListener; // globals.RegisterDirect("RemoveEventListener", &InternalScriptExports::RemoveEventListener); globals["QueueEvent"] = &InternalScriptExports::QueueEvent; // globals.RegisterDirect("TriggerEvent", &InternalScriptExports::TriggerEvent); // process system // globals.RegisterDirect("AttachProcess", &InternalScriptExports::AttachScriptProcess); // math // LuaPlus::LuaObject mathTable = globals.GetByName("GccMath"); // GCC_ASSERT(mathTable.IsTable()); // mathTable.RegisterDirect("GetYRotationFromVector", &InternalScriptExports::GetYRotationFromVector); // mathTable.RegisterDirect("WrapPi", &InternalScriptExports::WrapPi); // mathTable.RegisterDirect("GetVectorFromRotation", &InternalScriptExports::GetVectorFromRotation); // misc globals["Log"] = &InternalScriptExports::LuaLog; // globals.RegisterDirect("GetTickCount", &InternalScriptExports::GetTickCount); // Physics // globals.RegisterDirect("ApplyForce", &InternalScriptExports::ApplyForce); // globals.RegisterDirect("ApplyTorque", &InternalScriptExports::ApplyTorque); } //--------------------------------------------------------------------------------------------------------------------- // This function unregisters all the ScriptExports functions and gives any underlying systems a chance to destroy // themselves. It is called in the Application destructor. //--------------------------------------------------------------------------------------------------------------------- void ScriptExports::Unregister(void) { InternalScriptExports::Destroy(); }