diff options
author | Thorbjørn Lindeijer <thorbjorn@lindeijer.nl> | 2012-05-27 23:26:19 +0200 |
---|---|---|
committer | Thorbjørn Lindeijer <thorbjorn@lindeijer.nl> | 2013-01-09 17:12:15 +0100 |
commit | 0b339e547b77f80d6e81313bfb38249ce8995553 (patch) | |
tree | f22fee0142f72042430ace070a6e6ef4493e7c39 | |
parent | 16074a7c2c8197a061281a6880ddbc3967d8ea0c (diff) | |
download | manaserv-0b339e547b77f80d6e81313bfb38249ce8995553.tar.gz manaserv-0b339e547b77f80d6e81313bfb38249ce8995553.tar.bz2 manaserv-0b339e547b77f80d6e81313bfb38249ce8995553.tar.xz manaserv-0b339e547b77f80d6e81313bfb38249ce8995553.zip |
Replaced EventListener with signals based on libsigc++
This replaces the rather hard to understand event dispatcher with a
probably even harder to understand templated library, but fortunately
we can rely on the available documentation.
Hopefully it will also help with the readability of our code and with
adding additional signals to other classes.
Added libsigc++ to README and Travis CI configuration.
Reviewed-by: Erik Schilling
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | CMake/Modules/FindSigC++.cmake | 34 | ||||
-rw-r--r-- | CMake/Modules/LibFindMacros.cmake | 99 | ||||
-rw-r--r-- | README | 13 | ||||
-rw-r--r-- | src/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/game-server/being.cpp | 27 | ||||
-rw-r--r-- | src/game-server/being.h | 10 | ||||
-rw-r--r-- | src/game-server/character.cpp | 10 | ||||
-rw-r--r-- | src/game-server/character.h | 2 | ||||
-rw-r--r-- | src/game-server/entity.cpp | 47 | ||||
-rw-r--r-- | src/game-server/entity.h | 37 | ||||
-rw-r--r-- | src/game-server/eventlistener.h | 102 | ||||
-rw-r--r-- | src/game-server/monster.cpp | 83 | ||||
-rw-r--r-- | src/game-server/monster.h | 23 | ||||
-rw-r--r-- | src/game-server/quest.cpp | 78 | ||||
-rw-r--r-- | src/game-server/spawnarea.cpp | 20 | ||||
-rw-r--r-- | src/game-server/spawnarea.h | 2 | ||||
-rw-r--r-- | src/game-server/state.cpp | 6 | ||||
-rw-r--r-- | src/scripting/lua.cpp | 6 | ||||
-rw-r--r-- | src/scripting/luascript.cpp | 2 | ||||
-rw-r--r-- | src/scripting/luascript.h | 2 | ||||
-rw-r--r-- | src/scripting/script.cpp | 3 | ||||
-rw-r--r-- | src/scripting/script.h | 22 |
23 files changed, 291 insertions, 344 deletions
diff --git a/.travis.yml b/.travis.yml index bbebefd2..b56d187b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,5 @@ compiler: - clang before_install: - sudo apt-get update -qq - - sudo apt-get install -qq sqlite3 cmake make gcc libxml2-dev liblua5.1-0-dev libphysfs-dev libsqlite3-dev libsdl-gfx1.2-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-net1.2-dev libsdl-pango-dev libsdl-ttf2.0-dev libsdl1.2-dev libguichan-dev libphysfs-dev libenet1a libcurl4-openssl-dev libcurl3 zlib1g-dev libmysqlclient-dev + - sudo apt-get install -qq sqlite3 cmake make gcc libxml2-dev liblua5.1-0-dev libphysfs-dev libsqlite3-dev libsdl-gfx1.2-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-net1.2-dev libsdl-pango-dev libsdl-ttf2.0-dev libsdl1.2-dev libguichan-dev libphysfs-dev libenet1a libcurl4-openssl-dev libcurl3 zlib1g-dev libmysqlclient-dev libsigc++-2.0-dev script: cmake . -DWITH_MYSQL=ON && make diff --git a/CMake/Modules/FindSigC++.cmake b/CMake/Modules/FindSigC++.cmake new file mode 100644 index 00000000..35687d5b --- /dev/null +++ b/CMake/Modules/FindSigC++.cmake @@ -0,0 +1,34 @@ +# - Try to find SigC++-2.0 +# Once done, this will define +# +# SigC++_FOUND - system has SigC++ +# SigC++_INCLUDE_DIRS - the SigC++ include directories +# SigC++_LIBRARIES - link these to use SigC++ + +include(LibFindMacros) + +# Use pkg-config to get hints about paths +libfind_pkg_check_modules(SigC++_PKGCONF sigc++-2.0) + +# Main include dir +find_path(SigC++_INCLUDE_DIR + NAMES sigc++/sigc++.h + PATHS ${SigC++_PKGCONF_INCLUDE_DIRS} + PATH_SUFFIXES sigc++-2.0 +) + +# Glib-related libraries also use a separate config header, which is in lib dir +find_path(SigC++Config_INCLUDE_DIR + NAMES sigc++config.h + PATHS ${SigC++_PKGCONF_INCLUDE_DIRS} /usr + PATH_SUFFIXES lib/sigc++-2.0/include +) + +libfind_library(SigC++ sigc 2.0) + +# Set the include dir variables and the libraries and let libfind_process do +# the rest. NOTE: Singular variables for this library, plural for libraries +# this this lib depends on. +set(SigC++_PROCESS_INCLUDES SigC++_INCLUDE_DIR SigC++Config_INCLUDE_DIR) +set(SigC++_PROCESS_LIBS SigC++_LIBRARY) +libfind_process(SigC++) diff --git a/CMake/Modules/LibFindMacros.cmake b/CMake/Modules/LibFindMacros.cmake new file mode 100644 index 00000000..69975c51 --- /dev/null +++ b/CMake/Modules/LibFindMacros.cmake @@ -0,0 +1,99 @@ +# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments +# used for the current package. For this to work, the first parameter must be the +# prefix of the current package, then the prefix of the new package etc, which are +# passed to find_package. +macro (libfind_package PREFIX) + set (LIBFIND_PACKAGE_ARGS ${ARGN}) + if (${PREFIX}_FIND_QUIETLY) + set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) + endif (${PREFIX}_FIND_QUIETLY) + if (${PREFIX}_FIND_REQUIRED) + set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) + endif (${PREFIX}_FIND_REQUIRED) + find_package(${LIBFIND_PACKAGE_ARGS}) +endmacro (libfind_package) + +# CMake developers made the UsePkgConfig system deprecated in the same release (2.6) +# where they added pkg_check_modules. Consequently I need to support both in my scripts +# to avoid those deprecated warnings. Here's a helper that does just that. +# Works identically to pkg_check_modules, except that no checks are needed prior to use. +macro (libfind_pkg_check_modules PREFIX PKGNAME) + if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + include(UsePkgConfig) + pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) + else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(${PREFIX} ${PKGNAME}) + endif (PKG_CONFIG_FOUND) + endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) +endmacro (libfind_pkg_check_modules) + +# Do the final processing once the paths have been detected. +# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain +# all the variables, each of which contain one include directory. +# Ditto for ${PREFIX}_PROCESS_LIBS and library files. +# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. +# Also handles errors in case library detection was required, etc. +macro (libfind_process PREFIX) + # Skip processing if already processed during this run + if (NOT ${PREFIX}_FOUND) + # Start with the assumption that the library was found + set (${PREFIX}_FOUND TRUE) + + # Process all includes and set _FOUND to false if any are missing + foreach (i ${${PREFIX}_PROCESS_INCLUDES}) + if (${i}) + set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) + mark_as_advanced(${i}) + else (${i}) + set (${PREFIX}_FOUND FALSE) + endif (${i}) + endforeach (i) + + # Process all libraries and set _FOUND to false if any are missing + foreach (i ${${PREFIX}_PROCESS_LIBS}) + if (${i}) + set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) + mark_as_advanced(${i}) + else (${i}) + set (${PREFIX}_FOUND FALSE) + endif (${i}) + endforeach (i) + + # Print message and/or exit on fatal error + if (${PREFIX}_FOUND) + if (NOT ${PREFIX}_FIND_QUIETLY) + message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") + endif (NOT ${PREFIX}_FIND_QUIETLY) + else (${PREFIX}_FOUND) + if (${PREFIX}_FIND_REQUIRED) + foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) + message("${i}=${${i}}") + endforeach (i) + message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") + endif (${PREFIX}_FIND_REQUIRED) + endif (${PREFIX}_FOUND) + endif (NOT ${PREFIX}_FOUND) +endmacro (libfind_process) + +macro(libfind_library PREFIX basename) + set(TMP "") + if(MSVC80) + set(TMP -vc80) + endif(MSVC80) + if(MSVC90) + set(TMP -vc90) + endif(MSVC90) + set(${PREFIX}_LIBNAMES ${basename}${TMP}) + if(${ARGC} GREATER 2) + set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) + string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) + set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) + endif(${ARGC} GREATER 2) + find_library(${PREFIX}_LIBRARY + NAMES ${${PREFIX}_LIBNAMES} + PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} + ) +endmacro(libfind_library) + @@ -6,15 +6,16 @@ COMPILATION Before trying to compile, make sure all the dependencies are installed. For each dependency the Ubuntu package name is listed as well as the website. - * libxml2 (libxml2-dev) - http://xmlsoft.org/ - * Lua (liblua5.1-0-dev) - http://lua.org/ - * PhysFS (libphysfs-dev) - http://icculus.org/physfs/ - * SQLite 3 (libsqlite3-dev) - http://sqlite.org/ - * zlib (zlib1g-dev) - http://zlib.net/ + * libsigc++ 2.0 (libsigc++-2.0-dev) - http://libsigc.sourceforge.net/ + * libxml2 (libxml2-dev) - http://xmlsoft.org/ + * Lua (liblua5.1-0-dev) - http://lua.org/ + * PhysFS (libphysfs-dev) - http://icculus.org/physfs/ + * SQLite 3 (libsqlite3-dev) - http://sqlite.org/ + * zlib (zlib1g-dev) - http://zlib.net/ Optional dependencies: - * MySQL (libmysqlclient-dev) - http://dev.mysql.com/ + * MySQL (libmysqlclient-dev) - http://dev.mysql.com/ (replaces the SQLite 3 depency) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a4b38fc..73f969f7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ FIND_PACKAGE(LibXml2 REQUIRED) FIND_PACKAGE(PhysFS REQUIRED) FIND_PACKAGE(ZLIB REQUIRED) +FIND_PACKAGE(SigC++ REQUIRED) IF (CMAKE_COMPILER_IS_GNUCXX) # Help getting compilation warnings @@ -93,6 +94,8 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ${PHYSFS_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR} + ${SigC++_INCLUDE_DIR} + ${SigC++Config_INCLUDE_DIR} ) # Fix some stuff that gets not hidden by mainline modules @@ -214,7 +217,6 @@ SET(SRCS_MANASERVGAME game-server/effect.cpp game-server/entity.h game-server/entity.cpp - game-server/eventlistener.h game-server/gamehandler.h game-server/gamehandler.cpp game-server/inventory.h @@ -315,6 +317,7 @@ FOREACH(program ${PROGRAMS}) ${PHYSFS_LIBRARY} ${LIBXML2_LIBRARIES} ${ZLIB_LIBRARIES} + ${SigC++_LIBRARIES} ${OPTIONAL_LIBRARIES} ${EXTRA_LIBRARIES}) INSTALL(TARGETS ${program} RUNTIME DESTINATION ${PKG_BINDIR}) diff --git a/src/game-server/being.cpp b/src/game-server/being.cpp index ea7540ca..9ce19d76 100644 --- a/src/game-server/being.cpp +++ b/src/game-server/being.cpp @@ -27,7 +27,6 @@ #include "game-server/attributemanager.h" #include "game-server/character.h" #include "game-server/collisiondetection.h" -#include "game-server/eventlistener.h" #include "game-server/mapcomposite.h" #include "game-server/effect.h" #include "game-server/skillmanager.h" @@ -58,6 +57,9 @@ Being::Being(EntityType type): Attribute(*it1->second))); } + + signal_inserted.connect(sigc::mem_fun(this, &Being::inserted)); + // TODO: Way to define default base values? // Should this be handled by the virtual modifiedAttribute? // URGENT either way @@ -177,14 +179,7 @@ void Being::died() // reset target mTarget = NULL; - for (Listeners::iterator i = mListeners.begin(), - i_end = mListeners.end(); i != i_end;) - { - const EventListener &l = **i; - ++i; // In case the listener removes itself from the list on the fly. - if (l.dispatch->died) - l.dispatch->died(&l, this); - } + signal_died.emit(this); } void Being::processAttacks() @@ -512,6 +507,11 @@ bool Being::removeModifier(unsigned attr, double value, unsigned layer, return ret; } +void Being::setGender(BeingGender gender) +{ + mGender = gender; +} + void Being::setAttribute(unsigned id, double value) { AttributeMap::iterator ret = mAttributes.find(id); @@ -743,20 +743,13 @@ void Being::update() processAttacks(); } -void Being::inserted() +void Being::inserted(Entity *) { - Actor::inserted(); - // Reset the old position, since after insertion it is important that it is // in sync with the zone that we're currently present in. mOld = getPosition(); } -void Being::setGender(BeingGender gender) -{ - mGender = gender; -} - void Being::processAttack(Attack &attack) { performAttack(mTarget, attack.getAttackInfo()->getDamage()); diff --git a/src/game-server/being.h b/src/game-server/being.h index 0083be1f..a08df018 100644 --- a/src/game-server/being.h +++ b/src/game-server/being.h @@ -297,10 +297,7 @@ class Being : public Actor void setTarget(Being *target) { mTarget = target; } - /** - * Overridden in order to reset the old position upon insertion. - */ - virtual void inserted(); + sigc::signal<void, Being *> signal_died; protected: /** @@ -332,6 +329,11 @@ class Being : public Actor Being(const Being &rhs); Being &operator=(const Being &rhs); + /** + * Connected to signal_inserted to reset the old position. + */ + void inserted(Entity *); + Path mPath; BeingDirection mDirection; /**< Facing direction. */ diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp index 15ba940a..a9777991 100644 --- a/src/game-server/character.cpp +++ b/src/game-server/character.cpp @@ -24,7 +24,6 @@ #include "game-server/accountconnection.h" #include "game-server/attributemanager.h" #include "game-server/buysell.h" -#include "game-server/eventlistener.h" #include "game-server/inventory.h" #include "game-server/item.h" #include "game-server/itemmanager.h" @@ -792,14 +791,7 @@ void Character::disconnected() else GameState::remove(this); - for (Listeners::iterator i = mListeners.begin(), - i_end = mListeners.end(); i != i_end;) - { - const EventListener &l = **i; - ++i; // In case the listener removes itself from the list on the fly. - if (l.dispatch->disconnected) - l.dispatch->disconnected(&l, this); - } + signal_disconnected.emit(this); } bool Character::takeSpecial(int id) diff --git a/src/game-server/character.h b/src/game-server/character.h index 5f3d3a64..d225081b 100644 --- a/src/game-server/character.h +++ b/src/game-server/character.h @@ -428,6 +428,8 @@ class Character : public Being virtual void removeAttack(AttackInfo *attackInfo); + sigc::signal<void, Character *> signal_disconnected; + protected: /** * Gets the way the actor blocks pathfinding for other objects diff --git a/src/game-server/entity.cpp b/src/game-server/entity.cpp index 671ef5e6..6cb61e58 100644 --- a/src/game-server/entity.cpp +++ b/src/game-server/entity.cpp @@ -18,51 +18,4 @@ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>. */ -#include <cassert> - #include "game-server/entity.h" - -#include "game-server/eventlistener.h" - -Entity::~Entity() -{ - /* 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 Entity::addListener(const EventListener *l) -{ - mListeners.insert(l); -} - -void Entity::removeListener(const EventListener *l) -{ - mListeners.erase(l); -} - -void Entity::inserted() -{ - for (Listeners::iterator i = mListeners.begin(), - i_end = mListeners.end(); i != i_end;) - { - const EventListener &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 Entity::removed() -{ - for (Listeners::iterator i = mListeners.begin(), - i_end = mListeners.end(); i != i_end;) - { - const EventListener &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/entity.h b/src/game-server/entity.h index 652db7c7..91f13699 100644 --- a/src/game-server/entity.h +++ b/src/game-server/entity.h @@ -23,18 +23,20 @@ #include "common/manaserv_protocol.h" -using namespace ManaServ; - #include <set> -class EventListener; +#include <sigc++/signal.h> +#include <sigc++/trackable.h> + +using namespace ManaServ; + class MapComposite; /** * Base class for in-game objects. Knows only its type and the map it resides * on. Provides listeners. */ -class Entity +class Entity : public sigc::trackable { public: Entity(EntityType type, MapComposite *map = 0) @@ -42,7 +44,7 @@ class Entity mType(type) {} - virtual ~Entity(); + virtual ~Entity() {} /** * Gets type of this entity. @@ -88,29 +90,8 @@ class Entity virtual void setMap(MapComposite *map) { mMap = map; } - /** - * Adds a new listener. - */ - void addListener(const EventListener *); - - /** - * Removes an existing listener. - */ - void removeListener(const EventListener *); - - /** - * Calls all the "inserted" listeners. - */ - virtual void inserted(); - - /** - * Calls all the "removed" listeners. - */ - virtual void removed(); - - protected: - typedef std::set< const EventListener * > Listeners; - Listeners mListeners; /**< List of event listeners. */ + sigc::signal<void, Entity *> signal_inserted; + sigc::signal<void, Entity *> signal_removed; private: MapComposite *mMap; /**< Map the entity is on */ diff --git a/src/game-server/eventlistener.h b/src/game-server/eventlistener.h deleted file mode 100644 index 53d92077..00000000 --- a/src/game-server/eventlistener.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * The Mana Server - * Copyright (C) 2007-2010 The Mana World Development Team - * - * This file is part of The Mana Server. - * - * The Mana Server 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 Server 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 Server. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef GAMESERVER_EVENTLISTENER_H -#define GAMESERVER_EVENTLISTENER_H - -class Entity; -class Being; -class Character; - -struct EventDispatch; - -/** - * Pointer to a dispatch table. - */ -struct EventListener -{ - const EventDispatch *dispatch; - EventListener(const EventDispatch *d): dispatch(d) {} -}; - -/** - * Dispatch table for event notification. - */ -struct EventDispatch -{ - /** - * Called just after something is inserted in a map. - */ - void (*inserted)(const EventListener *, Entity *); - - /** - * Called just before something is removed from a map. - */ - void (*removed)(const EventListener *, Entity *); - - /** - * Called just after a being has died. - */ - void (*died)(const EventListener *, Being *); - - /** - * Called just before a character is deleted. - */ - void (*disconnected)(const EventListener *, 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(const EventListener *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/monster.cpp b/src/game-server/monster.cpp index cdfe063a..330e3f4f 100644 --- a/src/game-server/monster.cpp +++ b/src/game-server/monster.cpp @@ -35,18 +35,6 @@ #include <cmath> -struct MonsterTargetEventDispatch: EventDispatch -{ - MonsterTargetEventDispatch() - { - typedef EventListenerFactory<Monster, &Monster::mTargetListener> Factory; - removed = &Factory::create< Entity, &Monster::forgetTarget >::function; - died = &Factory::create<Entity, &Monster::forgetTarget, Being>::function; - } -}; - -static MonsterTargetEventDispatch monsterTargetEventDispatch; - MonsterClass::~MonsterClass() { for (std::vector<AttackInfo *>::iterator it = mAttacks.begin(), @@ -67,7 +55,6 @@ double MonsterClass::getVulnerability(Element element) const Monster::Monster(MonsterClass *specy): Being(OBJECT_MONSTER), mSpecy(specy), - mTargetListener(&monsterTargetEventDispatch), mOwner(NULL) { LOG_DEBUG("Monster spawned! (id: " << mSpecy->getId() << ")."); @@ -137,12 +124,6 @@ Monster::Monster(MonsterClass *specy): Monster::~Monster() { - // Remove death listeners. - for (std::map<Being *, int>::iterator i = mAnger.begin(), - i_end = mAnger.end(); i != i_end; ++i) - { - i->first->removeListener(&mTargetListener); - } } void Monster::update() @@ -229,11 +210,11 @@ void Monster::refreshTarget() // Determine how much we hate the target int targetPriority = 0; - std::map<Being *, int, std::greater<Being *> >::iterator angerIterator; - angerIterator = mAnger.find(target); + std::map<Being *, AggressionInfo>::iterator angerIterator = mAnger.find(target); if (angerIterator != mAnger.end()) { - targetPriority = angerIterator->second; + const AggressionInfo &aggressionInfo = angerIterator->second; + targetPriority = aggressionInfo.anger; } else if (mSpecy->isAggressive()) { @@ -359,11 +340,15 @@ int Monster::calculatePositionPriority(Point position, int targetPriority) } } -void Monster::forgetTarget(Entity *t) +void Monster::forgetTarget(Entity *entity) { - Being *b = static_cast< Being * >(t); + Being *b = static_cast< Being * >(entity); + { + AggressionInfo &aggressionInfo = mAnger[b]; + aggressionInfo.removedConnection.disconnect(); + aggressionInfo.diedConnection.disconnect(); + } mAnger.erase(b); - b->removeListener(&mTargetListener); if (b->getType() == OBJECT_CHARACTER) { @@ -375,20 +360,42 @@ void Monster::forgetTarget(Entity *t) void Monster::changeAnger(Actor *target, int amount) { - if (target && (target->getType() == OBJECT_MONSTER - || target->getType() == OBJECT_CHARACTER)) + const EntityType type = target->getType(); + if (type != OBJECT_MONSTER && type != OBJECT_CHARACTER) + return; + + Being *being = static_cast< Being * >(target); + + if (mAnger.find(being) != mAnger.end()) { - Being *t = static_cast< Being * >(target); - if (mAnger.find(t) != mAnger.end()) - { - mAnger[t] += amount; - } - else - { - mAnger[t] = amount; - t->addListener(&mTargetListener); - } + mAnger[being].anger += amount; + } + else + { + AggressionInfo &aggressionInfo = mAnger[being]; + aggressionInfo.anger = amount; + + // Forget target either when it's removed or died, whichever + // happens first. + aggressionInfo.removedConnection = + being->signal_removed.connect(sigc::mem_fun(this, &Monster::forgetTarget)); + aggressionInfo.diedConnection = + being->signal_died.connect(sigc::mem_fun(this, &Monster::forgetTarget)); + } +} + +std::map<Being *, int> Monster::getAngerList() const +{ + std::map<Being *, int> result; + std::map<Being *, AggressionInfo>::const_iterator i, i_end; + + for (i = mAnger.begin(), i_end = mAnger.end(); i != i_end; ++i) + { + const AggressionInfo &aggressionInfo = i->second; + result.insert(std::make_pair(i->first, aggressionInfo.anger)); } + + return result; } int Monster::damage(Actor *source, const Damage &damage) @@ -399,9 +406,7 @@ int Monster::damage(Actor *source, const Damage &damage) newDamage.delta = newDamage.delta * factor; int HPLoss = Being::damage(source, newDamage); if (source) - { changeAnger(source, HPLoss); - } if (HPLoss && source && source->getType() == OBJECT_CHARACTER) { diff --git a/src/game-server/monster.h b/src/game-server/monster.h index 73fec6aa..3313345c 100644 --- a/src/game-server/monster.h +++ b/src/game-server/monster.h @@ -22,7 +22,6 @@ #define MONSTER_H #include "game-server/being.h" -#include "game-server/eventlistener.h" #include "common/defines.h" #include "scripting/script.h" #include "utils/string.h" @@ -31,6 +30,9 @@ #include <vector> #include <string> +#include <sigc++/connection.h> + +class Character; class ItemClass; class Script; @@ -315,8 +317,7 @@ class Monster : public Being */ void changeAnger(Actor *target, int amount); - const std::map<Being *, int> &getAngerList() const - { return mAnger; } + std::map<Being *, int> getAngerList() const; /** * Calls the damage function in Being and updates the aggro list @@ -326,7 +327,7 @@ class Monster : public Being /** * Removes a being from the anger list. */ - void forgetTarget(Entity *being); + void forgetTarget(Entity *entity); /** * Called when an attribute modifier is changed. @@ -351,10 +352,16 @@ class Monster : public Being MonsterClass *mSpecy; /** Aggression towards other beings. */ - std::map<Being *, int> mAnger; - - /** Listener for updating the anger list. */ - EventListener mTargetListener; + struct AggressionInfo { + AggressionInfo() + : anger(0) + {} + + int anger; + sigc::connection removedConnection; + sigc::connection diedConnection; + }; + std::map<Being *, AggressionInfo> mAnger; /** * Character who currently owns this monster (killsteal protection). diff --git a/src/game-server/quest.cpp b/src/game-server/quest.cpp index 4d659227..c28a4213 100644 --- a/src/game-server/quest.cpp +++ b/src/game-server/quest.cpp @@ -18,24 +18,27 @@ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>. */ -#include <cassert> -#include <list> -#include <map> -#include <string> - #include "game-server/quest.h" #include "game-server/accountconnection.h" #include "game-server/character.h" -#include "game-server/eventlistener.h" #include "utils/logger.h" +#include <cassert> +#include <list> +#include <map> +#include <string> + +#include <sigc++/connection.h> + typedef std::list< QuestCallback * > QuestCallbacks; typedef std::map< std::string, QuestCallbacks > PendingVariables; struct PendingQuest { Character *character; + sigc::connection removedConnection; + sigc::connection disconnectedConnection; PendingVariables variables; }; @@ -72,22 +75,6 @@ void setQuestVar(Character *ch, const std::string &name, accountHandler->updateCharacterVar(ch, name, value); } -/** - * Listener for deleting related quests when a character disappears. - */ -struct QuestDeathListener: EventDispatch -{ - static void partialRemove(const EventListener *, Entity *); - - static void fullRemove(const EventListener *, Character *); - - QuestDeathListener() - { - removed = &partialRemove; - disconnected = &fullRemove; - } -}; - void QuestRefCallback::triggerCallback(Character *ch, const std::string &value) const { @@ -103,10 +90,7 @@ void QuestRefCallback::triggerCallback(Character *ch, s->execute(); } -static QuestDeathListener questDeathDummy; -static EventListener questDeathListener(&questDeathDummy); - -void QuestDeathListener::partialRemove(const EventListener *, Entity *t) +static void partialRemove(Entity *t) { int id = static_cast< Character * >(t)->getDatabaseID(); PendingVariables &variables = pendingQuests[id].variables; @@ -119,11 +103,18 @@ void QuestDeathListener::partialRemove(const EventListener *, Entity *t) // The listener is kept in case a fullRemove is needed later. } -void QuestDeathListener::fullRemove(const EventListener *, Character *ch) +static void fullRemove(Character *ch) { - ch->removeListener(&questDeathListener); + int id = ch->getDatabaseID(); + + { + PendingQuest &pendingQuest = pendingQuests[id]; + pendingQuest.removedConnection.disconnect(); + pendingQuest.disconnectedConnection.disconnect(); + } + // Remove anything related to this character. - pendingQuests.erase(ch->getDatabaseID()); + pendingQuests.erase(id); } void recoverQuestVar(Character *ch, const std::string &name, @@ -134,11 +125,19 @@ void recoverQuestVar(Character *ch, const std::string &name, PendingQuests::iterator i = pendingQuests.lower_bound(id); if (i == pendingQuests.end() || i->first != id) { - i = pendingQuests.insert(i, std::make_pair(id, PendingQuest())); - i->second.character = ch; - /* Register a listener, because we cannot afford to get invalid - pointers, when we finally recover the variable. */ - ch->addListener(&questDeathListener); + PendingQuest pendingQuest; + pendingQuest.character = ch; + + /* Connect to removed and disconnected signals, because we cannot + * afford to get invalid pointers, when we finally recover the + * variable. + */ + pendingQuest.removedConnection = + ch->signal_removed.connect(sigc::ptr_fun(partialRemove)); + pendingQuest.disconnectedConnection = + ch->signal_disconnected.connect(sigc::ptr_fun(fullRemove)); + + i = pendingQuests.insert(i, std::make_pair(id, pendingQuest)); } i->second.variables[name].push_back(f); accountHandler->requestCharacterVar(ch, name); @@ -149,12 +148,14 @@ void recoveredQuestVar(int id, const std::string &value) { PendingQuests::iterator i = pendingQuests.find(id); - if (i == pendingQuests.end()) return; + if (i == pendingQuests.end()) + return; - Character *ch = i->second.character; - ch->removeListener(&questDeathListener); + PendingQuest &pendingQuest = i->second; + pendingQuest.removedConnection.disconnect(); + pendingQuest.disconnectedConnection.disconnect(); - PendingVariables &variables = i->second.variables; + PendingVariables &variables = pendingQuest.variables; PendingVariables::iterator j = variables.find(name); if (j == variables.end()) { @@ -162,6 +163,7 @@ void recoveredQuestVar(int id, return; } + Character *ch = pendingQuest.character; ch->questCache[name] = value; // Call the registered callbacks. diff --git a/src/game-server/spawnarea.cpp b/src/game-server/spawnarea.cpp index 155d4c0d..73dca6b2 100644 --- a/src/game-server/spawnarea.cpp +++ b/src/game-server/spawnarea.cpp @@ -25,18 +25,6 @@ #include "game-server/state.h" #include "utils/logger.h" -struct SpawnAreaEventDispatch : EventDispatch -{ - SpawnAreaEventDispatch() - { - typedef EventListenerFactory< SpawnArea, &SpawnArea::mSpawnedListener > - Factory; - removed = &Factory::create< Entity, &SpawnArea::decrease >::function; - } -}; - -static SpawnAreaEventDispatch spawnAreaEventDispatch; - SpawnArea::SpawnArea(MapComposite *map, MonsterClass *specy, const Rectangle &zone, @@ -44,7 +32,6 @@ SpawnArea::SpawnArea(MapComposite *map, int spawnRate): Entity(OBJECT_OTHER, map), mSpecy(specy), - mSpawnedListener(&spawnAreaEventDispatch), mZone(zone), mMaxBeings(maxBeings), mSpawnRate(spawnRate), @@ -102,7 +89,9 @@ void SpawnArea::update() if (c) { - being->addListener(&mSpawnedListener); + being->signal_removed.connect( + sigc::mem_fun(this, &SpawnArea::decrease)); + being->setMap(map); being->setPosition(position); being->clearDestination(); @@ -125,8 +114,7 @@ void SpawnArea::update() } } -void SpawnArea::decrease(Entity *t) +void SpawnArea::decrease(Entity *) { --mNumBeings; - t->removeListener(&mSpawnedListener); } diff --git a/src/game-server/spawnarea.h b/src/game-server/spawnarea.h index cc0642f1..628c072e 100644 --- a/src/game-server/spawnarea.h +++ b/src/game-server/spawnarea.h @@ -21,7 +21,6 @@ #ifndef SPAWNAREA_H #define SPAWNAREA_H -#include "game-server/eventlistener.h" #include "game-server/entity.h" #include "utils/point.h" @@ -47,7 +46,6 @@ class SpawnArea : public Entity 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. */ diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp index ede94f86..a688caca 100644 --- a/src/game-server/state.cpp +++ b/src/game-server/state.cpp @@ -498,7 +498,7 @@ bool GameState::insert(Entity *ptr) if (!ptr->isVisible()) { map->insert(ptr); - ptr->inserted(); + ptr->signal_inserted.emit(ptr); return true; } @@ -523,7 +523,7 @@ bool GameState::insert(Entity *ptr) return false; } - obj->inserted(); + obj->signal_inserted.emit(obj); // DEBUG INFO switch (obj->getType()) @@ -596,7 +596,7 @@ void GameState::remove(Entity *ptr) MapComposite *map = ptr->getMap(); int visualRange = Configuration::getValue("game_visualRange", 448); - ptr->removed(); + ptr->signal_removed.emit(ptr); // DEBUG INFO switch (ptr->getType()) diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 545e365e..880216d2 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -1709,7 +1709,11 @@ static int chr_get_post(lua_State *s) static int being_register(lua_State *s) { Being *being = checkBeing(s, 1); - being->addListener(getScript(s)->getScriptListener()); + Script *script = getScript(s); + + being->signal_died.connect(sigc::mem_fun(script, &Script::processDeathEvent)); + being->signal_removed.connect(sigc::mem_fun(script, &Script::processRemoveEvent)); + return 0; } diff --git a/src/scripting/luascript.cpp b/src/scripting/luascript.cpp index 4104bc89..f4ea39ac 100644 --- a/src/scripting/luascript.cpp +++ b/src/scripting/luascript.cpp @@ -265,8 +265,6 @@ void LuaScript::processRemoveEvent(Entity *entity) // being. This might be very interesting for scripting quests. execute(); } - - entity->removeListener(getScriptListener()); } /** diff --git a/src/scripting/luascript.h b/src/scripting/luascript.h index 7631d982..9515bf0e 100644 --- a/src/scripting/luascript.h +++ b/src/scripting/luascript.h @@ -28,6 +28,8 @@ extern "C" { #include "scripting/script.h" +class Character; + /** * Implementation of the Script class for Lua. */ diff --git a/src/scripting/script.cpp b/src/scripting/script.cpp index 3e299461..63ab7ff4 100644 --- a/src/scripting/script.cpp +++ b/src/scripting/script.cpp @@ -40,8 +40,7 @@ Script::Ref Script::mUpdateCallback; Script::Script(): mCurrentThread(0), - mMap(0), - mEventListener(&scriptEventDispatch) + mMap(0) {} Script::~Script() diff --git a/src/scripting/script.h b/src/scripting/script.h index 238bc34c..8dee23a9 100644 --- a/src/scripting/script.h +++ b/src/scripting/script.h @@ -23,19 +23,21 @@ #include "common/inventorydata.h" #include "common/manaserv_protocol.h" -#include "game-server/eventlistener.h" #include <list> #include <string> #include <vector> +#include <sigc++/trackable.h> + +class Being; class MapComposite; class Entity; /** * Abstract interface for calling functions written in an external language. */ -class Script +class Script : public sigc::trackable { public: /** @@ -216,9 +218,6 @@ class Script MapComposite *getMap() const { return mMap; } - EventListener *getScriptListener() - { return &mEventListener; } - virtual void processDeathEvent(Being *entity) = 0; virtual void processRemoveEvent(Entity *entity) = 0; @@ -235,7 +234,6 @@ class Script private: MapComposite *mMap; - EventListener mEventListener; /**< Tracking of being deaths. */ std::vector<Thread*> mThreads; static Ref mCreateNpcDelayedCallback; @@ -245,16 +243,4 @@ class Script friend class Thread; }; -struct ScriptEventDispatch: EventDispatch -{ - ScriptEventDispatch() - { - typedef EventListenerFactory< Script, &Script::mEventListener > Factory; - died = &Factory::create< Being, &Script::processDeathEvent >::function; - removed = &Factory::create< Entity, &Script::processRemoveEvent >::function; - } -}; - -static ScriptEventDispatch scriptEventDispatch; - #endif // SCRIPTING_SCRIPT_H |