[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