summaryrefslogtreecommitdiff
path: root/src/account-server/accounthandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/account-server/accounthandler.cpp')
-rw-r--r--src/account-server/accounthandler.cpp707
1 files changed, 707 insertions, 0 deletions
diff --git a/src/account-server/accounthandler.cpp b/src/account-server/accounthandler.cpp
new file mode 100644
index 00000000..72a23818
--- /dev/null
+++ b/src/account-server/accounthandler.cpp
@@ -0,0 +1,707 @@
+/*
+ * The Mana World Server
+ * 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$
+ */
+
+#include "configuration.h"
+#include "debug.h"
+#include "point.h"
+#include "account-server/accounthandler.hpp"
+#include "account-server/account.hpp"
+#include "account-server/accountclient.hpp"
+#include "account-server/serverhandler.hpp"
+#include "account-server/storage.hpp"
+#include "chat-server/chathandler.hpp"
+#include "net/connectionhandler.hpp"
+#include "net/messagein.hpp"
+#include "net/messageout.hpp"
+#include "net/netcomputer.hpp"
+#include "utils/logger.h"
+#include "utils/stringfilter.h"
+
+bool
+AccountHandler::startListen(enet_uint16 port)
+{
+ LOG_INFO("Account handler started:", 0);
+ return ConnectionHandler::startListen(port);
+}
+
+NetComputer*
+AccountHandler::computerConnected(ENetPeer *peer)
+{
+ return new AccountClient(peer);
+}
+
+void
+AccountHandler::computerDisconnected(NetComputer *comp)
+{
+ delete comp;
+}
+
+/**
+ * Generic interface convention for getting a message and sending it to the
+ * correct subroutines. Account handler takes care of determining the
+ * current step in the account process, be it creation, setup, or login.
+ */
+void
+AccountHandler::processMessage(NetComputer *comp, MessageIn &message)
+{
+ AccountClient &computer = *static_cast< AccountClient * >(comp);
+
+ Storage &store = Storage::instance("tmw");
+
+#if defined (SQLITE_SUPPORT)
+ // Reopen the db in this thread for sqlite, to avoid
+ // Library Call out of sequence problem due to thread safe.
+ store.setUser(config.getValue("dbuser", ""));
+ store.setPassword(config.getValue("dbpass", ""));
+ store.close();
+ store.open();
+#endif
+
+ MessageOut result;
+
+ switch (message.getId())
+ {
+ case PAMSG_LOGIN:
+ handleLoginMessage(computer, message);
+ break;
+
+ case PAMSG_LOGOUT:
+ handleLogoutMessage(computer, message);
+ break;
+
+ case PAMSG_REGISTER:
+ handleRegisterMessage(computer, message);
+ break;
+
+ case PAMSG_UNREGISTER:
+ handleUnregisterMessage(computer, message);
+ break;
+
+ case PAMSG_EMAIL_CHANGE:
+ {
+ result.writeShort(APMSG_EMAIL_CHANGE_RESPONSE);
+
+ if (computer.getAccount().get() == NULL) {
+ result.writeByte(ERRMSG_NO_LOGIN);
+ LOG_INFO("Not logged in. Can't change your Account's Email.", 1);
+ break;
+ }
+
+ std::string email = message.readString();
+ if (!stringFilter->isEmailValid(email))
+ {
+ result.writeByte(ERRMSG_INVALID_ARGUMENT);
+ LOG_INFO(email << ": Invalid format, cannot change Email for " <<
+ computer.getAccount()->getName(), 1);
+ }
+ else if (stringFilter->findDoubleQuotes(email))
+ {
+ result.writeByte(ERRMSG_INVALID_ARGUMENT);
+ LOG_INFO(email << ": has got double quotes in it.", 1);
+ }
+ else if (store.doesEmailAddressExist(email))
+ {
+ result.writeByte(EMAILCHG_EXISTS_EMAIL);
+ LOG_INFO(email << ": New Email already exists.", 1);
+ }
+ else
+ {
+ computer.getAccount()->setEmail(email);
+ result.writeByte(ERRMSG_OK);
+ LOG_INFO(computer.getAccount()->getName() << ": Email changed to: " <<
+ email, 1);
+ }
+ }
+ break;
+
+ case PAMSG_EMAIL_GET:
+ {
+ result.writeShort(APMSG_EMAIL_GET_RESPONSE);
+ if (computer.getAccount().get() == NULL) {
+ result.writeByte(ERRMSG_NO_LOGIN);
+ LOG_INFO("Not logged in. Can't get your Account's current Email.", 1);
+ break;
+ }
+ else
+ {
+ result.writeByte(ERRMSG_OK);
+ result.writeString(computer.getAccount()->getEmail());
+ }
+ }
+ break;
+
+ case PAMSG_PASSWORD_CHANGE:
+ handlePasswordChangeMessage(computer, message);
+ break;
+
+ case PAMSG_CHAR_CREATE:
+ handleCharacterCreateMessage(computer, message);
+ break;
+
+ case PAMSG_CHAR_SELECT:
+ {
+ result.writeShort(APMSG_CHAR_SELECT_RESPONSE);
+
+ if (computer.getAccount().get() == NULL)
+ {
+ result.writeByte(ERRMSG_NO_LOGIN);
+ LOG_INFO("Not logged in. Can't select a Character.", 1);
+ break; // not logged in
+ }
+
+ unsigned char charNum = message.readByte();
+
+ Players &chars = computer.getAccount()->getCharacters();
+ // Character ID = 0 to Number of Characters - 1.
+ if (charNum >= chars.size()) {
+ // invalid char selection
+ result.writeByte(ERRMSG_INVALID_ARGUMENT);
+ LOG_INFO("Character Selection: Selection out of ID range.", 1);
+ break;
+ }
+
+ std::string address;
+ short port;
+ if (!serverHandler->getGameServerFromMap(chars[charNum]->getMapId(), address, port))
+ {
+ result.writeByte(ERRMSG_FAILURE);
+ LOG_ERROR("Character Selection: No game server for the map.", 0);
+ break;
+ }
+
+ // set character
+ computer.setCharacter(chars[charNum]);
+ PlayerPtr selectedChar = computer.getCharacter();
+ result.writeByte(ERRMSG_OK);
+
+ selectedChar->setDestination(selectedChar->getPosition());
+ selectedChar->setSpeed(150); // TODO
+
+ LOG_INFO(selectedChar->getName()
+ << " is trying to enter the servers.", 1);
+
+ std::string magic_token(32, ' ');
+ for (int i = 0; i < 32; ++i) {
+ magic_token[i] =
+ 1 + (int) (127 * (rand() / (RAND_MAX + 1.0)));
+ }
+ result.writeString(magic_token, 32);
+ result.writeString(address);
+ result.writeShort(port);
+ // TODO: get correct address and port for the chat server
+ result.writeString(config.getValue("accountServerAddress", "localhost"));
+ result.writeShort(int(config.getValue("accountServerPort", DEFAULT_SERVER_PORT)) + 2);
+
+ serverHandler->registerGameClient(magic_token, selectedChar);
+ registerChatClient(magic_token, selectedChar->getName(),
+ AL_NORMAL);
+ }
+ break;
+
+ case PAMSG_CHAR_DELETE:
+ {
+ result.writeShort(APMSG_CHAR_DELETE_RESPONSE);
+
+ if (computer.getAccount().get() == NULL)
+ {
+ result.writeByte(ERRMSG_NO_LOGIN);
+ LOG_INFO("Not logged in. Can't delete a Character.", 1);
+ break; // not logged in
+ }
+
+ unsigned char charNum = message.readByte();
+
+ Players &chars = computer.getAccount()->getCharacters();
+ // Character ID = 0 to Number of Characters - 1.
+ if (charNum >= chars.size()) {
+ // invalid char selection
+ result.writeByte(ERRMSG_INVALID_ARGUMENT);
+ LOG_INFO("Character Deletion : Selection out of ID range.", 1);
+ break;
+ }
+
+ // Delete the character
+ // if the character to delete is the current character, get off of it in
+ // memory.
+ if ( computer.getCharacter().get() != NULL )
+ {
+ if ( computer.getCharacter()->getName() == chars[charNum].get()->getName() )
+ {
+ computer.unsetCharacter();
+ }
+ }
+
+ std::string deletedCharacter = chars[charNum].get()->getName();
+ computer.getAccount()->delCharacter(deletedCharacter);
+ store.flush(computer.getAccount());
+ LOG_INFO(deletedCharacter << ": Character deleted...", 1);
+ result.writeByte(ERRMSG_OK);
+
+ }
+ break;
+
+ default:
+ LOG_WARN("Invalid message type", 0);
+ result.writeShort(XXMSG_INVALID);
+ break;
+ }
+
+ // return result
+ if (result.getLength() > 0)
+ computer.send(result);
+}
+
+void
+AccountHandler::handleLoginMessage(AccountClient &computer, MessageIn &msg)
+{
+ unsigned long clientVersion = msg.readLong();
+ std::string username = msg.readString();
+ std::string password = msg.readString();
+
+ LOG_INFO(username << " is trying to login.", 1);
+
+ MessageOut reply(APMSG_LOGIN_RESPONSE);
+
+ if (clientVersion < config.getValue("clientVersion", 0))
+ {
+ LOG_INFO("Client has an insufficient version number to login.", 1);
+ reply.writeByte(LOGIN_INVALID_VERSION);
+ }
+ if (stringFilter->findDoubleQuotes(username))
+ {
+ LOG_INFO(username << ": has got double quotes in it.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ if (computer.getAccount().get() != NULL) {
+ LOG_INFO("Already logged in as " << computer.getAccount()->getName()
+ << ".", 1);
+ LOG_INFO("Please logout first.", 1);
+ reply.writeByte(ERRMSG_FAILURE);
+ }
+ if (getClientNumber() >= MAX_CLIENTS )
+ {
+ LOG_INFO("Client couldn't login. Already has " << MAX_CLIENTS
+ << " logged in.", 1);
+ reply.writeByte(LOGIN_SERVER_FULL);
+ }
+ else
+ {
+ // Check if the account exists
+ Storage &store = Storage::instance("tmw");
+ AccountPtr acc = store.getAccount(username);
+
+ if (!acc.get() || acc->getPassword() != password)
+ {
+ LOG_INFO(username << ": Account does not exist or the password is "
+ "invalid.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else
+ {
+ LOG_INFO("Login OK by " << username, 1);
+
+ // Associate account with connection
+ computer.setAccount(acc);
+
+ reply.writeByte(ERRMSG_OK);
+ computer.send(reply);
+
+ // Return information about available characters
+ Players &chars = computer.getAccount()->getCharacters();
+
+ LOG_INFO(username << "'s account has " << chars.size()
+ << " character(s).", 1);
+
+ // Send characters list
+ for (unsigned int i = 0; i < chars.size(); i++)
+ {
+ MessageOut charInfo(APMSG_CHAR_INFO);
+ charInfo.writeByte(i); // Slot
+ charInfo.writeString(chars[i]->getName());
+ charInfo.writeByte((unsigned char) chars[i]->getGender());
+ charInfo.writeByte(chars[i]->getHairStyle());
+ charInfo.writeByte(chars[i]->getHairColor());
+ charInfo.writeByte(chars[i]->getLevel());
+ charInfo.writeShort(chars[i]->getMoney());
+ for (int j = 0; j < NB_RSTAT; ++j)
+ charInfo.writeShort(chars[i]->getRawStat(j));
+ computer.send(charInfo);
+ }
+ return;
+ }
+ }
+
+ computer.send(reply);
+}
+
+void
+AccountHandler::handleLogoutMessage(AccountClient &computer, MessageIn &msg)
+{
+ MessageOut reply(APMSG_LOGOUT_RESPONSE);
+
+ if (computer.getAccount().get() == NULL)
+ {
+ LOG_INFO("Can't logout. Not even logged in.", 1);
+ reply.writeByte(ERRMSG_NO_LOGIN);
+ }
+ else
+ {
+ LOG_INFO(computer.getAccount()->getName() << " logged out.", 1);
+ computer.unsetAccount();
+ reply.writeByte(ERRMSG_OK);
+ }
+
+ computer.send(reply);
+}
+
+void
+AccountHandler::handleRegisterMessage(AccountClient &computer, MessageIn &msg)
+{
+ unsigned long clientVersion = msg.readLong();
+ std::string username = msg.readString();
+ std::string password = msg.readString();
+ std::string email = msg.readString();
+
+ LOG_INFO(username << " is trying to register.", 1);
+
+ MessageOut reply(APMSG_REGISTER_RESPONSE);
+
+ if (clientVersion < config.getValue("clientVersion", 0))
+ {
+ LOG_INFO("Client has an unsufficient version number to login.", 1);
+ reply.writeByte(REGISTER_INVALID_VERSION);
+ }
+ else if (stringFilter->findDoubleQuotes(username))
+ {
+ LOG_INFO(username << ": has got double quotes in it.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else if (stringFilter->findDoubleQuotes(email))
+ {
+ LOG_INFO(email << ": has got double quotes in it.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else if ((username.length() < MIN_LOGIN_LENGTH) ||
+ (username.length() > MAX_LOGIN_LENGTH))
+ {
+ LOG_INFO(username << ": Username too short or too long.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else if ((password.length() < MIN_PASSWORD_LENGTH) ||
+ (password.length() > MAX_PASSWORD_LENGTH))
+ {
+ LOG_INFO(email << ": Password too short or too long.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else if (!stringFilter->isEmailValid(email))
+ {
+ LOG_INFO(email << ": Email Invalid, only a@b.c format is accepted.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ // Checking if the Name is slang's free.
+ else if (!stringFilter->filterContent(username))
+ {
+ LOG_INFO(username << ": has got bad words in it.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else
+ {
+ Storage &store = Storage::instance("tmw");
+ AccountPtr accPtr = store.getAccount(username);
+
+ // Check whether the account already exists.
+ if (accPtr.get())
+ {
+ LOG_INFO(username << ": Username already exists.", 1);
+ reply.writeByte(REGISTER_EXISTS_USERNAME);
+ }
+ // Find out whether the email is already in use.
+ else if (store.doesEmailAddressExist(email))
+ {
+ LOG_INFO(email << ": Email already exists.", 1);
+ reply.writeByte(REGISTER_EXISTS_EMAIL);
+ }
+ else
+ {
+ AccountPtr acc(new Account(username, password, email));
+ store.addAccount(acc);
+ LOG_INFO(username << ": Account registered.", 1);
+
+ reply.writeByte(ERRMSG_OK);
+ }
+ }
+
+ computer.send(reply);
+}
+
+void
+AccountHandler::handleUnregisterMessage(AccountClient &computer,
+ MessageIn &msg)
+{
+ std::string username = msg.readString();
+ std::string password = msg.readString();
+
+ LOG_INFO(username << " wants to be deleted from our accounts.", 1);
+
+ MessageOut reply(APMSG_UNREGISTER_RESPONSE);
+
+ if (stringFilter->findDoubleQuotes(username))
+ {
+ LOG_INFO(username << ": has got double quotes in it.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else
+ {
+ // See if the account exists
+ Storage &store = Storage::instance("tmw");
+ AccountPtr accPtr = store.getAccount(username);
+
+ if (!accPtr.get() || accPtr->getPassword() != password)
+ {
+ LOG_INFO("Account does not exist or bad password for "
+ << username << ".", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else
+ {
+ // If the account to delete is the current account we're logged in.
+ // Get out of it in memory.
+ if (computer.getAccount().get() != NULL )
+ {
+ if (computer.getAccount()->getName() == username)
+ {
+ computer.unsetAccount();
+ }
+ }
+
+ // Delete account and associated characters
+ LOG_INFO("Farewell " << username << " ...", 1);
+ store.delAccount(accPtr);
+ reply.writeByte(ERRMSG_OK);
+ }
+ }
+
+ computer.send(reply);
+}
+
+void
+AccountHandler::handlePasswordChangeMessage(AccountClient &computer,
+ MessageIn &msg)
+{
+ std::string oldPassword = msg.readString();
+ std::string newPassword = msg.readString();
+
+ MessageOut reply(APMSG_PASSWORD_CHANGE_RESPONSE);
+
+ if (computer.getAccount().get() == NULL)
+ {
+ LOG_INFO("Not logged in. Can't change your Account's Password.", 1);
+ reply.writeByte(ERRMSG_NO_LOGIN);
+ }
+ else if (newPassword.length() < MIN_PASSWORD_LENGTH ||
+ newPassword.length() > MAX_PASSWORD_LENGTH)
+ {
+ LOG_INFO(computer.getAccount()->getName() <<
+ ": New password too long or too short.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else if (stringFilter->findDoubleQuotes(newPassword))
+ {
+ LOG_INFO(newPassword << ": has got double quotes in it.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else if (oldPassword != computer.getAccount()->getPassword())
+ {
+ LOG_INFO(computer.getAccount()->getName() <<
+ ": Old password is wrong.", 1);
+ reply.writeByte(ERRMSG_FAILURE);
+ }
+ else
+ {
+ LOG_INFO(computer.getAccount()->getName() <<
+ ": The password was changed.", 1);
+ computer.getAccount()->setPassword(newPassword);
+ reply.writeByte(ERRMSG_OK);
+ }
+
+ computer.send(reply);
+}
+
+void
+AccountHandler::handleCharacterCreateMessage(AccountClient &computer,
+ MessageIn &msg)
+{
+ std::string name = msg.readString();
+ char hairStyle = msg.readByte();
+ char hairColor = msg.readByte();
+ Gender gender = (Gender) msg.readByte();
+
+ MessageOut reply(APMSG_CHAR_CREATE_RESPONSE);
+
+ if (computer.getAccount().get() == NULL) {
+ LOG_INFO("Not logged in. Can't create a Character.", 1);
+ reply.writeByte(ERRMSG_NO_LOGIN);
+ }
+ else if (!stringFilter->filterContent(name))
+ {
+ LOG_INFO(name << ": Character has got bad words in it.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else if (stringFilter->findDoubleQuotes(name))
+ {
+ LOG_INFO(name << ": has got double quotes in it.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else if ((hairStyle < 0) || (hairStyle > (signed) MAX_HAIRSTYLE_VALUE))
+ {
+ LOG_INFO(name << ": Character's hair Style is invalid.", 1);
+ reply.writeByte(CREATE_INVALID_HAIRSTYLE);
+ }
+ else if ((hairColor < 0) || (hairColor > (signed) MAX_HAIRCOLOR_VALUE))
+ {
+ LOG_INFO(name << ": Character's hair Color is invalid.", 1);
+ reply.writeByte(CREATE_INVALID_HAIRCOLOR);
+ }
+ else if ((gender < 0) || (gender > (signed) MAX_GENDER_VALUE))
+ {
+ LOG_INFO(name << ": Character's gender is invalid.", 1);
+ reply.writeByte(CREATE_INVALID_GENDER);
+ }
+ else if ((name.length() < MIN_CHARACTER_LENGTH) ||
+ (name.length() > MAX_CHARACTER_LENGTH))
+ {
+ LOG_INFO(name << ": Character's name too short or too long.", 1);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else
+ {
+ Storage &store = Storage::instance("tmw");
+ if (store.doesCharacterNameExist(name))
+ {
+ LOG_INFO(name << ": Character's name already exists.", 1);
+ reply.writeByte(CREATE_EXISTS_NAME);
+ computer.send(reply);
+ return;
+ }
+
+ // A player shouldn't have more than MAX_OF_CHARACTERS characters.
+ Players &chars = computer.getAccount()->getCharacters();
+ if (chars.size() >= MAX_OF_CHARACTERS)
+ {
+ LOG_INFO("Already has " << chars.size()
+ << " characters. Can't create another Character.", 1);
+ reply.writeByte(CREATE_TOO_MUCH_CHARACTERS);
+ computer.send(reply);
+ return;
+ }
+
+ // LATER_ON: Add race, face and maybe special attributes.
+
+ // Customization of player's stats...
+ RawStatistics rawStats;
+ for (int i = 0; i < NB_RSTAT; ++i)
+ rawStats.stats[i] = msg.readShort();
+
+ // We see if the difference between the lowest stat and the highest
+ // isn't too big.
+ unsigned short lowestStat = 0;
+ unsigned short highestStat = 0;
+ unsigned int totalStats = 0;
+ bool validNonZeroRawStats = true;
+ for (int i = 0; i < NB_RSTAT; ++i)
+ {
+ unsigned short stat = rawStats.stats[i];
+ // For good total stat check.
+ totalStats = totalStats + stat;
+
+ // For checking if all stats are at least > 0
+ if (stat <= 0) validNonZeroRawStats = false;
+ if (lowestStat == 0 || lowestStat > stat) lowestStat = stat;
+ if (highestStat == 0 || highestStat < stat) highestStat = stat;
+ }
+
+ if (totalStats > POINTS_TO_DISTRIBUTES_AT_LVL1)
+ {
+ LOG_INFO(name << ": Character's stats are too high to be of "
+ "level 1.", 1);
+ reply.writeByte(CREATE_RAW_STATS_TOO_HIGH);
+ }
+ else if (totalStats < POINTS_TO_DISTRIBUTES_AT_LVL1)
+ {
+ LOG_INFO(name << ": Character's stats are too low to be of "
+ "level 1.", 1);
+ reply.writeByte(CREATE_RAW_STATS_TOO_LOW);
+ }
+ else if ((highestStat - lowestStat) > (signed) MAX_DIFF_BETWEEN_STATS)
+ {
+ LOG_INFO(name << ": Character's stats difference is too high to "
+ "be accepted.", 1);
+ reply.writeByte(CREATE_RAW_STATS_INVALID_DIFF);
+ }
+ else if (!validNonZeroRawStats)
+ {
+ LOG_INFO(name << ": One stat is equal to zero.", 1);
+ reply.writeByte(CREATE_RAW_STATS_EQUAL_TO_ZERO);
+ }
+ else
+ {
+ PlayerPtr newCharacter(new Player(name));
+ for (int i = 0; i < NB_RSTAT; ++i)
+ newCharacter->setRawStat(i, rawStats.stats[i]);
+ newCharacter->setMoney(0);
+ newCharacter->setLevel(1);
+ newCharacter->setGender(gender);
+ newCharacter->setHairStyle(hairStyle);
+ newCharacter->setHairColor(hairColor);
+ newCharacter->setMapId((int) config.getValue("defaultMap", 1));
+ Point startingPos = { (int) config.getValue("startX", 0),
+ (int) config.getValue("startY", 0) };
+ newCharacter->setPosition(startingPos);
+ computer.getAccount()->addCharacter(newCharacter);
+
+ LOG_INFO("Character " << name << " was created for "
+ << computer.getAccount()->getName() << "'s account.", 1);
+
+ store.flush(computer.getAccount()); // flush changes
+ reply.writeByte(ERRMSG_OK);
+ computer.send(reply);
+
+ // Send new characters infos back to client
+ MessageOut charInfo(APMSG_CHAR_INFO);
+ int slot = chars.size() - 1;
+ charInfo.writeByte(slot);
+ charInfo.writeString(chars[slot]->getName());
+ charInfo.writeByte((unsigned char) chars[slot]->getGender());
+ charInfo.writeByte(chars[slot]->getHairStyle());
+ charInfo.writeByte(chars[slot]->getHairColor());
+ charInfo.writeByte(chars[slot]->getLevel());
+ charInfo.writeShort(chars[slot]->getMoney());
+ for (int j = 0; j < NB_RSTAT; ++j)
+ charInfo.writeShort(chars[slot]->getRawStat(j));
+ computer.send(charInfo);
+ return;
+ }
+ }
+
+ computer.send(reply);
+}