summaryrefslogtreecommitdiff
path: root/src/net/eathena/charserverhandler.cpp
diff options
context:
space:
mode:
authorAndrei Karas <akaras@inbox.ru>2012-06-24 19:34:08 +0300
committerAndrei Karas <akaras@inbox.ru>2012-06-24 21:41:20 +0300
commit3b537e109df901df3da4c706f827c8d3d39a7238 (patch)
treeae5a83f71954aaeb45ce3f8d609e6f4a8dc0678c /src/net/eathena/charserverhandler.cpp
parent4ef35e9ef757da1db724c5d784048601144c934c (diff)
downloadmanaplus-3b537e109df901df3da4c706f827c8d3d39a7238.tar.gz
manaplus-3b537e109df901df3da4c706f827c8d3d39a7238.tar.bz2
manaplus-3b537e109df901df3da4c706f827c8d3d39a7238.tar.xz
manaplus-3b537e109df901df3da4c706f827c8d3d39a7238.zip
Add basic support for eathena stable.
Can register, create char, connect to map server and get map info.
Diffstat (limited to 'src/net/eathena/charserverhandler.cpp')
-rw-r--r--src/net/eathena/charserverhandler.cpp380
1 files changed, 380 insertions, 0 deletions
diff --git a/src/net/eathena/charserverhandler.cpp b/src/net/eathena/charserverhandler.cpp
new file mode 100644
index 000000000..28288f244
--- /dev/null
+++ b/src/net/eathena/charserverhandler.cpp
@@ -0,0 +1,380 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2012 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/eathena/charserverhandler.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "logger.h"
+
+#include "gui/charcreatedialog.h"
+
+#include "net/logindata.h"
+#include "net/messagein.h"
+#include "net/net.h"
+
+#include "net/eathena/gamehandler.h"
+#include "net/eathena/loginhandler.h"
+#include "net/eathena/network.h"
+#include "net/eathena/protocol.h"
+
+#include "resources/colordb.h"
+#include "resources/itemdb.h"
+#include "resources/iteminfo.h"
+
+#include "utils/dtor.h"
+
+#include "debug.h"
+
+extern Net::CharHandler *charHandler;
+
+namespace EAthena
+{
+
+extern ServerInfo charServer;
+extern ServerInfo mapServer;
+
+CharServerHandler::CharServerHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_CHAR_LOGIN,
+ SMSG_CHAR_LOGIN_ERROR,
+ SMSG_CHAR_CREATE_SUCCEEDED,
+ SMSG_CHAR_CREATE_SUCCEEDED2,
+ SMSG_CHAR_CREATE_FAILED,
+ SMSG_CHAR_DELETE_SUCCEEDED,
+ SMSG_CHAR_DELETE_FAILED,
+ SMSG_CHAR_MAP_INFO,
+ SMSG_CHANGE_MAP_SERVER,
+ 0
+ };
+ handledMessages = _messages;
+ charHandler = this;
+}
+
+void CharServerHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_CHAR_LOGIN:
+ processCharLogin(msg);
+ break;
+
+ case SMSG_CHAR_LOGIN_ERROR:
+ processCharLoginError(msg);
+ break;
+
+ case SMSG_CHAR_CREATE_SUCCEEDED:
+ processCharCreate(msg, false);
+ break;
+
+ case SMSG_CHAR_CREATE_SUCCEEDED2:
+ processCharCreate(msg, true);
+ break;
+
+ case SMSG_CHAR_CREATE_FAILED:
+ processCharCreateFailed(msg);
+ break;
+
+ case SMSG_CHAR_DELETE_SUCCEEDED:
+ processCharDelete(msg);
+ break;
+
+ case SMSG_CHAR_DELETE_FAILED:
+ processCharDeleteFailed(msg);
+ break;
+
+ case SMSG_CHAR_MAP_INFO:
+ {
+// msg.skip(4); // CharID, must be the same as player_node->charID
+ PlayerInfo::setCharId(msg.readInt32());
+ GameHandler *gh = static_cast<GameHandler*>(Net::getGameHandler());
+ gh->setMap(msg.readString(16));
+ if (config.getBoolValue("usePersistentIP"))
+ {
+ msg.readInt32();
+ mapServer.hostname = Client::getServerName();
+ }
+ else
+ {
+ mapServer.hostname = ipToString(msg.readInt32());
+ }
+ mapServer.port = msg.readInt16();
+
+ // Prevent the selected local player from being deleted
+ player_node = mSelectedCharacter->dummy;
+ PlayerInfo::setBackend(mSelectedCharacter->data);
+
+ mSelectedCharacter->dummy = nullptr;
+
+ delete_all(mCharacters);
+ mCharacters.clear();
+ updateCharSelectDialog();
+
+ if (mNetwork)
+ mNetwork->disconnect();
+ Client::setState(STATE_CONNECT_GAME);
+ }
+ break;
+
+ case SMSG_CHANGE_MAP_SERVER:
+ {
+ GameHandler *gh = static_cast<GameHandler*>(Net::getGameHandler());
+ if (!gh || !mNetwork)
+ return;
+ gh->setMap(msg.readString(16));
+ int x = msg.readInt16();
+ int y = msg.readInt16();
+ mapServer.hostname = ipToString(msg.readInt32());
+ mapServer.port = msg.readInt16();
+
+ mNetwork->disconnect();
+ Client::setState(STATE_CHANGE_MAP);
+ if (player_node)
+ {
+ player_node->setTileCoords(x, y);
+ player_node->setMap(nullptr);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void CharServerHandler::readPlayerData(Net::MessageIn &msg,
+ Net::Character *character,
+ bool withColors)
+{
+ if (!character)
+ return;
+
+ const Token &token =
+ static_cast<LoginHandler*>(Net::getLoginHandler())->getToken();
+
+ LocalPlayer *tempPlayer = new LocalPlayer(msg.readInt32(), 0);
+ tempPlayer->setGender(token.sex);
+
+ PlayerInfoBackend &data = character->data;
+ data.mAttributes[EXP] = msg.readInt32();
+ data.mAttributes[MONEY] = msg.readInt32();
+ data.mStats[JOB].exp = msg.readInt32();
+
+ int temp = msg.readInt32();
+ data.mStats[JOB].base = temp;
+ data.mStats[JOB].mod = temp;
+
+ int shoes = msg.readInt16();
+ int gloves = msg.readInt16();
+ int cape = msg.readInt16();
+ int misc1 = msg.readInt16();
+
+ msg.readInt32(); // option
+ msg.readInt32(); // karma
+ msg.readInt32(); // manner
+ msg.readInt16(); // character points left
+
+ data.mAttributes[HP] = msg.readInt16();
+ data.mAttributes[MAX_HP] = msg.readInt16();
+ data.mAttributes[MP] = msg.readInt16();
+ data.mAttributes[MAX_MP] = msg.readInt16();
+
+ msg.readInt16(); // speed
+ tempPlayer->setSubtype(msg.readInt16()); // class (used for race)
+ int hairStyle = msg.readInt16();
+ uint16_t weapon = msg.readInt16(); // server not used it. may be need use?
+ tempPlayer->setSprite(SPRITE_WEAPON, weapon, "", 1, true);
+
+ data.mAttributes[LEVEL] = msg.readInt16();
+
+ msg.readInt16(); // skill point
+ int bottomClothes = msg.readInt16();
+ int shield = msg.readInt16();
+
+ int hat = msg.readInt16(); // head option top
+ int topClothes = msg.readInt16();
+
+ tempPlayer->setSprite(SPRITE_HAIR, hairStyle * -1,
+ ItemDB::get(-hairStyle).getDyeColorsString(msg.readInt16()));
+
+ int misc2 = msg.readInt16();
+ tempPlayer->setName(msg.readString(24));
+
+ character->dummy = tempPlayer;
+
+ for (int i = 0; i < 6; i++)
+ character->data.mStats[i + STR].base = msg.readInt8();
+
+ if (withColors)
+ {
+ tempPlayer->setSprite(SPRITE_SHOE, shoes, "", msg.readInt8());
+ tempPlayer->setSprite(SPRITE_GLOVES, gloves, "", msg.readInt8());
+ tempPlayer->setSprite(SPRITE_CAPE, cape, "", msg.readInt8());
+ tempPlayer->setSprite(SPRITE_MISC1, misc1, "", msg.readInt8());
+ tempPlayer->setSprite(SPRITE_BOTTOMCLOTHES, bottomClothes,
+ "", msg.readInt8());
+ //to avoid show error (error.xml) need remove this sprite
+ if (!config.getBoolValue("hideShield"))
+ tempPlayer->setSprite(SPRITE_SHIELD, shield, "", msg.readInt8());
+ else
+ msg.readInt8();
+
+ tempPlayer->setSprite(SPRITE_HAT, hat, "",
+ msg.readInt8()); // head option top
+ tempPlayer->setSprite(SPRITE_TOPCLOTHES, topClothes, "",
+ msg.readInt8());
+ tempPlayer->setSprite(SPRITE_MISC2, misc2, "", msg.readInt8());
+ msg.skip(5);
+ character->slot = msg.readInt8(); // character slot
+ }
+ else
+ {
+ tempPlayer->setSprite(SPRITE_SHOE, shoes);
+ tempPlayer->setSprite(SPRITE_GLOVES, gloves);
+ tempPlayer->setSprite(SPRITE_CAPE, cape);
+ tempPlayer->setSprite(SPRITE_MISC1, misc1);
+ tempPlayer->setSprite(SPRITE_BOTTOMCLOTHES, bottomClothes);
+ //to avoid show error (error.xml) need remove this sprite
+ if (!config.getBoolValue("hideShield"))
+ tempPlayer->setSprite(SPRITE_SHIELD, shield);
+
+ tempPlayer->setSprite(SPRITE_HAT, hat); // head option top
+ tempPlayer->setSprite(SPRITE_TOPCLOTHES, topClothes);
+ tempPlayer->setSprite(SPRITE_MISC2, misc2);
+ character->slot = msg.readInt8(); // character slot
+ }
+
+ msg.readInt8(); // unknown
+}
+
+void CharServerHandler::chooseCharacter(Net::Character *character)
+{
+ if (!character)
+ return;
+
+ mSelectedCharacter = character;
+ mCharSelectDialog = nullptr;
+
+ MessageOut outMsg(CMSG_CHAR_SELECT);
+ outMsg.writeInt8(static_cast<unsigned char>(mSelectedCharacter->slot));
+}
+
+void CharServerHandler::newCharacter(const std::string &name, int slot,
+ bool gender A_UNUSED, int hairstyle,
+ int hairColor, unsigned char race,
+ const std::vector<int> &stats)
+{
+ MessageOut outMsg(CMSG_CHAR_CREATE);
+ outMsg.writeString(name, 24);
+ for (int i = 0; i < 6; i++)
+ outMsg.writeInt8(static_cast<unsigned char>(stats[i]));
+
+ outMsg.writeInt8(static_cast<unsigned char>(slot));
+ outMsg.writeInt16(static_cast<short>(hairColor));
+ outMsg.writeInt16(static_cast<short>(hairstyle));
+ if (serverVersion >= 2)
+ outMsg.writeInt8(race);
+}
+
+void CharServerHandler::deleteCharacter(Net::Character *character)
+{
+ if (!character)
+ return;
+
+ mSelectedCharacter = character;
+
+ MessageOut outMsg(CMSG_CHAR_DELETE);
+ outMsg.writeInt32(mSelectedCharacter->dummy->getId());
+ outMsg.writeString("a@a.com", 40);
+}
+
+void CharServerHandler::switchCharacter()
+{
+ // This is really a map-server packet
+ MessageOut outMsg(CMSG_PLAYER_RESTART);
+ outMsg.writeInt8(1);
+}
+
+void CharServerHandler::connect()
+{
+ const Token &token =
+ static_cast<LoginHandler*>(Net::getLoginHandler())->getToken();
+
+ if (!mNetwork)
+ return;
+
+ mNetwork->disconnect();
+ mNetwork->connect(charServer);
+ MessageOut outMsg(CMSG_CHAR_SERVER_CONNECT);
+ outMsg.writeInt32(token.account_ID);
+ outMsg.writeInt32(token.session_ID1);
+ outMsg.writeInt32(token.session_ID2);
+ // [Fate] The next word is unused by the old char server, so we squeeze in
+ // mana client version information
+ if (serverVersion > 0)
+ outMsg.writeInt16(CLIENT_PROTOCOL_VERSION);
+ else
+ outMsg.writeInt16(CLIENT_TMW_PROTOCOL_VERSION);
+ outMsg.writeInt8(Being::genderToInt(token.sex));
+
+ // We get 4 useless bytes before the real answer comes in (what are these?)
+ 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); // 0 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);
+ if (character && character->dummy)
+ {
+ logger->log("CharServer: Player: %s (%d)",
+ character->dummy->getName().c_str(), character->slot);
+ }
+ }
+
+ Client::setState(STATE_CHAR_SELECT);
+}
+
+} // namespace EAthena