diff options
author | Guillaume Melquiond <guillaume.melquiond@gmail.com> | 2007-08-29 14:17:22 +0000 |
---|---|---|
committer | Guillaume Melquiond <guillaume.melquiond@gmail.com> | 2007-08-29 14:17:22 +0000 |
commit | 26b3e1094d85ef89c90376688000836c8ee43979 (patch) | |
tree | f934d936d7c8319e742e946a6bb5f37ab7289b30 | |
parent | b82bf7df7053a78d51706a5a017d61f564e4677e (diff) | |
download | manaserv-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-- | ChangeLog | 15 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/game-server/being.cpp | 28 | ||||
-rw-r--r-- | src/game-server/being.hpp | 24 | ||||
-rw-r--r-- | src/game-server/character.cpp | 12 | ||||
-rw-r--r-- | src/game-server/character.hpp | 5 | ||||
-rw-r--r-- | src/game-server/deathlistener.hpp | 55 | ||||
-rw-r--r-- | src/game-server/eventlistener.hpp | 104 | ||||
-rw-r--r-- | src/game-server/gamehandler.cpp | 3 | ||||
-rw-r--r-- | src/game-server/monster.cpp | 34 | ||||
-rw-r--r-- | src/game-server/monster.hpp | 24 | ||||
-rw-r--r-- | src/game-server/quest.cpp | 49 | ||||
-rw-r--r-- | src/game-server/spawnarea.cpp | 25 | ||||
-rw-r--r-- | src/game-server/spawnarea.hpp | 16 | ||||
-rw-r--r-- | src/game-server/state.cpp | 8 | ||||
-rw-r--r-- | src/game-server/thing.cpp | 70 | ||||
-rw-r--r-- | src/game-server/thing.hpp | 33 |
17 files changed, 353 insertions, 155 deletions
@@ -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>&EventListenerFactory< _, DispatchPointerName >::create< _, MemberFunctionName >::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. */ |