summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Melquiond <guillaume.melquiond@gmail.com>2007-08-29 14:17:22 +0000
committerGuillaume Melquiond <guillaume.melquiond@gmail.com>2007-08-29 14:17:22 +0000
commit26b3e1094d85ef89c90376688000836c8ee43979 (patch)
treef934d936d7c8319e742e946a6bb5f37ab7289b30
parentb82bf7df7053a78d51706a5a017d61f564e4677e (diff)
downloadmanaserv-26b3e1094d85ef89c90376688000836c8ee43979.tar.gz
manaserv-26b3e1094d85ef89c90376688000836c8ee43979.tar.bz2
manaserv-26b3e1094d85ef89c90376688000836c8ee43979.tar.xz
manaserv-26b3e1094d85ef89c90376688000836c8ee43979.zip
Replaced event system. Fixed race condition between quest variable recovery and character removal.
-rw-r--r--ChangeLog15
-rw-r--r--src/Makefile.am3
-rw-r--r--src/game-server/being.cpp28
-rw-r--r--src/game-server/being.hpp24
-rw-r--r--src/game-server/character.cpp12
-rw-r--r--src/game-server/character.hpp5
-rw-r--r--src/game-server/deathlistener.hpp55
-rw-r--r--src/game-server/eventlistener.hpp104
-rw-r--r--src/game-server/gamehandler.cpp3
-rw-r--r--src/game-server/monster.cpp34
-rw-r--r--src/game-server/monster.hpp24
-rw-r--r--src/game-server/quest.cpp49
-rw-r--r--src/game-server/spawnarea.cpp25
-rw-r--r--src/game-server/spawnarea.hpp16
-rw-r--r--src/game-server/state.cpp8
-rw-r--r--src/game-server/thing.cpp70
-rw-r--r--src/game-server/thing.hpp33
17 files changed, 353 insertions, 155 deletions
diff --git a/ChangeLog b/ChangeLog
index a65cd27c..d643bfeb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,21 @@
so that they can also touch local players.
* src/game-server/comand.cpp: Implemented "goto" and "recall" remote
commands.
+ * src/Makefile.am, src/game-server/deathlistener.hpp,
+ src/game-server/eventlistener.hpp: Replaced event system.
+ * src/games-server/thing.hpp, src/game-server/thing.cpp: Placed
+ listener handling at the lowest level. Added "inserted" and "removed"
+ event.
+ * src/game-server/being.hpp, src/game-server/being.cpp,
+ src/game-server/character.hpp, src/game-server/character.cpp: Added
+ "died" and "disconnected" event.
+ * src/game-server/state.cpp, src/game-server/gamehandler.cpp: Added
+ event notification.
+ * src/game-server/spawnarea.cpp, src/game-server/spawnarea.hpp,
+ src/game-server/monster.cpp, src/game-server/monster.hpp: Modified to
+ use the new event system.
+ * src/game-server/quest.cpp: Fixed event listener on character removal
+ and/or disconnection.
2007-08-28 Guillaume Melquiond <guillaume.melquiond@gmail.com>
diff --git a/src/Makefile.am b/src/Makefile.am
index a2d22615..d24b3042 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -88,7 +88,7 @@ tmwserv_game_SOURCES = \
game-server/collisiondetection.hpp \
game-server/collisiondetection.cpp \
game-server/command.cpp \
- game-server/deathlistener.hpp \
+ game-server/eventlistener.hpp \
game-server/gamehandler.hpp \
game-server/gamehandler.cpp \
game-server/inventory.hpp \
@@ -122,6 +122,7 @@ tmwserv_game_SOURCES = \
game-server/state.cpp \
game-server/testing.cpp \
game-server/thing.hpp \
+ game-server/thing.cpp \
game-server/trade.hpp \
game-server/trade.cpp \
game-server/trigger.hpp \
diff --git a/src/game-server/being.cpp b/src/game-server/being.cpp
index bc690231..7c09d97e 100644
--- a/src/game-server/being.cpp
+++ b/src/game-server/being.cpp
@@ -26,7 +26,7 @@
#include "defines.h"
#include "game-server/collisiondetection.hpp"
-#include "game-server/deathlistener.hpp"
+#include "game-server/eventlistener.hpp"
#include "game-server/mapcomposite.hpp"
#include "utils/logger.h"
@@ -43,18 +43,6 @@ Being::Being(int type, int id):
}
}
-Being::~Being()
-{
- // Notify death listeners
- DeathListeners::iterator i_end = mDeathListeners.end();
- DeathListeners::iterator i;
- for (i = mDeathListeners.begin(); i != i_end; ++i)
- {
- (*i)->deleted(this);
- }
-
-}
-
int Being::damage(Object *, Damage const &damage)
{
if (mAction == DEAD) return 0;
@@ -122,13 +110,13 @@ int Being::damage(Object *, Damage const &damage)
{
HP.mod -= HPloss;
modifiedAttribute(BASE_ATTR_HP);
- if (HP.base + HP.mod == 0) die();
+ if (HP.base + HP.mod == 0) died();
}
return HPloss;
}
-void Being::die()
+void Being::died()
{
if (mAction == DEAD) return;
@@ -137,12 +125,12 @@ void Being::die()
// dead beings stay where they are
clearDestination();
- // Notify death listeners
- DeathListeners::iterator i_end = mDeathListeners.end();
- DeathListeners::iterator i;
- for (i = mDeathListeners.begin(); i != i_end; ++i)
+ for (Listeners::iterator i = mListeners.begin(),
+ i_end = mListeners.end(); i != i_end;)
{
- (*i)->died(this);
+ EventListener const &l = **i;
+ ++i; // In case the listener removes itself from the list on the fly.
+ if (l.dispatch->died) l.dispatch->died(&l, this);
}
}
diff --git a/src/game-server/being.hpp b/src/game-server/being.hpp
index a390a4bf..15a3e54e 100644
--- a/src/game-server/being.hpp
+++ b/src/game-server/being.hpp
@@ -30,7 +30,6 @@
#include "game-server/movingobject.hpp"
class Being;
-class DeathListener;
class MapComposite;
/**
@@ -126,8 +125,6 @@ class Being : public MovingObject
*/
Being(int type, int id);
- ~Being();
-
/**
* Cleans obsolete attribute modifiers.
*/
@@ -141,9 +138,9 @@ class Being : public MovingObject
virtual int damage(Object *source, Damage const &damage);
/**
- * Kills the being
+ * Changes status and calls all the "died" listeners.
*/
- virtual void die();
+ virtual void died();
/**
* Gets the damage list.
@@ -210,22 +207,6 @@ class Being : public MovingObject
void dispellModifiers(int level);
/**
- * Adds a death listener.
- */
- void addDeathListener(DeathListener *listener)
- {
- mDeathListeners.push_back(listener);
- }
-
- /**
- * Removes a death listener.
- */
- void removeDeathListener(DeathListener *listener)
- {
- mDeathListeners.remove(listener);
- }
-
- /**
* Called when an attribute modifier is changed.
*/
virtual void modifiedAttribute(int) {}
@@ -233,7 +214,6 @@ class Being : public MovingObject
protected:
Action mAction;
std::vector< Attribute > mAttributes;
- std::list<DeathListener*> mDeathListeners;
private:
Being(Being const &rhs);
diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp
index ae888d44..b9bb061b 100644
--- a/src/game-server/character.cpp
+++ b/src/game-server/character.cpp
@@ -27,6 +27,7 @@
#include "defines.h"
#include "game-server/buysell.hpp"
+#include "game-server/eventlistener.hpp"
#include "game-server/inventory.hpp"
#include "game-server/item.hpp"
#include "game-server/itemmanager.hpp"
@@ -202,3 +203,14 @@ void Character::flagAttribute(int attr)
i = std::find(mModifiedAttributes.begin(), i_end, (unsigned char)attr);
if (i == i_end) mModifiedAttributes.push_back(attr);
}
+
+void Character::disconnected()
+{
+ for (Listeners::iterator i = mListeners.begin(),
+ i_end = mListeners.end(); i != i_end;)
+ {
+ EventListener const &l = **i;
+ ++i; // In case the listener removes itself from the list on the fly.
+ if (l.dispatch->disconnected) l.dispatch->disconnected(&l, this);
+ }
+}
diff --git a/src/game-server/character.hpp b/src/game-server/character.hpp
index eff99286..fd4e0054 100644
--- a/src/game-server/character.hpp
+++ b/src/game-server/character.hpp
@@ -209,6 +209,11 @@ class Character : public Being
void modifiedAttribute(int);
/**
+ * Calls all the "disconnected" listener.
+ */
+ void disconnected();
+
+ /**
* Associative array containing all the quest variables known by the
* server.
*/
diff --git a/src/game-server/deathlistener.hpp b/src/game-server/deathlistener.hpp
deleted file mode 100644
index 06bb760f..00000000
--- a/src/game-server/deathlistener.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * The Mana World Server
- * Copyright 2004 The Mana World Development Team
- *
- * This file is part of The Mana World.
- *
- * The Mana World 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 any later version.
- *
- * The Mana World 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 The Mana World; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * $Id$
- */
-
-#ifndef _TMWSERV_DEATHLISTENER
-#define _TMWSERV_DEATHLISTENER
-
-#include <list>
-
-class Being;
-
-/**
- * The listener that's notified when a being dies.
- */
-class DeathListener
-{
- public:
- /**
- * The obligatory empty virtual destructor.
- */
- virtual ~DeathListener() {}
-
- /**
- * Called when a being died.
- */
- virtual void died(Being *) {}
-
- /**
- * Called when a being is deleted.
- */
- virtual void deleted(Being *) {}
-};
-
-typedef std::list<DeathListener*> DeathListeners;
-
-
-#endif // _TMWSERV_DEATHLISTENER
diff --git a/src/game-server/eventlistener.hpp b/src/game-server/eventlistener.hpp
new file mode 100644
index 00000000..2b5c2b81
--- /dev/null
+++ b/src/game-server/eventlistener.hpp
@@ -0,0 +1,104 @@
+/*
+ * The Mana World Server
+ * Copyright 2007 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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 any later version.
+ *
+ * The Mana World 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 The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+#ifndef _TMWSERV_GAMESERVER_EVENTLISTENER_
+#define _TMWSERV_GAMESERVER_EVENTLISTENER_
+
+class Thing;
+class Being;
+class Character;
+
+struct EventDispatch;
+
+/**
+ * Pointer to a dispatch table.
+ */
+struct EventListener
+{
+ EventDispatch const *dispatch;
+ EventListener(EventDispatch const *d): dispatch(d) {}
+};
+
+/**
+ * Dispatch table for event notification.
+ */
+struct EventDispatch
+{
+ /**
+ * Called just after something is inserted in a map.
+ */
+ void (*inserted)(EventListener const *, Thing *);
+
+ /**
+ * Called just before something is removed from a map.
+ */
+ void (*removed)(EventListener const *, Thing *);
+
+ /**
+ * Called just after a being has died.
+ */
+ void (*died)(EventListener const *, Being *);
+
+ /**
+ * Called just before a character is deleted.
+ */
+ void (*disconnected)(EventListener const *, Character *);
+
+ /**
+ * Initializes dispatch methods as missing.
+ */
+ EventDispatch():
+ inserted(0), removed(0), died(0), disconnected(0)
+ {}
+};
+
+/**
+ * Helper for using member functions as dispatch methods. The 3-level structure
+ * is due to default template parameter not being allowed on functions yet.
+ * Conceptually, this helper takes two parameters: the name of the member
+ * variable pointing to the dispatch table and the name of the member function
+ * to call on dispatch. With these two parameters, it creates a dispatch
+ * method. When called, this free function forwards the call to the member
+ * function.
+ * Pseudo-syntax for getting a dispatch method:
+ * <code>&amp;EventListenerFactory&lt; _, DispatchPointerName &gt;::create&lt; _, MemberFunctionName &gt;::function</code>
+ * See the start of the spawnarea.cpp file for a complete example.
+ */
+template< class T, EventListener T::*D >
+struct EventListenerFactory
+{
+ template< class U, void (T::*F)(U *), class V = U >
+ struct create
+ {
+ static void function(EventListener const *d, V *u)
+ {
+ /* Get the address of the T object by substracting the offset of D
+ from the pointer d. */
+ T *t = (T *)((char *)d -
+ ((char *)&(((T *)42)->*D) - (char *)&(*(T *)42)));
+ // Then call the method F of this T object.
+ (t->*F)(u);
+ }
+ };
+};
+
+#endif
diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp
index 40507d9a..63a34689 100644
--- a/src/game-server/gamehandler.cpp
+++ b/src/game-server/gamehandler.cpp
@@ -70,6 +70,7 @@ void GameHandler::computerDisconnected(NetComputer *comp)
{
accountHandler->sendCharacterData(ch);
GameState::remove(ch);
+ ch->disconnected();
delete ch;
}
delete &computer;
@@ -106,6 +107,7 @@ void GameHandler::completeServerChange(int id, std::string const &token,
msg.writeString(address);
msg.writeShort(port);
c->send(msg);
+ c->character->disconnected();
delete c->character;
c->character = NULL;
c->status = CLIENT_LOGIN;
@@ -339,6 +341,7 @@ void GameHandler::processMessage(NetComputer *comp, MessageIn &message)
accountHandler->sendCharacterData(computer.character);
// Done with the character
+ computer.character->disconnected();
delete computer.character;
computer.character = NULL;
computer.status = CLIENT_LOGIN;
diff --git a/src/game-server/monster.cpp b/src/game-server/monster.cpp
index e859c9f9..c8a34578 100644
--- a/src/game-server/monster.cpp
+++ b/src/game-server/monster.cpp
@@ -42,10 +42,23 @@ ItemClass *MonsterClass::getRandomDrop() const
return NULL;
}
+struct MonsterTargetEventDispatch: EventDispatch
+{
+ MonsterTargetEventDispatch()
+ {
+ typedef EventListenerFactory< Monster, &Monster::mTargetListener > Factory;
+ removed = &Factory::create< Thing, &Monster::forgetTarget >::function;
+ died = &Factory::create< Thing, &Monster::forgetTarget, Being >::function;
+ }
+};
+
+static MonsterTargetEventDispatch monsterTargetEventDispatch;
+
Monster::Monster(MonsterClass *specy):
Being(OBJECT_MONSTER, 65535),
mSpecy(specy),
mCountDown(0),
+ mTargetListener(&monsterTargetEventDispatch),
mAttackTime(0),
mAttackPreDelay(5),
mAttackAftDelay(10)
@@ -69,11 +82,11 @@ Monster::Monster(MonsterClass *specy):
Monster::~Monster()
{
- // deactivate death listeners
- std::map<Being *, int>::iterator i;
- for (i = mAnger.begin(); i != mAnger.end(); i++)
+ // Remove death listeners.
+ for (std::map<Being *, int>::iterator i = mAnger.begin(),
+ i_end = mAnger.end(); i != i_end; ++i)
{
- i->first->removeDeathListener(this);
+ i->first->removeListener(&mTargetListener);
}
}
@@ -228,10 +241,11 @@ int Monster::calculatePositionPriority(Point position, int targetPriority)
}
}
-void Monster::died(Being *being)
+void Monster::forgetTarget(Thing *t)
{
- mAnger.erase(being);
- mDeathListeners.remove((DeathListener *)being);
+ Being *b = static_cast< Being * >(t);
+ mAnger.erase(b);
+ b->removeListener(&mTargetListener);
}
int Monster::damage(Object *source, Damage const &damage)
@@ -245,7 +259,7 @@ int Monster::damage(Object *source, Damage const &damage)
if (ib.second)
{
- s->addDeathListener(this);
+ s->addListener(&mTargetListener);
}
else
{
@@ -255,10 +269,10 @@ int Monster::damage(Object *source, Damage const &damage)
return HPLoss;
}
-void Monster::die()
+void Monster::died()
{
+ Being::died();
mCountDown = 50; // Sets remove time to 5 seconds
- Being::die();
if (ItemClass *drop = mSpecy->getRandomDrop())
{
Item *item = new Item(drop, 1);
diff --git a/src/game-server/monster.hpp b/src/game-server/monster.hpp
index 15e2a558..40ab2047 100644
--- a/src/game-server/monster.hpp
+++ b/src/game-server/monster.hpp
@@ -27,7 +27,7 @@
#include <vector>
#include "game-server/being.hpp"
-#include "game-server/deathlistener.hpp"
+#include "game-server/eventlistener.hpp"
class ItemClass;
class MapComposite;
@@ -94,7 +94,7 @@ struct AttackPosition
/**
* The class for a fightable monster with its own AI
*/
-class Monster : public Being, public DeathListener
+class Monster : public Being
{
public:
/**
@@ -124,9 +124,9 @@ class Monster : public Being, public DeathListener
void perform();
/**
- * Kills the being
+ * Kills the being.
*/
- virtual void die();
+ void died();
/**
* Calls the damage function in Being and updates the aggro list
@@ -134,18 +134,9 @@ class Monster : public Being, public DeathListener
virtual int damage(Object *source, Damage const &damage);
/**
- * Getting informed that a being that might be on the target list died
+ * Removes a being from the anger list.
*/
- virtual void died(Being *being);
-
- /**
- * Getting informed that a being that might be on the target list is
- * deleted
- */
- virtual void deleted(Being *being)
- {
- died(being);
- }
+ void forgetTarget(Thing *being);
private:
int calculatePositionPriority(Point position, int targetPriority);
@@ -153,6 +144,7 @@ class Monster : public Being, public DeathListener
MonsterClass *mSpecy; /**< Monster specy. */
int mCountDown; /**< Count down till next random movement (temporary). */
std::map<Being *, int> mAnger; /**< Aggression towards other beings */
+ EventListener mTargetListener; /**< Listener for updating the anger list. */
int mAttackTime; /**< Delay until monster can attack */
// TODO: the following vars should all be the same for all monsters of
// the same type. So they should be put into some central data structure
@@ -162,6 +154,8 @@ class Monster : public Being, public DeathListener
bool mAgressive; /**< Does the monster attack without being provoked? */
unsigned mAgressionRange; /**< Distance the monster tracks enemies in */
std::list<AttackPosition> mAttackPositions; /**< set positions relative to target from which the monster can attack */
+
+ friend struct MonsterTargetEventDispatch;
};
#endif // _TMWSERV_MONSTER_H_
diff --git a/src/game-server/quest.cpp b/src/game-server/quest.cpp
index 118fde9d..18e7f178 100644
--- a/src/game-server/quest.cpp
+++ b/src/game-server/quest.cpp
@@ -21,7 +21,7 @@
* $Id$
*/
-#include <algorithm>
+#include <cassert>
#include <list>
#include <map>
#include <string>
@@ -31,7 +31,7 @@
#include "defines.h"
#include "game-server/accountconnection.hpp"
#include "game-server/character.hpp"
-#include "game-server/deathlistener.hpp"
+#include "game-server/eventlistener.hpp"
#include "utils/logger.h"
typedef std::list< QuestCallback > QuestCallbacks;
@@ -79,25 +79,43 @@ void setQuestVar(Character *ch, std::string const &name,
/**
* Listener for deleting related quests when a character disappears.
*/
-struct QuestDeathListener: DeathListener
+struct QuestDeathListener: EventDispatch
{
- void deleted(Being *b)
+ static void partialRemove(EventListener const *, Thing *t)
{
- /* FIXME: At this point, Character has already been destroyed and we
- are potentially reading garbage or segfaulting. This is a misfeature
- of DeathListener and it should be fixed there. Anyway, as we are
- calling a non-virtual method and it accesses a primitive datatype,
- we should be safe with any compiler without vicious compliance. */
- int id = static_cast< Character * >(b)->getDatabaseID();
- pendingQuests.erase(id);
+ int id = static_cast< Character * >(t)->getDatabaseID();
+ PendingVariables &variables = pendingQuests[id].variables;
+ // Remove all the callbacks, but do not remove the variable names.
+ for (PendingVariables::iterator i = variables.begin(),
+ i_end = variables.end(); i != i_end; ++i)
+ {
+ i->second.clear();
+ }
+ // The listener is kept in case a fullRemove is needed later.
+ }
+
+ static void fullRemove(EventListener const *, Character *ch)
+ {
+ extern EventListener questDeathListener;
+ ch->removeListener(&questDeathListener);
+ // Remove anything related to this character.
+ pendingQuests.erase(ch->getDatabaseID());
+ }
+
+ QuestDeathListener()
+ {
+ removed = &partialRemove;
+ disconnected = &fullRemove;
}
};
-static QuestDeathListener questDeathListener;
+static QuestDeathListener questDeathDummy;
+static EventListener questDeathListener = { &questDeathDummy };
void recoverQuestVar(Character *ch, std::string const &name,
QuestCallback const &f)
{
+ assert(ch->questCache.find(name) == ch->questCache.end());
int id = ch->getDatabaseID();
PendingQuests::iterator i = pendingQuests.lower_bound(id);
if (i == pendingQuests.end() || i->first != id)
@@ -106,7 +124,7 @@ void recoverQuestVar(Character *ch, std::string const &name,
i->second.character = ch;
/* Register a listener, because we cannot afford to get invalid
pointers, when we finally recover the variable. */
- ch->addDeathListener(&questDeathListener);
+ ch->addListener(&questDeathListener);
}
i->second.variables[name].push_back(f);
accountHandler->requestQuestVar(ch, name);
@@ -117,6 +135,10 @@ void recoveredQuestVar(int id, std::string const &name,
{
PendingQuests::iterator i = pendingQuests.find(id);
if (i == pendingQuests.end()) return;
+
+ Character *ch = i->second.character;
+ ch->removeListener(&questDeathListener);
+
PendingVariables &variables = i->second.variables;
PendingVariables::iterator j = variables.find(name);
if (j == variables.end())
@@ -125,7 +147,6 @@ void recoveredQuestVar(int id, std::string const &name,
return;
}
- Character *ch = i->second.character;
ch->questCache[name] = value;
// Call the registered callbacks.
diff --git a/src/game-server/spawnarea.cpp b/src/game-server/spawnarea.cpp
index 0f2ff595..63bdc904 100644
--- a/src/game-server/spawnarea.cpp
+++ b/src/game-server/spawnarea.cpp
@@ -21,21 +21,28 @@
* $Id$
*/
-#include "spawnarea.hpp"
+#include "game-server/spawnarea.hpp"
#include "game-server/mapcomposite.hpp"
#include "game-server/monster.hpp"
#include "game-server/state.hpp"
-
#include "utils/logger.h"
-/*
- * TODO: Allow specifying being type and use it.
- */
+struct SpawnAreaEventDispatch: EventDispatch
+{
+ SpawnAreaEventDispatch()
+ {
+ typedef EventListenerFactory< SpawnArea, &SpawnArea::mSpawnedListener > Factory;
+ removed = &Factory::create< Thing, &SpawnArea::decrease >::function;
+ }
+};
+
+static SpawnAreaEventDispatch spawnAreaEventDispatch;
SpawnArea::SpawnArea(MapComposite *map, MonsterClass *specy, const Rectangle &zone):
Thing(OBJECT_OTHER, map),
mSpecy(specy),
+ mSpawnedListener(&spawnAreaEventDispatch),
mZone(zone),
mMaxBeings(10),
mSpawnRate(10),
@@ -68,7 +75,7 @@ SpawnArea::update()
if (c)
{
Being *being = new Monster(mSpecy);
- being->addDeathListener(this);
+ being->addListener(&mSpawnedListener);
being->setMap(map);
being->setPosition(position);
@@ -94,8 +101,8 @@ SpawnArea::update()
}
}
-void
-SpawnArea::died(Being *being)
+void SpawnArea::decrease(Thing *t)
{
- mNumBeings--;
+ --mNumBeings;
+ t->removeListener(&mSpawnedListener);
}
diff --git a/src/game-server/spawnarea.hpp b/src/game-server/spawnarea.hpp
index cd682647..a565890d 100644
--- a/src/game-server/spawnarea.hpp
+++ b/src/game-server/spawnarea.hpp
@@ -25,7 +25,7 @@
#define _TMWSERV_SPAWNAREA
#include "point.h"
-#include "game-server/deathlistener.hpp"
+#include "game-server/eventlistener.hpp"
#include "game-server/thing.hpp"
class Being;
@@ -35,22 +35,28 @@ class MonsterClass;
* A spawn area, where monsters spawn. The area is a rectangular field and will
* spawn a certain number of a given monster type.
*/
-class SpawnArea : public Thing, public DeathListener
+class SpawnArea : public Thing
{
public:
SpawnArea(MapComposite *, MonsterClass *, Rectangle const &zone);
- virtual void update();
+ void update();
- virtual void died(Being *being);
+ /**
+ * Keeps track of the number of spawned being.
+ */
+ void decrease(Thing *);
- protected:
+ private:
MonsterClass *mSpecy; /**< Specy of monster that spawns in this area. */
+ EventListener mSpawnedListener; /**< Tracking of spawned monsters. */
Rectangle mZone;
int mMaxBeings; /**< Maximum population of this area. */
int mSpawnRate; /**< Number of beings spawning per minute. */
int mNumBeings; /**< Current population of this area. */
int mNextSpawn; /**< The time until next being spawn. */
+
+ friend struct SpawnAreaEventDispatch;
};
#endif
diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp
index ed111abc..8545820d 100644
--- a/src/game-server/state.cpp
+++ b/src/game-server/state.cpp
@@ -404,7 +404,9 @@ void GameState::update()
remove(o);
if (o->getType() == OBJECT_CHARACTER)
{
- gameHandler->kill(static_cast< Character * >(o));
+ Character *ch = static_cast< Character * >(o);
+ ch->disconnected();
+ gameHandler->kill(ch);
}
delete o;
} break;
@@ -456,6 +458,8 @@ void GameState::insert(Thing *ptr)
return;
}
+ ptr->inserted();
+
if (ptr->isVisible())
{
Object *obj = static_cast< Object * >(ptr);
@@ -478,6 +482,8 @@ void GameState::remove(Thing *ptr)
assert(!dbgLockObjects);
MapComposite *map = ptr->getMap();
+ ptr->removed();
+
if (ptr->canMove())
{
if (ptr->getType() == OBJECT_CHARACTER)
diff --git a/src/game-server/thing.cpp b/src/game-server/thing.cpp
new file mode 100644
index 00000000..1900dd07
--- /dev/null
+++ b/src/game-server/thing.cpp
@@ -0,0 +1,70 @@
+/*
+ * The Mana World Server
+ * Copyright 2007 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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 any later version.
+ *
+ * The Mana World 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 The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+#include <cassert>
+
+#include "game-server/thing.hpp"
+
+#include "game-server/eventlistener.hpp"
+
+Thing::~Thing()
+{
+ /* As another object will stop listening and call removeListener when it is
+ deleted, the following assertion ensures that all the calls to
+ removeListener have been performed will this object was still alive. It
+ is not strictly necessary, as there are cases where no removal is
+ performed (e.g. ~SpawnArea). But this is rather exceptional, so keep the
+ assertion to catch all the other forgotten calls to removeListener. */
+ assert(mListeners.empty());
+}
+
+void Thing::addListener(EventListener const *l)
+{
+ mListeners.insert(l);
+}
+
+void Thing::removeListener(EventListener const *l)
+{
+ mListeners.erase(l);
+}
+
+void Thing::inserted()
+{
+ for (Listeners::iterator i = mListeners.begin(),
+ i_end = mListeners.end(); i != i_end;)
+ {
+ EventListener const &l = **i;
+ ++i; // In case the listener removes itself from the list on the fly.
+ if (l.dispatch->inserted) l.dispatch->inserted(&l, this);
+ }
+}
+
+void Thing::removed()
+{
+ for (Listeners::iterator i = mListeners.begin(),
+ i_end = mListeners.end(); i != i_end;)
+ {
+ EventListener const &l = **i;
+ ++i; // In case the listener removes itself from the list on the fly.
+ if (l.dispatch->removed) l.dispatch->removed(&l, this);
+ }
+}
diff --git a/src/game-server/thing.hpp b/src/game-server/thing.hpp
index 1fdf584e..2f160d37 100644
--- a/src/game-server/thing.hpp
+++ b/src/game-server/thing.hpp
@@ -23,6 +23,9 @@
#ifndef _TMWSERV_THING_H_
#define _TMWSERV_THING_H_
+#include <set>
+
+class EventListener;
class MapComposite;
/**
@@ -43,7 +46,7 @@ enum
/**
* Base class for in-game objects. Knows only its type and the map is resides
- * on.
+ * on. Provides listeners.
*/
class Thing
{
@@ -57,9 +60,9 @@ class Thing
{}
/**
- * Empty virtual destructor.
+ * Destructor.
*/
- virtual ~Thing() {}
+ virtual ~Thing();
/**
* Gets type of this thing.
@@ -106,6 +109,30 @@ class Thing
void setMap(MapComposite *map)
{ mMap = map; }
+ /**
+ * Adds a new listener.
+ */
+ void addListener(EventListener const *);
+
+ /**
+ * Removes an existing listener.
+ */
+ void removeListener(EventListener const *);
+
+ /**
+ * Calls all the "inserted" listeners.
+ */
+ virtual void inserted();
+
+ /**
+ * Calls all the "removed" listeners.
+ */
+ virtual void removed();
+
+ protected:
+ typedef std::set< EventListener const * > Listeners;
+ Listeners mListeners; /**< List of event listeners. */
+
private:
MapComposite *mMap; /**< Map the thing is on */
char mType; /**< Type of this thing. */