[Orxonox-commit 7054] r11673 - in code/branches/ScriptableController_HS17/src/orxonox: infos scriptablecontroller

kohlia at orxonox.net kohlia at orxonox.net
Thu Dec 14 16:04:15 CET 2017


Author: kohlia
Date: 2017-12-14 16:04:14 +0100 (Thu, 14 Dec 2017)
New Revision: 11673

Modified:
   code/branches/ScriptableController_HS17/src/orxonox/infos/GametypeInfo.cc
   code/branches/ScriptableController_HS17/src/orxonox/infos/GametypeInfo.h
   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_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:
The ScriptableController should work now. A demo level called scriptableControllerTest exists as well.



Modified: code/branches/ScriptableController_HS17/src/orxonox/infos/GametypeInfo.cc
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/infos/GametypeInfo.cc	2017-12-14 15:03:06 UTC (rev 11672)
+++ code/branches/ScriptableController_HS17/src/orxonox/infos/GametypeInfo.cc	2017-12-14 15:04:14 UTC (rev 11673)
@@ -77,6 +77,7 @@
         this->counter_ = 10;
         this->spawned_ = false;
         this->readyToSpawn_ = false;
+        this->isFirstSpawn_ = true;
 
         this->registerVariables();
     }
@@ -314,10 +315,13 @@
                 this->setSpawnedHelper(player, true);
         }
 
-        if(player->isHumanPlayer() && player->isLocalPlayer())
+        // TODO We might want to handle the subsequent spawns as well somehow
+        if(player->isHumanPlayer() && player->isLocalPlayer() && this->isFirstSpawn_)
         {
+            this->isFirstSpawn_ = false;
             this->getLevel()->getScriptableController()->setPlayer(player);
 
+            // This handles paths relative to the 'level' directory
             std::string script = this->getLevel()->getScript();
             if(script.at(0) != '/')
                 script = "../levels/" + script; // Not very dynamic

Modified: code/branches/ScriptableController_HS17/src/orxonox/infos/GametypeInfo.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/infos/GametypeInfo.h	2017-12-14 15:03:06 UTC (rev 11672)
+++ code/branches/ScriptableController_HS17/src/orxonox/infos/GametypeInfo.h	2017-12-14 15:04:14 UTC (rev 11673)
@@ -82,7 +82,7 @@
             */
             inline bool isStartCountdownRunning() const
                 { return this->bStartCountdownRunning_; }
-            
+
             void changedStartCountdownRunning(void); // Is called when the start countdown has been either started or stopped.
 
             /**
@@ -167,6 +167,7 @@
             std::set<PlayerInfo*> spawnedPlayers_; //!< A set of players that are currently spawned.
             bool spawned_; //!< Whether the local Player is currently spawned.
             bool readyToSpawn_; //!< Whether the local Player is ready to spawn.
+            bool isFirstSpawn_;
     };
 }
 

Modified: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/is_callable.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/is_callable.h	2017-12-14 15:03:06 UTC (rev 11672)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/is_callable.h	2017-12-14 15:04:14 UTC (rev 11673)
@@ -3,7 +3,9 @@
 
 #include <type_traits>
 
-// See https://stackoverflow.com/a/15396757/7421666
+/**
+ * @brief Implementation of IsCallable
+ */
 template<typename T>
 struct IsCallableImpl
 {
@@ -26,8 +28,14 @@
     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.
+/**
+ * @brief Checks if a type is callable
+ *
+ * Callable means here, that it has has an 'operator()'. This will not work for
+ * normal functions without modifications, but we don't need that case anyway.
+ *
+ * See https://stackoverflow.com/a/15396757/7421666
+ */
 template<typename T>
 struct IsCallable
     : std::conditional<

Modified: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.h	2017-12-14 15:03:06 UTC (rev 11672)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.h	2017-12-14 15:04:14 UTC (rev 11673)
@@ -5,22 +5,35 @@
 
 struct lua_State;
 
-// Makes certain functions visible to lua while staying type-safe on the
-// C++ side.
+/**
+ * @brief 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:
-    //
-    // LuaTB<decltype(foo)>::registerFunction<foo>( ... );
+    /**
+     * @brief Make a function visible to lua
+     * @param _this Pointer to the object that owns the function
+     * @param lua Pointer to the lua state
+     * @param name Name that will be visible to lua for this function
+     *
+     * Only class functions are supported, because that's  all we need at
+     * the moment. Extending it to support normal functions as well should
+     * be fairly easy, though.
+     *
+     * If you want to make a function 'Foo::bar' visible, you should call
+     * it like this (assuming no C++17 support):
+     *
+     * LuaTB<Foo, decltype(&Foo::bar)>::registerFunction<&Foo::bar>( ... );
+     */
     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
+// compiler needs to know for which types it has to instantiate those.
 #include "luatb.ipp"
 
 #endif // LUATB_H

Modified: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.h	2017-12-14 15:03:06 UTC (rev 11672)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.h	2017-12-14 15:04:14 UTC (rev 11673)
@@ -7,15 +7,25 @@
 #include "is_callable.h"
 #include "core/CoreIncludes.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.
+
+/**
+ * @brief Represents a typed version of the lua stack
+ *
+ * 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 a non-function argument from the lua stack and convert it to type 'T'
-    // Pops the value from the stack.
+    /**
+     * @brief Get a non-function argument from the lua stack and convert it to type 'T'
+     * @param lua Pointer to the lua state
+     * @return Top element of the lua stack with the appropriate type
+     *
+     * Note: Pops the value from the stack.
+     */
     template<typename T>
     static typename std::enable_if<!IsCallable<T>::value, T>::type getArgument(lua_State *lua)
     {
@@ -24,10 +34,16 @@
         return value;
     }
 
-    // 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.
+    /**
+     * @brief Get a function-type argument from the lua stack and convert it to type 'T'
+     * @param lua Pointer to the lua state
+     * @return Top element of the lua stack with the appropriate type
+     *
+     * Specialization if 'T' is a callable type (std::function). Saves the lua
+     * function in the registry and constructs a function to call it again.
+     *
+     * Note: Pops the value from the stack.
+     */
     template<typename T>
     static typename std::enable_if<IsCallable<T>::value, T>::type getArgument(lua_State *lua)
     {
@@ -38,6 +54,7 @@
         int *ref = new int(luaL_ref(lua, LUA_REGISTRYINDEX));
         LuaTBTypedStack::callbackRefs.push_back(std::unique_ptr<int>(ref));
 
+        // 'decltype(&T::operator())' represents the function signature of the callable object.
         return GetLuaCallback<decltype(&T::operator())>::value(lua, ref);
     }
 
@@ -46,25 +63,48 @@
 
     // Gets a value from the top of the lua stack and returns it with the proper type.
     // Does not pop the value!
+    /**
+     * @brief Get a value from the lua stack and convert it to type 'T'
+     * @param lua Pointer to the lua state
+     * @return Top element of the lua stack with the appropriate type
+     *
+     * Note: Does NOT pop the value from the stack.
+     */
     template<typename T> static T getFromLuaStack(lua_State *lua);
 
     // 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'.
+    /**
+     * @brief Needed to get a lambda to call a lua function
+     *
+     * We need this class, becasue we need to specify the signature of the function
+     * before.
+     */
     template<typename Ret, typename... Args>
     struct GetLuaCallback<void (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.
+        /**
+         * @brief Get a lambda to call a lua function later
+         * @param lua Pointer to the lua state
+         * @param ref Pointer to the lua registry reference where the function is stored
+         * @return Lambda that will call the function specified with ref
+         */
         static std::function<void (Args...)> value(lua_State *lua, int *ref)
         {
+            // We can't return 'callLuaFunction' directly, because we need the
+            // additional arguments 'lua' and 'ref' which we would like to hide
+            // from the user.
             return [lua, ref](Args... args){callLuaFunction<Ret, Args...>(lua, ref, args...);};
         }
     };
 
-    // Pushes all arguments to the lua stack and calls a lua function afterwards
+    /**
+     * @brief Pushes all arguments to the lua stack and calls a lua function afterwards
+     * @param lua Pointer to the lua state
+     * @param ref Pointer to the lua registry reference where the function is stored
+     * @param args Arguments for the function
+     */
     template<typename Ret, typename... Args>
     static void callLuaFunction(lua_State *lua, int *ref, Args... args)
     {
@@ -84,14 +124,24 @@
         *ref = luaL_ref(lua, LUA_REGISTRYINDEX);
     }
 
-    // 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.
+    /**
+     * @brief Pushes nothing onto the stack
+     *
+     * This is needed in case the function has no arguments at all. Otherwise, we
+     * would have a missing argument for such a function. This is also why we pass
+     * a dummy argument in 'callLuaFunction', so we have at least one argument. It
+     * is the termination point for the recursive template.
+     */
     template<typename T>
     static void pushArgumentsToLuaStack(lua_State *, T)
     { }
 
-    // Recursively pushes arguments to the lua stack
+    /**
+     * @brief Recursively pushes arguments to the lua stack
+     * @param lua Pointer to the lua state
+     * @param first First argument to push onto the stack
+     * @param args The remaining arguments to push onto the stack
+     */
     template<typename First, typename... Args>
     static void pushArgumentsToLuaStack(lua_State *lua, First first, Args... args)
     {
@@ -99,7 +149,13 @@
         pushArgumentsToLuaStack<Args...>(lua, args...);
     }
 
-    // Pushes a value to the lua stack. Only the specializations are valid.
+    /**
+     * @brief Pushes a value to the lua stack
+     * @param lua Pointer to the lua state
+     * @param value The value to push
+     *
+     * Note: Only the specializations are valid
+     */
     template<typename T>
     static void pushToLuaStack(lua_State *lua, T value);
 

Modified: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.cc
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.cc	2017-12-14 15:03:06 UTC (rev 11672)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.cc	2017-12-14 15:04:14 UTC (rev 11673)
@@ -49,7 +49,7 @@
         return ret;
     }
 
-    // Remeber the api
+    // Remember the api
     this->apis_.push_back(std::unique_ptr<ScriptableControllerAPI>(api));
 
     return 0;
@@ -72,7 +72,6 @@
 
 void ScriptableController::registerPawn(std::string id, Pawn *pawn)
 {
-    this->worldEntities_[id] = pawn;
     this->pawns_[id] = pawn;
     this->pawnsReverse_[pawn] = id;
 }
@@ -86,11 +85,13 @@
         return;
     }
 
-    for(auto &api : this->apis_)
-        api->pawnKilled(pawn_id_iter->second, pawn);
-
+    // The ID of the pawn should already be invalid when we call the callback
+    std::string id = pawn_id_iter->second;
     this->pawns_.erase(pawn_id_iter->second);
     this->pawnsReverse_.erase(pawn_id_iter);
+
+    for(auto &api : this->apis_)
+        api->pawnKilled(id, pawn);
 }
 
 void ScriptableController::pawnHit(Pawn *target, Pawn *source, double new_health, double new_shield)
@@ -109,18 +110,6 @@
         api->pawnHit(target_id_iter->second, source_id_iter->second, new_health, new_shield);
 }
 
-void ScriptableController::killPawn(std::string id)
-{
-    auto pawn = this->getPawnByID(id);
-    if(pawn == nullptr)
-    {
-        orxout(user_warning) << "Tried to destroy unknown pawn " << id << std::endl;
-        return;
-    }
-
-    pawn->kill();
-}
-
 WorldEntity *ScriptableController::getWorldEntityByID(std::string id) const
 {
     if(id == "player" || id == "Player" || id == "PLAYER")
@@ -137,7 +126,6 @@
 
     auto entity = this->mobileEntities_.find(id);
     return entity != this->mobileEntities_.end() ? entity->second : nullptr;
-
 }
 
 Pawn *ScriptableController::getPawnByID(std::string id) const

Modified: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.h	2017-12-14 15:03:06 UTC (rev 11672)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.h	2017-12-14 15:04:14 UTC (rev 11673)
@@ -97,12 +97,6 @@
     void pawnHit(Pawn *target, Pawn *source, double new_health, double new_shield);
 
     /**
-     * @brief Kill a Pawn
-     * @param id The Pawn to kill
-     */
-    void killPawn(std::string id);
-
-    /**
      * @brief Convert an ID to a WorldEntity pointer
      * @param id The ID of the WorldEntity
      * @return A pointer to the WorldEntity, nullptr if it's not found

Modified: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.cc
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.cc	2017-12-14 15:03:06 UTC (rev 11672)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.cc	2017-12-14 15:04:14 UTC (rev 11673)
@@ -17,7 +17,7 @@
     this->lua_ = lua;
     this->controller_ = controller;
 
-    // Haven't found a shorter way yet to write that...
+    // Haven't found a shorter way yet to write that... We need C++17!
     LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::orxPrint)>::registerFunction<&ScriptableControllerAPI::orxPrint>(this, lua, "orxPrint");
 
     LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAfterTimeout)>::registerFunction<&ScriptableControllerAPI::registerAfterTimeout>(this, lua, "registerAfterTimeout");
@@ -100,10 +100,11 @@
 
 void ScriptableControllerAPI::killPawn(std::string id)
 {
-    // We don't kill the pawn here directly, because this function is called from LUA and thus
-    // runs in a different thread. So we schedule the kill for later in the main thread
-    // (in 'periodic').
-    this->pawnsToKill_.push_back(id);
+    Pawn *pawn = this->controller_->getPawnByID(id);
+    if(pawn == nullptr)
+        orxout(user_warning) << "Trying to kill an unknown pawn" << std::endl;
+    else
+        pawn->kill();
 }
 
 void ScriptableControllerAPI::spawn(std::string type, std::string id)
@@ -167,6 +168,7 @@
 
     const Vector3 &old = entity->getPosition();
 
+    // If one of the values is NaN, don't change that value
     x = std::isnan(x) ? old.x : x;
     y = std::isnan(y) ? old.y : y;
     z = std::isnan(z) ? old.z : z;
@@ -188,11 +190,13 @@
 
     entity->getOrientation().ToAngleAxis(old_angle, old_axis);
 
+    // If one of the values is NaN, don't change that value
     x = std::isnan(x) ? old_axis.x : x;
     y = std::isnan(y) ? old_axis.y : y;
     z = std::isnan(z) ? old_axis.z : z;
     angle = std::isnan(x) ? old_angle.valueDegrees() : angle;
 
+
     entity->setOrientation(Vector3(x, y, z), Degree(angle));
 }
 
@@ -207,6 +211,7 @@
 
     const Vector3 &old = entity->getVelocity();
 
+    // If one of the values is NaN, don't change that value
     x = std::isnan(x) ? old.x : x;
     y = std::isnan(y) ? old.y : y;
     z = std::isnan(z) ? old.z : z;
@@ -225,6 +230,7 @@
 
     const Vector3 &old = entity->getAngularVelocity();
 
+    // If one of the values is NaN, don't change that value
     x = std::isnan(x) ? old.x : x;
     y = std::isnan(y) ? old.y : y;
     z = std::isnan(z) ? old.z : z;
@@ -248,6 +254,24 @@
         else
             near_obj_handler++;
     }
+
+    auto near_point_handler = this->nearPointHandlers_.begin();
+    while(near_point_handler != this->nearPointHandlers_.end())
+    {
+        if(near_point_handler->entity_ == pawn)
+            near_point_handler = this->nearPointHandlers_.erase(near_point_handler);
+        else
+            near_point_handler++;
+    }
+
+    auto area_handler = this->areaHandlers_.begin();
+    while(area_handler != this->areaHandlers_.end())
+    {
+        if(area_handler->entity_ == pawn)
+            area_handler = this->areaHandlers_.erase(area_handler);
+        else
+            area_handler++;
+    }
 }
 
 void ScriptableControllerAPI::pawnHit(std::string target_id, std::string source_id, double new_health, double new_shield)
@@ -318,14 +342,6 @@
             }
         }
     }
-
-    // Pawns to kill
-    // TODO Possible race condidtion when the player destroys the pawn
-    // between the callback and the next periodic call.
-    for(auto &pawn : this->pawnsToKill_)
-        this->controller_->killPawn(pawn);
-
-    this->pawnsToKill_.clear();
 }
 
 }

Modified: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.h	2017-12-14 15:03:06 UTC (rev 11672)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.h	2017-12-14 15:04:14 UTC (rev 11673)
@@ -145,6 +145,9 @@
      * @brief Spawn an object
      * @param type Name of the class of the object you want to spawn
      * @param id The newly created ID that can be used to access this object
+     *
+     * IMPORTANT: Do not use this function yet, it only has minimal functionality and is not
+     * really helpful as it is.
      */
     void spawn(std::string type, std::string id);
 
@@ -196,23 +199,6 @@
 
 private:
     /**
-     * @brief Called by ScriptableController when a pawn is killed
-     * @param id The dead pawn
-     *
-     * Calls the lua callbacks associated with this event.
-     */
-    void pawnKilled(std::string id, Pawn *pawn);
-
-    /**
-     * @brief Called by ScriptableController when a Pawn is hit
-     * @param target_id The hit Pawn
-     * @param source_id The shooting Pawn
-     * @param new_health The new health of the hit Pawn
-     * @param new_shield The new shield health of the hit Pawn
-     */
-    void pawnHit(std::string target_id, std::string source_id, double new_health, double new_shield);
-
-    /**
      * @brief Groups everything together that is needed to handle a near-object event
      */
     struct NearObjectHandler
@@ -259,13 +245,11 @@
         std::function<void (std::string)> callback_;
     };
 
-
     lua_State *lua_;
     ScriptableController *controller_;
     std::list<NearObjectHandler> nearObjectHandlers_;
     std::list<NearPointHandler> nearPointHandlers_;
     std::list<AreaHandler> areaHandlers_;
-    std::list<std::string> pawnsToKill_;
     std::map<std::string, std::list<std::function<void (std::string)> > > pawnDestroyedHandlers_;
     std::map<std::string, std::list<std::function<void (std::string, std::string, double, double)> > > pawnHitHandlers_;
     Timer periodicTimer;
@@ -272,11 +256,27 @@
     static const double periodic_interval;
 
     /**
+     * @brief Called by ScriptableController when a pawn is killed
+     * @param id The dead pawn
+     *
+     * Calls the lua callbacks associated with this event.
+     */
+    void pawnKilled(std::string id, Pawn *pawn);
+
+    /**
+     * @brief Called by ScriptableController when a Pawn is hit
+     * @param target_id The hit Pawn
+     * @param source_id The shooting Pawn
+     * @param new_health The new health of the hit Pawn
+     * @param new_shield The new shield health of the hit Pawn
+     */
+    void pawnHit(std::string target_id, std::string source_id, double new_health, double new_shield);
+
+    /**
      * @brief Called every 0.5s
      *
-     * This handles things that have to be checked periodically (like area events) and
-     * things that are done by lua that have to synchronize with the main thread (like
-     * killing a pawn). Doing this in every tick would be an overkill.
+     * This handles things that have to be checked periodically (like area events)
+     * but doing this in every tick would be an overkill.
      */
     void periodic(void);
 };



More information about the Orxonox-commit mailing list