From: Matthias Braun Date: Mon, 24 Apr 2006 16:37:10 +0000 (+0000) Subject: make GameObjects reference counted (this avoids crashs when scripts hold reference... X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=13c6f377301f91c8d79ab768ba97b5eb771ed624;p=supertux.git make GameObjects reference counted (this avoids crashs when scripts hold reference to gameobjects for too long and should allow a better implementation of destroy listeners alter) SVN-Revision: 3413 --- diff --git a/src/game_object.hpp b/src/game_object.hpp index e70b842f6..7d4b05f71 100644 --- a/src/game_object.hpp +++ b/src/game_object.hpp @@ -20,6 +20,7 @@ #define SUPERTUX_GAMEOBJECT_H #include +#include "refcounter.hpp" class DrawingContext; class ObjectRemoveListener; @@ -35,7 +36,7 @@ class ObjectRemoveListener; * functions. * - a 32bit bitset for flags... */ -class GameObject +class GameObject : public RefCounter { public: GameObject(); @@ -55,14 +56,16 @@ public: /** returns true if the object is not scheduled to be removed yet */ bool is_valid() const - { - return !wants_to_die; - } + { + return !wants_to_die; + } + /** schedules this object to be removed at the end of the frame */ void remove_me() { wants_to_die = true; } + /** registers a remove listener which will be called if the object * gets removed/destroyed */ diff --git a/src/player_status.cpp b/src/player_status.cpp index 24a1b992e..c39c6faf4 100644 --- a/src/player_status.cpp +++ b/src/player_status.cpp @@ -45,16 +45,6 @@ PlayerStatus::PlayerStatus() max_score_multiplier(1) { reset(); - key_brass.reset(sprite_manager->create("images/objects/keys/key_brass.sprite")); - key_iron.reset(sprite_manager->create("images/objects/keys/key_iron.sprite")); - key_bronze.reset(sprite_manager->create("images/objects/keys/key_bronze.sprite")); - key_silver.reset(sprite_manager->create("images/objects/keys/key_silver.sprite")); - key_gold.reset(sprite_manager->create("images/objects/keys/key_gold.sprite")); - key_brass->set_action("outline"); - key_iron->set_action("outline"); - key_bronze->set_action("outline"); - key_silver->set_action("outline"); - key_gold->set_action("outline"); tux_life.reset(sprite_manager->create("images/creatures/tux_small/tux-life.sprite")); @@ -68,7 +58,6 @@ PlayerStatus::~PlayerStatus() void PlayerStatus::reset() { coins = START_COINS; - keys = 0; bonus = NO_BONUS; score_multiplier = 1; max_score_multiplier = 1; @@ -85,17 +74,6 @@ PlayerStatus::add_coins(int count) } void -PlayerStatus::set_keys(int new_key) -{ - keys |= new_key; - key_brass->set_action(keys & KEY_BRASS ? "display" : "outline"); - key_iron->set_action(keys & KEY_IRON ? "display" : "outline"); - key_bronze->set_action(keys & KEY_BRONZE ? "display" : "outline"); - key_silver->set_action(keys & KEY_SILVER ? "display" : "outline"); - key_gold->set_action(keys & KEY_GOLD ? "display" : "outline"); -} - -void PlayerStatus::write(lisp::Writer& writer) { switch(bonus) { @@ -118,12 +96,6 @@ PlayerStatus::write(lisp::Writer& writer) writer.write_int("fireflowers", max_fire_bullets); writer.write_int("iceflowers", max_ice_bullets); - writer.write_bool("key-brass", keys & KEY_BRASS); - writer.write_bool("key-iron", keys & KEY_IRON); - writer.write_bool("key-bronze", keys & KEY_BRONZE); - writer.write_bool("key-silver", keys & KEY_SILVER); - writer.write_bool("key-gold", keys & KEY_GOLD); - writer.write_int("coins", coins); writer.write_int("max-score-multiplier", max_score_multiplier); } @@ -151,41 +123,11 @@ PlayerStatus::read(const lisp::Lisp& lisp) lisp.get("fireflowers", max_fire_bullets); lisp.get("iceflowers", max_ice_bullets); - bool val = false; - if(lisp.get("key-brass", val) && val == true) - set_keys(KEY_BRASS); - if(lisp.get("key-iron", val) && val == true) - set_keys(KEY_IRON); - if(lisp.get("key-bronze", val) && val == true) - set_keys(KEY_BRONZE); - if(lisp.get("key-silver", val) && val == true) - set_keys(KEY_SILVER); - if(lisp.get("key-gold", val) && val == true) - set_keys(KEY_GOLD); - lisp.get("coins", coins); lisp.get("max-score-multiplier", max_score_multiplier); } void -PlayerStatus::draw_keys(DrawingContext& context) -{ - const float SPACING = 10; - float x,y; - x = BORDER_X; y = BORDER_Y; - key_brass->draw(context, Vector(x, y), LAYER_FOREGROUND1); - x += key_brass->get_width() + SPACING; - key_iron->draw(context, Vector(x, y), LAYER_FOREGROUND1); - x += key_iron->get_width() + SPACING; - key_bronze->draw(context, Vector(x, y), LAYER_FOREGROUND1); - x += key_bronze->get_width() + SPACING; - key_silver->draw(context, Vector(x, y), LAYER_FOREGROUND1); - x += key_silver->get_width() + SPACING; - key_gold->draw(context, Vector(x, y), LAYER_FOREGROUND1); - x += key_gold->get_width() + SPACING; -} - -void PlayerStatus::draw(DrawingContext& context) { context.push_transform(); @@ -199,8 +141,6 @@ PlayerStatus::draw(DrawingContext& context) context.draw_text(white_text, coinstext, Vector(SCREEN_WIDTH - white_text->get_text_width(coinstext) - gold_text->get_text_width(" 99999") - BORDER_X, BORDER_Y), LEFT_ALLIGN, LAYER_FOREGROUND1); context.draw_text(gold_text, str, Vector(SCREEN_WIDTH - BORDER_X, BORDER_Y), RIGHT_ALLIGN, LAYER_FOREGROUND1); - //draw_keys(context); - context.pop_transform(); } @@ -211,7 +151,6 @@ PlayerStatus::operator= (const PlayerStatus& other) bonus = other.bonus; score_multiplier = other.score_multiplier; max_score_multiplier = other.max_score_multiplier; - keys = other.keys; } bool diff --git a/src/player_status.hpp b/src/player_status.hpp index 0e768ae46..3df230be3 100644 --- a/src/player_status.hpp +++ b/src/player_status.hpp @@ -47,13 +47,11 @@ public: ~PlayerStatus(); void reset(); void add_coins(int count); - void set_keys(int new_key); void write(lisp::Writer& writer); void read(const lisp::Lisp& lisp); void draw(DrawingContext& context); - void draw_keys(DrawingContext& context); bool consoleCommand(std::string command, std::vector arguments); /**< callback from Console; return false if command was unknown, true otherwise */ @@ -67,26 +65,11 @@ public: void operator= (const PlayerStatus& other); - enum { - KEY_BRASS = 0x001, - KEY_IRON = 0x002, - KEY_BRONZE = 0x004, - KEY_SILVER = 0x008, - KEY_GOLD = 0x010, - }; - private: // don't use this PlayerStatus(const PlayerStatus& other); - int keys; - std::auto_ptr tux_life; - std::auto_ptr key_iron; - std::auto_ptr key_brass; - std::auto_ptr key_bronze; - std::auto_ptr key_silver; - std::auto_ptr key_gold; }; // global player state diff --git a/src/ref.hpp b/src/ref.hpp new file mode 100644 index 000000000..ff7e07c03 --- /dev/null +++ b/src/ref.hpp @@ -0,0 +1,88 @@ +// $Id: main.cpp 3385 2006-04-23 13:08:57Z matzebraun $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +#ifndef __REF_HPP__ +#define __REF_HPP__ + +/** This class behaves like a pointer to a refcounted object, but increments the + * reference count when new objects are assigned and decrements the refcounter + * when it's lifetime has experied. (similar to std::auto_ptr) + */ +template +class Ref +{ +public: + Ref(T* object = 0) + : object(object) + { + if(object) + object->ref(); + } + Ref(const Ref& other) + : object(other.object) + { + if(object) + object->ref(); + } + ~Ref() + { + if(object) + object->unref(); + } + + void operator= (const Ref& other) + { + *this = other.get(); + } + + void operator= (T* object) + { + if(object) + object->ref(); + if(this->object) + this->object->unref(); + this->object = object; + } + + T* operator ->() const + { + return object; + } + + T& operator* () const + { + return *object; + } + + operator const T* () const + { + return object; + } + + T* get() const + { + return object; + } + +private: + T* object; +}; + +#endif + diff --git a/src/refcounter.hpp b/src/refcounter.hpp new file mode 100644 index 000000000..1c26b594d --- /dev/null +++ b/src/refcounter.hpp @@ -0,0 +1,61 @@ +// $Id: refcounter.hpp 1195 2006-01-07 10:37:52Z grumbel $ +// +// Windstille - A Jump'n Shoot Game +// Copyright (C) 2005 Matthias Braun +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#ifndef __REFCOUNTER_HPP__ +#define __REFCOUNTER_HPP__ + +#include + +/** + * A base class that provides reference counting facilities + */ +class RefCounter +{ +public: + RefCounter() + : refcount(0) + { } + + /** increases reference count */ + void ref() + { + refcount++; + } + /** decreases reference count. Destroys the object if the reference count + * reaches 0 + */ + void unref() + { + refcount--; + if(refcount <= 0) { + delete this; + return; + } + } + +protected: + virtual ~RefCounter() + { + assert(refcount == 0); + } + +private: + int refcount; +}; + +#endif diff --git a/src/scripting/floating_image.cpp b/src/scripting/floating_image.cpp index 5f323c2dc..1647685cd 100644 --- a/src/scripting/floating_image.cpp +++ b/src/scripting/floating_image.cpp @@ -16,7 +16,6 @@ // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - #include #include @@ -33,13 +32,12 @@ FloatingImage::FloatingImage(const std::string& spritefile) { using namespace WorldMapNS; - floating_image = new _FloatingImage(spritefile); + floating_image = new _FloatingImage(spritefile); if(Sector::current() != NULL) { - Sector::current()->add_object(floating_image); + Sector::current()->add_object(floating_image.get()); } else if(WorldMap::current() != NULL) { - WorldMap::current()->add_object(floating_image); + WorldMap::current()->add_object(floating_image.get()); } else { - delete floating_image; throw new std::runtime_error("Neither sector nor worldmap active"); } } @@ -47,7 +45,6 @@ FloatingImage::FloatingImage(const std::string& spritefile) FloatingImage::~FloatingImage() { floating_image->remove_me(); - // no delete here, Sector will do that } void diff --git a/src/scripting/floating_image.hpp b/src/scripting/floating_image.hpp index 78f39df74..68d82f056 100644 --- a/src/scripting/floating_image.hpp +++ b/src/scripting/floating_image.hpp @@ -23,6 +23,7 @@ #ifndef SCRIPTING_API #define __suspend #include +#include "ref.hpp" class FloatingImage; typedef FloatingImage _FloatingImage; @@ -51,7 +52,7 @@ public: #ifndef SCRIPTING_API private: - _FloatingImage* floating_image; + Ref<_FloatingImage> floating_image; #endif }; diff --git a/src/scripting/functions.cpp b/src/scripting/functions.cpp index 1285da65b..874dfa651 100644 --- a/src/scripting/functions.cpp +++ b/src/scripting/functions.cpp @@ -143,11 +143,6 @@ void import(HSQUIRRELVM vm, const std::string& filename) sq_pop(vm, 1); } -void add_key(int new_key) -{ - player_status->set_keys(new_key); -} - void debug_collrects(bool enable) { Sector::show_collrects = enable; diff --git a/src/scripting/functions.hpp b/src/scripting/functions.hpp index 250fa7bf8..c905a05d5 100644 --- a/src/scripting/functions.hpp +++ b/src/scripting/functions.hpp @@ -30,13 +30,6 @@ namespace Scripting { -//TODO: Get this from PlayerStatus (update MiniSwig!) -static const int KEY_BRASS = 0x001; -static const int KEY_IRON = 0x002; -static const int KEY_BRONZE = 0x004; -static const int KEY_SILVER = 0x008; -static const int KEY_GOLD = 0x010; - /** * Display the value of the argument. This is usefull for inspecting tables. */ @@ -112,11 +105,6 @@ void import(HSQUIRRELVM v, const std::string& filename); void save_state(); /** - * Add a key to the inventory - */ -void add_key(int new_key); - -/** * enable/disable drawing of collision rectangles */ void debug_collrects(bool enable); diff --git a/src/scripting/wrapper.cpp b/src/scripting/wrapper.cpp index 57d74ed9d..cb77fabf1 100644 --- a/src/scripting/wrapper.cpp +++ b/src/scripting/wrapper.cpp @@ -1748,29 +1748,6 @@ static int save_state_wrapper(HSQUIRRELVM vm) } -static int add_key_wrapper(HSQUIRRELVM vm) -{ - int arg0; - if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) { - sq_throwerror(vm, _SC("Argument 1 not an integer")); - return SQ_ERROR; - } - - try { - Scripting::add_key(arg0); - - return 0; - - } catch(std::exception& e) { - sq_throwerror(vm, e.what()); - return SQ_ERROR; - } catch(...) { - sq_throwerror(vm, _SC("Unexpected exception while executing function 'add_key'")); - return SQ_ERROR; - } - -} - static int debug_collrects_wrapper(HSQUIRRELVM vm) { SQBool arg0; @@ -2264,36 +2241,6 @@ void register_supertux_wrapper(HSQUIRRELVM v) { using namespace Wrapper; - sq_pushstring(v, "KEY_BRASS", -1); - sq_pushinteger(v, 1); - if(SQ_FAILED(sq_createslot(v, -3))) { - throw SquirrelError(v, "Couldn't register constant 'KEY_BRASS'"); - } - - sq_pushstring(v, "KEY_IRON", -1); - sq_pushinteger(v, 2); - if(SQ_FAILED(sq_createslot(v, -3))) { - throw SquirrelError(v, "Couldn't register constant 'KEY_IRON'"); - } - - sq_pushstring(v, "KEY_BRONZE", -1); - sq_pushinteger(v, 4); - if(SQ_FAILED(sq_createslot(v, -3))) { - throw SquirrelError(v, "Couldn't register constant 'KEY_BRONZE'"); - } - - sq_pushstring(v, "KEY_SILVER", -1); - sq_pushinteger(v, 8); - if(SQ_FAILED(sq_createslot(v, -3))) { - throw SquirrelError(v, "Couldn't register constant 'KEY_SILVER'"); - } - - sq_pushstring(v, "KEY_GOLD", -1); - sq_pushinteger(v, 16); - if(SQ_FAILED(sq_createslot(v, -3))) { - throw SquirrelError(v, "Couldn't register constant 'KEY_GOLD'"); - } - sq_pushstring(v, "ANCHOR_TOP", -1); sq_pushinteger(v, 16); if(SQ_FAILED(sq_createslot(v, -3))) { @@ -2432,12 +2379,6 @@ void register_supertux_wrapper(HSQUIRRELVM v) throw SquirrelError(v, "Couldn't register function 'save_state'"); } - sq_pushstring(v, "add_key", -1); - sq_newclosure(v, &add_key_wrapper, 0); - if(SQ_FAILED(sq_createslot(v, -3))) { - throw SquirrelError(v, "Couldn't register function 'add_key'"); - } - sq_pushstring(v, "debug_collrects", -1); sq_newclosure(v, &debug_collrects_wrapper, 0); if(SQ_FAILED(sq_createslot(v, -3))) { diff --git a/src/sector.cpp b/src/sector.cpp index dc9a699a6..295c57dbb 100644 --- a/src/sector.cpp +++ b/src/sector.cpp @@ -115,10 +115,11 @@ Sector::~Sector() update_game_objects(); assert(gameobjects_new.size() == 0); - for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end(); - ++i) { - before_object_remove(*i); - delete *i; + for(GameObjects::iterator i = gameobjects.begin(); + i != gameobjects.end(); ++i) { + GameObject* object = *i; + before_object_remove(object); + object->unref(); } for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end(); @@ -454,6 +455,7 @@ Sector::add_object(GameObject* object) } #endif + object->ref(); gameobjects_new.push_back(object); } @@ -617,7 +619,7 @@ Sector::update_game_objects() before_object_remove(object); - delete *i; + object->unref(); i = gameobjects.erase(i); } diff --git a/src/worldmap/worldmap.cpp b/src/worldmap/worldmap.cpp index 7f841b8f4..8ae1a65a3 100644 --- a/src/worldmap/worldmap.cpp +++ b/src/worldmap/worldmap.cpp @@ -185,8 +185,10 @@ WorldMap::~WorldMap() current_ = NULL; for(GameObjects::iterator i = game_objects.begin(); - i != game_objects.end(); ++i) - delete *i; + i != game_objects.end(); ++i) { + GameObject* object = *i; + object->unref(); + } for(SpawnPoints::iterator i = spawn_points.begin(); i != spawn_points.end(); ++i) { @@ -202,6 +204,7 @@ WorldMap::add_object(GameObject* object) solids = tilemap; } + object->ref(); game_objects.push_back(object); } @@ -241,15 +244,15 @@ WorldMap::load(const std::string& filename) } else if(iter.item() == "level") { LevelTile* level = new LevelTile(levels_path, iter.lisp()); levels.push_back(level); - game_objects.push_back(level); + add_object(level); } else if(iter.item() == "special-tile") { SpecialTile* special_tile = new SpecialTile(iter.lisp()); special_tiles.push_back(special_tile); - game_objects.push_back(special_tile); + add_object(special_tile); } else if(iter.item() == "sprite-change") { SpriteChange* sprite_change = new SpriteChange(iter.lisp()); sprite_changes.push_back(sprite_change); - game_objects.push_back(sprite_change); + add_object(sprite_change); } else if(iter.item() == "name") { // skip } else { @@ -462,9 +465,8 @@ WorldMap::update(float delta) } // update GameObjects - for(GameObjects::iterator i = game_objects.begin(); - i != game_objects.end(); ++i) { - GameObject* object = *i; + for(size_t i = 0; i < game_objects.size(); ++i) { + GameObject* object = game_objects[i]; object->update(delta); } @@ -473,7 +475,7 @@ WorldMap::update(float delta) i != game_objects.end(); ) { GameObject* object = *i; if(!object->is_valid()) { - delete object; + object->unref(); i = game_objects.erase(i); } else { ++i;