From 0cc6167c407c1cf18158ca0e154a3b1cab48853d Mon Sep 17 00:00:00 2001
From: Andrei Karas <akaras@inbox.ru>
Date: Thu, 28 Jul 2011 03:27:26 +0300
Subject: Extract shared logic from charserverhandler and loginhandler netcode
 to ea namespace.

---
 src/net/ea/charserverhandler.cpp   | 183 +++++++++++++++++++++++++
 src/net/ea/charserverhandler.h     |  89 ++++++++++++
 src/net/ea/eaprotocol.h            |   5 +-
 src/net/ea/loginhandler.cpp        | 268 +++++++++++++++++++++++++++++++++++++
 src/net/ea/loginhandler.h          | 107 +++++++++++++++
 src/net/ea/token.h                 |  44 ++++++
 src/net/tmwa/charserverhandler.cpp | 197 ++++++---------------------
 src/net/tmwa/charserverhandler.h   |  29 ++--
 src/net/tmwa/gamehandler.h         |   3 +-
 src/net/tmwa/loginhandler.cpp      | 256 +++++------------------------------
 src/net/tmwa/loginhandler.h        |  37 +----
 src/net/tmwa/token.h               |  44 ------
 12 files changed, 787 insertions(+), 475 deletions(-)
 create mode 100644 src/net/ea/charserverhandler.cpp
 create mode 100644 src/net/ea/charserverhandler.h
 create mode 100644 src/net/ea/loginhandler.cpp
 create mode 100644 src/net/ea/loginhandler.h
 create mode 100644 src/net/ea/token.h
 delete mode 100644 src/net/tmwa/token.h

(limited to 'src/net')

diff --git a/src/net/ea/charserverhandler.cpp b/src/net/ea/charserverhandler.cpp
new file mode 100644
index 000000000..e1fcfda66
--- /dev/null
+++ b/src/net/ea/charserverhandler.cpp
@@ -0,0 +1,183 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2004-2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program 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.
+ *
+ *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/ea/charserverhandler.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "game.h"
+#include "log.h"
+
+#include "gui/charcreatedialog.h"
+#include "gui/okdialog.h"
+
+#include "net/logindata.h"
+#include "net/messageout.h"
+#include "net/net.h"
+
+#include "net/ea/loginhandler.h"
+#include "net/ea/eaprotocol.h"
+
+#include "resources/colordb.h"
+
+#include "utils/dtor.h"
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include "debug.h"
+
+extern Net::CharHandler *charHandler;
+
+namespace Ea
+{
+
+CharServerHandler::CharServerHandler()
+{
+}
+
+void CharServerHandler::setCharSelectDialog(CharSelectDialog *window)
+{
+    mCharSelectDialog = window;
+    updateCharSelectDialog();
+}
+
+void CharServerHandler::setCharCreateDialog(CharCreateDialog *window)
+{
+    mCharCreateDialog = window;
+
+    if (!mCharCreateDialog)
+        return;
+
+    std::vector<std::string> attributes;
+    attributes.push_back(_("Strength:"));
+    attributes.push_back(_("Agility:"));
+    attributes.push_back(_("Vitality:"));
+    attributes.push_back(_("Intelligence:"));
+    attributes.push_back(_("Dexterity:"));
+    attributes.push_back(_("Luck:"));
+
+    const Token &token =
+        static_cast<LoginHandler*>(Net::getLoginHandler())->getToken();
+
+    mCharCreateDialog->setAttributes(attributes, 30, 1, 9);
+    mCharCreateDialog->setFixedGender(true, token.sex);
+}
+
+void CharServerHandler::requestCharacters()
+{
+    connect();
+}
+
+unsigned int CharServerHandler::baseSprite() const
+{
+    return EA_SPRITE_BASE;
+}
+
+unsigned int CharServerHandler::hairSprite() const
+{
+    return EA_SPRITE_HAIR;
+}
+
+unsigned int CharServerHandler::maxSprite() const
+{
+    return EA_SPRITE_VECTOREND;
+}
+
+void CharServerHandler::processCharLoginError(Net::MessageIn &msg)
+{
+    switch (msg.readInt8())
+    {
+        case 0:
+            errorMessage = _("Access denied. Most likely, there are "
+                             "too many players on this server.");
+            break;
+        case 1:
+            errorMessage = _("Cannot use this ID.");
+            break;
+        default:
+            errorMessage = _("Unknown char-server failure.");
+            break;
+    }
+    Client::setState(STATE_ERROR);
+}
+
+void CharServerHandler::processCharCreate(Net::MessageIn &msg, bool withColors)
+{
+    Net::Character *character = new Net::Character;
+    readPlayerData(msg, character, withColors);
+    mCharacters.push_back(character);
+
+    updateCharSelectDialog();
+
+    // Close the character create dialog
+    if (mCharCreateDialog)
+    {
+        mCharCreateDialog->scheduleDelete();
+        mCharCreateDialog = 0;
+    }
+}
+
+void CharServerHandler::processCharCreateFailed(Net::MessageIn &msg)
+{
+    switch (msg.readInt8())
+    {
+        case 1:
+        case 0:
+        default:
+            errorMessage = _("Failed to create character. Most "
+                                "likely the name is already taken.");
+            break;
+        case 2:
+            errorMessage = _("Wrong name.");
+            break;
+        case 3:
+            errorMessage = _("Incorrect stats.");
+            break;
+        case 4:
+            errorMessage = _("Incorrect hair.");
+            break;
+        case 5:
+            errorMessage = _("Incorrect slot.");
+            break;
+    }
+    new OkDialog(_("Error"), errorMessage);
+    if (mCharCreateDialog)
+        mCharCreateDialog->unlock();
+}
+
+void CharServerHandler::processCharDelete(Net::MessageIn &msg A_UNUSED)
+{
+    delete mSelectedCharacter;
+    mCharacters.remove(mSelectedCharacter);
+    mSelectedCharacter = 0;
+    updateCharSelectDialog();
+    unlockCharSelectDialog();
+    new OkDialog(_("Info"), _("Character deleted."));
+}
+
+void CharServerHandler::processCharDeleteFailed(Net::MessageIn &msg A_UNUSED)
+{
+    unlockCharSelectDialog();
+    new OkDialog(_("Error"), _("Failed to delete character."));
+}
+
+} // namespace Ea
diff --git a/src/net/ea/charserverhandler.h b/src/net/ea/charserverhandler.h
new file mode 100644
index 000000000..bd39781cd
--- /dev/null
+++ b/src/net/ea/charserverhandler.h
@@ -0,0 +1,89 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2004-2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program 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.
+ *
+ *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NET_EA_CHARSERVERHANDLER_H
+#define NET_EA_CHARSERVERHANDLER_H
+
+#include "net/charhandler.h"
+#include "net/messagein.h"
+#include "net/net.h"
+#include "net/serverinfo.h"
+
+#ifdef __GNUC__
+#define A_UNUSED  __attribute__ ((unused))
+#else
+#define A_UNUSED
+#endif
+
+class LoginData;
+
+namespace Ea
+{
+
+/**
+ * Deals with incoming messages from the character server.
+ */
+class CharServerHandler : public Net::CharHandler
+{
+    public:
+        CharServerHandler();
+
+        virtual void setCharSelectDialog(CharSelectDialog *window);
+
+        /**
+         * Sets the character create dialog. The handler will clean up this
+         * dialog when a new character is succesfully created, and will unlock
+         * the dialog when a new character failed to be created.
+         */
+        virtual void setCharCreateDialog(CharCreateDialog *window);
+
+        virtual void requestCharacters();
+
+        virtual unsigned int baseSprite() const;
+
+        virtual unsigned int hairSprite() const;
+
+        virtual unsigned int maxSprite() const;
+
+        virtual void connect() = 0;
+
+        virtual void processCharLogin(Net::MessageIn &msg) = 0;
+
+        virtual void processCharLoginError(Net::MessageIn &msg);
+
+        virtual void processCharCreate(Net::MessageIn &msg, bool withColors);
+
+        virtual void processCharCreateFailed(Net::MessageIn &msg);
+
+        virtual void processCharDelete(Net::MessageIn &msg);
+
+        virtual void processCharDeleteFailed(Net::MessageIn &msg);
+
+    protected:
+        virtual void readPlayerData(Net::MessageIn &msg,
+                                    Net::Character *character,
+                                    bool withColors) = 0;
+};
+
+} // namespace Ea
+
+#endif // NET_EA_CHARSERVERHANDLER_H
diff --git a/src/net/ea/eaprotocol.h b/src/net/ea/eaprotocol.h
index d72ca3515..11cc0180d 100644
--- a/src/net/ea/eaprotocol.h
+++ b/src/net/ea/eaprotocol.h
@@ -36,7 +36,10 @@ enum
     EA_SPRITE_CAPE,
     EA_SPRITE_GLOVES,
     EA_SPRITE_WEAPON,
-    EA_SPRITE_SHIELD
+    EA_SPRITE_SHIELD,
+    EA_SPRITE_EVOL1,
+    EA_SPRITE_EVOL2,
+    EA_SPRITE_VECTOREND
 };
 
 #endif
diff --git a/src/net/ea/loginhandler.cpp b/src/net/ea/loginhandler.cpp
new file mode 100644
index 000000000..f6d8e5cf7
--- /dev/null
+++ b/src/net/ea/loginhandler.cpp
@@ -0,0 +1,268 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2004-2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program 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.
+ *
+ *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/ea/loginhandler.h"
+
+#include "client.h"
+#include "log.h"
+#include "configuration.h"
+
+#include "net/logindata.h"
+#include "net/messageout.h"
+
+#include "utils/dtor.h"
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include "debug.h"
+
+namespace Ea
+{
+
+LoginHandler::LoginHandler():
+        mVersionResponse(false),
+        mRegistrationEnabled(true)
+{
+}
+
+LoginHandler::~LoginHandler()
+{
+    delete_all(mWorlds);
+}
+
+bool LoginHandler::isRegistrationEnabled()
+{
+    return mRegistrationEnabled;
+}
+
+void LoginHandler::getRegistrationDetails()
+{
+    // Not supported, so move on
+    Client::setState(STATE_REGISTER);
+}
+
+void LoginHandler::loginAccount(LoginData *loginData)
+{
+
+    if (loginData)
+    {
+        // Since we're attempting to use the tAthena protocol,
+        // let's reset the character slots to the good value,
+        // in case we just logged out a Manaserv server
+        // with a different config.
+        loginData->resetCharacterSlots();
+
+        sendLoginRegister(loginData->username, loginData->password);
+    }
+}
+
+void LoginHandler::chooseServer(unsigned int server)
+{
+    if (server >= mWorlds.size() || !mWorlds[server])
+        return;
+
+    ServerInfo *charServer = getCharServer();
+    if (charServer)
+    {
+        if (config.getBoolValue("usePersistentIP"))
+            charServer->hostname = Client::getServerName();
+        else
+            charServer->hostname = ipToString(mWorlds[server]->address);
+        charServer->port = mWorlds[server]->port;
+    }
+
+    Client::setState(STATE_UPDATE);
+}
+
+void LoginHandler::registerAccount(LoginData *loginData)
+{
+    if (!loginData)
+        return;
+
+    std::string username = loginData->username;
+    username.append((loginData->gender == GENDER_FEMALE) ? "_F" : "_M");
+
+    sendLoginRegister(username, loginData->password);
+}
+
+Worlds LoginHandler::getWorlds() const
+{
+    return mWorlds;
+}
+
+void LoginHandler::clearWorlds()
+{
+    delete_all(mWorlds);
+    mWorlds.clear();
+}
+
+void LoginHandler::procecessCharPasswordResponse(Net::MessageIn &msg)
+{
+    // 0: acc not found, 1: success, 2: password mismatch, 3: pass too short
+    int errMsg = msg.readInt8();
+    // Successful pass change
+    if (errMsg == 1)
+    {
+        Client::setState(STATE_CHANGEPASSWORD_SUCCESS);
+    }
+    // pass change failed
+    else
+    {
+        switch (errMsg)
+        {
+            case 0:
+                errorMessage =
+                    _("Account was not found. Please re-login.");
+                break;
+            case 2:
+                errorMessage = _("Old password incorrect.");
+                break;
+            case 3:
+                errorMessage = _("New password too short.");
+                break;
+            default:
+                errorMessage = _("Unknown error.");
+                break;
+        }
+        Client::setState(STATE_ACCOUNTCHANGE_ERROR);
+    }
+}
+
+void LoginHandler::processUpdateHost(Net::MessageIn &msg)
+{
+    int len;
+
+    len = msg.readInt16() - 4;
+    mUpdateHost = msg.readString(len);
+    loginData.updateHost = mUpdateHost;
+
+    logger->log("Received update host \"%s\" from login server.",
+        mUpdateHost.c_str());
+}
+
+void LoginHandler::processLoginData(Net::MessageIn &msg)
+{
+    // Skip the length word
+    msg.skip(2);
+
+    clearWorlds();
+
+    int worldCount = (msg.getLength() - 47) / 32;
+
+    mToken.session_ID1 = msg.readInt32();
+    mToken.account_ID = msg.readInt32();
+    mToken.session_ID2 = msg.readInt32();
+    msg.skip(30);                           // unknown
+    // reserve bits for future usage
+    mToken.sex = (msg.readInt8() & 1) ? GENDER_MALE : GENDER_FEMALE;
+
+    for (int i = 0; i < worldCount; i++)
+    {
+        WorldInfo *world = new WorldInfo;
+
+        world->address = msg.readInt32();
+        world->port = msg.readInt16();
+        world->name = msg.readString(20);
+        world->online_users = msg.readInt32();
+        config.setValue("updatehost", mUpdateHost);
+        world->updateHost = mUpdateHost;
+        msg.skip(2);                        // unknown
+
+        logger->log("Network: Server: %s (%s:%d)",
+                world->name.c_str(),
+                ipToString(world->address),
+                world->port);
+
+        mWorlds.push_back(world);
+    }
+    Client::setState(STATE_WORLD_SELECT);
+}
+
+void LoginHandler::processLoginError(Net::MessageIn &msg)
+{
+    int code = msg.readInt8();
+    logger->log("Login::error code: %i", code);
+
+    switch (code)
+    {
+        case 0:
+            errorMessage = _("Unregistered ID.");
+            break;
+        case 1:
+            errorMessage = _("Wrong password.");
+            break;
+        case 2:
+            errorMessage = _("Account expired.");
+            break;
+        case 3:
+            errorMessage = _("Rejected from server.");
+            break;
+        case 4:
+            errorMessage = _("You have been permanently banned from "
+                              "the game. Please contact the GM team.");
+            break;
+        case 5:
+            errorMessage = _("Client too old.");
+            break;
+        case 6:
+            errorMessage = strprintf(_("You have been temporarily "
+                                        "banned from the game until "
+                                        "%s.\nPlease contact the GM "
+                                        "team via the forums."),
+                                        msg.readString(20).c_str());
+            break;
+        case 7:
+            errorMessage = _("Server overpopulated.");
+            break;
+        case 9:
+            errorMessage = _("This user name is already taken.");
+            break;
+        case 10:
+            errorMessage = _("Wrong name.");
+            break;
+        case 99:
+            errorMessage = _("Username permanently erased.");
+            break;
+        default:
+            errorMessage = _("Unknown error.");
+            break;
+    }
+    Client::setState(STATE_ERROR);
+}
+
+void LoginHandler::logout()
+{
+    // TODO
+}
+
+void LoginHandler::changeEmail(const std::string &email A_UNUSED)
+{
+    // TODO
+}
+
+void LoginHandler::unregisterAccount(const std::string &username A_UNUSED,
+                                     const std::string &password A_UNUSED)
+{
+    // TODO
+}
+
+} // namespace Ea
diff --git a/src/net/ea/loginhandler.h b/src/net/ea/loginhandler.h
new file mode 100644
index 000000000..3b035b530
--- /dev/null
+++ b/src/net/ea/loginhandler.h
@@ -0,0 +1,107 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2004-2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program 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.
+ *
+ *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NET_EA_LOGINHANDLER_H
+#define NET_EA_LOGINHANDLER_H
+
+#include "net/loginhandler.h"
+#include "net/net.h"
+#include "net/messagein.h"
+
+#include "net/ea/token.h"
+
+#include <string>
+
+#ifdef __GNUC__
+#define A_UNUSED  __attribute__ ((unused))
+#else
+#define A_UNUSED
+#endif
+
+class LoginData;
+
+namespace Ea
+{
+
+class LoginHandler : public Net::LoginHandler
+{
+    public:
+        LoginHandler();
+
+        ~LoginHandler();
+
+        int supportedOptionalActions() const
+        { return SetGenderOnRegister; }
+
+        virtual bool isRegistrationEnabled();
+
+        virtual void getRegistrationDetails();
+
+        virtual unsigned int getMaxPasswordLength() const
+        { return 25; }
+
+        virtual void loginAccount(LoginData *loginData);
+
+        virtual void chooseServer(unsigned int server);
+
+        virtual void registerAccount(LoginData *loginData);
+
+        virtual Worlds getWorlds() const;
+
+        virtual void clearWorlds();
+
+        virtual ServerInfo *getCharServer() = 0;
+
+        const Token &getToken() const
+        { return mToken; }
+
+        virtual void logout();
+
+        virtual void changeEmail(const std::string &email);
+
+        virtual void unregisterAccount(const std::string &username,
+                                       const std::string &password);
+
+        virtual void procecessCharPasswordResponse(Net::MessageIn &msg);
+
+        virtual void processUpdateHost(Net::MessageIn &msg);
+
+        virtual void processLoginData(Net::MessageIn &msg);
+
+        virtual void processLoginError(Net::MessageIn &msg);
+
+        virtual void processServerVersion(Net::MessageIn &msg) = 0;
+
+    protected:
+        virtual void sendLoginRegister(const std::string &username,
+                                       const std::string &password) = 0;
+
+        bool mVersionResponse;
+        bool mRegistrationEnabled;
+        std::string mUpdateHost;
+        Worlds mWorlds;
+        Token mToken;
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_LOGINHANDLER_H
diff --git a/src/net/ea/token.h b/src/net/ea/token.h
new file mode 100644
index 000000000..40a7058ff
--- /dev/null
+++ b/src/net/ea/token.h
@@ -0,0 +1,44 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program 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.
+ *
+ *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "being.h"
+
+#ifndef NET_EA_TOKEN_H
+#define NET_EA_TOKEN_H
+
+struct Token
+{
+    int account_ID;
+    int session_ID1;
+    int session_ID2;
+    Gender sex;
+
+    void clear()
+    {
+        account_ID = 0;
+        session_ID1 = 0;
+        session_ID2 = 0;
+        sex = GENDER_UNSPECIFIED;
+    }
+};
+
+#endif // NET_EA_TOKEN_H
diff --git a/src/net/tmwa/charserverhandler.cpp b/src/net/tmwa/charserverhandler.cpp
index 0a28a9a8c..57f65cbd4 100644
--- a/src/net/tmwa/charserverhandler.cpp
+++ b/src/net/tmwa/charserverhandler.cpp
@@ -79,131 +79,32 @@ void CharServerHandler::handleMessage(Net::MessageIn &msg)
 {
     switch (msg.getId())
     {
-            case SMSG_CHAR_LOGIN:
-            {
-                msg.skip(2);  // Length word
-                int slots = msg.readInt16();
-                if (slots > 0 && slots < 30)
-                {
-                    loginData.characterSlots
-                        = static_cast<short unsigned int>(slots);
-                }
-                bool version = msg.readInt8() == 1 && serverVersion > 0;
-                msg.skip(17); // Unused
-
-                delete_all(mCharacters);
-                mCharacters.clear();
-
-                // Derive number of characters from message length
-                int count = (msg.getLength() - 24);
-                if (version)
-                    count /= 120;
-                else
-                    count /= 106;
-
-                for (int i = 0; i < count; ++i)
-                {
-                    Net::Character *character = new Net::Character;
-                    readPlayerData(msg, character, version);
-                    mCharacters.push_back(character);
-                    logger->log("CharServer: Player: %s (%d)",
-                        character->dummy->getName().c_str(), character->slot);
-                }
-
-                Client::setState(STATE_CHAR_SELECT);
-            }
+        case SMSG_CHAR_LOGIN:
+            processCharLogin(msg);
             break;
 
         case SMSG_CHAR_LOGIN_ERROR:
-            switch (msg.readInt8())
-            {
-                case 0:
-                    errorMessage = _("Access denied. Most likely, there are "
-                                     "too many players on this server.");
-                    break;
-                case 1:
-                    errorMessage = _("Cannot use this ID.");
-                    break;
-                default:
-                    errorMessage = _("Unknown char-server failure.");
-                    break;
-            }
-            Client::setState(STATE_ERROR);
+            processCharLoginError(msg);
             break;
 
         case SMSG_CHAR_CREATE_SUCCEEDED:
-            {
-                Net::Character *character = new Net::Character;
-                readPlayerData(msg, character, false);
-                mCharacters.push_back(character);
-
-                updateCharSelectDialog();
-
-                // Close the character create dialog
-                if (mCharCreateDialog)
-                {
-                    mCharCreateDialog->scheduleDelete();
-                    mCharCreateDialog = 0;
-                }
-            }
+            processCharCreate(msg, false);
             break;
 
         case SMSG_CHAR_CREATE_SUCCEEDED2:
-            {
-                Net::Character *character = new Net::Character;
-                readPlayerData(msg, character, true);
-                mCharacters.push_back(character);
-
-                updateCharSelectDialog();
-
-                // Close the character create dialog
-                if (mCharCreateDialog)
-                {
-                    mCharCreateDialog->scheduleDelete();
-                    mCharCreateDialog = 0;
-                }
-            }
+            processCharCreate(msg, true);
             break;
 
         case SMSG_CHAR_CREATE_FAILED:
-            switch (msg.readInt8())
-            {
-                case 1:
-                case 0:
-                default:
-                    errorMessage = _("Failed to create character. Most "
-                                       "likely the name is already taken.");
-                    break;
-                case 2:
-                    errorMessage = _("Wrong name.");
-                    break;
-                case 3:
-                    errorMessage = _("Incorrect stats.");
-                    break;
-                case 4:
-                    errorMessage = _("Incorrect hair.");
-                    break;
-                case 5:
-                    errorMessage = _("Incorrect slot.");
-                    break;
-            }
-            new OkDialog(_("Error"), errorMessage);
-            if (mCharCreateDialog)
-                mCharCreateDialog->unlock();
+            processCharCreateFailed(msg);
             break;
 
         case SMSG_CHAR_DELETE_SUCCEEDED:
-            delete mSelectedCharacter;
-            mCharacters.remove(mSelectedCharacter);
-            mSelectedCharacter = 0;
-            updateCharSelectDialog();
-            unlockCharSelectDialog();
-            new OkDialog(_("Info"), _("Character deleted."));
+            processCharDelete(msg);
             break;
 
         case SMSG_CHAR_DELETE_FAILED:
-            unlockCharSelectDialog();
-            new OkDialog(_("Error"), _("Failed to delete character."));
+            processCharDeleteFailed(msg);
             break;
 
         case SMSG_CHAR_MAP_INFO:
@@ -366,39 +267,6 @@ void CharServerHandler::readPlayerData(Net::MessageIn &msg,
     msg.readInt8();                        // unknown
 }
 
-void CharServerHandler::setCharSelectDialog(CharSelectDialog *window)
-{
-    mCharSelectDialog = window;
-    updateCharSelectDialog();
-}
-
-void CharServerHandler::setCharCreateDialog(CharCreateDialog *window)
-{
-    mCharCreateDialog = window;
-
-    if (!mCharCreateDialog)
-        return;
-
-    std::vector<std::string> attributes;
-    attributes.push_back(_("Strength:"));
-    attributes.push_back(_("Agility:"));
-    attributes.push_back(_("Vitality:"));
-    attributes.push_back(_("Intelligence:"));
-    attributes.push_back(_("Dexterity:"));
-    attributes.push_back(_("Luck:"));
-
-    const Token &token =
-        static_cast<LoginHandler*>(Net::getLoginHandler())->getToken();
-
-    mCharCreateDialog->setAttributes(attributes, 30, 1, 9);
-    mCharCreateDialog->setFixedGender(true, token.sex);
-}
-
-void CharServerHandler::requestCharacters()
-{
-    connect();
-}
-
 void CharServerHandler::chooseCharacter(Net::Character *character)
 {
     mSelectedCharacter = character;
@@ -444,21 +312,6 @@ void CharServerHandler::switchCharacter()
     outMsg.writeInt8(1);
 }
 
-unsigned int CharServerHandler::baseSprite() const
-{
-    return SPRITE_BASE;
-}
-
-unsigned int CharServerHandler::hairSprite() const
-{
-    return SPRITE_HAIR;
-}
-
-unsigned int CharServerHandler::maxSprite() const
-{
-    return SPRITE_VECTOREND;
-}
-
 void CharServerHandler::connect()
 {
     const Token &token =
@@ -485,4 +338,38 @@ void CharServerHandler::connect()
     mNetwork->skip(4);
 }
 
+void CharServerHandler::processCharLogin(Net::MessageIn &msg)
+{
+    msg.skip(2);  // Length word
+    int slots = msg.readInt16();
+    if (slots > 0 && slots < 30)
+    {
+        loginData.characterSlots
+            = static_cast<short unsigned int>(slots);
+    }
+    bool version = msg.readInt8() == 1 && serverVersion > 0;
+    msg.skip(17); // Unused
+
+    delete_all(mCharacters);
+    mCharacters.clear();
+
+    // Derive number of characters from message length
+    int count = (msg.getLength() - 24);
+    if (version)
+        count /= 120;
+    else
+        count /= 106;
+
+    for (int i = 0; i < count; ++i)
+    {
+        Net::Character *character = new Net::Character;
+        readPlayerData(msg, character, version);
+        mCharacters.push_back(character);
+        logger->log("CharServer: Player: %s (%d)",
+            character->dummy->getName().c_str(), character->slot);
+    }
+
+    Client::setState(STATE_CHAR_SELECT);
+}
+
 } // namespace TmwAthena
diff --git a/src/net/tmwa/charserverhandler.h b/src/net/tmwa/charserverhandler.h
index 62ff2b4de..4bd3153e8 100644
--- a/src/net/tmwa/charserverhandler.h
+++ b/src/net/tmwa/charserverhandler.h
@@ -24,10 +24,14 @@
 #define NET_TA_CHARSERVERHANDLER_H
 
 #include "net/charhandler.h"
+
+#include "net/ea/charserverhandler.h"
+
 #include "net/serverinfo.h"
 
 #include "net/tmwa/messagehandler.h"
-#include "net/tmwa/token.h"
+
+#include "net/ea/token.h"
 
 #ifdef __GNUC__
 #define A_UNUSED  __attribute__ ((unused))
@@ -43,24 +47,13 @@ namespace TmwAthena
 /**
  * Deals with incoming messages from the character server.
  */
-class CharServerHandler : public MessageHandler, public Net::CharHandler
+class CharServerHandler : public MessageHandler, public Ea::CharServerHandler
 {
     public:
         CharServerHandler();
 
         virtual void handleMessage(Net::MessageIn &msg);
 
-        void setCharSelectDialog(CharSelectDialog *window);
-
-        /**
-         * Sets the character create dialog. The handler will clean up this
-         * dialog when a new character is succesfully created, and will unlock
-         * the dialog when a new character failed to be created.
-         */
-        void setCharCreateDialog(CharCreateDialog *window);
-
-        void requestCharacters();
-
         void chooseCharacter(Net::Character *character);
 
         void newCharacter(const std::string &name, int slot,
@@ -72,15 +65,11 @@ class CharServerHandler : public MessageHandler, public Net::CharHandler
 
         void switchCharacter();
 
-        unsigned int baseSprite() const;
-
-        unsigned int hairSprite() const;
-
-        unsigned int maxSprite() const;
-
         void connect();
 
-    private:
+        void processCharLogin(Net::MessageIn &msg);
+
+    protected:
         void readPlayerData(Net::MessageIn &msg, Net::Character *character,
                             bool withColors);
 };
diff --git a/src/net/tmwa/gamehandler.h b/src/net/tmwa/gamehandler.h
index d6004054c..4abfd43c0 100644
--- a/src/net/tmwa/gamehandler.h
+++ b/src/net/tmwa/gamehandler.h
@@ -30,7 +30,8 @@
 #include "net/serverinfo.h"
 
 #include "net/tmwa/messagehandler.h"
-#include "net/tmwa/token.h"
+
+#include "net/ea/token.h"
 
 #ifdef __GNUC__
 #define A_UNUSED  __attribute__ ((unused))
diff --git a/src/net/tmwa/loginhandler.cpp b/src/net/tmwa/loginhandler.cpp
index 0cc1670bb..62d8833ec 100644
--- a/src/net/tmwa/loginhandler.cpp
+++ b/src/net/tmwa/loginhandler.cpp
@@ -46,9 +46,7 @@ namespace TmwAthena
 
 extern ServerInfo charServer;
 
-LoginHandler::LoginHandler():
-        mVersionResponse(false),
-        mRegistrationEnabled(true)
+LoginHandler::LoginHandler()
 {
     static const Uint16 _messages[] =
     {
@@ -65,172 +63,30 @@ LoginHandler::LoginHandler():
 
 LoginHandler::~LoginHandler()
 {
-    delete_all(mWorlds);
 }
 
 void LoginHandler::handleMessage(Net::MessageIn &msg)
 {
-    int code, worldCount;
-
     switch (msg.getId())
     {
         case SMSG_CHAR_PASSWORD_RESPONSE:
-        {
-            // 0: acc not found, 1: success, 2: password mismatch, 3: pass too short
-            int errMsg = msg.readInt8();
-            // Successful pass change
-            if (errMsg == 1)
-            {
-                Client::setState(STATE_CHANGEPASSWORD_SUCCESS);
-            }
-            // pass change failed
-            else
-            {
-                switch (errMsg)
-                {
-                    case 0:
-                        errorMessage =
-                            _("Account was not found. Please re-login.");
-                        break;
-                    case 2:
-                        errorMessage = _("Old password incorrect.");
-                        break;
-                    case 3:
-                        errorMessage = _("New password too short.");
-                        break;
-                    default:
-                        errorMessage = _("Unknown error.");
-                        break;
-                }
-                Client::setState(STATE_ACCOUNTCHANGE_ERROR);
-            }
-        }
+            procecessCharPasswordResponse(msg);
             break;
 
         case SMSG_UPDATE_HOST:
-             int len;
-
-             len = msg.readInt16() - 4;
-             mUpdateHost = msg.readString(len);
-             loginData.updateHost = mUpdateHost;
-
-             logger->log("Received update host \"%s\" from login server.",
-                     mUpdateHost.c_str());
-             break;
+            processUpdateHost(msg);
+            break;
 
         case SMSG_LOGIN_DATA:
-            // Skip the length word
-            msg.skip(2);
-
-            clearWorlds();
-
-            worldCount = (msg.getLength() - 47) / 32;
-
-            mToken.session_ID1 = msg.readInt32();
-            mToken.account_ID = msg.readInt32();
-            mToken.session_ID2 = msg.readInt32();
-            msg.skip(30);                           // unknown
-            // reserve bits for future usage
-            mToken.sex = (msg.readInt8() & 1) ? GENDER_MALE : GENDER_FEMALE;
-
-            for (int i = 0; i < worldCount; i++)
-            {
-                WorldInfo *world = new WorldInfo;
-
-                world->address = msg.readInt32();
-                world->port = msg.readInt16();
-                world->name = msg.readString(20);
-                world->online_users = msg.readInt32();
-                config.setValue("updatehost", mUpdateHost);
-                world->updateHost = mUpdateHost;
-                msg.skip(2);                        // unknown
-
-                logger->log("Network: Server: %s (%s:%d)",
-                        world->name.c_str(),
-                        ipToString(world->address),
-                        world->port);
-
-                mWorlds.push_back(world);
-            }
-            Client::setState(STATE_WORLD_SELECT);
+            processLoginData(msg);
             break;
 
         case SMSG_LOGIN_ERROR:
-            code = msg.readInt8();
-            logger->log("Login::error code: %i", code);
-
-            switch (code)
-            {
-                case 0:
-                    errorMessage = _("Unregistered ID.");
-                    break;
-                case 1:
-                    errorMessage = _("Wrong password.");
-                    break;
-                case 2:
-                    errorMessage = _("Account expired.");
-                    break;
-                case 3:
-                    errorMessage = _("Rejected from server.");
-                    break;
-                case 4:
-                    errorMessage = _("You have been permanently banned from "
-                                     "the game. Please contact the GM team.");
-                    break;
-                case 5:
-                    errorMessage = _("Client too old.");
-                    break;
-                case 6:
-                    errorMessage = strprintf(_("You have been temporarily "
-                                               "banned from the game until "
-                                               "%s.\nPlease contact the GM "
-                                               "team via the forums."),
-                                               msg.readString(20).c_str());
-                    break;
-                case 7:
-                    errorMessage = _("Server overpopulated.");
-                    break;
-                case 9:
-                    errorMessage = _("This user name is already taken.");
-                    break;
-                case 10:
-                    errorMessage = _("Wrong name.");
-                    break;
-                case 99:
-                    errorMessage = _("Username permanently erased.");
-                    break;
-                default:
-                    errorMessage = _("Unknown error.");
-                    break;
-            }
-            Client::setState(STATE_ERROR);
+            processLoginError(msg);
             break;
 
         case SMSG_SERVER_VERSION_RESPONSE:
-            {
-                // TODO: verify these!
-
-                char b1 = msg.readInt8(); // -1
-                char b2 = msg.readInt8(); // T
-                char b3 = msg.readInt8(); // M
-                char b4 = msg.readInt8(); // W
-                if (b1 == -1 && b2 == 'E' && b3 == 'V' && b4 == 'L')
-                {
-                    unsigned int options = msg.readInt8();
-                    mRegistrationEnabled = options;
-                    msg.skip(2);
-                    serverVersion = msg.readInt8();
-                }
-                else
-                {
-                    unsigned int options = msg.readInt32();
-                    mRegistrationEnabled = options;
-                    serverVersion = 0;
-                }
-
-                // Leave this last
-                mVersionResponse = true;
-            }
+            processServerVersion(msg);
             break;
 
         default:
@@ -261,42 +117,6 @@ void LoginHandler::disconnect()
         mNetwork->disconnect();
 }
 
-bool LoginHandler::isRegistrationEnabled()
-{
-    return mRegistrationEnabled;
-}
-
-void LoginHandler::getRegistrationDetails()
-{
-    // Not supported, so move on
-    Client::setState(STATE_REGISTER);
-}
-
-void LoginHandler::loginAccount(LoginData *loginData)
-{
-
-    if (loginData)
-    {
-        // Since we're attempting to use the tAthena protocol,
-        // let's reset the character slots to the good value,
-        // in case we just logged out a Manaserv server
-        // with a different config.
-        loginData->resetCharacterSlots();
-
-        sendLoginRegister(loginData->username, loginData->password);
-    }
-}
-
-void LoginHandler::logout()
-{
-    // TODO
-}
-
-void LoginHandler::changeEmail(const std::string &email A_UNUSED)
-{
-    // TODO
-}
-
 void LoginHandler::changePassword(const std::string &username A_UNUSED,
                                   const std::string &oldPassword,
                                   const std::string &newPassword)
@@ -306,38 +126,6 @@ void LoginHandler::changePassword(const std::string &username A_UNUSED,
     outMsg.writeString(newPassword, 24);
 }
 
-void LoginHandler::chooseServer(unsigned int server)
-{
-    if (server >= mWorlds.size() || !mWorlds[server])
-        return;
-
-    charServer.clear();
-    if (config.getBoolValue("usePersistentIP"))
-        charServer.hostname = Client::getServerName();
-    else
-        charServer.hostname = ipToString(mWorlds[server]->address);
-    charServer.port = mWorlds[server]->port;
-
-    Client::setState(STATE_UPDATE);
-}
-
-void LoginHandler::registerAccount(LoginData *loginData)
-{
-    if (!loginData)
-        return;
-
-    std::string username = loginData->username;
-    username.append((loginData->gender == GENDER_FEMALE) ? "_F" : "_M");
-
-    sendLoginRegister(username, loginData->password);
-}
-
-void LoginHandler::unregisterAccount(const std::string &username A_UNUSED,
-                                     const std::string &password A_UNUSED)
-{
-    // TODO
-}
-
 void LoginHandler::sendLoginRegister(const std::string &username,
                                      const std::string &password)
 {
@@ -355,15 +143,35 @@ void LoginHandler::sendLoginRegister(const std::string &username,
     outMsg.writeInt8(0x03);
 }
 
-Worlds LoginHandler::getWorlds() const
+ServerInfo *LoginHandler::getCharServer()
 {
-    return mWorlds;
+    return &charServer;
 }
 
-void LoginHandler::clearWorlds()
+void LoginHandler::processServerVersion(Net::MessageIn &msg)
 {
-    delete_all(mWorlds);
-    mWorlds.clear();
+    // TODO: verify these!
+
+    char b1 = msg.readInt8(); // -1
+    char b2 = msg.readInt8(); // T
+    char b3 = msg.readInt8(); // M
+    char b4 = msg.readInt8(); // W
+    if (b1 == -1 && b2 == 'E' && b3 == 'V' && b4 == 'L')
+    {
+        unsigned int options = msg.readInt8();
+        mRegistrationEnabled = options;
+        msg.skip(2);
+        serverVersion = msg.readInt8();
+    }
+    else
+    {
+        unsigned int options = msg.readInt32();
+        mRegistrationEnabled = options;
+        serverVersion = 0;
+    }
+
+    // Leave this last
+    mVersionResponse = true;
 }
 
 } // namespace TmwAthena
diff --git a/src/net/tmwa/loginhandler.h b/src/net/tmwa/loginhandler.h
index 8c6b48cc2..1e73d3c99 100644
--- a/src/net/tmwa/loginhandler.h
+++ b/src/net/tmwa/loginhandler.h
@@ -25,8 +25,11 @@
 
 #include "net/loginhandler.h"
 
+#include "net/ea/loginhandler.h"
+
 #include "net/tmwa/messagehandler.h"
-#include "net/tmwa/token.h"
+
+#include "net/ea/token.h"
 
 #include <string>
 
@@ -41,7 +44,7 @@ class LoginData;
 namespace TmwAthena
 {
 
-class LoginHandler : public MessageHandler, public Net::LoginHandler
+class LoginHandler : public MessageHandler, public Ea::LoginHandler
 {
     public:
         LoginHandler();
@@ -59,46 +62,20 @@ class LoginHandler : public MessageHandler, public Net::LoginHandler
         int supportedOptionalActions() const
         { return SetGenderOnRegister; }
 
-        bool isRegistrationEnabled();
-
-        void getRegistrationDetails();
-
         unsigned int getMaxPasswordLength() const
         { return 25; }
 
-        void loginAccount(LoginData *loginData);
-
-        void logout();
-
-        void changeEmail(const std::string &email);
-
         void changePassword(const std::string &username,
                             const std::string &oldPassword,
                             const std::string &newPassword);
 
-        void chooseServer(unsigned int server);
+        ServerInfo *getCharServer();
 
-        void registerAccount(LoginData *loginData);
-
-        void unregisterAccount(const std::string &username,
-                               const std::string &password);
-
-        Worlds getWorlds() const;
-
-        void clearWorlds();
-
-        const Token &getToken() const 
-        { return mToken; }
+        void processServerVersion(Net::MessageIn &msg);
 
     private:
         void sendLoginRegister(const std::string &username,
                                const std::string &password);
-
-        bool mVersionResponse;
-        bool mRegistrationEnabled;
-        std::string mUpdateHost;
-        Worlds mWorlds;
-        Token mToken;
 };
 
 } // namespace TmwAthena
diff --git a/src/net/tmwa/token.h b/src/net/tmwa/token.h
deleted file mode 100644
index ba39bb796..000000000
--- a/src/net/tmwa/token.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- *  The ManaPlus Client
- *  Copyright (C) 2009  The Mana World Development Team
- *  Copyright (C) 2009-2010  The Mana Developers
- *  Copyright (C) 2011  The ManaPlus Developers
- *
- *  This file is part of The ManaPlus Client.
- *
- *  This program 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.
- *
- *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "being.h"
-
-#ifndef NET_TA_TOKEN_H
-#define NET_TA_TOKEN_H
-
-struct Token
-{
-    int account_ID;
-    int session_ID1;
-    int session_ID2;
-    Gender sex;
-
-    void clear()
-    {
-        account_ID = 0;
-        session_ID1 = 0;
-        session_ID2 = 0;
-        sex = GENDER_UNSPECIFIED;
-    }
-};
-
-#endif // NET_TA_TOKEN_H
-- 
cgit v1.2.3-70-g09d2