lock unit_map while running filter or applying effects

This specially prevents lua form removing the unit that is currently
filtered or that currently gets effects, which would cause crashes
otherwise.
This commit is contained in:
gfgtdf 2016-03-14 14:42:01 +01:00
parent 43275cd9ba
commit ff5a37a064
2 changed files with 38 additions and 1 deletions

View file

@ -149,6 +149,20 @@ static lg::log_domain log_scripting_lua("scripting/lua");
std::vector<config> game_lua_kernel::preload_scripts;
config game_lua_kernel::preload_config;
struct map_locker
{
map_locker(game_lua_kernel* kernel) : kernel_(kernel)
{
++kernel_->map_locked_;
}
~map_locker()
{
--kernel_->map_locked_;
}
game_lua_kernel* kernel_;
};
void game_lua_kernel::extract_preload_scripts(config const &game_config)
{
game_lua_kernel::preload_scripts.clear();
@ -2285,6 +2299,9 @@ int game_lua_kernel::intf_print(lua_State *L) {
*/
int game_lua_kernel::intf_put_unit(lua_State *L)
{
if(map_locked_) {
return luaL_error(L, "Attempted to move a unit while the map is locked");
}
int unit_arg = 1;
int fire_event_arg = 4;
@ -2372,6 +2389,9 @@ int game_lua_kernel::intf_put_unit(lua_State *L)
*/
int game_lua_kernel::intf_erase_unit(lua_State *L)
{
if(map_locked_) {
return luaL_error(L, "Attempted to remove a unit while the map is locked");
}
map_location loc;
if (lua_isnumber(L, 1)) {
@ -2420,6 +2440,9 @@ int game_lua_kernel::intf_erase_unit(lua_State *L)
*/
int game_lua_kernel::intf_put_recall_unit(lua_State *L)
{
if(map_locked_) {
return luaL_error(L, "Attempted to move a unit while the map is locked");
}
lua_unit *lu = NULL;
unit_ptr u = unit_ptr();
int side = lua_tointeger(L, 2);
@ -2460,6 +2483,9 @@ int game_lua_kernel::intf_put_recall_unit(lua_State *L)
*/
int game_lua_kernel::intf_extract_unit(lua_State *L)
{
if(map_locked_) {
return luaL_error(L, "Attempted to remove a unit while the map is locked");
}
if (!luaW_hasmetatable(L, 1, getunitKey))
return luaL_typerror(L, 1, "unit");
lua_unit *lu = static_cast<lua_unit *>(lua_touserdata(L, 1));
@ -4159,6 +4185,7 @@ game_lua_kernel::game_lua_kernel(CVideo * video, game_state & gs, play_controlle
, reports_(reports_object)
, level_lua_()
, queued_events_()
, map_locked_(0)
{
static game_events::queued_event default_queued_event("_from_lua", map_location(), map_location(), config());
queued_events_.push(&default_queued_event);
@ -4741,7 +4768,7 @@ bool game_lua_kernel::run_wml_conditional(std::string const &cmd, vconfig const
bool game_lua_kernel::run_filter(char const *name, unit const &u)
{
lua_State *L = mState;
map_locker(this);
unit_map::const_unit_iterator ui = units().find(u.get_location());
if (!ui.valid()) return false;
@ -4774,6 +4801,7 @@ void game_lua_kernel::apply_effect(const std::string& name, unit& u, const confi
if (!luaW_getglobal(L, "wesnoth", "effects", name.c_str(), NULL)) {
return;
}
map_locker(this);
// Stack: effect_func
lua_unit* lu = luaW_pushlocalunit(L, u);
// Stack: effect_func, unit

View file

@ -167,6 +167,15 @@ class game_lua_kernel : public lua_kernel_base
public:
std::vector<team> & teams();
/**
A value != 0 means that the shouldn't remove any units from the map, usually because
we are currently operating on a unit& and removing it might cause memory corruptions
note that we don't check for the dtor of lua owned units because we assume that
we operate on such a unit that the lua function that invoked the operation on that unit
(like wesnoth.add_modification, wesnoth.match_unit ..) have a local copy of that
lua_unit* userdata in its stack that prevents it from beeing collected.
*/
int map_locked_;
game_lua_kernel(CVideo *, game_state &, play_controller &, reports &);
void set_game_display(game_display * gd);