diff options
author | Yohann Ferreira <yohann_dot_ferreira_at_orange_dot_efer> | 2012-01-10 01:43:59 +0100 |
---|---|---|
committer | Yohann Ferreira <yohann_dot_ferreira_at_orange_dot_efer> | 2012-01-10 20:42:24 +0100 |
commit | 8172b66469a257b61769e0ce69baa1a0f75eb785 (patch) | |
tree | e19beffb3d3b92703d347f7fd70137a0a2b44e2f /src | |
parent | d4ee26fb6b05c07c84f61799bcb2cba81873b873 (diff) | |
download | manaserv-8172b66469a257b61769e0ce69baa1a0f75eb785.tar.gz manaserv-8172b66469a257b61769e0ce69baa1a0f75eb785.tar.bz2 manaserv-8172b66469a257b61769e0ce69baa1a0f75eb785.tar.xz manaserv-8172b66469a257b61769e0ce69baa1a0f75eb785.zip |
Made the game server execute the chr_respawn_accept script even
in case of disconnection.
I made the Character::disconnected() function handle that case,
permitting also to centralize GameState::remove() calls there.
I also made the GameState::enqueueWarp() function test whether
the Character pointer is about to be deleted, so that the warp
can be handled directly to avoid a crash.
Last but not least, I also made the Character::update() function
not update the Character specials and hp to avoid discrepancies
seen in the client.
Resolves: Mana-Mantis #309.
Reviewed-by: Ablu.
Diffstat (limited to 'src')
-rw-r--r-- | src/game-server/character.cpp | 42 | ||||
-rw-r--r-- | src/game-server/character.h | 10 | ||||
-rw-r--r-- | src/game-server/gamehandler.cpp | 6 | ||||
-rw-r--r-- | src/game-server/state.cpp | 13 |
4 files changed, 52 insertions, 19 deletions
diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp index 9867b582..70dd3444 100644 --- a/src/game-server/character.cpp +++ b/src/game-server/character.cpp @@ -55,6 +55,7 @@ const float Character::EXP_LEVEL_FLEXIBILITY = 1.0f; Character::Character(MessageIn &msg): Being(OBJECT_CHARACTER), mClient(NULL), + mConnected(true), mTransactionHandler(NULL), mRechargePerSpecial(0), mSpecialUpdateNeeded(false), @@ -94,13 +95,20 @@ Character::Character(MessageIn &msg): void Character::update() { - // Update character level + // First, deal with being generic updates + Being::update(); + + // Update character level if needed. if (mRecalculateLevel) { mRecalculateLevel = false; recalculateLevel(); } + // Dead character: don't regenerate anything else + if (getAction() == DEAD) + return; + // Update special recharge std::list<Special *> rechargeNeeded; int numRechargeNeeded = 0; @@ -138,7 +146,6 @@ void Character::update() mStatusEffects[it->first] = it->second.time; it++; } - Being::update(); } void Character::perform() @@ -208,19 +215,18 @@ void Character::respawn() mTarget = NULL; // Execute respawn script - if (!Script::executeGlobalEventFunction("on_chr_death_accept", this)) - { - // Script-controlled respawning didn't work - fall back to - // hardcoded logic. - mAttributes[ATTR_HP].setBase(mAttributes[ATTR_MAX_HP].getModifiedAttribute()); - updateDerivedAttributes(ATTR_HP); - // Warp back to spawn point. - int spawnMap = Configuration::getValue("char_respawnMap", 1); - int spawnX = Configuration::getValue("char_respawnX", 1024); - int spawnY = Configuration::getValue("char_respawnY", 1024); - GameState::enqueueWarp(this, MapManager::getMap(spawnMap), spawnX, spawnY); - } + if (Script::executeGlobalEventFunction("on_chr_death_accept", this)) + return; + + // Script-controlled respawning didn't work - fall back to hardcoded logic. + mAttributes[ATTR_HP].setBase(mAttributes[ATTR_MAX_HP].getModifiedAttribute()); + updateDerivedAttributes(ATTR_HP); + // Warp back to spawn point. + int spawnMap = Configuration::getValue("char_respawnMap", 1); + int spawnX = Configuration::getValue("char_respawnX", 1024); + int spawnY = Configuration::getValue("char_respawnY", 1024); + GameState::enqueueWarp(this, MapManager::getMap(spawnMap), spawnX, spawnY); } void Character::useSpecial(int id) @@ -664,6 +670,14 @@ AttribmodResponseCode Character::useCorrectionPoint(size_t attribute) void Character::disconnected() { + mConnected = false; + + // Make the dead characters respawn, even in case of disconnection. + if (getAction() == DEAD) + respawn(); + else + GameState::remove(this); + for (Listeners::iterator i = mListeners.begin(), i_end = mListeners.end(); i != i_end;) { diff --git a/src/game-server/character.h b/src/game-server/character.h index dc452ba2..7777217d 100644 --- a/src/game-server/character.h +++ b/src/game-server/character.h @@ -358,6 +358,9 @@ class Character : public Being bool isMuted() const { return isTimerRunning(T_C_MUTE); } + bool isConnected() const + { return mConnected; } + protected: /** * Gets the way the actor blocks pathfinding for other objects @@ -416,6 +419,13 @@ class Character : public Being { TRANS_NONE, TRANS_TRADE, TRANS_BUYSELL }; GameClient *mClient; /**< Client computer. */ + + /** + * Tells whether the character client is connected. + * Useful when dealing with enqueued events. + */ + bool mConnected; + /** Handler of the transaction the character is involved in. */ void *mTransactionHandler; diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp index af6cff1c..cb6c820f 100644 --- a/src/game-server/gamehandler.cpp +++ b/src/game-server/gamehandler.cpp @@ -72,7 +72,6 @@ void GameHandler::computerDisconnected(NetComputer *comp) else if (Character *ch = computer.character) { accountHandler->sendCharacterData(ch); - GameState::remove(ch); ch->disconnected(); delete ch; } @@ -695,12 +694,9 @@ void GameHandler::handleDisconnect(GameClient &client, MessageIn &message) client.character->getDatabaseID(), magic_token); } - // TODO: implement a delayed remove - GameState::remove(client.character); - accountHandler->sendCharacterData(client.character); - // Done with the character + // Done with the character, also handle possible respawn case client.character->disconnected(); delete client.character; client.character = 0; diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp index 67130070..1d72e201 100644 --- a/src/game-server/state.cpp +++ b/src/game-server/state.cpp @@ -720,6 +720,11 @@ void GameState::warp(Character *ptr, MapComposite *map, int x, int y) a disconnection. */ accountHandler->sendCharacterData(ptr); + // If the player has just left, The character pointer is also about + // to be deleted. So we don't have to do anything else. + if (!ptr->isConnected()) + return; + if (map->isActive()) { if (!insert(ptr)) @@ -766,6 +771,14 @@ void GameState::enqueueRemove(Actor *ptr) void GameState::enqueueWarp(Character *ptr, MapComposite *m, int x, int y) { + // When the player has just disconnected, better not wait for the pointer + // to become invalid. + if (!ptr->isConnected()) + { + warp(ptr, m, x, y); + return; + } + DelayedEvent e = { EVENT_WARP, x, y, m }; enqueueEvent(ptr, e); } |