Skip to content

Commit

Permalink
lock unit_map while running filter or applying effects
Browse files Browse the repository at this point in the history
This specially prevents lua form removing the unit that is currently
filtered or that currently gets effects, which would cause crashes
otherwise.
  • Loading branch information
gfgtdf committed Mar 14, 2016
1 parent 43275cd commit ff5a37a
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 1 deletion.
30 changes: 29 additions & 1 deletion src/scripting/game_lua_kernel.cpp
Expand Up @@ -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();
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions src/scripting/game_lua_kernel.hpp
Expand Up @@ -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);
Expand Down

0 comments on commit ff5a37a

Please sign in to comment.