[Orxonox-commit 6898] r11519 - in code/branches/ScriptableController_HS17/src/orxonox: . scriptablecontroller
kohlia at orxonox.net
kohlia at orxonox.net
Mon Oct 30 16:06:11 CET 2017
Author: kohlia
Date: 2017-10-30 16:06:11 +0100 (Mon, 30 Oct 2017)
New Revision: 11519
Added:
code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/
code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/CMakeLists.txt
code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/is_callable.h
code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.h
code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.ipp
code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.cc
code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.h
code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.cc
code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.h
code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.cc
code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.h
Log:
See previous
Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/CMakeLists.txt
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/CMakeLists.txt (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/CMakeLists.txt 2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,6 @@
+ADD_SOURCE_FILES(ORXONOX_SRC_FILES
+ luatb_typed_stack.cc
+ scriptable_controller.cc
+ scriptable_controller_api.cc
+)
+
Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/is_callable.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/is_callable.h (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/is_callable.h 2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,40 @@
+#ifndef IS_CALLABLE_H
+#define IS_CALLABLE_H
+
+#include <type_traits>
+
+// See https://stackoverflow.com/a/15396757/7421666
+template<typename T>
+struct IsCallableImpl
+{
+private:
+ typedef char(&yes)[1];
+ typedef char(&no)[2];
+
+ struct Fallback { void operator()(); };
+ struct Derived : T, Fallback { };
+
+ template<typename U, U> struct Check;
+
+ template<typename>
+ static yes test(...);
+
+ template<typename C>
+ static no test(Check<void (Fallback::*)(), &C::operator()>*);
+
+public:
+ static const bool value = sizeof(test<Derived>(0)) == sizeof(yes);
+};
+
+// Checks if a type is callable (has an 'operator()'). This will not work for
+// normal functions without modifications, but we don't need that case anyway.
+template<typename T>
+struct IsCallable
+ : std::conditional<
+ std::is_class<T>::value,
+ IsCallableImpl<T>,
+ std::false_type
+ >::type
+{ };
+
+#endif // IS_CALLABLE_H
Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.h (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.h 2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,26 @@
+#ifndef LUATB_H
+#define LUATB_H
+
+#include <string>
+
+struct lua_State;
+
+// Makes certain functions visible to lua while staying type-safe on the
+// C++ side.
+template<typename ThisType, typename FunctionType>
+class LuaTB
+{
+public:
+ // Make a function visible to lua. If you want to make a function 'foo'
+ // visible, you should call it like this:
+ //
+ // LuaWrapper<decltype(foo)>::registerFunction<foo>( ... );
+ template<FunctionType func>
+ static void registerFunction(ThisType *_this, lua_State *lua, std::string name);
+};
+
+// We need to include all type-dependant template implementations, because the
+// compiler needs to know for which types it has to instantiate those
+#include "luatb.ipp"
+
+#endif // LUATB_H
Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.ipp
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.ipp (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.ipp 2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,57 @@
+
+#include <lua.hpp>
+#include <iostream>
+#include <type_traits>
+#include "scriptable_controller_api.h"
+#include "luatb_typed_stack.h"
+
+
+// TODO Make free functions work
+
+// This class is only valid for function types. As a neat side-effect, having
+// this specialization in the .ipp file, also hides all of the private members
+// from the user (kind of).
+template<typename ThisType, typename Ret, typename... Args>
+class LuaTB<ThisType, Ret (ThisType::*)(Args...)>
+{
+public:
+ template<Ret (ThisType::*func)(Args...)>
+ static void registerFunction(ThisType *_this, lua_State *lua, std::string name)
+ {
+ // Store the 'this' pointer of the caller in the extraspace
+// *static_cast<ThisType**>(lua_getextraspace(lua)) = _this;
+
+ // Make a function visible to lua that will call 'func' with the correct
+ // arguments
+ lua_register(lua, name.c_str(), toLuaSignature<func>);
+ }
+
+private:
+ // Represents a function that can made visible to lua with the correct
+ // signature. It will call the corresponding C++ function with the
+ // correctly typed arguments
+ template<Ret (ThisType::*func)(Args...)>
+ static int toLuaSignature(lua_State *lua)
+ {
+ // The number of arguments is the first item on the stack
+ int argc = lua_tointeger(lua, lua_gettop(lua));
+ lua_pop(lua, 1);
+
+ // Check if the number of arguments match
+ if(argc != sizeof...(Args))
+ {
+ std::cerr << "ERROR: LuaTB: Lua script called a function with wrong number of arguments" << std::endl;
+ return LUA_ERRSYNTAX;
+ }
+
+ // Retrieve 'this' pointer of caller
+// ThisType *_this = *static_cast<ThisType**>(lua_getextraspace(lua));
+ThisType *_this = orxonox::ScriptableControllerAPI::this_;
+
+ // Call getArgument for every argument seperately to convert each argument
+ // to the correct type and call the function afterwards
+ ((*_this).*func)( (LuaTBTypedStack::getArgument<Args>(lua))... );
+
+ return 0;
+ }
+};
Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.cc
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.cc (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.cc 2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,29 @@
+
+#include "luatb_typed_stack.h"
+#include <string>
+
+// Explicit and full specializations need to be in a .cpp file
+
+template<>
+int LuaTBTypedStack::popFromLuaStack<int>(lua_State *lua)
+{ return lua_tointeger(lua, lua_gettop(lua)); }
+
+template<>
+double LuaTBTypedStack::popFromLuaStack<double>(lua_State *lua)
+{ return lua_tonumber(lua, lua_gettop(lua)); }
+
+template<>
+std::string LuaTBTypedStack::popFromLuaStack<std::string>(lua_State *lua)
+{ return lua_tostring(lua, lua_gettop(lua)); }
+
+template<>
+void LuaTBTypedStack::pushToLuaStack<int>(lua_State *lua, int value)
+{ lua_pushinteger(lua, value); }
+
+template<>
+void LuaTBTypedStack::pushToLuaStack<double>(lua_State *lua, double value)
+{ lua_pushnumber(lua, value); }
+
+template<>
+void LuaTBTypedStack::pushToLuaStack<std::string>(lua_State *lua, std::string value)
+{ lua_pushstring(lua, value.c_str()); }
Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.h (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.h 2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,92 @@
+#ifndef LUATB_TYPED_STACK_H
+#define LUATB_TYPED_STACK_H
+
+#include <type_traits>
+#include <functional>
+#include <lua.hpp>
+#include "is_callable.h"
+
+// We need a separate class for this because we need to specialize the functions
+// and that's not possible if we didn't specialize the class. And the logical
+// separation makes sense as well.
+class LuaTBTypedStack
+{
+public:
+ // Get an argument from the lua stack and convert it to type 'T'
+ template<typename T>
+ static T getArgument(lua_State *lua)
+ {
+ T value = LuaTBTypedStack::popFromLuaStack<T>(lua);
+ lua_pop(lua, 1);
+ return value;
+ }
+
+private:
+ // Gets the top element of the lua stack and converts it to the appropriate
+ // type. Only works for certain types, if 'T' is none of these types, you'll
+ // get an error.
+ template<typename T>
+ static typename std::enable_if<!IsCallable<T>::value, T>::type popFromLuaStack(lua_State *lua);
+
+ // Specialization if 'T' is a callable type (std::function). Saves the lua
+ // function in the registry and constructs a function to call it again.
+ // 'decltype(&T::operator())' represents the function signature of the callable
+ // object.
+ template<typename T>
+ static typename std::enable_if<IsCallable<T>::value, T>::type popFromLuaStack(lua_State *lua)
+ {
+ return GetLuaCallback<decltype(&T::operator())>::value(lua, luaL_ref(lua, LUA_REGISTRYINDEX));
+ }
+
+ // Get a function that will call a lua callback
+ template<typename T> struct GetLuaCallback;
+
+ // This class is only valid for std::function types. The type argument will not
+ // be a normal function signature because it is the signature of
+ // std::function<>::operator(), which is also the reason for the 'const'.
+ template<typename Ret, typename... Args>
+ struct GetLuaCallback<Ret(std::function<Ret(Args...)>::*)(Args...) const>
+ {
+ // Returns a lambda that will push the arguments to the lua stack and call
+ // the function afterwards. We can't return 'callLuaFunction' directly,
+ // because we need the additional arguments 'lua' and 'ref' which we would
+ // like to hide from the user.
+ static std::function<Ret (Args...)> value(lua_State *lua, int ref)
+ {
+ return [lua, ref](Args... args){return callLuaFunction<Ret, Args...>(lua, ref, args...);};
+ }
+ };
+
+ // Pushes all arguments to the lua stack and calls a lua function afterwards
+ template<typename Ret, typename... Args>
+ static Ret callLuaFunction(lua_State *lua, int ref, Args... args)
+ {
+ lua_rawgeti(lua, LUA_REGISTRYINDEX, ref);
+ // We pass one extra argument in case the function has no arguments at all
+ pushArgumentsToLuaStack<Args...>(lua, args..., false);
+ lua_pcall(lua, 1, sizeof...(args), 0);
+
+ // TODO
+ return Ret();
+ }
+ // This is needed in case the function has no arguments at all. Otherwise, we
+ // would have a missing argument for such function. This is also why we pass
+ // a dummy argument in 'callLuaFunction', so we have at least one argument.
+ template<typename T>
+ static void pushArgumentsToLuaStack(lua_State *, T)
+ { }
+
+ // Recursively pushes arguments to the lua stack
+ template<typename First, typename... Args>
+ static void pushArgumentsToLuaStack(lua_State *lua, First first, Args... args)
+ {
+ pushToLuaStack(lua, first);
+ pushArgumentsToLuaStack<Args...>(lua, args...);
+ }
+
+ // Pushes a value to the lua stack. Only the specializations are valid.
+ template<typename T>
+ static void pushToLuaStack(lua_State *lua, T value);
+};
+
+#endif // LUATB_TYPED_STACK_H
Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.cc
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.cc (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.cc 2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,83 @@
+
+#include "scriptable_controller.h"
+#include "luatb.h"
+
+namespace orxonox
+{
+
+// Used https://csl.name/post/lua-and-cpp/ as a reference
+int ScriptableController::runScript(const std::string &file_path)
+{
+ int ret;
+
+ // Create a lua object
+ lua_State *lua = luaL_newstate();
+ if(lua == nullptr)
+ {
+ orxout(internal_warning) << "ScriptableController: Falied to load script " << file_path << std::endl;
+ return LUA_ERRMEM;
+ }
+
+ // Make standard libraries available in the Lua object
+ luaL_openlibs(lua);
+
+ // Register the API
+ ScriptableControllerAPI *api = new ScriptableControllerAPI(lua, this);
+
+ // Load the program; this supports both source code and bytecode files.
+ if((ret = luaL_loadfile(lua, file_path.c_str())) != 0)
+ {
+ this->printLuaError(lua);
+ delete api;
+ return ret;
+ }
+
+ // Execute the script
+ if((ret = lua_pcall(lua, 0, LUA_MULTRET, 0)) != 0)
+ {
+ this->printLuaError(lua);
+ delete api;
+ return ret;
+ }
+
+ // Remeber the api
+ this->apis_.push_back(std::unique_ptr<ScriptableControllerAPI>(api));
+
+ // Only the caller knows, when the end-of-life of the script is reached,
+ // so we give him control over when to destroy it.
+ return 0;
+}
+
+void ScriptableController::registerWorldEntity(int id, WorldEntity *obj)
+{
+ this->worldEntities_[id] = obj;
+}
+
+void ScriptableController::registerControllableEntity(int id, ControllableEntity *obj)
+{
+ this->worldEntities_[id] = obj;
+}
+
+WorldEntity *ScriptableController::getWorldEntityByID(int id) const
+{
+ auto obj = this->worldEntities_.find(id);
+ return obj != this->worldEntities_.end() ? obj->second : nullptr;
+}
+
+ControllableEntity *ScriptableController::getControllableEntityByID(int id) const
+{
+ auto obj = this->controllabelEntities_.find(id);
+ return obj != this->controllabelEntities_.end() ? obj->second : nullptr;
+}
+
+void ScriptableController::printLuaError(lua_State *lua)
+{
+ // The error message is on top of the stack.
+ // Fetch it, print it and then pop it off the stack.
+ // Yes, this is 'const char*' and not 'std::string' because that's what lua gives us.
+ const char* message = lua_tostring(lua, -1);
+ std::cout << message << std::endl;
+ lua_pop(lua, 1);
+}
+
+}
Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.h (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.h 2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,41 @@
+#ifndef SCRIPTABLE_CONTROLLER_H
+#define SCRIPTABLE_CONTROLLER_H
+
+#include <lua.hpp>
+#include <string>
+#include <list>
+#include <map>
+#include <memory>
+#include "scriptable_controller_api.h"
+#include "core/CoreIncludes.h"
+#include "worldentities/WorldEntity.h"
+#include "worldentities/ControllableEntity.h"
+
+struct lua_State;
+
+namespace orxonox
+{
+
+class ScriptableController
+{
+public:
+ int runScript(const std::string &file_path);
+
+ void registerWorldEntity(int id, WorldEntity *obj);
+ void registerControllableEntity(int id, ControllableEntity *obj);
+
+ WorldEntity *getWorldEntityByID(int id) const;
+ ControllableEntity *getControllableEntityByID(int id) const;
+
+private:
+ std::list<std::unique_ptr<ScriptableControllerAPI> > apis_;
+ ControllableEntity *player_; // TODO
+ std::map<int, WorldEntity*> worldEntities_;
+ std::map<int, ControllableEntity*> controllabelEntities_;
+
+ void printLuaError(lua_State *lua);
+};
+
+}
+
+#endif // SCRIPTABLE_CONTROLLER_H
Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.cc
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.cc (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.cc 2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,69 @@
+
+#include "scriptable_controller_api.h"
+#include <chrono>
+#include "lua.hpp"
+#include "luatb.h"
+#include "scriptable_controller.h"
+
+namespace orxonox
+{
+
+ScriptableControllerAPI *ScriptableControllerAPI::this_;
+
+ScriptableControllerAPI::ScriptableControllerAPI(lua_State *lua, ScriptableController *controller)
+{
+ this->lua_ = lua;
+ this->controller_ = controller;
+ ScriptableControllerAPI::this_ = this;
+
+ // Haven't found a shorter way yet to write that...
+ LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::testOutput)>::registerFunction<&ScriptableControllerAPI::testOutput>(this, lua, "testOutput");
+ LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAfterTimeout)>::registerFunction<&ScriptableControllerAPI::registerAfterTimeout>(this, lua, "registerAfterTimeout");
+ LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAtNearObject)>::registerFunction<&ScriptableControllerAPI::registerAtNearObject>(this, lua, "registerAtNearObject");
+ LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAtAreaEnter)>::registerFunction<&ScriptableControllerAPI::registerAtAreaEnter>(this, lua, "registerAtAreaEnter");
+ LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAtAreaLeave)>::registerFunction<&ScriptableControllerAPI::registerAtAreaLeave>(this, lua, "registerAtAreaLeave");
+ LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAtObjectDestroyed)>::registerFunction<&ScriptableControllerAPI::registerAtObjectDestroyed>(this, lua, "registerAtObjectDestroyed");
+ LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAtPickup)>::registerFunction<&ScriptableControllerAPI::registerAtPickup>(this, lua, "registerAtPickup");
+}
+
+ScriptableControllerAPI::~ScriptableControllerAPI()
+{
+ lua_close(this->lua_);
+}
+
+void ScriptableControllerAPI::testOutput()
+{
+ orxout(user_info) << "Wheee!!!" << std::endl;
+}
+
+void ScriptableControllerAPI::registerAfterTimeout(std::function<void (void)> callback, int timeout_ms)
+{
+
+}
+
+int ScriptableControllerAPI::registerAtNearObject(std::function<void (int, int)> callback, int obj1, int obj2, double distance)
+{
+
+}
+
+int ScriptableControllerAPI::registerAtAreaEnter(std::function<void (int)> callback, int obj, int x, int y, int z, int dx, int dy, int dz)
+{
+
+}
+
+int ScriptableControllerAPI::registerAtAreaLeave(std::function<void (int)> callback, int obj, int x, int y, int z, int dx, int dy, int dz)
+{
+
+}
+
+int ScriptableControllerAPI::registerAtObjectDestroyed(std::function<void (int)> callback, int obj)
+{
+
+}
+
+int ScriptableControllerAPI::registerAtPickup(std::function<void (int)> callback, int pickup_id)
+{
+
+}
+
+}
Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.h (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.h 2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,44 @@
+#ifndef SCRIPTABLE_CONTROLLER_API_H
+#define SCRIPTABLE_CONTROLLER_API_H
+
+#include <functional>
+#include "core/CoreIncludes.h"
+
+// TODO Is pos int or double?
+
+struct lua_State;
+
+namespace orxonox
+{
+
+class ScriptableController;
+
+class ScriptableControllerAPI
+{
+public:
+ ScriptableControllerAPI(lua_State *lua, ScriptableController *controller);
+ ~ScriptableControllerAPI();
+
+ void testOutput(void);
+
+ void registerAfterTimeout(std::function<void (void)> callback, int timeout_ms);
+ int registerAtNearObject(std::function<void(int, int)> callback, int obj1, int obj2, double distance);
+ int registerAtAreaEnter(std::function<void (int)> callback, int obj, int x, int y, int z, int dx, int dy, int dz);
+ int registerAtAreaLeave(std::function<void (int)> callback, int obj, int x, int y, int z, int dx, int dy, int dz);
+ int registerAtObjectDestroyed(std::function<void (int)> callback, int obj);
+ int registerAtPickup(std::function<void (int)> callback, int pickup_id);
+
+ int destroyObject(int obj);
+ void removeObject(int obj);
+ int setObjectPosition(int obj, double x, double y, double z);
+
+ static ScriptableControllerAPI *this_;
+
+private:
+ lua_State *lua_;
+ ScriptableController *controller_;
+};
+
+}
+
+#endif // SCRIPTABLE_CONTROLLER_API_H
More information about the Orxonox-commit
mailing list