summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYohann Ferreira <yohann_dot_ferreira_at_orange_dot_efer>2012-01-10 01:43:59 +0100
committerYohann Ferreira <yohann_dot_ferreira_at_orange_dot_efer>2012-01-10 20:42:24 +0100
commit8172b66469a257b61769e0ce69baa1a0f75eb785 (patch)
treee19beffb3d3b92703d347f7fd70137a0a2b44e2f
parentd4ee26fb6b05c07c84f61799bcb2cba81873b873 (diff)
downloadmanaserv-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.
-rw-r--r--src/game-server/character.cpp42
-rw-r--r--src/game-server/character.h10
-rw-r--r--src/game-server/gamehandler.cpp6
-rw-r--r--src/game-server/state.cpp13
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);
}