From 775404c84c3250225d43f10c4a5363e997618cb2 Mon Sep 17 00:00:00 2001
From: Rogier Polak <rogier.l.a.polak@gmail.com>
Date: Fri, 23 Feb 2007 19:18:28 +0000
Subject: Added unregistering, logout_then_exit, switch_character and
 switch_accountserver.

---
 ChangeLog                               |  20 ++-
 src/Makefile.am                         |   6 +
 src/game.cpp                            |  42 ++----
 src/gui/char_select.cpp                 |  23 +++-
 src/gui/char_select.h                   |   8 +-
 src/gui/login.cpp                       |   2 +-
 src/gui/quitdialog.cpp                  | 129 ++++++++++++++++++
 src/gui/quitdialog.h                    |  71 ++++++++++
 src/gui/serverdialog.cpp                |   2 +-
 src/gui/unregisterdialog.cpp            | 171 ++++++++++++++++++++++++
 src/gui/unregisterdialog.h              |  73 ++++++++++
 src/lockedarray.h                       |  27 +++-
 src/logindata.h                         |   9 ++
 src/main.cpp                            | 230 +++++++++++++++++++++++++++++---
 src/main.h                              |  13 +-
 src/net/accountserver/account.cpp       |   7 +-
 src/net/accountserver/account.h         |   5 +-
 src/net/accountserver/accountserver.cpp |  10 +-
 src/net/accountserver/accountserver.h   |   3 +
 src/net/chatserver/chatserver.cpp       |   7 +
 src/net/chatserver/chatserver.h         |   2 +
 src/net/connection.cpp                  |   3 +-
 src/net/gameserver/gameserver.cpp       |   9 ++
 src/net/gameserver/gameserver.h         |   2 +
 src/net/loginhandler.cpp                |  30 +++++
 src/net/logouthandler.cpp               | 216 ++++++++++++++++++++++++++++++
 src/net/logouthandler.h                 |  63 +++++++++
 src/net/protocol.h                      |  10 +-
 28 files changed, 1127 insertions(+), 66 deletions(-)
 create mode 100644 src/gui/quitdialog.cpp
 create mode 100644 src/gui/quitdialog.h
 create mode 100644 src/gui/unregisterdialog.cpp
 create mode 100644 src/gui/unregisterdialog.h
 create mode 100644 src/net/logouthandler.cpp
 create mode 100644 src/net/logouthandler.h

diff --git a/ChangeLog b/ChangeLog
index f33e6a6b..d2a98990 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,20 @@
-2007-01-30  Bjørn Lindeijer  <bjorn@lindeijer.nl>
+2007-02-23  Rogier Polak  <rogier.l.a.polak@gmail.com>
+
+	* src/gui/char_select.h, src/gui/char_select.cpp,
+	src/gui/unregisterdialog.h, src/gui/unregisterdialog.cpp,
+	src/net/accountserver/account.h, src/net/accountserver/account.cpp,
+	src/gui/quitdialog.h, src/gui/quitdialog.cpp,
+	src/net/accountserver/accountserver.h,
+	src/net/accountserver/accountserver.cpp, src/net/loginhandler.cpp,
+	src/net/logouthandler.h, src/net/logouthandler.cpp, src/net/protocol.h,
+	src/net/gameserver/gameserver.h, src/net/gameserver/gameserver.cpp,
+	src/game.cpp, src/main.h, src/main.cpp: Added unregistering,
+	logout_then_exit, switch_character and switch_accountserver.
+	* src/lockedarray.h: Added a clear function that keeps the original
+	size and data type.
+	* src/logindata.h: Added a clear function.
+
+2007-01-30  Bjørn Lindeijer  <bjorn@lindeijer.nl>
 
 	* src/properties.h, src/being.cpp, src/beingmanager.h, src/sound.h,
 	src/gui/window.h, src/sound.cpp, src/main.h, src/being.h,
@@ -314,7 +330,7 @@
 
 2006-12-15  Philipp Sehmisch  <tmw@crushnet.org>
 
-	* data/graphics/tiles/desert1.png: Removed some unused legacy tiles and 
+	* data/graphics/tiles/desert1.png: Removed some unused legacy tiles and
 	added variant tiles for the cliffs.
 
 2006-12-14  Bjørn Lindeijer  <bjorn@lindeijer.nl>
diff --git a/src/Makefile.am b/src/Makefile.am
index 777cc30e..cb0345e5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -72,6 +72,8 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \
 	      gui/popupmenu.h \
 	      gui/progressbar.cpp \
 	      gui/progressbar.h \
+	      gui/quitdialog.cpp \
+	      gui/quitdialog.h \
 	      gui/radiobutton.cpp \
 	      gui/radiobutton.h \
 	      gui/register.cpp \
@@ -110,6 +112,8 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \
 	      gui/textfield.h \
 	      gui/trade.cpp \
 	      gui/trade.h \
+	      gui/unregisterdialog.cpp \
+	      gui/unregisterdialog.h \
 	      gui/viewport.cpp \
 	      gui/viewport.h \
 	      gui/window.cpp \
@@ -146,6 +150,8 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \
 	      net/itemhandler.cpp \
 	      net/loginhandler.h \
 	      net/loginhandler.cpp \
+	      net/logouthandler.cpp \
+	      net/logouthandler.h \
 	      net/messagehandler.cpp \
 	      net/messagehandler.h \
 	      net/messagein.cpp \
diff --git a/src/game.cpp b/src/game.cpp
index 40d78248..bc81add5 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -62,6 +62,7 @@
 #include "gui/status.h"
 #include "gui/trade.h"
 #include "gui/viewport.h"
+#include "gui/quitdialog.h"
 
 #include "net/beinghandler.h"
 #include "net/buysellhandler.h"
@@ -92,7 +93,7 @@ Joystick *joystick = NULL;
 
 extern Window *weightNotice;
 extern Window *deathNotice;
-ConfirmDialog *exitConfirm = NULL;
+QuitDialog *quitDialog = NULL;
 
 ChatWindow *chatWindow;
 MenuWindow *menuWindow;
@@ -120,22 +121,6 @@ FloorItemManager *floorItemManager = NULL;
 
 const int MAX_TIME = 10000;
 
-/**
- * Listener used for exitting handling.
- */
-namespace {
-    struct ExitListener : public gcn::ActionListener
-    {
-        void action(const gcn::ActionEvent &event)
-        {
-            if (event.getId() == "yes") {
-                done = true;
-            }
-            exitConfirm = NULL;
-        }
-    } exitListener;
-}
-
 /**
  * Advances game logic counter.
  */
@@ -245,6 +230,8 @@ Game::Game():
     mSkillHandler(new SkillHandler()),
     mTradeHandler(new TradeHandler())
 {
+    done = false;
+
     createGuiWindows();
     engine = new Engine;
 
@@ -285,6 +272,8 @@ Game::Game():
 
 Game::~Game()
 {
+    Net::clearHandlers();
+
     delete engine;
     delete player_node;
     destroyGuiWindows();
@@ -440,13 +429,8 @@ void Game::handleInput()
                         break;
                     }
 
-                    // Quit by pressing Enter if the exit confirm is there
-                    if (exitConfirm)
-                    {
-                        done = true;
-                    }
                     // Close the Browser if opened
-                    else if (helpWindow->isVisible())
+                    if (helpWindow->isVisible())
                     {
                         helpWindow->setVisible(false);
                     }
@@ -522,12 +506,14 @@ void Game::handleInput()
 
                     // Quitting confirmation dialog
                 case SDLK_ESCAPE:
-                    if (!exitConfirm) {
-                        exitConfirm = new ConfirmDialog(
-                                "Quit", "Are you sure you want to quit?");
-                        exitConfirm->addActionListener(&exitListener);
+                    if (!quitDialog)
+                    {
+                        quitDialog = new QuitDialog(&done, &quitDialog);
+                    }
+                    else
+                    {
+                        quitDialog->requestMoveToTop();
                     }
-                    exitConfirm->requestMoveToTop();
                     break;
 
                 default:
diff --git a/src/gui/char_select.cpp b/src/gui/char_select.cpp
index 4c4b99e5..5e329598 100644
--- a/src/gui/char_select.cpp
+++ b/src/gui/char_select.cpp
@@ -33,9 +33,12 @@
 #include "playerbox.h"
 #include "textfield.h"
 
+#include "unregisterdialog.h"
+
 #include "../game.h"
 #include "../localplayer.h"
 #include "../main.h"
+#include "../logindata.h"
 
 #include "../net/accountserver/account.h"
 
@@ -54,8 +57,8 @@ class CharDeleteConfirm : public ConfirmDialog
 };
 
 CharDeleteConfirm::CharDeleteConfirm(CharSelectDialog *m):
-    ConfirmDialog("Confirm", "Are you sure you want to delete this character?",
-            m),
+    ConfirmDialog("Confirm",
+                  "Are you sure you want to delete this character?", m),
     master(m)
 {
 }
@@ -69,16 +72,19 @@ void CharDeleteConfirm::action(const gcn::ActionEvent &event)
     ConfirmDialog::action(event);
 }
 
-CharSelectDialog::CharSelectDialog(LockedArray<LocalPlayer*> *charInfo):
+CharSelectDialog::CharSelectDialog(LockedArray<LocalPlayer*> *charInfo,
+                                   LoginData *loginData):
     Window("Select Character"),
-    mCharInfo(charInfo), mCharSelected(false)
+    mCharInfo(charInfo), mCharSelected(false), mLoginData(loginData)
 {
+
     mSelectButton = new Button("Ok", "ok", this);
     mCancelButton = new Button("Cancel", "cancel", this);
     mNewCharButton = new Button("New", "new", this);
     mDelCharButton = new Button("Delete", "delete", this);
     mPreviousButton = new Button("Previous", "previous", this);
     mNextButton = new Button("Next", "next", this);
+    mUnRegisterButton = new Button("Unregister", "unregister", this);
 
     mNameLabel = new gcn::Label("Name");
     mLevelLabel = new gcn::Label("Level");
@@ -104,10 +110,14 @@ CharSelectDialog::CharSelectDialog(LockedArray<LocalPlayer*> *charInfo):
     mSelectButton->setPosition(
             mCancelButton->getX() - 5 - mSelectButton->getWidth(),
             mNewCharButton->getY());
+    mUnRegisterButton->setPosition(
+            w - 5 - mUnRegisterButton->getWidth(),
+            mCancelButton->getY() - 5 - mUnRegisterButton->getHeight());
 
     add(mPlayerBox);
     add(mSelectButton);
     add(mCancelButton);
+    add(mUnRegisterButton);
     add(mNewCharButton);
     add(mDelCharButton);
     add(mPreviousButton);
@@ -130,6 +140,7 @@ void CharSelectDialog::action(const gcn::ActionEvent &event)
         mNewCharButton->setEnabled(false);
         mDelCharButton->setEnabled(false);
         mSelectButton->setEnabled(false);
+        mUnRegisterButton->setEnabled(false);
         mPreviousButton->setEnabled(false);
         mNextButton->setEnabled(false);
         mCharSelected = true;
@@ -166,6 +177,10 @@ void CharSelectDialog::action(const gcn::ActionEvent &event)
     {
         mCharInfo->next();
     }
+    else if (event.getId() == "unregister")
+    {
+        new UnRegisterDialog(this, mLoginData);
+    }
 }
 
 void CharSelectDialog::updatePlayerInfo()
diff --git a/src/gui/char_select.h b/src/gui/char_select.h
index dbce2cbf..7136f301 100644
--- a/src/gui/char_select.h
+++ b/src/gui/char_select.h
@@ -31,6 +31,8 @@
 
 #include <guichan/actionlistener.hpp>
 
+#include "../logindata.h"
+
 class Player;
 class LocalPlayer;
 class PlayerBox;
@@ -47,7 +49,8 @@ class CharSelectDialog : public Window, public gcn::ActionListener
         /**
          * Constructor.
          */
-        CharSelectDialog(LockedArray<LocalPlayer*> *charInfo);
+        CharSelectDialog(LockedArray<LocalPlayer*> *charInfo,
+                         LoginData *loginData);
 
         void action(const gcn::ActionEvent &event);
 
@@ -71,6 +74,7 @@ class CharSelectDialog : public Window, public gcn::ActionListener
         gcn::Button *mDelCharButton;
         gcn::Button *mPreviousButton;
         gcn::Button *mNextButton;
+        gcn::Button *mUnRegisterButton;
 
         gcn::Label *mNameLabel;
         gcn::Label *mLevelLabel;
@@ -80,6 +84,7 @@ class CharSelectDialog : public Window, public gcn::ActionListener
 
         bool mCharSelected;
 
+        LoginData *mLoginData;
         /**
          * Communicate character deletion to the server.
          */
@@ -130,6 +135,7 @@ class CharCreateDialog : public Window, public gcn::ActionListener
 
         int mSlot;
 
+
         /**
          * Communicate character creation to the server.
          */
diff --git a/src/gui/login.cpp b/src/gui/login.cpp
index 664074aa..9df3b489 100644
--- a/src/gui/login.cpp
+++ b/src/gui/login.cpp
@@ -144,7 +144,7 @@ LoginDialog::action(const gcn::ActionEvent &event)
     }
     else if (event.getId() == "cancel")
     {
-        state = STATE_EXIT;
+        state = STATE_FORCE_QUIT;
     }
     else if (event.getId() == "register")
     {
diff --git a/src/gui/quitdialog.cpp b/src/gui/quitdialog.cpp
new file mode 100644
index 00000000..97be5f46
--- /dev/null
+++ b/src/gui/quitdialog.cpp
@@ -0,0 +1,129 @@
+/*
+ *  The Mana World
+ *  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
+ *
+ */
+
+#include "quitdialog.h"
+#include <iostream>
+#include <string>
+
+#include <guichan/widgets/label.hpp>
+
+#include "../main.h"
+
+#include "button.h"
+#include "radiobutton.h"
+
+QuitDialog::QuitDialog(bool* quitGame, QuitDialog** pointerToMe):
+    Window("Quit", true, NULL), mQuitGame(quitGame), mMyPointer(pointerToMe)
+{
+
+    mLogoutQuit = new RadioButton("Quit", "quitdialog");
+    mForceQuit = new RadioButton("Quit", "quitdialog");
+    mSwitchAccountServer = new RadioButton("Switch server", "quitdialog");
+    mSwitchCharacter = new RadioButton("Switch character", "quitdialog");
+    mOkButton = new Button("OK", "ok", this);
+    mCancelButton = new Button("Cancel", "cancel", this);
+
+    setContentSize(200, 91);
+
+    mLogoutQuit->setPosition(5, 5);
+    mForceQuit->setPosition(5, 5);
+    mSwitchAccountServer->setPosition(5, 14 + mLogoutQuit->getHeight());
+    mSwitchCharacter->setPosition(5,
+           23 + mLogoutQuit->getHeight() + mSwitchAccountServer->getHeight());
+    mCancelButton->setPosition(
+           200 - mCancelButton->getWidth() - 5,
+           91 - mCancelButton->getHeight() - 5);
+    mOkButton->setPosition(
+           mCancelButton->getX() - mOkButton->getWidth() - 5,
+           91 - mOkButton->getHeight() - 5);
+
+    //All states, when we're not logged in to someone.
+    if (state == STATE_CHOOSE_SERVER ||
+        state == STATE_CONNECT_ACCOUNT ||
+        state == STATE_LOGIN ||
+        state == STATE_LOGIN_ATTEMPT ||
+        state == STATE_UPDATE)
+    {
+        mForceQuit->setMarked(true);
+        add(mForceQuit);
+    }
+    else
+    {
+        // Only added if we are connected to an accountserver or gameserver
+        mLogoutQuit->setMarked(true);
+        add(mLogoutQuit);
+        add(mSwitchAccountServer);
+
+        // Only added if we are connected to a gameserver
+        if (state == STATE_GAME) add(mSwitchCharacter);
+    }
+
+    add(mOkButton);
+    add(mCancelButton);
+
+    setLocationRelativeTo(getParent());
+    setVisible(true);
+
+    mOkButton->requestFocus();
+
+}
+
+QuitDialog::~QuitDialog()
+{
+    if (mMyPointer) *mMyPointer = NULL;
+}
+
+void
+QuitDialog::action(const gcn::ActionEvent &event)
+{
+    if (event.getId() == "ok")
+    {
+        if (mForceQuit->isMarked())
+        {
+            state = STATE_FORCE_QUIT;
+        }
+        else if (mLogoutQuit->isMarked())
+        {
+            if ((state == STATE_GAME) && (mQuitGame))
+            {
+                *mQuitGame = true;
+            }
+            state = STATE_EXIT;
+        }
+        else if (mSwitchAccountServer->isMarked())
+        {
+            if ((state == STATE_GAME) && (mQuitGame))
+            {
+                *mQuitGame = true;
+            }
+            state = STATE_SWITCH_ACCOUNTSERVER_ATTEMPT;
+        }
+        else if (mSwitchCharacter->isMarked())
+        {
+            if (mQuitGame) *mQuitGame = true;
+
+            state = STATE_SWITCH_CHARACTER;
+        }
+
+    }
+    scheduleDelete();
+}
diff --git a/src/gui/quitdialog.h b/src/gui/quitdialog.h
new file mode 100644
index 00000000..97a03f2e
--- /dev/null
+++ b/src/gui/quitdialog.h
@@ -0,0 +1,71 @@
+/*
+ *  The Mana World
+ *  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
+ *
+ */
+
+#ifndef _TMW_QUITDIALOG_H
+#define _TMW_QUITDIALOG_H
+
+#include <iosfwd>
+#include <guichan/actionlistener.hpp>
+
+#include "window.h"
+#include "../guichanfwd.h"
+#include "../main.h"
+
+/**
+ * The quit dialog.
+ *
+ * \ingroup Interface
+ */
+class QuitDialog : public Window, public gcn::ActionListener {
+    public:
+        /**
+         * Constructor
+         *
+         * @quitGame;    to be used for getting out of the while loop in Game
+         * @pointerToMe; will be set to NULL when the QuitDialog is destroyed
+         */
+        QuitDialog(bool* quitGame, QuitDialog** pointerToMe);
+
+        /**
+         * Destructor
+         */
+        ~QuitDialog();
+
+        /**
+         * Called when receiving actions from the widgets.
+         */
+        void action(const gcn::ActionEvent &event);
+
+    private:
+        gcn::RadioButton *mLogoutQuit;
+        gcn::RadioButton *mForceQuit;
+        gcn::RadioButton *mSwitchAccountServer;
+        gcn::RadioButton *mSwitchCharacter;
+        gcn::Button *mOkButton;
+        gcn::Button *mCancelButton;
+
+        bool* mQuitGame;
+        QuitDialog** mMyPointer;
+
+};
+
+#endif
diff --git a/src/gui/serverdialog.cpp b/src/gui/serverdialog.cpp
index bf29f0d3..b47ce749 100644
--- a/src/gui/serverdialog.cpp
+++ b/src/gui/serverdialog.cpp
@@ -243,6 +243,6 @@ ServerDialog::action(const gcn::ActionEvent &event)
     }
     else if (event.getId() == "cancel")
     {
-        state = STATE_EXIT;
+        state = STATE_FORCE_QUIT;
     }
 }
diff --git a/src/gui/unregisterdialog.cpp b/src/gui/unregisterdialog.cpp
new file mode 100644
index 00000000..03e4880f
--- /dev/null
+++ b/src/gui/unregisterdialog.cpp
@@ -0,0 +1,171 @@
+/*
+ *  The Mana World
+ *  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
+ *
+ */
+
+#include "unregisterdialog.h"
+
+#include <string>
+#include <sstream>
+
+#include <guichan/widgets/label.hpp>
+
+#include "../main.h"
+#include "../log.h"
+#include "../logindata.h"
+
+#include "button.h"
+#include "checkbox.h"
+#include "login.h"
+#include "passwordfield.h"
+#include "textfield.h"
+#include "ok_dialog.h"
+
+UnRegisterDialog::UnRegisterDialog(Window *parent, LoginData *loginData):
+    Window("Unregister", true, parent),
+    mWrongDataNoticeListener(new WrongDataNoticeListener()),
+    mLoginData(loginData)
+{
+    gcn::Label *userLabel = new gcn::Label("Name:");
+    gcn::Label *passwordLabel = new gcn::Label("Password:");
+    mUserField = new TextField(mLoginData->username);
+    mPasswordField = new PasswordField(mLoginData->password);
+    mUnRegisterButton = new Button("Unregister", "unregister", this);
+    mCancelButton = new Button("Cancel", "cancel", this);
+
+    const int width = 200;
+    const int height = 70;
+    setContentSize(width, height);
+
+    mUserField->setPosition(65, 5);
+    mUserField->setWidth(130);
+    mPasswordField->setPosition(
+            65, mUserField->getY() + mUserField->getHeight() + 7);
+    mPasswordField->setWidth(130);
+
+    userLabel->setPosition(5, mUserField->getY() + 1);
+    passwordLabel->setPosition(5, mPasswordField->getY() + 1);
+
+    mCancelButton->setPosition(
+            width - 5 - mCancelButton->getWidth(),
+            height - 5 - mCancelButton->getHeight());
+    mUnRegisterButton->setPosition(
+            mCancelButton->getX() - 5 - mUnRegisterButton->getWidth(),
+            mCancelButton->getY());
+
+    add(userLabel);
+    add(passwordLabel);
+    add(mUserField);
+    add(mPasswordField);
+    add(mUnRegisterButton);
+    add(mCancelButton);
+
+    setLocationRelativeTo(getParent());
+    setVisible(true);
+    mPasswordField->requestFocus();
+}
+
+UnRegisterDialog::~UnRegisterDialog()
+{
+    delete mWrongDataNoticeListener;
+}
+
+void
+UnRegisterDialog::action(const gcn::ActionEvent &event)
+{
+    if (event.getId() == "cancel")
+    {
+        scheduleDelete();
+    }
+    else if (event.getId() == "unregister")
+    {
+        const std::string username = mUserField->getText();
+        const std::string password = mPasswordField->getText();
+        logger->log("UnregisterDialog::unregistered, Username is %s",
+                     username.c_str());
+
+        std::stringstream errorMsg;
+        int error = 0;
+
+        // Check login
+        if (username.empty())
+        {
+            // No username
+            errorMsg << "Enter your username first.";
+            error = 1;
+        }
+        else if (username.length() < LEN_MIN_USERNAME)
+        {
+            // Name too short
+            errorMsg << "The username needs to be at least "
+                     << LEN_MIN_USERNAME
+                     << " characters long.";
+            error = 1;
+        }
+        else if (username.length() > LEN_MAX_USERNAME - 1 )
+        {
+            // Name too long
+            errorMsg << "The username needs to be less than "
+                     << LEN_MAX_USERNAME
+                     << " characters long.";
+            error = 1;
+        }
+        else if (password.length() < LEN_MIN_PASSWORD)
+        {
+            // Pass too short
+            errorMsg << "The password needs to be at least "
+                     << LEN_MIN_PASSWORD
+                     << " characters long.";
+            error = 2;
+        }
+        else if (password.length() > LEN_MAX_PASSWORD - 1 )
+        {
+            // Pass too long
+            errorMsg << "The password needs to be less than "
+                     << LEN_MAX_PASSWORD
+                     << " characters long.";
+            error = 2;
+        }
+
+        if (error > 0)
+        {
+            if (error == 1)
+            {
+                mWrongDataNoticeListener->setTarget(this->mUserField);
+            }
+            else if (error == 2)
+            {
+                mWrongDataNoticeListener->setTarget(this->mPasswordField);
+            }
+
+            OkDialog *dlg = new OkDialog("Error", errorMsg.str());
+            dlg->addActionListener(mWrongDataNoticeListener);
+        }
+        else
+        {
+            // No errors detected, unregister the new user.
+            mUnRegisterButton->setEnabled(false);
+            mLoginData->username = username;
+            mLoginData->password = password;
+            state = STATE_UNREGISTER_ATTEMPT;
+            scheduleDelete();
+        }
+    }
+}
diff --git a/src/gui/unregisterdialog.h b/src/gui/unregisterdialog.h
new file mode 100644
index 00000000..40fdf7fe
--- /dev/null
+++ b/src/gui/unregisterdialog.h
@@ -0,0 +1,73 @@
+/*
+ *  The Mana World
+ *  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: register.h 3036 2007-01-14 16:45:13Z b_lindeijer $
+ */
+
+#ifndef _TMW_UNREGISTERDIALOG_H
+#define _TMW_UNREGISTERDIALOG_H
+
+#include <iosfwd>
+#include <guichan/actionlistener.hpp>
+
+#include "window.h"
+#include "../guichanfwd.h"
+
+class LoginData;
+class OkDialog;
+class WrongDataNoticeListener;
+
+/**
+ * The Unregister dialog.
+ *
+ * \ingroup Interface
+ */
+class UnRegisterDialog : public Window, public gcn::ActionListener {
+    public:
+        /**
+         * Constructor
+         *
+         * @see Window::Window
+         */
+        UnRegisterDialog(Window *parent,LoginData *loginData);
+
+        /**
+         * Destructor
+         */
+        ~UnRegisterDialog();
+
+        /**
+         * Called when receiving actions from the widgets.
+         */
+        void action(const gcn::ActionEvent &event);
+
+    private:
+        gcn::TextField *mUserField;
+        gcn::TextField *mPasswordField;
+
+        gcn::Button *mUnRegisterButton;
+        gcn::Button *mCancelButton;
+
+        WrongDataNoticeListener *mWrongDataNoticeListener;
+
+        LoginData *mLoginData;
+};
+
+#endif
diff --git a/src/lockedarray.h b/src/lockedarray.h
index 7ec2f9da..f31292d7 100644
--- a/src/lockedarray.h
+++ b/src/lockedarray.h
@@ -46,7 +46,7 @@ class LockedArray
         bool isLocked() const { return mLocked; };
 
         T getEntry() const { return mData[mCurEntry]; };
-        void setEntry(T entry) { mData[mCurEntry] = entry; };
+        void setEntry(T entry) { mData[mCurEntry] = entry; mFilled = true; };
 
         void next();
         void prev();
@@ -55,6 +55,11 @@ class LockedArray
 
         unsigned int getSize() const { return mSize; };
 
+        /**
+         * Clears the array without changing size or data type
+         */
+        void clear();
+
     protected:
         unsigned int mSize;
 
@@ -62,11 +67,14 @@ class LockedArray
 
         unsigned int mCurEntry;
         bool mLocked;
+
+        bool mFilled;
 };
 
 template<class T>
 LockedArray<T>::LockedArray(unsigned int size):
-    mSize(size), mData(new T[size]), mCurEntry(0), mLocked(false)
+    mSize(size), mData(new T[size]), mCurEntry(0), mLocked(false),
+    mFilled(false)
 {
     std::fill_n(mData, mSize, (T)0);
 }
@@ -107,4 +115,19 @@ void LockedArray<T>::select(unsigned int pos)
         mCurEntry = 0;
 }
 
+template<class T>
+void LockedArray<T>::clear()
+{
+    if (!mFilled) return;
+
+    delete [] mData;
+
+    mData = new T[mSize];
+
+    std::fill_n(mData, mSize, (T)0);
+
+    mCurEntry = 0;
+
+    mLocked = false;
+}
 #endif
diff --git a/src/logindata.h b/src/logindata.h
index 70b80bb7..f9b520eb 100644
--- a/src/logindata.h
+++ b/src/logindata.h
@@ -37,6 +37,15 @@ struct LoginData
     int session_ID2;
 
     bool remember;
+
+    void clear()
+    {
+        username = "";
+        password = "";
+        hostname = "";
+        email = "";
+        port = 0;
+    };
 };
 
 #endif
diff --git a/src/main.cpp b/src/main.cpp
index dc186c84..e953d456 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -58,17 +58,21 @@
 #include "gui/gui.h"
 #include "gui/serverdialog.h"
 #include "gui/login.h"
+#include "gui/quitdialog.h"
 #include "gui/ok_dialog.h"
 #include "gui/register.h"
 #include "gui/updatewindow.h"
 #include "gui/textfield.h"
 
+
 #include "net/charserverhandler.h"
 #include "net/connection.h"
 #include "net/loginhandler.h"
+#include "net/logouthandler.h"
 #include "net/network.h"
 
 #include "net/accountserver/accountserver.h"
+#include "net/accountserver/account.h"
 
 #include "net/chatserver/chatserver.h"
 
@@ -427,6 +431,7 @@ void loadUpdates()
 CharServerHandler charServerHandler;
 LoginData loginData;
 LoginHandler loginHandler;
+LogoutHandler logoutHandler;
 LockedArray<LocalPlayer*> charInfo(MAX_SLOT + 1);
 
 // TODO Find some nice place for these functions
@@ -436,6 +441,7 @@ void accountLogin(LoginData *loginData)
 
     Net::registerHandler(&loginHandler);
 
+    charInfo.clear();
     charServerHandler.setCharInfo(&charInfo);
     Net::registerHandler(&charServerHandler);
 
@@ -461,6 +467,7 @@ void accountRegister(LoginData *loginData)
 
     Net::registerHandler(&loginHandler);
 
+    charInfo.clear();
     charServerHandler.setCharInfo(&charInfo);
     Net::registerHandler(&charServerHandler);
 
@@ -468,6 +475,109 @@ void accountRegister(LoginData *loginData)
             loginData->username, loginData->password, loginData->email);
 }
 
+void accountUnRegister(LoginData *loginData)
+{
+    Net::registerHandler(&logoutHandler);
+
+    Net::AccountServer::Account::unregister(loginData->username,
+                                                         loginData->password);
+
+}
+
+void switchCharacter(std::string* passToken)
+{
+    Net::registerHandler(&logoutHandler);
+
+    logoutHandler.reset();
+    logoutHandler.setScenario(LOGOUT_SWITCH_CHARACTER, passToken);
+
+    Net::GameServer::logout(true);
+    Net::ChatServer::logout();
+}
+
+void switchAccountServer()
+{
+    Net::registerHandler(&logoutHandler);
+
+    logoutHandler.reset();
+    logoutHandler.setScenario(LOGOUT_SWITCH_ACCOUNTSERVER);
+
+    //Can't logout if we were not logged in ...
+    if (accountServerConnection->isConnected())
+    {
+        Net::AccountServer::logout();
+    }
+    else
+    {
+        logoutHandler.setAccountLoggedOut();
+    }
+
+    if (gameServerConnection->isConnected())
+    {
+        Net::GameServer::logout(false);
+    }
+    else
+    {
+        logoutHandler.setGameLoggedOut();
+    }
+
+    if (chatServerConnection->isConnected())
+    {
+        Net::ChatServer::logout();
+    }
+    else
+    {
+        logoutHandler.setChatLoggedOut();
+    }
+}
+
+void logoutThenExit()
+{
+    Net::registerHandler(&logoutHandler);
+
+    logoutHandler.reset();
+    logoutHandler.setScenario(LOGOUT_EXIT);
+
+    //Can't logout if we were not logged in ...
+    if (accountServerConnection->isConnected())
+    {
+        Net::AccountServer::logout();
+    }
+    else
+    {
+        logoutHandler.setAccountLoggedOut();
+    }
+
+    if (gameServerConnection->isConnected())
+    {
+        Net::GameServer::logout(false);
+    }
+    else
+    {
+        logoutHandler.setGameLoggedOut();
+    }
+
+    if (chatServerConnection->isConnected())
+    {
+        Net::ChatServer::logout();
+    }
+    else
+    {
+        logoutHandler.setChatLoggedOut();
+    }
+}
+
+void reconnectAccount(const std::string& passToken)
+{
+    Net::registerHandler(&loginHandler);
+
+    charInfo.clear();
+    charServerHandler.setCharInfo(&charInfo);
+    Net::registerHandler(&charServerHandler);
+
+    Net::AccountServer::reconnectAccount(accountServerConnection, passToken);
+}
+
 void xmlNullLogger(void *ctx, const char *msg, ...)
 {
     // Does nothing, that's the whole point of it
@@ -520,6 +630,7 @@ int main(int argc, char *argv[])
     initEngine();
 
     Window *currentDialog = NULL;
+    QuitDialog* quitDialog = NULL;
     Image *login_wallpaper = NULL;
     Game *game = NULL;
 
@@ -560,18 +671,25 @@ int main(int argc, char *argv[])
     unsigned int oldstate = !state; // We start with a status change.
 
     SDL_Event event;
-    while (state != STATE_EXIT)
+    while (state != STATE_FORCE_QUIT)
     {
         // Handle SDL events
         while (SDL_PollEvent(&event)) {
             switch (event.type) {
                 case SDL_QUIT:
-                    state = STATE_EXIT;
+                    state = STATE_FORCE_QUIT;
                     break;
 
                 case SDL_KEYDOWN:
                     if (event.key.keysym.sym == SDLK_ESCAPE)
-                        state = STATE_EXIT;
+                        if (!quitDialog)
+                        {
+                            quitDialog = new QuitDialog(NULL, &quitDialog);
+                        }
+                        else
+                        {
+                            quitDialog->requestMoveToTop();
+                        }
                     break;
             }
 
@@ -581,15 +699,6 @@ int main(int argc, char *argv[])
         gui->logic();
         Net::flush();
 
-        if (state > STATE_CONNECT_ACCOUNT && state < STATE_GAME)
-        {
-            if (!accountServerConnection->isConnected())
-            {
-                state = STATE_ERROR;
-                errorMessage = "Got disconnected from account server!";
-            }
-        }
-
         if (!login_wallpaper)
         {
             login_wallpaper = ResourceManager::getInstance()->
@@ -608,7 +717,7 @@ int main(int argc, char *argv[])
         gui->draw();
         graphics->updateScreen();
 
-        // TODO: Add connect timeout to go back to choose server
+        // TODO: Add connect timeouts
         if (state == STATE_CONNECT_ACCOUNT &&
                 accountServerConnection->isConnected())
         {
@@ -624,8 +733,16 @@ int main(int argc, char *argv[])
         {
             accountServerConnection->disconnect();
             Net::clearHandlers();
+
             state = STATE_GAME;
         }
+        else if (state == STATE_RECONNECT_ACCOUNT &&
+                 accountServerConnection->isConnected())
+        {
+            reconnectAccount(token);
+
+            state = STATE_WAIT;
+        }
 
         if (state != oldstate) {
             // Load updates after exiting the update state
@@ -645,6 +762,11 @@ int main(int argc, char *argv[])
                 delete currentDialog;
                 currentDialog = NULL;
             }
+            // State has changed, while the quitDialog was active, it might
+            // not be correct anymore
+            if (quitDialog) {
+                quitDialog->scheduleDelete();
+            }
 
             switch (state) {
                 case STATE_CHOOSE_SERVER:
@@ -699,6 +821,21 @@ int main(int argc, char *argv[])
                     accountLogin(&loginData);
                     break;
 
+                case STATE_SWITCH_ACCOUNTSERVER:
+                    logger->log("State: SWITCH_ACCOUNTSERVER");
+
+                    gameServerConnection->disconnect();
+                    chatServerConnection->disconnect();
+                    accountServerConnection->disconnect();
+
+                    state = STATE_CHOOSE_SERVER;
+                    break;
+
+                case STATE_SWITCH_ACCOUNTSERVER_ATTEMPT:
+                    logger->log("State: SWITCH_ACCOUNTSERVER_ATTEMPT");
+                    switchAccountServer();
+                    break;
+
                 case STATE_REGISTER:
                     logger->log("State: REGISTER");
                     currentDialog = new RegisterDialog(&loginData);
@@ -710,7 +847,8 @@ int main(int argc, char *argv[])
 
                 case STATE_CHAR_SELECT:
                     logger->log("State: CHAR_SELECT");
-                    currentDialog = new CharSelectDialog(&charInfo);
+                    currentDialog =
+                                  new CharSelectDialog(&charInfo, &loginData);
 
                     if (((CharSelectDialog*) currentDialog)->
                             selectByName(options.playername))
@@ -724,6 +862,23 @@ int main(int argc, char *argv[])
                             gcn::ActionEvent(NULL, "ok"));
                     break;
 
+                case STATE_UNREGISTER_ATTEMPT:
+                    logger->log("State: UNREGISTER ATTEMPT");
+                    accountUnRegister(&loginData);
+                    loginData.clear();
+                    break;
+
+                case STATE_UNREGISTER:
+                    logger->log("State: UNREGISTER");
+                    accountServerConnection->disconnect();
+                    currentDialog = new OkDialog("Unregister succesfull",
+                                         "Farewell, come back any time ....");
+
+                    //The errorlistener sets the state to STATE_CHOOSE_SERVER
+                    currentDialog->addActionListener(&errorListener);
+                    currentDialog = NULL; // OkDialog deletes itself
+                    break;
+
                 case STATE_ERROR:
                     logger->log("State: ERROR");
                     currentDialog = new OkDialog("Error", errorMessage);
@@ -749,29 +904,66 @@ int main(int argc, char *argv[])
                     sound.fadeOutMusic(1000);
 
                     currentDialog = NULL;
-                    login_wallpaper->decRef();
-                    login_wallpaper = NULL;
 
                     logger->log("State: GAME");
                     game = new Game;
                     game->logic();
                     delete game;
-                    state = STATE_EXIT;
+
+                    //If the quitdialog didn't set the next state
+                    if (state == STATE_GAME)
+                    {
+                        state = STATE_EXIT;
+                    }
+                    else if (state != STATE_FORCE_QUIT)
+                    {
+                        //TODO: solve this problem
+                        delete gui; // Crashes otherwise
+                        gui = new Gui(graphics);
+                    }
+                    break;
+
+                case STATE_SWITCH_CHARACTER:
+                    logger->log("State: SWITCH_CHARACTER");
+                    switchCharacter(&token);
+                    break;
+
+                case STATE_RECONNECT_ACCOUNT:
+                    logger->log("State: RECONNECT_ACCOUNT");
+
+                    //done with game&chat
+                    gameServerConnection->disconnect();
+                    chatServerConnection->disconnect();
+
+                    accountServerConnection->connect(loginData.hostname,
+                                                              loginData.port);
+                    break;
+
+                case STATE_WAIT:
+                    break;
+
+                case STATE_EXIT:
+                    logger->log("State: EXIT");
+                    logoutThenExit();
                     break;
 
                 default:
-                    state = STATE_EXIT;
+                    state = STATE_FORCE_QUIT;
                     break;
             }
         }
     }
 
+    accountServerConnection->disconnect();
+    gameServerConnection->disconnect();
+    chatServerConnection->disconnect();
+
     delete accountServerConnection;
     delete gameServerConnection;
     delete chatServerConnection;
     Net::finalize();
 
-    logger->log("State: EXIT");
+    logger->log("Quitting");
     exit_engine();
     PHYSFS_deinit();
     delete logger;
diff --git a/src/main.h b/src/main.h
index c001a374..057e781f 100644
--- a/src/main.h
+++ b/src/main.h
@@ -76,11 +76,20 @@ enum {
     STATE_LOGIN_ATTEMPT,
     STATE_REGISTER,
     STATE_REGISTER_ATTEMPT,
-    STATE_CHAR_SELECT,
     STATE_ERROR,
+    STATE_CHAR_SELECT,
+    STATE_UNREGISTER_ATTEMPT,
+    STATE_UNREGISTER,
+    STATE_SWITCH_CHARACTER,
+    STATE_RECONNECT_ACCOUNT,
+    STATE_SWITCH_ACCOUNTSERVER_ATTEMPT,
+    STATE_SWITCH_ACCOUNTSERVER,
+    STATE_LOGOUT_ATTEMPT,
     STATE_CONNECT_GAME,
     STATE_GAME,
-    STATE_EXIT
+    STATE_WAIT,
+    STATE_EXIT,
+    STATE_FORCE_QUIT
 };
 
 /* length definitions for several char[]s in order
diff --git a/src/net/accountserver/account.cpp b/src/net/accountserver/account.cpp
index daf94a65..16f81f44 100644
--- a/src/net/accountserver/account.cpp
+++ b/src/net/accountserver/account.cpp
@@ -68,9 +68,14 @@ void Net::AccountServer::Account::selectCharacter(char slot)
     Net::AccountServer::connection->send(msg);
 }
 
-void Net::AccountServer::Account::unregister()
+void Net::AccountServer::Account::unregister(const std::string &username,
+                                             const std::string &password)
 {
     MessageOut msg(PAMSG_UNREGISTER);
+
+    msg.writeString(username);
+    msg.writeString(password);
+
     Net::AccountServer::connection->send(msg);
 }
 
diff --git a/src/net/accountserver/account.h b/src/net/accountserver/account.h
index aaf3afe0..d3c84a15 100644
--- a/src/net/accountserver/account.h
+++ b/src/net/accountserver/account.h
@@ -41,13 +41,14 @@ namespace Net
 
             void selectCharacter(char slot);
 
-            void unregister();
+            void unregister(const std::string &username,
+                            const std::string &password);
 
             void changeEmail(const std::string &email);
 
             void getEmail();
 
-            void changePassword(const std::string &oldPassowrd,
+            void changePassword(const std::string &oldPassword,
                     const std::string &newPassword);
         }
     }
diff --git a/src/net/accountserver/accountserver.cpp b/src/net/accountserver/accountserver.cpp
index 8fde6d5e..92d803e6 100644
--- a/src/net/accountserver/accountserver.cpp
+++ b/src/net/accountserver/accountserver.cpp
@@ -63,6 +63,14 @@ void Net::AccountServer::logout()
 {
     MessageOut msg(PAMSG_LOGOUT);
     Net::AccountServer::connection->send(msg);
+}
 
-    Net::AccountServer::connection = 0;
+void Net::AccountServer::reconnectAccount(Net::Connection *connection,
+                                          const std::string &passToken)
+{
+    Net::AccountServer::connection = connection;
+
+    MessageOut msg(PAMSG_RECONNECT);
+    msg.writeString(passToken, 32);
+    Net::AccountServer::connection->send(msg);
 }
diff --git a/src/net/accountserver/accountserver.h b/src/net/accountserver/accountserver.h
index c05b5317..8bfe991c 100644
--- a/src/net/accountserver/accountserver.h
+++ b/src/net/accountserver/accountserver.h
@@ -40,6 +40,9 @@ namespace Net
                 const std::string &email);
 
         void logout();
+
+        void reconnectAccount(Net::Connection *connection,
+                                                const std::string &passToken);
     }
 }
 
diff --git a/src/net/chatserver/chatserver.cpp b/src/net/chatserver/chatserver.cpp
index e6a3331d..32979ea5 100644
--- a/src/net/chatserver/chatserver.cpp
+++ b/src/net/chatserver/chatserver.cpp
@@ -43,6 +43,13 @@ void Net::ChatServer::connect(Net::Connection *connection,
     connection->send(msg);
 }
 
+void Net::ChatServer::logout()
+{
+    MessageOut msg(PCMSG_DISCONNECT);
+
+    connection->send(msg);
+}
+
 void Net::ChatServer::chat(short channel, const std::string &text)
 {
     MessageOut msg(PCMSG_CHAT);
diff --git a/src/net/chatserver/chatserver.h b/src/net/chatserver/chatserver.h
index 93fe17c4..cf86baf3 100644
--- a/src/net/chatserver/chatserver.h
+++ b/src/net/chatserver/chatserver.h
@@ -34,6 +34,8 @@ namespace Net
     {
         void connect(Net::Connection *connection, const std::string &token);
 
+        void logout();
+
         void chat(short channel, const std::string &text);
 
         void announce(const std::string &text);
diff --git a/src/net/connection.cpp b/src/net/connection.cpp
index 4ce05d4a..ce060ae7 100644
--- a/src/net/connection.cpp
+++ b/src/net/connection.cpp
@@ -84,7 +84,8 @@ void Net::Connection::disconnect()
 
 bool Net::Connection::isConnected()
 {
-    return mConnection && mConnection->state == ENET_PEER_STATE_CONNECTED;
+    return bool (mConnection) ?
+                    (mConnection->state == ENET_PEER_STATE_CONNECTED) : false;
 }
 
 void Net::Connection::send(const MessageOut &msg)
diff --git a/src/net/gameserver/gameserver.cpp b/src/net/gameserver/gameserver.cpp
index 04e5bb08..8f8ad8ac 100644
--- a/src/net/gameserver/gameserver.cpp
+++ b/src/net/gameserver/gameserver.cpp
@@ -40,3 +40,12 @@ void Net::GameServer::connect(Net::Connection *connection,
 
     Net::GameServer::connection->send(msg);
 }
+
+void Net::GameServer::logout(bool reconnectAccount)
+{
+    MessageOut msg(PGMSG_DISCONNECT);
+
+    msg.writeByte((unsigned char) reconnectAccount);
+
+    Net::GameServer::connection->send(msg);
+}
diff --git a/src/net/gameserver/gameserver.h b/src/net/gameserver/gameserver.h
index ee49d7e3..5bf196b6 100644
--- a/src/net/gameserver/gameserver.h
+++ b/src/net/gameserver/gameserver.h
@@ -33,6 +33,8 @@ namespace Net
     namespace GameServer
     {
         void connect(Net::Connection *connection, const std::string &token);
+
+        void logout(bool reconnectAccount);
     }
 }
 
diff --git a/src/net/loginhandler.cpp b/src/net/loginhandler.cpp
index 73be4b2f..c68a620a 100644
--- a/src/net/loginhandler.cpp
+++ b/src/net/loginhandler.cpp
@@ -33,6 +33,7 @@ LoginHandler::LoginHandler()
     static const Uint16 _messages[] = {
         APMSG_LOGIN_RESPONSE,
         APMSG_REGISTER_RESPONSE,
+        APMSG_RECONNECT_RESPONSE,
         0
     };
     handledMessages = _messages;
@@ -106,5 +107,34 @@ void LoginHandler::handleMessage(MessageIn &msg)
             }
         }
             break;
+        case APMSG_RECONNECT_RESPONSE:
+        {
+            int errMsg = msg.readByte();
+            // Successful login
+            if (errMsg == ERRMSG_OK)
+            {
+                state = STATE_CHAR_SELECT;
+            }
+            // Login failed
+            else
+            {
+                switch (errMsg) {
+                    case ERRMSG_INVALID_ARGUMENT:
+                        errorMessage = "Wrong magic_token";
+                        break;
+                    case ERRMSG_FAILURE:
+                        errorMessage = "Already logged in";
+                        break;
+                    case LOGIN_SERVER_FULL:
+                        errorMessage = "Server is full";
+                        break;
+                    default:
+                        errorMessage = "Unknown error";
+                        break;
+                }
+                state = STATE_ERROR;
+            }
+        }
+            break;
     }
 }
diff --git a/src/net/logouthandler.cpp b/src/net/logouthandler.cpp
new file mode 100644
index 00000000..b52a38c5
--- /dev/null
+++ b/src/net/logouthandler.cpp
@@ -0,0 +1,216 @@
+/*
+ *  The Mana World
+ *  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
+ *
+ */
+
+#include "logouthandler.h"
+
+#include "messagein.h"
+#include "protocol.h"
+
+#include "../main.h"
+
+LogoutHandler::LogoutHandler():
+mPassToken(NULL), mScenario(LOGOUT_EXIT),
+mLoggedOutAccount(false), mLoggedOutGame(false), mLoggedOutChat(false)
+{
+    static const Uint16 _messages[] = {
+        APMSG_LOGOUT_RESPONSE,
+        APMSG_UNREGISTER_RESPONSE,
+        GPMSG_DISCONNECT_RESPONSE,
+        CPMSG_DISCONNECT_RESPONSE,
+        0
+    };
+    handledMessages = _messages;
+}
+
+void LogoutHandler::handleMessage(MessageIn &msg)
+{
+    switch (msg.getId())
+    {
+        case APMSG_LOGOUT_RESPONSE:
+        {
+            int errMsg = msg.readByte();
+
+            // Successful logout
+            if (errMsg == ERRMSG_OK)
+            {
+                mLoggedOutAccount = true;
+
+                switch (mScenario)
+                {
+                    case LOGOUT_SWITCH_ACCOUNTSERVER:
+                        if (mLoggedOutGame && mLoggedOutChat)
+                                           state = STATE_SWITCH_ACCOUNTSERVER;
+                        break;
+
+                    case LOGOUT_EXIT:
+                    default:
+                        if (mLoggedOutGame && mLoggedOutChat)
+                                                     state = STATE_FORCE_QUIT;
+                        break;
+                }
+            }
+            // Logout failed
+            else
+            {
+                switch (errMsg) {
+                    case ERRMSG_NO_LOGIN:
+                        errorMessage = "Accountserver: Not logged in";
+                        break;
+                    default:
+                        errorMessage = "Accountserver: Unknown error";
+                        break;
+                }
+                state = STATE_ERROR;
+            }
+        }
+            break;
+        case APMSG_UNREGISTER_RESPONSE:
+        {
+            int errMsg = msg.readByte();
+            // Successful unregistration
+            if (errMsg == ERRMSG_OK)
+            {
+                state = STATE_UNREGISTER;
+            }
+            // Unregistration failed
+            else
+            {
+                switch (errMsg) {
+                    case ERRMSG_INVALID_ARGUMENT:
+                        errorMessage =
+                                  "Accountserver: Wrong username or password";
+                        break;
+                    default:
+                        errorMessage = "Accountserver: Unknown error";
+                        break;
+                }
+                state = STATE_ERROR;
+            }
+        }
+            break;
+        case GPMSG_DISCONNECT_RESPONSE:
+        {
+            int errMsg = msg.readByte();
+            // Successful logout
+            if (errMsg == ERRMSG_OK)
+            {
+                mLoggedOutGame = true;
+
+                switch (mScenario)
+                {
+                    case LOGOUT_SWITCH_CHARACTER:
+                        if (mPassToken)
+                        {
+                            *mPassToken = msg.readString(32);
+                            mPassToken = NULL;
+                        }
+                        if (mLoggedOutChat) state = STATE_RECONNECT_ACCOUNT;
+                        break;
+
+                    case LOGOUT_SWITCH_ACCOUNTSERVER:
+                        if (mLoggedOutAccount && mLoggedOutChat)
+                                           state = STATE_SWITCH_ACCOUNTSERVER;
+                        break;
+
+                    case LOGOUT_EXIT:
+                    default:
+                        if (mLoggedOutAccount && mLoggedOutChat)
+                                                     state = STATE_FORCE_QUIT;
+                        break;
+                }
+            }
+            // Logout failed
+            else
+            {
+                switch (errMsg) {
+                    case ERRMSG_NO_LOGIN:
+                        errorMessage = "Gameserver: Not logged in";
+                        break;
+                    default:
+                        errorMessage = "Gameserver: Unknown error";
+                        break;
+                }
+                state = STATE_ERROR;
+            }
+        }
+            break;
+        case CPMSG_DISCONNECT_RESPONSE:
+        {
+            int errMsg = msg.readByte();
+            // Successful logout
+            if (errMsg == ERRMSG_OK)
+            {
+                mLoggedOutChat = true;
+
+                switch (mScenario)
+                {
+                    case LOGOUT_SWITCH_CHARACTER:
+                        if (mLoggedOutGame) state = STATE_RECONNECT_ACCOUNT;
+                        break;
+
+                    case LOGOUT_SWITCH_ACCOUNTSERVER:
+                        if (mLoggedOutAccount && mLoggedOutGame)
+                                          state = STATE_SWITCH_ACCOUNTSERVER;
+                        break;
+
+                    case LOGOUT_EXIT:
+                    default:
+                        if (mLoggedOutAccount && mLoggedOutGame)
+                        {
+                            state = STATE_FORCE_QUIT;
+                        }
+                        break;
+                }
+            }
+            else
+            {
+                switch (errMsg) {
+                    case ERRMSG_NO_LOGIN:
+                        errorMessage = "Chatserver: Not logged in";
+                        break;
+                    default:
+                        errorMessage = "Chatserver: Unknown error";
+                        break;
+                }
+                state = STATE_ERROR;
+            }
+        }
+            break;
+    }
+}
+
+void
+LogoutHandler::setScenario(unsigned short scenario, std::string* passToken)
+{
+    mScenario = scenario;
+    mPassToken = passToken;
+}
+
+void
+LogoutHandler::reset()
+{
+    mPassToken = NULL;
+    mScenario = LOGOUT_EXIT;
+    mLoggedOutAccount = false;
+    mLoggedOutGame = false;
+    mLoggedOutChat = false;
+}
diff --git a/src/net/logouthandler.h b/src/net/logouthandler.h
new file mode 100644
index 00000000..bf4e1221
--- /dev/null
+++ b/src/net/logouthandler.h
@@ -0,0 +1,63 @@
+/*
+ *  The Mana World
+ *  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
+ *
+ */
+
+#ifndef _TMW_NET_LOGOUTHANDLER_H
+#define _TMW_NET_LOGOUTHANDLER_H
+
+#include <string>
+
+#include "messagehandler.h"
+
+/**
+ * The different scenarios for which LogoutHandler can be used
+ */
+enum {
+    LOGOUT_EXIT,
+    LOGOUT_SWITCH_ACCOUNTSERVER,
+    LOGOUT_SWITCH_CHARACTER
+};
+
+class LogoutHandler : public MessageHandler
+{
+    public:
+        LogoutHandler();
+
+        void handleMessage(MessageIn &msg);
+
+        void setScenario(unsigned short scenario,
+                                               std::string* passToken = NULL);
+
+        void reset();
+
+        void setAccountLoggedOut(){ mLoggedOutAccount = true; }
+        void setGameLoggedOut(){ mLoggedOutGame = true; }
+        void setChatLoggedOut(){ mLoggedOutChat = true; }
+
+    private:
+        std::string* mPassToken;
+        unsigned short mScenario;
+        bool mLoggedOutAccount;
+        bool mLoggedOutGame;
+        bool mLoggedOutChat;
+};
+
+#endif
diff --git a/src/net/protocol.h b/src/net/protocol.h
index 096ba29c..78a42e42 100644
--- a/src/net/protocol.h
+++ b/src/net/protocol.h
@@ -119,7 +119,7 @@ enum {
     // Login/Register
     PAMSG_REGISTER                 = 0x0000, // L version, S username, S password, S email
     APMSG_REGISTER_RESPONSE        = 0x0002, // B error
-    PAMSG_UNREGISTER               = 0x0003, // -
+    PAMSG_UNREGISTER               = 0x0003, // S username, S password
     APMSG_UNREGISTER_RESPONSE      = 0x0004, // B error
     PAMSG_LOGIN                    = 0x0010, // L version, S username, S password
     APMSG_LOGIN_RESPONSE           = 0x0012, // B error
@@ -144,6 +144,14 @@ enum {
     PCMSG_CONNECT                  = 0x0053, // B*32 token
     CPMSG_CONNECT_RESPONSE         = 0x0054, // B error
 
+    PGMSG_DISCONNECT               = 0x0060, // B reconnect account
+    GPMSG_DISCONNECT_RESPONSE      = 0x0061, // B error, B*32 token
+    PCMSG_DISCONNECT               = 0x0063, // -
+    CPMSG_DISCONNECT_RESPONSE      = 0x0064, // B error
+
+    PAMSG_RECONNECT                = 0x0065, // B*32 token
+    APMSG_RECONNECT_RESPONSE       = 0x0066, // B error
+
     // Game
     GPMSG_PLAYER_MAP_CHANGE        = 0x0100, // S filename, W x, W y
     GPMSG_PLAYER_SERVER_CHANGE     = 0x0101, // B*32 token, S game address, W game port
-- 
cgit v1.2.3-70-g09d2