make GameObjects reference counted (this avoids crashs when scripts hold reference...
authorMatthias Braun <matze@braunis.de>
Mon, 24 Apr 2006 16:37:10 +0000 (16:37 +0000)
committerMatthias Braun <matze@braunis.de>
Mon, 24 Apr 2006 16:37:10 +0000 (16:37 +0000)
SVN-Revision: 3413

12 files changed:
src/game_object.hpp
src/player_status.cpp
src/player_status.hpp
src/ref.hpp [new file with mode: 0644]
src/refcounter.hpp [new file with mode: 0644]
src/scripting/floating_image.cpp
src/scripting/floating_image.hpp
src/scripting/functions.cpp
src/scripting/functions.hpp
src/scripting/wrapper.cpp
src/sector.cpp
src/worldmap/worldmap.cpp

index e70b842..7d4b05f 100644 (file)
@@ -20,6 +20,7 @@
 #define SUPERTUX_GAMEOBJECT_H
 
 #include <string>
+#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
    */
index 24a1b99..c39c6fa 100644 (file)
@@ -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
index 0e768ae..3df230b 100644 (file)
@@ -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<std::string> 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<Sprite> tux_life;
-  std::auto_ptr<Sprite> key_iron;
-  std::auto_ptr<Sprite> key_brass;
-  std::auto_ptr<Sprite> key_bronze;
-  std::auto_ptr<Sprite> key_silver;
-  std::auto_ptr<Sprite> key_gold;
 };
 
 // global player state
diff --git a/src/ref.hpp b/src/ref.hpp
new file mode 100644 (file)
index 0000000..ff7e07c
--- /dev/null
@@ -0,0 +1,88 @@
+//  $Id: main.cpp 3385 2006-04-23 13:08:57Z matzebraun $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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<typename T>
+class Ref
+{
+public:
+  Ref(T* object = 0)
+    : object(object)
+  {
+    if(object)
+      object->ref();
+  }
+  Ref(const Ref<T>& other)
+    : object(other.object)
+  {
+    if(object)
+      object->ref();
+  }
+  ~Ref()
+  {
+    if(object)
+      object->unref();
+  }
+
+  void operator= (const Ref<T>& 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 (file)
index 0000000..1c26b59
--- /dev/null
@@ -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 <matze@braunis.de>
+//
+//  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 <assert.h>
+
+/**
+ * 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
index 5f323c2..1647685 100644 (file)
@@ -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 <config.h>
 
 #include <assert.h>
@@ -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
index 78f39df..68d82f0 100644 (file)
@@ -23,6 +23,7 @@
 #ifndef SCRIPTING_API
 #define __suspend
 #include <string>
+#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
 };
 
index 1285da6..874dfa6 100644 (file)
@@ -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;
index 250fa7b..c905a05 100644 (file)
 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);
index 57d74ed..cb77fab 100644 (file)
@@ -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))) {
index dc9a699..295c57d 100644 (file)
@@ -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);
   }
 
index 7f841b8..8ae1a65 100644 (file)
@@ -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;