summaryrefslogtreecommitdiff
path: root/src/net/eathena
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
parent4ef35e9ef757da1db724c5d784048601144c934c (diff)
downloadManaVerse-3b537e109df901df3da4c706f827c8d3d39a7238.tar.gz
ManaVerse-3b537e109df901df3da4c706f827c8d3d39a7238.tar.bz2
ManaVerse-3b537e109df901df3da4c706f827c8d3d39a7238.tar.xz
ManaVerse-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')
-rw-r--r--src/net/eathena/adminhandler.cpp104
-rw-r--r--src/net/eathena/adminhandler.h54
-rw-r--r--src/net/eathena/beinghandler.cpp898
-rw-r--r--src/net/eathena/beinghandler.h61
-rw-r--r--src/net/eathena/buysellhandler.cpp148
-rw-r--r--src/net/eathena/buysellhandler.h53
-rw-r--r--src/net/eathena/charserverhandler.cpp380
-rw-r--r--src/net/eathena/charserverhandler.h73
-rw-r--r--src/net/eathena/chathandler.cpp265
-rw-r--r--src/net/eathena/chathandler.h63
-rw-r--r--src/net/eathena/gamehandler.cpp178
-rw-r--r--src/net/eathena/gamehandler.h65
-rw-r--r--src/net/eathena/generalhandler.cpp307
-rw-r--r--src/net/eathena/generalhandler.h80
-rw-r--r--src/net/eathena/gui/guildtab.cpp55
-rw-r--r--src/net/eathena/gui/guildtab.h44
-rw-r--r--src/net/eathena/gui/partytab.cpp58
-rw-r--r--src/net/eathena/gui/partytab.h44
-rw-r--r--src/net/eathena/guildhandler.cpp319
-rw-r--r--src/net/eathena/guildhandler.h72
-rw-r--r--src/net/eathena/inventoryhandler.cpp207
-rw-r--r--src/net/eathena/inventoryhandler.h61
-rw-r--r--src/net/eathena/itemhandler.cpp67
-rw-r--r--src/net/eathena/itemhandler.h43
-rw-r--r--src/net/eathena/loginhandler.cpp218
-rw-r--r--src/net/eathena/loginhandler.h81
-rw-r--r--src/net/eathena/messagehandler.cpp51
-rw-r--r--src/net/eathena/messagehandler.h60
-rw-r--r--src/net/eathena/messagein.cpp84
-rw-r--r--src/net/eathena/messagein.h53
-rw-r--r--src/net/eathena/messageout.cpp147
-rw-r--r--src/net/eathena/messageout.h70
-rw-r--r--src/net/eathena/network.cpp520
-rw-r--r--src/net/eathena/network.h138
-rw-r--r--src/net/eathena/npchandler.cpp309
-rw-r--r--src/net/eathena/npchandler.h79
-rw-r--r--src/net/eathena/partyhandler.cpp210
-rw-r--r--src/net/eathena/partyhandler.h69
-rw-r--r--src/net/eathena/playerhandler.cpp313
-rw-r--r--src/net/eathena/playerhandler.h66
-rw-r--r--src/net/eathena/protocol.h347
-rw-r--r--src/net/eathena/specialhandler.cpp96
-rw-r--r--src/net/eathena/specialhandler.h52
-rw-r--r--src/net/eathena/tradehandler.cpp148
-rw-r--r--src/net/eathena/tradehandler.h60
45 files changed, 6870 insertions, 0 deletions
diff --git a/src/net/eathena/adminhandler.cpp b/src/net/eathena/adminhandler.cpp
new file mode 100644
index 000000000..0ff3aa03d
--- /dev/null
+++ b/src/net/eathena/adminhandler.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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/adminhandler.h"
+
+#include "net/ea/adminhandler.h"
+
+#include "actorspritemanager.h"
+#include "being.h"
+#include "depricatedevent.h"
+#include "game.h"
+#include "logger.h"
+#include "playerrelations.h"
+
+#include "net/chathandler.h"
+#include "net/net.h"
+
+#include "net/eathena/protocol.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include <string>
+
+#include "debug.h"
+
+extern Net::AdminHandler *adminHandler;
+
+namespace EAthena
+{
+
+AdminHandler::AdminHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_ADMIN_KICK_ACK,
+ 0
+ };
+ handledMessages = _messages;
+ adminHandler = this;
+}
+
+void AdminHandler::handleMessage(Net::MessageIn &msg)
+{
+ int id;
+ switch (msg.getId())
+ {
+ case SMSG_ADMIN_KICK_ACK:
+ id = msg.readInt32();
+ if (id == 0)
+ SERVER_NOTICE(_("Kick failed!"))
+ else
+ SERVER_NOTICE(_("Kick succeeded!"))
+ default:
+ break;
+ }
+}
+
+void AdminHandler::announce(const std::string &text)
+{
+ MessageOut outMsg(CMSG_ADMIN_ANNOUNCE);
+ outMsg.writeInt16(static_cast<int16_t>(text.length() + 4));
+ outMsg.writeString(text, static_cast<int>(text.length()));
+}
+
+void AdminHandler::localAnnounce(const std::string &text)
+{
+ MessageOut outMsg(CMSG_ADMIN_LOCAL_ANNOUNCE);
+ outMsg.writeInt16(static_cast<int16_t>(text.length() + 4));
+ outMsg.writeString(text, static_cast<int>(text.length()));
+}
+
+void AdminHandler::hide(bool h A_UNUSED)
+{
+ MessageOut outMsg(CMSG_ADMIN_HIDE);
+ outMsg.writeInt32(0); //unused
+}
+
+void AdminHandler::kick(int playerId)
+{
+ MessageOut outMsg(CMSG_ADMIN_KICK);
+ outMsg.writeInt32(playerId);
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/adminhandler.h b/src/net/eathena/adminhandler.h
new file mode 100644
index 000000000..85b0da0bd
--- /dev/null
+++ b/src/net/eathena/adminhandler.h
@@ -0,0 +1,54 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_ADMINHANDLER_H
+#define NET_EATHENA_ADMINHANDLER_H
+
+#include "net/adminhandler.h"
+#include "net/net.h"
+
+#include "net/ea/adminhandler.h"
+
+#include "net/eathena/messagehandler.h"
+
+namespace EAthena
+{
+
+class AdminHandler : public MessageHandler, public Ea::AdminHandler
+{
+ public:
+ AdminHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void announce(const std::string &text);
+
+ void localAnnounce(const std::string &text);
+
+ void hide(bool h);
+
+ void kick(int playerId);
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_ADMINHANDLER_H
diff --git a/src/net/eathena/beinghandler.cpp b/src/net/eathena/beinghandler.cpp
new file mode 100644
index 000000000..f7d2d3ea1
--- /dev/null
+++ b/src/net/eathena/beinghandler.cpp
@@ -0,0 +1,898 @@
+/*
+ * 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/beinghandler.h"
+
+#include "actorspritemanager.h"
+#include "being.h"
+#include "client.h"
+#include "effectmanager.h"
+#include "guild.h"
+#include "guildmanager.h"
+#include "keyboardconfig.h"
+#include "localplayer.h"
+#include "logger.h"
+#include "party.h"
+#include "playerrelations.h"
+#include "configuration.h"
+
+#include "gui/botcheckerwindow.h"
+#include "gui/outfitwindow.h"
+#include "gui/socialwindow.h"
+#include "gui/killstats.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include "net/playerhandler.h"
+#include "net/net.h"
+
+#include "net/eathena/protocol.h"
+
+#include "resources/colordb.h"
+#include "resources/itemdb.h"
+#include "resources/iteminfo.h"
+
+#include <iostream>
+
+#include "debug.h"
+
+extern Net::BeingHandler *beingHandler;
+
+namespace EAthena
+{
+
+BeingHandler::BeingHandler(bool enableSync):
+ Ea::BeingHandler(enableSync)
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_BEING_VISIBLE,
+ SMSG_BEING_MOVE,
+ SMSG_BEING_MOVE2,
+ SMSG_BEING_REMOVE,
+ SMSG_SKILL_DAMAGE,
+ SMSG_BEING_ACTION,
+ SMSG_BEING_SELFEFFECT,
+ SMSG_BEING_EMOTION,
+ SMSG_BEING_CHANGE_LOOKS,
+ SMSG_BEING_CHANGE_LOOKS2,
+ SMSG_BEING_NAME_RESPONSE,
+ SMSG_BEING_NAME_RESPONSE2,
+ SMSG_PLAYER_GUILD_PARTY_INFO,
+ SMSG_BEING_CHANGE_DIRECTION,
+ SMSG_PLAYER_UPDATE_1,
+ SMSG_PLAYER_UPDATE_2,
+ SMSG_PLAYER_MOVE,
+ SMSG_PLAYER_STOP,
+ SMSG_PLAYER_MOVE_TO_ATTACK,
+ SMSG_PLAYER_STATUS_CHANGE,
+ SMSG_BEING_STATUS_CHANGE,
+ SMSG_BEING_RESURRECT,
+ SMSG_SOLVE_CHAR_NAME,
+ SMSG_BEING_SPAWN,
+ SMSG_SKILL_CASTING,
+ SMSG_SKILL_CAST_CANCEL,
+ SMSG_SKILL_NO_DAMAGE,
+ SMSG_BEING_IP_RESPONSE,
+ SMSG_PVP_MAP_MODE,
+ SMSG_PVP_SET,
+ 0
+ };
+ handledMessages = _messages;
+ beingHandler = this;
+}
+
+void BeingHandler::requestNameById(int id)
+{
+ MessageOut outMsg(0x0094);
+ outMsg.writeInt32(id); //readLong(2));
+}
+
+void BeingHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_BEING_VISIBLE: // changed
+ case SMSG_BEING_MOVE:
+ processBeingVisibleOrMove(msg, msg.getId() == SMSG_BEING_VISIBLE);
+ break;
+
+ case SMSG_BEING_MOVE2:
+ processBeingMove2(msg);
+ break;
+
+ case SMSG_BEING_SPAWN: // changed
+ processBeingSpawn(msg);
+ break;
+
+ case SMSG_BEING_REMOVE:
+ processBeingRemove(msg);
+ break;
+
+ case SMSG_BEING_RESURRECT:
+ processBeingResurrect(msg);
+ break;
+
+ case SMSG_SKILL_DAMAGE:
+ processSkillDamage(msg);
+ break;
+
+ case SMSG_BEING_ACTION:
+ processBeingAction(msg);
+ break;
+
+ case SMSG_BEING_SELFEFFECT:
+ processBeingSelfEffect(msg);
+ break;
+
+ case SMSG_BEING_EMOTION:
+ processBeingEmotion(msg);
+ break;
+
+ case SMSG_BEING_CHANGE_LOOKS:
+ case SMSG_BEING_CHANGE_LOOKS2:
+ processBeingChangeLook(msg,
+ msg.getId() == SMSG_BEING_CHANGE_LOOKS2);
+ break;
+
+ case SMSG_BEING_NAME_RESPONSE:
+ processNameResponse(msg);
+ break;
+
+ case SMSG_BEING_NAME_RESPONSE2:
+ processNameResponse2(msg);
+ break;
+
+ case SMSG_BEING_IP_RESPONSE:
+ processIpResponse(msg);
+ break;
+
+ case SMSG_SOLVE_CHAR_NAME:
+ break;
+
+ case SMSG_PLAYER_GUILD_PARTY_INFO:
+ processPlayerGuilPartyInfo(msg);
+ break;
+
+ case SMSG_BEING_CHANGE_DIRECTION:
+ processBeingChangeDirection(msg);
+ break;
+
+ case SMSG_PLAYER_UPDATE_1:
+ case SMSG_PLAYER_UPDATE_2:
+ case SMSG_PLAYER_MOVE:
+ int type;
+ switch (msg.getId())
+ {
+ case SMSG_PLAYER_UPDATE_1:
+ type = 1;
+ break;
+ case SMSG_PLAYER_UPDATE_2:
+ type = 2;
+ break;
+ case SMSG_PLAYER_MOVE:
+ type = 3;
+ break;
+ default:
+ return;
+ }
+ processPlayerMoveUpdate(msg, type);
+
+ break;
+ case SMSG_PLAYER_STOP:
+ processPlayerStop(msg);
+ break;
+
+ case SMSG_PLAYER_MOVE_TO_ATTACK:
+ processPlayerMoveToAttack(msg);
+ break;
+
+ case SMSG_PLAYER_STATUS_CHANGE:
+ processPlaterStatusChange(msg);
+ break;
+
+ case SMSG_BEING_STATUS_CHANGE:
+ processBeingStatusChange(msg);
+ break;
+
+ case SMSG_SKILL_CASTING:
+ processSkilCasting(msg);
+ break;
+
+ case SMSG_SKILL_CAST_CANCEL:
+ msg.readInt32(); // id
+ break;
+
+ case SMSG_SKILL_NO_DAMAGE:
+ processSkillNoDamage(msg);
+ break;
+
+ case SMSG_PVP_MAP_MODE:
+ processPvpMapMode(msg);
+ break;
+
+ case SMSG_PVP_SET:
+ processPvpSet(msg);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void BeingHandler::undress(Being *being)
+{
+ being->setSprite(SPRITE_BOTTOMCLOTHES, 0);
+ being->setSprite(SPRITE_TOPCLOTHES, 0);
+ being->setSprite(SPRITE_HAT, 0);
+ being->setSprite(SPRITE_SHOE, 0);
+ being->setSprite(SPRITE_GLOVES, 0);
+// being->setSprite(SPRITE_WEAPON, 0, "", true);
+}
+
+void BeingHandler::processBeingChangeLook(Net::MessageIn &msg, bool look2)
+{
+ if (!actorSpriteManager)
+ return;
+
+ Being *dstBeing;
+ /*
+ * SMSG_BEING_CHANGE_LOOKS (0x00c3) and
+ * SMSG_BEING_CHANGE_LOOKS2 (0x01d7) do basically the same
+ * thing. The difference is that ...LOOKS carries a single
+ * 8 bit value, where ...LOOKS2 carries two 16 bit values.
+ *
+ * If type = 2, then the first 16 bit value is the weapon ID,
+ * and the second 16 bit value is the shield ID. If no
+ * shield is equipped, or type is not 2, then the second
+ * 16 bit value will be 0.
+ */
+
+ if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32())))
+ return;
+
+ int type = msg.readInt8();
+ int id = 0;
+ int id2 = 0;
+ std::string color;
+
+ if (!look2)
+ {
+ id = msg.readInt8();
+ id2 = 1; // default color
+ }
+ else
+ { // SMSG_BEING_CHANGE_LOOKS2
+ id = msg.readInt16();
+ if (type == 2 || serverVersion > 0)
+ id2 = msg.readInt16();
+ else
+ id2 = 1;
+ color = "";
+ }
+
+ if (dstBeing->getType() == Being::PLAYER)
+ dstBeing->setOtherTime();
+
+ if (!player_node)
+ return;
+
+ switch (type)
+ {
+ case 0: // change race
+ dstBeing->setSubtype(static_cast<uint16_t>(id));
+ break;
+ case 1: // eAthena LOOK_HAIR
+ dstBeing->setSpriteID(SPRITE_HAIR, id *-1);
+ break;
+ case 2: // Weapon ID in id, Shield ID in id2
+ dstBeing->setSprite(SPRITE_WEAPON, id, "", 1, true);
+ if (!config.getBoolValue("hideShield"))
+ dstBeing->setSprite(SPRITE_SHIELD, id2);
+ player_node->imitateOutfit(dstBeing, SPRITE_SHIELD);
+ break;
+ case 3: // Change lower headgear for eAthena, pants for us
+ dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, id, color,
+ static_cast<unsigned char>(id2));
+ player_node->imitateOutfit(dstBeing, SPRITE_BOTTOMCLOTHES);
+ break;
+ case 4: // Change upper headgear for eAthena, hat for us
+ dstBeing->setSprite(SPRITE_HAT, id, color,
+ static_cast<unsigned char>(id2));
+ player_node->imitateOutfit(dstBeing, SPRITE_HAT);
+ break;
+ case 5: // Change middle headgear for eathena, armor for us
+ dstBeing->setSprite(SPRITE_TOPCLOTHES, id, color,
+ static_cast<unsigned char>(id2));
+ player_node->imitateOutfit(dstBeing, SPRITE_TOPCLOTHES);
+ break;
+ case 6: // eAthena LOOK_HAIR_COLOR
+ dstBeing->setSpriteColor(SPRITE_HAIR, ItemDB::get(
+ dstBeing->getSpriteID(SPRITE_HAIR)).getDyeColorsString(id));
+ break;
+ case 7: // Clothes color
+ // ignoring it
+ break;
+ case 8: // eAthena LOOK_SHIELD
+ if (!config.getBoolValue("hideShield"))
+ {
+ dstBeing->setSprite(SPRITE_SHIELD, id, color,
+ static_cast<unsigned char>(id2));
+ }
+ player_node->imitateOutfit(dstBeing, SPRITE_SHIELD);
+ break;
+ case 9: // eAthena LOOK_SHOES
+ dstBeing->setSprite(SPRITE_SHOE, id, color,
+ static_cast<unsigned char>(id2));
+ player_node->imitateOutfit(dstBeing, SPRITE_SHOE);
+ break;
+ case 10: // LOOK_GLOVES
+ dstBeing->setSprite(SPRITE_GLOVES, id, color,
+ static_cast<unsigned char>(id2));
+ player_node->imitateOutfit(dstBeing, SPRITE_GLOVES);
+ break;
+ case 11: // LOOK_CAPE
+ dstBeing->setSprite(SPRITE_CAPE, id, color,
+ static_cast<unsigned char>(id2));
+ player_node->imitateOutfit(dstBeing, SPRITE_CAPE);
+ break;
+ case 12:
+ dstBeing->setSprite(SPRITE_MISC1, id, color,
+ static_cast<unsigned char>(id2));
+ player_node->imitateOutfit(dstBeing, SPRITE_MISC1);
+ break;
+ case 13:
+ dstBeing->setSprite(SPRITE_MISC2, id, color,
+ static_cast<unsigned char>(id2));
+ player_node->imitateOutfit(dstBeing, SPRITE_MISC2);
+ break;
+ case 14:
+ dstBeing->setSprite(SPRITE_EVOL1, id, color,
+ static_cast<unsigned char>(id2));
+ player_node->imitateOutfit(dstBeing, SPRITE_EVOL1);
+ break;
+ case 15:
+ dstBeing->setSprite(SPRITE_EVOL2, id, color,
+ static_cast<unsigned char>(id2));
+ player_node->imitateOutfit(dstBeing, SPRITE_EVOL2);
+ break;
+ default:
+ logger->log("QQQ3 CHANGE_LOOKS: unsupported type: "
+ "%d, id: %d", type, id);
+ if (dstBeing)
+ {
+ logger->log("ID: " + toString(dstBeing->getId()));
+ logger->log("name: " + toString(dstBeing->getName()));
+ }
+ break;
+ }
+}
+
+void BeingHandler::processNameResponse2(Net::MessageIn &msg)
+{
+ if (!actorSpriteManager || !player_node)
+ return;
+
+ Being *dstBeing;
+
+ int len = msg.readInt16();
+ int beingId = msg.readInt32();
+ std::string str = msg.readString(len - 8);
+ if ((dstBeing = actorSpriteManager->findBeing(beingId)))
+ {
+ if (beingId == player_node->getId())
+ {
+ player_node->pingResponse();
+ }
+ else
+ {
+ dstBeing->setName(str);
+ dstBeing->updateGuild();
+ dstBeing->addToCache();
+
+ if (dstBeing->getType() == Being::PLAYER)
+ dstBeing->updateColors();
+
+ if (player_node)
+ {
+ Party *party = player_node->getParty();
+ if (party && party->isMember(dstBeing->getId()))
+ {
+ PartyMember *member = party->getMember(
+ dstBeing->getId());
+
+ if (member)
+ member->setName(dstBeing->getName());
+ }
+ player_node->checkNewName(dstBeing);
+ }
+ }
+ }
+}
+
+void BeingHandler::processPlayerMoveUpdate(Net::MessageIn &msg, int msgType)
+{
+ if (!actorSpriteManager || !player_node)
+ return;
+
+ uint16_t headTop, headMid, headBottom;
+ uint16_t weapon, shield;
+ uint16_t gmstatus;
+ int level;
+ int guild;
+ Being *dstBeing;
+ int hairStyle, hairColor;
+ unsigned char colors[9];
+
+
+ // An update about a player, potentially including movement.
+ int id = msg.readInt32();
+ short speed = msg.readInt16();
+ uint16_t stunMode = msg.readInt16(); // opt1; Aethyra use this as cape
+ uint32_t statusEffects = msg.readInt16(); // opt2;
+ // Aethyra use this as misc1
+ statusEffects |= (static_cast<uint32_t>(msg.readInt16()))
+ << 16; // status.options; Aethyra uses this as misc2
+ short job = msg.readInt16();
+
+ dstBeing = actorSpriteManager->findBeing(id);
+
+ if (!dstBeing)
+ {
+ if (actorSpriteManager->isBlocked(id) == true)
+ return;
+
+ dstBeing = createBeing(id, job);
+
+ if (!dstBeing)
+ return;
+ }
+
+ uint8_t dir = dstBeing->getDirectionDelayed();
+ if (dir)
+ {
+ if (dir != dstBeing->getDirection())
+ dstBeing->setDirection(dir);
+ }
+
+ if (Party *party = player_node->getParty())
+ {
+ if (party->isMember(id))
+ dstBeing->setParty(party);
+ }
+
+ dstBeing->setWalkSpeed(Vector(speed, speed, 0));
+ dstBeing->setSubtype(job);
+ hairStyle = msg.readInt16();
+ weapon = msg.readInt16();
+ shield = msg.readInt16();
+ headBottom = msg.readInt16();
+
+ if (msgType == 3)
+ msg.readInt32(); // server tick
+
+ headTop = msg.readInt16();
+ headMid = msg.readInt16();
+ hairColor = msg.readInt16();
+
+ colors[0] = msg.readInt8();
+ colors[1] = msg.readInt8();
+ colors[2] = msg.readInt8();
+
+ msg.readInt8(); //unused
+// shoes = msg.readInt16();
+// gloves = msg.readInt16(); //sd->head_dir
+
+ guild = msg.readInt32(); // guild
+
+ if (!guildManager || !GuildManager::getEnableGuildBot())
+ {
+ if (guild == 0)
+ dstBeing->clearGuilds();
+ else
+ dstBeing->setGuild(Guild::getGuild(static_cast<short>(guild)));
+ }
+
+ msg.readInt16(); // emblem
+ msg.readInt16(); // manner
+ dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3
+ msg.readInt8(); // karma
+ // reserving bit for future usage
+ dstBeing->setGender(Being::intToGender(msg.readInt8() & 3));
+
+ // Set these after the gender, as the sprites may be gender-specific
+ dstBeing->setSprite(SPRITE_WEAPON, weapon, "", 1, true);
+ if (!config.getBoolValue("hideShield"))
+ dstBeing->setSprite(SPRITE_SHIELD, shield);
+ //dstBeing->setSprite(SPRITE_SHOE, shoes);
+ if (serverVersion > 0)
+ {
+ dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom,
+ "", colors[0]);
+ dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid, "", colors[2]);
+ dstBeing->setSprite(SPRITE_HAT, headTop, "", colors[1]);
+ }
+ else
+ {
+ dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom);
+ dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid);
+ dstBeing->setSprite(SPRITE_HAT, headTop);
+ }
+ //dstBeing->setSprite(SPRITE_GLOVES, gloves);
+ //dstBeing->setSprite(SPRITE_CAPE, cape);
+ //dstBeing->setSprite(SPRITE_MISC1, misc1);
+ //dstBeing->setSprite(SPRITE_MISC2, misc2);
+ dstBeing->setSprite(SPRITE_HAIR, hairStyle * -1,
+ ItemDB::get(-hairStyle).getDyeColorsString(hairColor));
+
+ player_node->imitateOutfit(dstBeing);
+
+ if (msgType == 3)
+ {
+ uint16_t srcX, srcY, dstX, dstY;
+ msg.readCoordinatePair(srcX, srcY, dstX, dstY);
+
+ player_node->followMoveTo(dstBeing, srcX, srcY, dstX, dstY);
+
+ dstBeing->setTileCoords(srcX, srcY);
+ dstBeing->setDestination(dstX, dstY);
+
+ // because server don't send direction in move packet,
+ // we fixing it
+
+ if (srcX != dstX || srcY != dstY)
+ {
+ int d = dstBeing->calcDirection(dstX, dstY);
+
+ if (d && dstBeing->getDirection() != d)
+ dstBeing->setDirectionDelayed(static_cast<uint8_t>(d));
+ }
+
+ if (player_node->getCurrentAction() != Being::STAND)
+ player_node->imitateAction(dstBeing, Being::STAND);
+ if (player_node->getDirection() != dstBeing->getDirection())
+ {
+ player_node->imitateDirection(dstBeing,
+ dstBeing->getDirection());
+ }
+ }
+ else
+ {
+// uint8_t dir;
+ uint16_t x, y;
+ msg.readCoordinates(x, y, dir);
+ dstBeing->setTileCoords(x, y);
+ dstBeing->setDirection(dir);
+
+ player_node->imitateDirection(dstBeing, dir);
+ }
+
+ gmstatus = msg.readInt16();
+
+ if (gmstatus & 0x80)
+ dstBeing->setGM(true);
+
+ if (msgType == 1 || msgType == 2)
+ {
+ int type = msg.readInt8();
+ switch (type)
+ {
+ case 0:
+ dstBeing->setAction(Being::STAND);
+ player_node->imitateAction(dstBeing, Being::STAND);
+ break;
+
+ case 1:
+ dstBeing->setAction(Being::DEAD);
+ break;
+
+ case 2:
+ dstBeing->setAction(Being::SIT);
+ player_node->imitateAction(dstBeing, Being::SIT);
+ break;
+
+ default:
+ //need set stay state?
+ logger->log("QQQ2 SMSG_PLAYER_UPDATE_1:"
+ + toString(id) + " " + toString(type));
+ if (dstBeing)
+ {
+ logger->log("dstBeing id:"
+ + toString(dstBeing->getId()));
+ logger->log("dstBeing name:"
+ + dstBeing->getName());
+ }
+ break;
+
+ }
+ }
+ else if (msgType == 3)
+ {
+ msg.readInt8(); // unknown
+ }
+
+ level = msg.readInt8(); // Lv
+ if (level)
+ dstBeing->setLevel(level);
+
+ msg.readInt8(); // unknown
+
+ if (dstBeing->getType() != Being::PLAYER
+ || msgType != 3)
+ {
+ dstBeing->setActionTime(tick_time);
+// dstBeing->reset();
+ }
+
+ dstBeing->setStunMode(stunMode);
+ dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>(
+ (statusEffects >> 16) & 0xffff));
+ dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
+
+ if (msgType == 3 && dstBeing->getType() == Being::PLAYER)
+ dstBeing->setMoveTime();
+}
+
+void BeingHandler::processBeingVisibleOrMove(Net::MessageIn &msg, bool visible)
+{
+ if (!actorSpriteManager)
+ return;
+
+ int id;
+ short job, speed, gender;
+ uint16_t headTop, headMid, headBottom;
+ uint16_t shoes, gloves;
+ uint16_t weapon, shield;
+ uint16_t stunMode;
+ uint32_t statusEffects;
+ Being *dstBeing;
+ int hairStyle, hairColor;
+ int spawnId;
+
+ if (visible)
+ msg.readInt8(); // padding?
+
+ // Information about a being in range
+ id = msg.readInt32();
+ if (id == mSpawnId)
+ spawnId = mSpawnId;
+ else
+ spawnId = 0;
+ mSpawnId = 0;
+ speed = msg.readInt16();
+ stunMode = msg.readInt16(); // opt1
+ statusEffects = msg.readInt16(); // opt2
+ statusEffects |= (static_cast<uint32_t>(msg.readInt16())) << 16; // option
+ job = msg.readInt16(); // class
+
+ dstBeing = actorSpriteManager->findBeing(id);
+
+ if (dstBeing && dstBeing->getType() == Being::MONSTER
+ && !dstBeing->isAlive())
+ {
+ actorSpriteManager->destroy(dstBeing);
+ actorSpriteManager->erase(dstBeing);
+ dstBeing = nullptr;
+ }
+
+ if (!dstBeing)
+ {
+ // Being with id >= 110000000 and job 0 are better
+ // known as ghosts, so don't create those.
+ if (job == 0 && id >= 110000000)
+ return;
+
+ if (actorSpriteManager->isBlocked(id) == true)
+ return;
+
+ dstBeing = createBeing(id, job);
+
+ if (!dstBeing)
+ return;
+
+ if (job == 1022 && killStats)
+ killStats->jackoAlive(dstBeing->getId());
+ }
+ else
+ {
+ // undeleting marked for deletion being
+ if (dstBeing->getType() == Being::NPC)
+ actorSpriteManager->undelete(dstBeing);
+ }
+
+ if (dstBeing->getType() == Being::PLAYER)
+ dstBeing->setMoveTime();
+
+ if (spawnId)
+ {
+ dstBeing->setAction(Being::SPAWN);
+ }
+ else if (visible)
+ {
+ dstBeing->clearPath();
+ dstBeing->setActionTime(tick_time);
+ dstBeing->setAction(Being::STAND);
+ }
+
+ // Prevent division by 0 when calculating frame
+ if (speed == 0)
+ speed = 150;
+
+ dstBeing->setWalkSpeed(Vector(speed, speed, 0));
+ dstBeing->setSubtype(job);
+ if (dstBeing->getType() == ActorSprite::MONSTER && player_node)
+ player_node->checkNewName(dstBeing);
+
+ hairStyle = msg.readInt16();
+ weapon = msg.readInt16();
+ headBottom = msg.readInt16();
+
+// if (!visible)
+// msg.readInt32(); // server tick
+
+ shield = msg.readInt16();
+ headTop = msg.readInt16();
+ headMid = msg.readInt16();
+ hairColor = msg.readInt16();
+ shoes = msg.readInt16(); // clothes color - "abused" as shoes
+
+ if (dstBeing->getType() == ActorSprite::MONSTER)
+ {
+ if (serverVersion > 0)
+ {
+ int hp = msg.readInt32();
+ int maxHP = msg.readInt32();
+ if (hp && maxHP)
+ {
+ dstBeing->setMaxHP(maxHP);
+ int oldHP = dstBeing->getHP();
+ if (!oldHP || oldHP > hp)
+ dstBeing->setHP(hp);
+ }
+ }
+ else
+ {
+ msg.readInt32();
+ msg.readInt32();
+ }
+ gloves = 0;
+ }
+ else
+ {
+ gloves = msg.readInt16(); // head dir - "abused" as gloves
+ msg.readInt32(); // guild
+ msg.readInt16(); // guild emblem
+ }
+// logger->log("being guild: " + toString(guild));
+/*
+ if (guild == 0)
+ dstBeing->clearGuilds();
+ else
+ dstBeing->setGuild(Guild::getGuild(static_cast<short>(guild)));
+*/
+
+ msg.readInt16(); // manner
+ dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3
+ if (serverVersion > 0 && dstBeing->getType() == ActorSprite::MONSTER)
+ {
+ int attackRange = msg.readInt8(); // karma
+ dstBeing->setAttackRange(attackRange);
+ }
+ else
+ {
+ msg.readInt8(); // karma
+ }
+ gender = msg.readInt8();
+
+ // reserving bits for future usage
+
+ if (dstBeing->getType() == ActorSprite::PLAYER)
+ {
+ gender &= 3;
+ dstBeing->setGender(Being::intToGender(gender));
+ // Set these after the gender, as the sprites may be gender-specific
+ setSprite(dstBeing, SPRITE_HAIR, hairStyle * -1,
+ ItemDB::get(-hairStyle).getDyeColorsString(hairColor));
+ setSprite(dstBeing, SPRITE_BOTTOMCLOTHES, headBottom);
+ setSprite(dstBeing, SPRITE_TOPCLOTHES, headMid);
+ setSprite(dstBeing, SPRITE_HAT, headTop);
+ setSprite(dstBeing, SPRITE_SHOE, shoes);
+ setSprite(dstBeing, SPRITE_GLOVES, gloves);
+ setSprite(dstBeing, SPRITE_WEAPON, weapon, "", true);
+ if (!config.getBoolValue("hideShield"))
+ setSprite(dstBeing, SPRITE_SHIELD, shield);
+ }
+ else if (dstBeing->getType() == ActorSprite::NPC)
+ {
+ switch (gender)
+ {
+ case 2:
+ dstBeing->setGender(GENDER_FEMALE);
+ break;
+ case 3:
+ dstBeing->setGender(GENDER_MALE);
+ break;
+ case 4:
+ dstBeing->setGender(GENDER_OTHER);
+ break;
+ default:
+ dstBeing->setGender(GENDER_UNSPECIFIED);
+ break;
+ }
+ }
+
+ if (!visible)
+ {
+ uint16_t srcX, srcY, dstX, dstY;
+ msg.readCoordinatePair(srcX, srcY, dstX, dstY);
+ dstBeing->setAction(Being::STAND);
+ dstBeing->setTileCoords(srcX, srcY);
+ dstBeing->setDestination(dstX, dstY);
+ }
+ else
+ {
+ uint8_t dir;
+ uint16_t x, y;
+ msg.readCoordinates(x, y, dir);
+ dstBeing->setTileCoords(x, y);
+
+ if (job == 45 && socialWindow && outfitWindow)
+ {
+ int num = socialWindow->getPortalIndex(x, y);
+ if (num >= 0)
+ {
+ dstBeing->setName(keyboard.getKeyShortString(
+ outfitWindow->keyName(num)));
+ }
+ else
+ {
+ dstBeing->setName("");
+ }
+ }
+
+ dstBeing->setDirection(dir);
+ }
+
+ msg.readInt8(); // unknown
+ msg.readInt8(); // state / sit
+ msg.readInt16(); // level
+
+ dstBeing->setStunMode(stunMode);
+ dstBeing->setStatusEffectBlock(0, static_cast<uint16_t>(
+ (statusEffects >> 16) & 0xffff));
+ dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
+
+}
+
+void BeingHandler::processBeingSpawn(Net::MessageIn &msg)
+{
+ // skipping this packet
+ mSpawnId = msg.readInt32(); // id
+ msg.readInt16(); // speed
+ msg.readInt16(); // opt1
+ msg.readInt16(); // opt2
+ msg.readInt16(); // option
+ msg.readInt16(); // disguise
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/beinghandler.h b/src/net/eathena/beinghandler.h
new file mode 100644
index 000000000..0ddb0b348
--- /dev/null
+++ b/src/net/eathena/beinghandler.h
@@ -0,0 +1,61 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_BEINGHANDLER_H
+#define NET_EATHENA_BEINGHANDLER_H
+
+#include "net/beinghandler.h"
+#include "net/net.h"
+
+#include "net/ea/beinghandler.h"
+
+#include "net/eathena/messagehandler.h"
+
+namespace EAthena
+{
+
+class BeingHandler : public MessageHandler, public Ea::BeingHandler
+{
+ public:
+ BeingHandler(bool enableSync);
+
+ virtual void handleMessage(Net::MessageIn &msg);
+
+ virtual void requestNameById(int id);
+
+ virtual void undress(Being *being);
+
+ void processBeingVisibleOrMove(Net::MessageIn &msg, bool visible);
+
+ void processBeingSpawn(Net::MessageIn &msg);
+
+ protected:
+ virtual void processBeingChangeLook(Net::MessageIn &msg, bool look2);
+
+ void processNameResponse2(Net::MessageIn &msg);
+
+ virtual void processPlayerMoveUpdate(Net::MessageIn &msg, int type);
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_BEINGHANDLER_H
diff --git a/src/net/eathena/buysellhandler.cpp b/src/net/eathena/buysellhandler.cpp
new file mode 100644
index 000000000..4526f226d
--- /dev/null
+++ b/src/net/eathena/buysellhandler.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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/buysellhandler.h"
+
+#include "actorspritemanager.h"
+#include "configuration.h"
+#include "depricatedevent.h"
+#include "inventory.h"
+#include "item.h"
+#include "localplayer.h"
+#include "playerinfo.h"
+#include "shopitem.h"
+
+#include "gui/buydialog.h"
+#include "gui/buyselldialog.h"
+#include "gui/selldialog.h"
+#include "gui/shopwindow.h"
+
+#include "gui/widgets/chattab.h"
+
+#include "net/chathandler.h"
+#include "net/messagein.h"
+#include "net/net.h"
+
+#include "net/ea/eaprotocol.h"
+
+#include "net/eathena/chathandler.h"
+#include "net/eathena/protocol.h"
+
+#include "utils/gettext.h"
+
+#include "debug.h"
+
+extern Net::BuySellHandler *buySellHandler;
+
+namespace EAthena
+{
+
+BuySellHandler::BuySellHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_NPC_BUY_SELL_CHOICE,
+ SMSG_NPC_BUY,
+ SMSG_NPC_SELL,
+ SMSG_NPC_BUY_RESPONSE,
+ SMSG_NPC_SELL_RESPONSE,
+ 0
+ };
+ handledMessages = _messages;
+ buySellHandler = this;
+ mBuyDialog = nullptr;
+}
+
+void BuySellHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_NPC_BUY_SELL_CHOICE:
+ processNpcBuySellChoice(msg);
+ break;
+
+ case SMSG_NPC_BUY:
+ processNpcBuy(msg);
+ break;
+
+ case SMSG_NPC_SELL:
+ processNpcSell(msg, INVENTORY_OFFSET);
+ break;
+
+ case SMSG_NPC_BUY_RESPONSE:
+ processNpcBuyResponse(msg);
+ break;
+
+ case SMSG_NPC_SELL_RESPONSE:
+ processNpcSellResponse(msg);
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+void BuySellHandler::processNpcBuy(Net::MessageIn &msg)
+{
+ msg.readInt16(); // length
+ int sz = 11;
+ if (serverVersion > 0)
+ sz += 1;
+ int n_items = (msg.getLength() - 4) / sz;
+ mBuyDialog = new BuyDialog(mNpcId);
+ mBuyDialog->setMoney(PlayerInfo::getAttribute(MONEY));
+
+ for (int k = 0; k < n_items; k++)
+ {
+ int value = msg.readInt32();
+ msg.readInt32(); // DCvalue
+ msg.readInt8(); // type
+ int itemId = msg.readInt16();
+ unsigned char color = 1;
+ if (serverVersion > 0)
+ color = msg.readInt8();
+ mBuyDialog->addItem(itemId, color, 0, value);
+ }
+}
+
+void BuySellHandler::processNpcSellResponse(Net::MessageIn &msg)
+{
+ switch (msg.readInt8())
+ {
+ case 0:
+ SERVER_NOTICE(_("Thanks for selling."))
+ break;
+ case 1:
+ default:
+ SERVER_NOTICE(_("Unable to sell."))
+ break;
+ case 2:
+ SERVER_NOTICE(_("Unable to sell while trading."))
+ break;
+ case 3:
+ SERVER_NOTICE(_("Unable to sell unsellable item."))
+ break;
+ }
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/buysellhandler.h b/src/net/eathena/buysellhandler.h
new file mode 100644
index 000000000..b3440454e
--- /dev/null
+++ b/src/net/eathena/buysellhandler.h
@@ -0,0 +1,53 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_BUYSELLHANDLER_H
+#define NET_EATHENA_BUYSELLHANDLER_H
+
+#include "net/buysellhandler.h"
+
+#include "net/ea/buysellhandler.h"
+
+#include "being.h"
+
+#include "net/net.h"
+
+#include "net/eathena/messagehandler.h"
+
+namespace EAthena
+{
+
+class BuySellHandler : public MessageHandler, public Ea::BuySellHandler
+{
+ public:
+ BuySellHandler();
+
+ virtual void handleMessage(Net::MessageIn &msg);
+
+ virtual void processNpcBuy(Net::MessageIn &msg);
+
+ virtual void processNpcSellResponse(Net::MessageIn &msg);
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_BUYSELLHANDLER_H
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
diff --git a/src/net/eathena/charserverhandler.h b/src/net/eathena/charserverhandler.h
new file mode 100644
index 000000000..a264156cf
--- /dev/null
+++ b/src/net/eathena/charserverhandler.h
@@ -0,0 +1,73 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_CHARSERVERHANDLER_H
+#define NET_EATHENA_CHARSERVERHANDLER_H
+
+#include "net/charhandler.h"
+
+#include "net/ea/charserverhandler.h"
+
+#include "net/serverinfo.h"
+
+#include "net/eathena/messagehandler.h"
+
+#include "net/ea/token.h"
+
+class LoginData;
+
+namespace EAthena
+{
+
+/**
+ * Deals with incoming messages from the character server.
+ */
+class CharServerHandler : public MessageHandler, public Ea::CharServerHandler
+{
+ public:
+ CharServerHandler();
+
+ virtual void handleMessage(Net::MessageIn &msg);
+
+ void chooseCharacter(Net::Character *character);
+
+ void newCharacter(const std::string &name, int slot,
+ bool gender, int hairstyle, int hairColor,
+ unsigned char race,
+ const std::vector<int> &stats);
+
+ void deleteCharacter(Net::Character *character);
+
+ void switchCharacter();
+
+ void connect();
+
+ void processCharLogin(Net::MessageIn &msg);
+
+ protected:
+ void readPlayerData(Net::MessageIn &msg, Net::Character *character,
+ bool withColors);
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_CHARSERVERHANDLER_H
diff --git a/src/net/eathena/chathandler.cpp b/src/net/eathena/chathandler.cpp
new file mode 100644
index 000000000..05e98daad
--- /dev/null
+++ b/src/net/eathena/chathandler.cpp
@@ -0,0 +1,265 @@
+/*
+ * 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/chathandler.h"
+
+#include "actorspritemanager.h"
+#include "being.h"
+#include "configuration.h"
+#include "depricatedevent.h"
+#include "game.h"
+#include "localplayer.h"
+#include "playerrelations.h"
+#include "logger.h"
+
+#include "gui/chatwindow.h"
+#include "gui/shopwindow.h"
+
+#include "gui/widgets/chattab.h"
+
+#include "net/messagein.h"
+#include "net/messageout.h"
+
+#include "net/eathena/protocol.h"
+
+#include <string>
+
+#include "debug.h"
+
+extern Net::ChatHandler *chatHandler;
+
+namespace EAthena
+{
+
+ChatHandler::ChatHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_BEING_CHAT,
+ SMSG_PLAYER_CHAT,
+ SMSG_WHISPER,
+ SMSG_WHISPER_RESPONSE,
+ SMSG_GM_CHAT,
+ SMSG_MVP, // MVP
+ SMSG_IGNORE_ALL_RESPONSE,
+ 0
+ };
+ handledMessages = _messages;
+ chatHandler = this;
+}
+
+void ChatHandler::handleMessage(Net::MessageIn &msg)
+{
+ if (!localChatTab)
+ return;
+
+ switch (msg.getId())
+ {
+ case SMSG_WHISPER_RESPONSE:
+ processWhisperResponse(msg);
+ break;
+
+ // Received whisper
+ case SMSG_WHISPER:
+ processWhisper(msg);
+ break;
+
+ // Received speech from being
+ case SMSG_BEING_CHAT:
+ processBeingChat(msg);
+ break;
+
+ case SMSG_PLAYER_CHAT:
+ case SMSG_GM_CHAT:
+ processChat(msg, msg.getId() == SMSG_PLAYER_CHAT);
+ break;
+
+ case SMSG_MVP:
+ processMVP(msg);
+ break;
+
+ case SMSG_IGNORE_ALL_RESPONSE:
+ processIgnoreAllResponse(msg);
+
+ default:
+ break;
+ }
+}
+
+void ChatHandler::talk(const std::string &text)
+{
+ if (!player_node)
+ return;
+
+ std::string mes = player_node->getName() + " : " + text;
+
+ MessageOut outMsg(CMSG_CHAT_MESSAGE);
+ // Added + 1 in order to let eAthena parse admin commands correctly
+ outMsg.writeInt16(static_cast<short>(mes.length() + 4 + 1));
+ outMsg.writeString(mes, static_cast<int>(mes.length() + 1));
+}
+
+void ChatHandler::talkRaw(const std::string &mes)
+{
+ MessageOut outMsg(CMSG_CHAT_MESSAGE);
+ // Added + 1 in order to let eAthena parse admin commands correctly
+ outMsg.writeInt16(static_cast<short>(mes.length() + 4 + 1));
+ outMsg.writeString(mes, static_cast<int>(mes.length() + 1));
+}
+
+void ChatHandler::privateMessage(const std::string &recipient,
+ const std::string &text)
+{
+ MessageOut outMsg(CMSG_CHAT_WHISPER);
+ outMsg.writeInt16(static_cast<short>(text.length() + 28));
+ outMsg.writeString(recipient, 24);
+ outMsg.writeString(text, static_cast<int>(text.length()));
+ mSentWhispers.push(recipient);
+}
+
+void ChatHandler::who()
+{
+ MessageOut outMsg(CMSG_WHO_REQUEST);
+}
+
+void ChatHandler::sendRaw(const std::string &args)
+{
+ std::string line = args;
+ std::string str;
+ MessageOut *outMsg = nullptr;
+
+ if (line == "")
+ return;
+
+ size_t pos = line.find(" ");
+ if (pos != std::string::npos)
+ {
+ str = line.substr(0, pos);
+ outMsg = new MessageOut(static_cast<short>(atoi(str.c_str())));
+ line = line.substr(pos + 1);
+ pos = line.find(" ");
+ }
+ else
+ {
+ outMsg = new MessageOut(static_cast<short>(atoi(line.c_str())));
+ delete outMsg;
+ return;
+ }
+
+ while (pos != std::string::npos)
+ {
+ str = line.substr(0, pos);
+ processRaw(*outMsg, str);
+ line = line.substr(pos + 1);
+ pos = line.find(" ");
+ }
+ if (line != "")
+ processRaw(*outMsg, line);
+ delete outMsg;
+}
+
+void ChatHandler::processRaw(MessageOut &outMsg, std::string &line)
+{
+ size_t pos = line.find(":");
+ if (pos == std::string::npos)
+ {
+ int i = atoi(line.c_str());
+ if (line.length() <= 3)
+ outMsg.writeInt8(static_cast<char>(i));
+ else if (line.length() <= 5)
+ outMsg.writeInt16(static_cast<short>(i));
+ else
+ outMsg.writeInt32(i);
+ }
+ else
+ {
+ std::string header = line.substr(0, pos);
+ std::string data = line.substr(pos + 1);
+ if (header.length() != 1)
+ return;
+
+ int i = 0;
+
+ switch (header[0])
+ {
+ case '1':
+ case '2':
+ case '4':
+ i = atoi(data.c_str());
+ break;
+ default:
+ break;
+ }
+
+ switch (header[0])
+ {
+ case '1':
+ outMsg.writeInt8(static_cast<char>(i));
+ break;
+ case '2':
+ outMsg.writeInt16(static_cast<short>(i));
+ break;
+ case '4':
+ outMsg.writeInt32(i);
+ break;
+ case 'c':
+ {
+ pos = line.find(",");
+ if (pos != std::string::npos)
+ {
+ unsigned short x = static_cast<unsigned short>(
+ atoi(data.substr(0, pos).c_str()));
+ data = data.substr(pos + 1);
+ pos = line.find(",");
+ if (pos == std::string::npos)
+ break;
+
+ unsigned short y = static_cast<unsigned short>(
+ atoi(data.substr(0, pos).c_str()));
+ int dir = atoi(data.substr(pos + 1).c_str());
+ outMsg.writeCoordinates(x, y,
+ static_cast<unsigned char>(dir));
+ }
+ break;
+ }
+ case 't':
+ outMsg.writeString(data, static_cast<int>(data.length()));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void ChatHandler::ignoreAll()
+{
+ MessageOut outMsg(CMSG_IGNORE_ALL);
+ outMsg.writeInt8(0);
+}
+
+void ChatHandler::unIgnoreAll()
+{
+ MessageOut outMsg(CMSG_IGNORE_ALL);
+ outMsg.writeInt8(1);
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/chathandler.h b/src/net/eathena/chathandler.h
new file mode 100644
index 000000000..cf999fd63
--- /dev/null
+++ b/src/net/eathena/chathandler.h
@@ -0,0 +1,63 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_CHATHANDLER_H
+#define NET_EATHENA_CHATHANDLER_H
+
+#include "net/chathandler.h"
+#include "net/net.h"
+
+#include "net/ea/chathandler.h"
+
+#include "net/eathena/messagehandler.h"
+
+namespace EAthena
+{
+
+class ChatHandler : public MessageHandler, public Ea::ChatHandler
+{
+ public:
+ ChatHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void talk(const std::string &text);
+
+ void talkRaw(const std::string &text);
+
+ void privateMessage(const std::string &recipient,
+ const std::string &text);
+
+ void who();
+
+ void sendRaw(const std::string &args);
+
+ void ignoreAll();
+
+ void unIgnoreAll();
+
+ void processRaw(MessageOut &outMsg, std::string &line);
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_CHATHANDLER_H
diff --git a/src/net/eathena/gamehandler.cpp b/src/net/eathena/gamehandler.cpp
new file mode 100644
index 000000000..387768227
--- /dev/null
+++ b/src/net/eathena/gamehandler.cpp
@@ -0,0 +1,178 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 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/gamehandler.h"
+
+#include "client.h"
+#include "depricatedevent.h"
+#include "game.h"
+#include "localplayer.h"
+#include "logger.h"
+
+#include "net/messagein.h"
+#include "net/messageout.h"
+
+#include "net/eathena/loginhandler.h"
+#include "net/eathena/network.h"
+#include "net/eathena/protocol.h"
+
+#include "debug.h"
+
+extern Net::GameHandler *gameHandler;
+
+namespace EAthena
+{
+
+extern ServerInfo mapServer;
+
+GameHandler::GameHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_MAP_LOGIN_SUCCESS,
+ SMSG_SERVER_PING,
+ SMSG_WHO_ANSWER,
+ SMSG_CHAR_SWITCH_RESPONSE,
+ SMSG_MAP_QUIT_RESPONSE,
+ SMSG_MAP_CHAR_ID,
+ 0
+ };
+ handledMessages = _messages;
+ gameHandler = this;
+}
+
+void GameHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_MAP_LOGIN_SUCCESS:
+ processMapLogin(msg);
+ break;
+
+ case SMSG_SERVER_PING:
+ // We ignore this for now
+ // int tick = msg.readInt32()
+ break;
+
+ case SMSG_WHO_ANSWER:
+ processWhoAnswer(msg);
+ break;
+
+ case SMSG_CHAR_SWITCH_RESPONSE:
+ processCharSwitchResponse(msg);
+ break;
+
+ case SMSG_MAP_QUIT_RESPONSE:
+ processMapQuitResponse(msg);
+ break;
+
+ case SMSG_MAP_CHAR_ID:
+ processMapCharId(msg);
+ default:
+ break;
+ }
+}
+
+void GameHandler::mapLoadedEvent()
+{
+ MessageOut outMsg(CMSG_MAP_LOADED);
+}
+
+void GameHandler::connect()
+{
+ if (!mNetwork)
+ return;
+
+ mNetwork->connect(mapServer);
+
+ const Token &token =
+ static_cast<LoginHandler*>(Net::getLoginHandler())->getToken();
+
+
+ if (Client::getState() == STATE_CONNECT_GAME)
+ {
+ // Change the player's ID to the account ID to match what eAthena uses
+ if (player_node)
+ {
+ mCharID = player_node->getId();
+ player_node->setId(token.account_ID);
+ }
+ else
+ {
+ mCharID = 0;
+ }
+ }
+
+ // Send login infos
+ MessageOut outMsg(CMSG_MAP_SERVER_CONNECT);
+ outMsg.writeInt32(token.account_ID);
+ outMsg.writeInt32(mCharID);
+ outMsg.writeInt32(token.session_ID1);
+ outMsg.writeInt32(token.session_ID2);
+ outMsg.writeInt8(Being::genderToInt(token.sex));
+
+/*
+ if (player_node)
+ {
+ // Change the player's ID to the account ID to match what eAthena uses
+ player_node->setId(token.account_ID);
+ }
+*/
+ // We get 4 useless bytes before the real answer comes in (what are these?)
+// mNetwork->skip(4);
+}
+
+bool GameHandler::isConnected()
+{
+ if (!mNetwork)
+ return false;
+ return mNetwork->isConnected();
+}
+
+void GameHandler::disconnect()
+{
+ if (mNetwork)
+ mNetwork->disconnect();
+}
+
+void GameHandler::quit()
+{
+ MessageOut outMsg(CMSG_CLIENT_QUIT);
+}
+
+void GameHandler::ping(int tick)
+{
+ MessageOut msg(CMSG_CLIENT_PING);
+ msg.writeInt32(tick);
+}
+
+void GameHandler::disconnect2()
+{
+ MessageOut outMsg(CMSG_CLIENT_DISCONNECT);
+}
+
+void GameHandler::processMapCharId(Net::MessageIn &msg)
+{
+ msg.readInt32(); // char id
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/gamehandler.h b/src/net/eathena/gamehandler.h
new file mode 100644
index 000000000..c7c45a177
--- /dev/null
+++ b/src/net/eathena/gamehandler.h
@@ -0,0 +1,65 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 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/>.
+ */
+
+#ifndef NET_EATHENA_MAPHANDLER_H
+#define NET_EATHENA_MAPHANDLER_H
+
+#include "net/gamehandler.h"
+#include "net/net.h"
+#include "net/serverinfo.h"
+
+#include "net/eathena/messagehandler.h"
+
+#include "net/ea/gamehandler.h"
+
+namespace EAthena
+{
+
+class GameHandler : public MessageHandler, public Ea::GameHandler
+{
+ public:
+ GameHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void connect();
+
+ bool isConnected();
+
+ void disconnect();
+
+ void quit();
+
+ void ping(int tick);
+
+ void clear();
+
+ void disconnect2();
+
+ void mapLoadedEvent();
+
+ void processMapCharId(Net::MessageIn &msg);
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_MAPHANDLER_H
diff --git a/src/net/eathena/generalhandler.cpp b/src/net/eathena/generalhandler.cpp
new file mode 100644
index 000000000..7b287e6e7
--- /dev/null
+++ b/src/net/eathena/generalhandler.cpp
@@ -0,0 +1,307 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 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/generalhandler.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "logger.h"
+
+#include "gui/charselectdialog.h"
+#include "gui/inventorywindow.h"
+#include "gui/registerdialog.h"
+#include "gui/skilldialog.h"
+#include "gui/socialwindow.h"
+#include "gui/statuswindow.h"
+
+#include "net/messagein.h"
+#include "net/messageout.h"
+#include "net/serverinfo.h"
+
+#include "net/ea/guildhandler.h"
+
+#include "net/ea/gui/guildtab.h"
+#include "net/ea/gui/partytab.h"
+
+#include "net/eathena/adminhandler.h"
+#include "net/eathena/beinghandler.h"
+#include "net/eathena/buysellhandler.h"
+#include "net/eathena/chathandler.h"
+#include "net/eathena/charserverhandler.h"
+#include "net/eathena/gamehandler.h"
+#include "net/eathena/guildhandler.h"
+#include "net/eathena/inventoryhandler.h"
+#include "net/eathena/itemhandler.h"
+#include "net/eathena/loginhandler.h"
+#include "net/eathena/network.h"
+#include "net/eathena/npchandler.h"
+#include "net/eathena/partyhandler.h"
+#include "net/eathena/playerhandler.h"
+#include "net/eathena/protocol.h"
+#include "net/eathena/tradehandler.h"
+#include "net/eathena/specialhandler.h"
+
+#include "net/eathena/gui/guildtab.h"
+#include "net/eathena/gui/partytab.h"
+
+#include "resources/itemdb.h"
+
+#include "utils/gettext.h"
+
+#include <list>
+
+#include "debug.h"
+
+extern Net::GeneralHandler *generalHandler;
+
+namespace EAthena
+{
+
+ServerInfo charServer;
+ServerInfo mapServer;
+
+GeneralHandler::GeneralHandler():
+ mAdminHandler(new AdminHandler),
+ mBeingHandler(new BeingHandler(config.getBoolValue("EnableSync"))),
+ mBuySellHandler(new BuySellHandler),
+ mCharHandler(new CharServerHandler),
+ mChatHandler(new ChatHandler),
+ mGameHandler(new GameHandler),
+ mGuildHandler(new GuildHandler),
+ mInventoryHandler(new InventoryHandler),
+ mItemHandler(new ItemHandler),
+ mLoginHandler(new LoginHandler),
+ mNpcHandler(new NpcHandler),
+ mPartyHandler(new PartyHandler),
+ mPlayerHandler(new PlayerHandler),
+ mSpecialHandler(new SpecialHandler),
+ mTradeHandler(new TradeHandler)
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_CONNECTION_PROBLEM,
+ 0
+ };
+ handledMessages = _messages;
+ generalHandler = this;
+
+ std::vector<ItemDB::Stat> stats;
+ stats.push_back(ItemDB::Stat("str", _("Strength %+d")));
+ stats.push_back(ItemDB::Stat("agi", _("Agility %+d")));
+ stats.push_back(ItemDB::Stat("vit", _("Vitality %+d")));
+ stats.push_back(ItemDB::Stat("int", _("Intelligence %+d")));
+ stats.push_back(ItemDB::Stat("dex", _("Dexterity %+d")));
+ stats.push_back(ItemDB::Stat("luck", _("Luck %+d")));
+
+ ItemDB::setStatsList(stats);
+
+ listen(CHANNEL_GAME);
+}
+
+GeneralHandler::~GeneralHandler()
+{
+ delete mNetwork;
+ mNetwork = nullptr;
+}
+
+void GeneralHandler::handleMessage(Net::MessageIn &msg)
+{
+ int code;
+
+ switch (msg.getId())
+ {
+ case SMSG_CONNECTION_PROBLEM:
+ code = msg.readInt8();
+ logger->log("Connection problem: %i", code);
+
+ switch (code)
+ {
+ case 0:
+ errorMessage = _("Authentication failed.");
+ break;
+ case 1:
+ errorMessage = _("No servers available.");
+ break;
+ case 2:
+ if (Client::getState() == STATE_GAME)
+ {
+ errorMessage = _("Someone else is trying to use this "
+ "account.");
+ }
+ else
+ {
+ errorMessage = _("This account is already logged in.");
+ }
+ break;
+ case 3:
+ errorMessage = _("Speed hack detected.");
+ break;
+ case 8:
+ errorMessage = _("Duplicated login.");
+ break;
+ default:
+ errorMessage = _("Unknown connection error.");
+ break;
+ }
+ Client::setState(STATE_ERROR);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void GeneralHandler::load()
+{
+ (new Network)->registerHandler(this);
+
+ if (!mNetwork)
+ return;
+
+ mNetwork->registerHandler(mAdminHandler.get());
+ mNetwork->registerHandler(mBeingHandler.get());
+ mNetwork->registerHandler(mBuySellHandler.get());
+ mNetwork->registerHandler(mChatHandler.get());
+ mNetwork->registerHandler(mCharHandler.get());
+ mNetwork->registerHandler(mGameHandler.get());
+ mNetwork->registerHandler(mGuildHandler.get());
+ mNetwork->registerHandler(mInventoryHandler.get());
+ mNetwork->registerHandler(mItemHandler.get());
+ mNetwork->registerHandler(mLoginHandler.get());
+ mNetwork->registerHandler(mNpcHandler.get());
+ mNetwork->registerHandler(mPlayerHandler.get());
+ mNetwork->registerHandler(mSpecialHandler.get());
+ mNetwork->registerHandler(mTradeHandler.get());
+ mNetwork->registerHandler(mPartyHandler.get());
+}
+
+void GeneralHandler::reload()
+{
+ if (mNetwork)
+ mNetwork->disconnect();
+
+ static_cast<LoginHandler*>(mLoginHandler.get())->clearWorlds();
+ static_cast<CharServerHandler*>(
+ mCharHandler.get())->setCharCreateDialog(nullptr);
+ static_cast<CharServerHandler*>(
+ mCharHandler.get())->setCharSelectDialog(nullptr);
+
+ static_cast<PartyHandler*>(mPartyHandler.get())->reload();
+}
+
+void GeneralHandler::reloadPartially()
+{
+ static_cast<PartyHandler*>(mPartyHandler.get())->reload();
+}
+
+void GeneralHandler::unload()
+{
+ clearHandlers();
+}
+
+void GeneralHandler::flushNetwork()
+{
+ if (!mNetwork)
+ return;
+
+ mNetwork->flush();
+ mNetwork->dispatchMessages();
+
+ if (mNetwork->getState() == Network::NET_ERROR)
+ {
+ if (!mNetwork->getError().empty())
+ errorMessage = mNetwork->getError();
+ else
+ errorMessage = _("Got disconnected from server!");
+
+ Client::setState(STATE_ERROR);
+ }
+}
+
+void GeneralHandler::clearHandlers()
+{
+ if (mNetwork)
+ mNetwork->clearHandlers();
+}
+
+void GeneralHandler::processEvent(Channels channel,
+ const DepricatedEvent &event)
+{
+ if (channel == CHANNEL_GAME)
+ {
+ if (event.getName() == EVENT_GUIWINDOWSLOADED)
+ {
+ if (inventoryWindow)
+ inventoryWindow->setSplitAllowed(false);
+ if (skillDialog)
+ skillDialog->loadSkills("ea-skills.xml");
+
+ if (!statusWindow)
+ return;
+
+ // protection against double addition attributes.
+ statusWindow->clearAttributes();
+
+ statusWindow->addAttribute(STR, _("Strength"), true, "");
+ statusWindow->addAttribute(AGI, _("Agility"), true, "");
+ statusWindow->addAttribute(VIT, _("Vitality"), true, "");
+ statusWindow->addAttribute(INT, _("Intelligence"), true, "");
+ statusWindow->addAttribute(DEX, _("Dexterity"), true, "");
+ statusWindow->addAttribute(LUK, _("Luck"), true, "");
+
+ statusWindow->addAttribute(ATK, _("Attack"), false, "");
+ statusWindow->addAttribute(DEF, _("Defense"), false, "");
+ statusWindow->addAttribute(MATK, _("M.Attack"), false, "");
+ statusWindow->addAttribute(MDEF, _("M.Defense"), false, "");
+ // xgettext:no-c-format
+ statusWindow->addAttribute(HIT, _("% Accuracy"), false, "");
+ // xgettext:no-c-format
+ statusWindow->addAttribute(FLEE, _("% Evade"), false, "");
+ // xgettext:no-c-format
+ statusWindow->addAttribute(CRIT, _("% Critical"), false, "");
+ statusWindow->addAttribute(ATTACK_DELAY, _("Attack Delay"),
+ false, "");
+ statusWindow->addAttribute(WALK_SPEED, _("Walk Delay"),
+ false, "");
+ statusWindow->addAttribute(ATTACK_RANGE, _("Attack Range"),
+ false, "");
+ statusWindow->addAttribute(ATTACK_SPEED, _("Damage per sec."),
+ false, "");
+ }
+ else if (event.getName() == EVENT_GUIWINDOWSUNLOADING)
+ {
+ if (socialWindow)
+ {
+ socialWindow->removeTab(Ea::taGuild);
+ socialWindow->removeTab(Ea::taParty);
+ }
+
+ delete Ea::guildTab;
+ Ea::guildTab = nullptr;
+
+ delete Ea::partyTab;
+ Ea::partyTab = nullptr;
+ }
+ }
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/generalhandler.h b/src/net/eathena/generalhandler.h
new file mode 100644
index 000000000..9855be47e
--- /dev/null
+++ b/src/net/eathena/generalhandler.h
@@ -0,0 +1,80 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 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/>.
+ */
+
+#ifndef NET_EATHENA_GENERALHANDLER_H
+#define NET_EATHENA_GENERALHANDLER_H
+
+#include "listener.h"
+
+#include "net/generalhandler.h"
+#include "net/net.h"
+
+#include "net/eathena/messagehandler.h"
+
+namespace EAthena
+{
+
+class GeneralHandler : public MessageHandler, public Net::GeneralHandler,
+ public Listener
+{
+ public:
+ GeneralHandler();
+
+ ~GeneralHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void load();
+
+ void reload();
+
+ void unload();
+
+ void flushNetwork();
+
+ void clearHandlers();
+
+ void processEvent(Channels channel, const DepricatedEvent &event);
+
+ void reloadPartially();
+
+ protected:
+ MessageHandlerPtr mAdminHandler;
+ MessageHandlerPtr mBeingHandler;
+ MessageHandlerPtr mBuySellHandler;
+ MessageHandlerPtr mCharHandler;
+ MessageHandlerPtr mChatHandler;
+ MessageHandlerPtr mGameHandler;
+ MessageHandlerPtr mGuildHandler;
+ MessageHandlerPtr mInventoryHandler;
+ MessageHandlerPtr mItemHandler;
+ MessageHandlerPtr mLoginHandler;
+ MessageHandlerPtr mNpcHandler;
+ MessageHandlerPtr mPartyHandler;
+ MessageHandlerPtr mPlayerHandler;
+ MessageHandlerPtr mSpecialHandler;
+ MessageHandlerPtr mTradeHandler;
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_GENERALHANDLER_H
diff --git a/src/net/eathena/gui/guildtab.cpp b/src/net/eathena/gui/guildtab.cpp
new file mode 100644
index 000000000..33b29db65
--- /dev/null
+++ b/src/net/eathena/gui/guildtab.cpp
@@ -0,0 +1,55 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2008-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/gui/guildtab.h"
+
+#include "chatlogger.h"
+#include "commandhandler.h"
+#include "guild.h"
+#include "localplayer.h"
+
+#include "gui/theme.h"
+
+#include "net/net.h"
+#include "net/guildhandler.h"
+
+#include "resources/iteminfo.h"
+#include "resources/itemdb.h"
+
+#include "utils/dtor.h"
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include "debug.h"
+
+namespace EAthena
+{
+GuildTab::GuildTab() :
+ Ea::GuildTab()
+{
+}
+
+GuildTab::~GuildTab()
+{
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/gui/guildtab.h b/src/net/eathena/gui/guildtab.h
new file mode 100644
index 000000000..55a622c9d
--- /dev/null
+++ b/src/net/eathena/gui/guildtab.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-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/>.
+ */
+
+#ifndef EATHENA_GUILDTAB_H
+#define EATHENA_GUILDTAB_H
+
+#include "net/ea/gui/guildtab.h"
+
+namespace EAthena
+{
+
+/**
+ * A tab for a guild chat channel.
+ */
+class GuildTab : public Ea::GuildTab
+{
+ public:
+ GuildTab();
+
+ ~GuildTab();
+};
+
+} // namespace EAthena
+
+#endif // EATHENA_GUILDTAB_H
diff --git a/src/net/eathena/gui/partytab.cpp b/src/net/eathena/gui/partytab.cpp
new file mode 100644
index 000000000..8435f1c57
--- /dev/null
+++ b/src/net/eathena/gui/partytab.cpp
@@ -0,0 +1,58 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2008-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/gui/partytab.h"
+
+#include "chatlogger.h"
+#include "commandhandler.h"
+#include "localplayer.h"
+#include "party.h"
+
+#include "gui/theme.h"
+
+#include "net/net.h"
+#include "net/partyhandler.h"
+
+#include "resources/iteminfo.h"
+#include "resources/itemdb.h"
+
+#include "utils/dtor.h"
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include "net/chathandler.h"
+
+#include "debug.h"
+
+namespace EAthena
+{
+
+PartyTab::PartyTab() :
+ Ea::PartyTab()
+{
+}
+
+PartyTab::~PartyTab()
+{
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/gui/partytab.h b/src/net/eathena/gui/partytab.h
new file mode 100644
index 000000000..5e9a42e59
--- /dev/null
+++ b/src/net/eathena/gui/partytab.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-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/>.
+ */
+
+#ifndef EATHENA_PARTYTAB_H
+#define EATHENA_PARTYTAB_H
+
+#include "net/ea/gui/partytab.h"
+
+namespace EAthena
+{
+
+/**
+ * A tab for a party chat channel.
+ */
+class PartyTab : public Ea::PartyTab
+{
+ public:
+ PartyTab();
+
+ ~PartyTab();
+};
+
+} // namespace EAthena
+
+#endif // EATHENA_PARTYTAB_H
diff --git a/src/net/eathena/guildhandler.cpp b/src/net/eathena/guildhandler.cpp
new file mode 100644
index 000000000..6b8db8983
--- /dev/null
+++ b/src/net/eathena/guildhandler.cpp
@@ -0,0 +1,319 @@
+/*
+ * The ManaPlus Client
+ * 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/guildhandler.h"
+
+#include "actorspritemanager.h"
+#include "localplayer.h"
+#include "logger.h"
+#include "playerinfo.h"
+
+#include "net/eathena/messagein.h"
+#include "net/eathena/protocol.h"
+
+#include "debug.h"
+
+extern Net::GuildHandler *guildHandler;
+
+namespace EAthena
+{
+
+GuildHandler::GuildHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_GUILD_CREATE_RESPONSE,
+ SMSG_GUILD_POSITION_INFO,
+ SMSG_GUILD_MEMBER_LOGIN,
+ SMSG_GUILD_MASTER_OR_MEMBER,
+ SMSG_GUILD_BASIC_INFO,
+ SMSG_GUILD_ALIANCE_INFO,
+ SMSG_GUILD_MEMBER_LIST,
+ SMSG_GUILD_POS_NAME_LIST,
+ SMSG_GUILD_POS_INFO_LIST,
+ SMSG_GUILD_POSITION_CHANGED,
+ SMSG_GUILD_MEMBER_POS_CHANGE,
+ SMSG_GUILD_EMBLEM,
+ SMSG_GUILD_SKILL_INFO,
+ SMSG_GUILD_NOTICE,
+ SMSG_GUILD_INVITE,
+ SMSG_GUILD_INVITE_ACK,
+ SMSG_GUILD_LEAVE,
+ SMSG_GUILD_EXPULSION,
+ SMSG_GUILD_EXPULSION_LIST,
+ SMSG_GUILD_MESSAGE,
+ SMSG_GUILD_SKILL_UP,
+ SMSG_GUILD_REQ_ALLIANCE,
+ SMSG_GUILD_REQ_ALLIANCE_ACK,
+ SMSG_GUILD_DEL_ALLIANCE,
+ SMSG_GUILD_OPPOSITION_ACK,
+ SMSG_GUILD_BROKEN,
+ 0
+ };
+ handledMessages = _messages;
+
+ guildHandler = this;
+}
+
+GuildHandler::~GuildHandler()
+{
+}
+
+void GuildHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_GUILD_CREATE_RESPONSE:
+ processGuildCreateResponse(msg);
+ break;
+
+ case SMSG_GUILD_POSITION_INFO:
+ processGuildPositionInfo(msg);
+ break;
+
+ case SMSG_GUILD_MEMBER_LOGIN:
+ processGuildMemberLogin(msg);
+ break;
+
+ case SMSG_GUILD_MASTER_OR_MEMBER:
+ processGuildMasterOrMember(msg);
+ break;
+
+ case SMSG_GUILD_BASIC_INFO:
+ processGuildBasicInfo(msg);
+ break;
+
+ case SMSG_GUILD_ALIANCE_INFO:
+ processGuildAlianceInfo(msg);
+ break;
+
+ case SMSG_GUILD_MEMBER_LIST:
+ processGuildMemberList(msg);
+ break;
+
+ case SMSG_GUILD_POS_NAME_LIST:
+ processGuildPosNameList(msg);
+ break;
+
+ case SMSG_GUILD_POS_INFO_LIST:
+ processGuildPosInfoList(msg);
+ break;
+
+ case SMSG_GUILD_POSITION_CHANGED:
+ processGuildPositionChanged(msg);
+ break;
+
+ case SMSG_GUILD_MEMBER_POS_CHANGE:
+ processGuildMemberPosChange(msg);
+ break;
+
+ case SMSG_GUILD_EMBLEM:
+ processGuildEmblem(msg);
+ break;
+
+ case SMSG_GUILD_SKILL_INFO:
+ processGuildSkillInfo(msg);
+ break;
+
+ case SMSG_GUILD_NOTICE:
+ processGuildNotice(msg);
+ break;
+
+ case SMSG_GUILD_INVITE:
+ processGuildInvite(msg);
+ break;
+
+ case SMSG_GUILD_INVITE_ACK:
+ processGuildInviteAck(msg);
+ break;
+
+ case SMSG_GUILD_LEAVE:
+ processGuildLeave(msg);
+ break;
+
+ case SMSG_GUILD_EXPULSION:
+ processGuildExpulsion(msg);
+ break;
+
+ case SMSG_GUILD_EXPULSION_LIST:
+ processGuildExpulsionList(msg);
+ break;
+
+ case SMSG_GUILD_MESSAGE:
+ processGuildMessage(msg);
+ break;
+
+ case SMSG_GUILD_SKILL_UP:
+ processGuildSkillUp(msg);
+ break;
+
+ case SMSG_GUILD_REQ_ALLIANCE:
+ processGuildReqAlliance(msg);
+ break;
+
+ case SMSG_GUILD_REQ_ALLIANCE_ACK:
+ processGuildReqAllianceAck(msg);
+ break;
+
+ case SMSG_GUILD_DEL_ALLIANCE:
+ processGuildDelAlliance(msg);
+ break;
+
+ case SMSG_GUILD_OPPOSITION_ACK:
+ processGuildOppositionAck(msg);
+ break;
+
+ case SMSG_GUILD_BROKEN:
+ processGuildBroken(msg);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void GuildHandler::create(const std::string &name)
+{
+ MessageOut msg(CMSG_GUILD_CREATE);
+ msg.writeInt32(0); // Unused
+ msg.writeString(name, 24);
+}
+
+void GuildHandler::invite(int guildId A_UNUSED,
+ const std::string &name A_UNUSED)
+{
+ if (!actorSpriteManager)
+ return;
+
+ Being* being = actorSpriteManager->findBeingByName(name, Being::PLAYER);
+ if (being)
+ {
+ MessageOut msg(CMSG_GUILD_INVITE);
+ msg.writeInt32(being->getId());
+ msg.writeInt32(0); // Unused
+ msg.writeInt32(0); // Unused
+ }
+}
+
+void GuildHandler::invite(int guildId A_UNUSED, Being *being)
+{
+ if (!being)
+ return;
+
+ MessageOut msg(CMSG_GUILD_INVITE);
+ msg.writeInt32(being->getId());
+ msg.writeInt32(0); // Unused
+ msg.writeInt32(0); // Unused
+}
+
+void GuildHandler::inviteResponse(int guildId, bool response)
+{
+ MessageOut msg(CMSG_GUILD_INVITE_REPLY);
+ msg.writeInt32(guildId);
+ msg.writeInt8(response);
+ msg.writeInt8(0); // Unused
+ msg.writeInt16(0); // Unused
+}
+
+void GuildHandler::leave(int guildId)
+{
+ if (!player_node)
+ return;
+
+ MessageOut msg(CMSG_GUILD_LEAVE);
+ msg.writeInt32(guildId);
+ msg.writeInt32(player_node->getId()); // Account ID
+ msg.writeInt32(PlayerInfo::getCharId()); // Char ID
+ msg.writeString("", 40); // Message
+}
+
+void GuildHandler::kick(GuildMember *member, std::string reason)
+{
+ if (!member || !member->getGuild())
+ return;
+
+ MessageOut msg(CMSG_GUILD_EXPULSION);
+ msg.writeInt32(member->getGuild()->getId());
+ msg.writeInt32(member->getID()); // Account ID
+ msg.writeInt32(member->getCharId()); // Char ID
+ msg.writeString(reason, 40); // Message
+}
+
+void GuildHandler::chat(int guildId A_UNUSED, const std::string &text)
+{
+ if (!player_node)
+ return;
+
+ std::string str = player_node->getName() + " : " + text;
+ MessageOut msg(CMSG_GUILD_MESSAGE);
+ msg.writeInt16(static_cast<uint16_t>(str.size() + 4));
+ msg.writeString(str, static_cast<int>(str.length()));
+}
+
+void GuildHandler::memberList(int guildId A_UNUSED)
+{
+ // TODO four types of info requests:
+ // 0 = basic info + alliance info
+ // 1 = position name list + member list
+ // 2 = position name list + position info list
+ // 3 = skill info
+ // 4 = expulsion list
+
+ MessageOut msg(CMSG_GUILD_REQUEST_INFO);
+ msg.writeInt32(1); // Request member list
+}
+
+void GuildHandler::info(int guildId A_UNUSED)
+{
+ // TODO four types of info requests:
+ // 0 = basic info + alliance info
+ // 1 = position name list + member list
+ // 2 = position name list + position info list
+ // 3 = skill info
+ // 4 = expulsion list
+
+ showBasicInfo = true;
+ MessageOut msg(CMSG_GUILD_REQUEST_INFO);
+ msg.writeInt32(0); // Request basic info
+}
+
+void GuildHandler::changeMemberPostion(GuildMember *member, int level)
+{
+ if (!member || !member->getGuild())
+ return;
+
+ MessageOut msg(CMSG_GUILD_CHANGE_MEMBER_POS);
+ msg.writeInt16(16); // size less then 16 <= 4 + 12
+ msg.writeInt32(member->getID()); // Account ID
+ msg.writeInt32(member->getCharId()); // Char ID
+ msg.writeInt32(level); // pos
+}
+
+void GuildHandler::changeNotice(int guildId, std::string msg1,
+ std::string msg2)
+{
+ MessageOut msg(CMSG_GUILD_CHANGE_NOTICE);
+ msg.writeInt32(guildId);
+ msg.writeString(msg1, 60); // msg1
+ msg.writeString(msg2, 120); // msg2
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/guildhandler.h b/src/net/eathena/guildhandler.h
new file mode 100644
index 000000000..186d1f630
--- /dev/null
+++ b/src/net/eathena/guildhandler.h
@@ -0,0 +1,72 @@
+/*
+ * The ManaPlus Client
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_GUILDHANDLER_H
+#define NET_EATHENA_GUILDHANDLER_H
+
+#include "net/guildhandler.h"
+
+#include "net/ea/guildhandler.h"
+
+#include "net/ea/gui/guildtab.h"
+
+#include "net/eathena/messagehandler.h"
+
+namespace EAthena
+{
+
+class GuildHandler : public Ea::GuildHandler, public MessageHandler
+{
+ public:
+ GuildHandler();
+
+ ~GuildHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void create(const std::string &name);
+
+ void invite(int guildId, const std::string &name);
+
+ void invite(int guildId, Being *being);
+
+ void inviteResponse(int guildId, bool response);
+
+ void leave(int guildId);
+
+ void kick(GuildMember *member, std::string reason = "");
+
+ void chat(int guildId, const std::string &text);
+
+ void memberList(int guildId);
+
+ void info(int guildId);
+
+ void changeMemberPostion(GuildMember *member, int level);
+
+ void changeNotice(int guildId, std::string msg1, std::string msg2);
+};
+
+ extern Ea::GuildTab *guildTab;
+ extern Guild *taGuild;
+}
+
+#endif // NET_EATHENA_GUILDHANDLER_H
diff --git a/src/net/eathena/inventoryhandler.cpp b/src/net/eathena/inventoryhandler.cpp
new file mode 100644
index 000000000..b7841f940
--- /dev/null
+++ b/src/net/eathena/inventoryhandler.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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/inventoryhandler.h"
+
+#include "logger.h"
+
+#include "net/messagein.h"
+
+#include "net/eathena/protocol.h"
+
+#include "net/ea/eaprotocol.h"
+
+#include "debug.h"
+
+extern Net::InventoryHandler *inventoryHandler;
+
+namespace EAthena
+{
+
+InventoryHandler::InventoryHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_PLAYER_INVENTORY,
+ SMSG_PLAYER_INVENTORY_ADD,
+ SMSG_PLAYER_INVENTORY_REMOVE,
+ SMSG_PLAYER_INVENTORY_USE,
+ SMSG_ITEM_USE_RESPONSE,
+ SMSG_PLAYER_STORAGE_ITEMS,
+ SMSG_PLAYER_STORAGE_EQUIP,
+ SMSG_PLAYER_STORAGE_STATUS,
+ SMSG_PLAYER_STORAGE_ADD,
+ SMSG_PLAYER_STORAGE_REMOVE,
+ SMSG_PLAYER_STORAGE_CLOSE,
+ SMSG_PLAYER_EQUIPMENT,
+ SMSG_PLAYER_EQUIP,
+ SMSG_PLAYER_UNEQUIP,
+ SMSG_PLAYER_ARROW_EQUIP,
+ SMSG_PLAYER_ATTACK_RANGE,
+ 0
+ };
+ handledMessages = _messages;
+ inventoryHandler = this;
+}
+
+InventoryHandler::~InventoryHandler()
+{
+}
+
+void InventoryHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_PLAYER_INVENTORY:
+ case SMSG_PLAYER_STORAGE_ITEMS:
+ processPlayerInventory(msg, msg.getId() == SMSG_PLAYER_INVENTORY);
+ break;
+
+ case SMSG_PLAYER_STORAGE_EQUIP:
+ processPlayerStorageEquip(msg);
+ break;
+
+ case SMSG_PLAYER_INVENTORY_ADD:
+ processPlayerInventoryAdd(msg);
+ break;
+
+ case SMSG_PLAYER_INVENTORY_REMOVE:
+ processPlayerInventoryRemove(msg);
+ break;
+
+ case SMSG_PLAYER_INVENTORY_USE:
+ processPlayerInventoryUse(msg);
+ break;
+
+ case SMSG_ITEM_USE_RESPONSE:
+ processItemUseResponse(msg);
+ break;
+
+ case SMSG_PLAYER_STORAGE_STATUS:
+ processPlayerStorageStatus(msg);
+ break;
+
+ case SMSG_PLAYER_STORAGE_ADD:
+ processPlayerStorageAdd(msg);
+ break;
+
+ case SMSG_PLAYER_STORAGE_REMOVE:
+ processPlayerStorageRemove(msg);
+ break;
+
+ case SMSG_PLAYER_STORAGE_CLOSE:
+ processPlayerStorageClose(msg);
+ break;
+
+ case SMSG_PLAYER_EQUIPMENT:
+ processPlayerEquipment(msg);
+ break;
+
+ case SMSG_PLAYER_EQUIP:
+ processPlayerEquip(msg);
+ break;
+
+ case SMSG_PLAYER_UNEQUIP:
+ processPlayerUnEquip(msg);
+ break;
+
+ case SMSG_PLAYER_ATTACK_RANGE:
+ processPlayerAttackRange(msg);
+ break;
+
+ case SMSG_PLAYER_ARROW_EQUIP:
+ processPlayerArrowEquip(msg);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void InventoryHandler::equipItem(const Item *item)
+{
+ if (!item)
+ return;
+
+ MessageOut outMsg(CMSG_PLAYER_EQUIP);
+ outMsg.writeInt16(static_cast<int16_t>(
+ item->getInvIndex() + INVENTORY_OFFSET));
+ outMsg.writeInt16(0);
+}
+
+void InventoryHandler::unequipItem(const Item *item)
+{
+ if (!item)
+ return;
+
+ MessageOut outMsg(CMSG_PLAYER_UNEQUIP);
+ outMsg.writeInt16(static_cast<int16_t>(
+ item->getInvIndex() + INVENTORY_OFFSET));
+}
+
+void InventoryHandler::useItem(const Item *item)
+{
+ if (!item)
+ return;
+
+ MessageOut outMsg(CMSG_PLAYER_INVENTORY_USE);
+ outMsg.writeInt16(static_cast<int16_t>(
+ item->getInvIndex() + INVENTORY_OFFSET));
+ outMsg.writeInt32(item->getId()); // unused
+}
+
+void InventoryHandler::dropItem(const Item *item, int amount)
+{
+ if (!item)
+ return;
+
+ // TODO: Fix wrong coordinates of drops, serverside? (what's wrong here?)
+ MessageOut outMsg(CMSG_PLAYER_INVENTORY_DROP);
+ outMsg.writeInt16(static_cast<int16_t>(
+ item->getInvIndex() + INVENTORY_OFFSET));
+ outMsg.writeInt16(static_cast<int16_t>(amount));
+}
+
+void InventoryHandler::closeStorage(int type A_UNUSED)
+{
+ MessageOut outMsg(CMSG_CLOSE_STORAGE);
+}
+
+void InventoryHandler::moveItem2(int source, int slot, int amount,
+ int destination)
+{
+ if (source == Inventory::INVENTORY && destination == Inventory::STORAGE)
+ {
+ MessageOut outMsg(CMSG_MOVE_TO_STORAGE);
+ outMsg.writeInt16(static_cast<int16_t>(slot + INVENTORY_OFFSET));
+ outMsg.writeInt32(amount);
+ }
+ else if (source == Inventory::STORAGE
+ && destination == Inventory::INVENTORY)
+ {
+ MessageOut outMsg(CSMG_MOVE_FROM_STORAGE);
+ outMsg.writeInt16(static_cast<int16_t>(slot + STORAGE_OFFSET));
+ outMsg.writeInt32(amount);
+ }
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/inventoryhandler.h b/src/net/eathena/inventoryhandler.h
new file mode 100644
index 000000000..c59621c4a
--- /dev/null
+++ b/src/net/eathena/inventoryhandler.h
@@ -0,0 +1,61 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_INVENTORYHANDLER_H
+#define NET_EATHENA_INVENTORYHANDLER_H
+
+#include "logger.h"
+
+#include "net/net.h"
+
+#include "net/ea/inventoryhandler.h"
+
+#include "net/eathena/messagehandler.h"
+
+namespace EAthena
+{
+
+class InventoryHandler : public MessageHandler, public Ea::InventoryHandler
+{
+ public:
+ InventoryHandler();
+
+ ~InventoryHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void equipItem(const Item *item);
+
+ void unequipItem(const Item *item);
+
+ void useItem(const Item *item);
+
+ void dropItem(const Item *item, int amount);
+
+ void closeStorage(int type);
+
+ void moveItem2(int source, int slot, int amount, int destination);
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_INVENTORYHANDLER_H
diff --git a/src/net/eathena/itemhandler.cpp b/src/net/eathena/itemhandler.cpp
new file mode 100644
index 000000000..814e3c76e
--- /dev/null
+++ b/src/net/eathena/itemhandler.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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/itemhandler.h"
+
+#include "net/messagein.h"
+
+#include "net/eathena/protocol.h"
+
+#include "debug.h"
+
+namespace EAthena
+{
+
+ItemHandler::ItemHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_ITEM_VISIBLE,
+ SMSG_ITEM_DROPPED,
+ SMSG_ITEM_REMOVE,
+ 0
+ };
+ handledMessages = _messages;
+}
+
+void ItemHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_ITEM_VISIBLE:
+ processItemVisible(msg);
+ break;
+
+ case SMSG_ITEM_DROPPED:
+ processItemDropped(msg);
+ break;
+
+ case SMSG_ITEM_REMOVE:
+ processItemRemove(msg);
+ break;
+
+ default:
+ break;
+ }
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/itemhandler.h b/src/net/eathena/itemhandler.h
new file mode 100644
index 000000000..63adb56ac
--- /dev/null
+++ b/src/net/eathena/itemhandler.h
@@ -0,0 +1,43 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_ITEMHANDLER_H
+#define NET_EATHENA_ITEMHANDLER_H
+
+#include "net/eathena/messagehandler.h"
+
+#include "net/ea/itemhandler.h"
+
+namespace EAthena
+{
+
+class ItemHandler : public MessageHandler, public Ea::ItemHandler
+{
+ public:
+ ItemHandler();
+
+ virtual void handleMessage(Net::MessageIn &msg);
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_ITEMHANDLER_H
diff --git a/src/net/eathena/loginhandler.cpp b/src/net/eathena/loginhandler.cpp
new file mode 100644
index 000000000..0ddc64a7b
--- /dev/null
+++ b/src/net/eathena/loginhandler.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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/loginhandler.h"
+
+#include "client.h"
+#include "logger.h"
+#include "configuration.h"
+
+#include "net/messagein.h"
+
+#include "net/eathena/network.h"
+#include "net/eathena/protocol.h"
+
+#include "utils/gettext.h"
+#include "utils/paths.h"
+
+#include "debug.h"
+
+extern Net::LoginHandler *loginHandler;
+
+namespace EAthena
+{
+
+extern ServerInfo charServer;
+
+LoginHandler::LoginHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_UPDATE_HOST,
+ SMSG_UPDATE_HOST2,
+ SMSG_LOGIN_DATA,
+ SMSG_LOGIN_ERROR,
+ SMSG_CHAR_PASSWORD_RESPONSE,
+ SMSG_SERVER_VERSION_RESPONSE,
+ 0
+ };
+ handledMessages = _messages;
+ loginHandler = this;
+}
+
+LoginHandler::~LoginHandler()
+{
+}
+
+void LoginHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_CHAR_PASSWORD_RESPONSE:
+ procecessCharPasswordResponse(msg);
+ break;
+
+ case SMSG_UPDATE_HOST:
+ processUpdateHost(msg);
+ break;
+
+ case SMSG_UPDATE_HOST2:
+ processUpdateHost2(msg);
+ break;
+
+ case SMSG_LOGIN_DATA:
+ processLoginData(msg);
+ break;
+
+ case SMSG_LOGIN_ERROR:
+ processLoginError(msg);
+ break;
+
+ case SMSG_SERVER_VERSION_RESPONSE:
+ processServerVersion(msg);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void LoginHandler::connect()
+{
+ if (!mNetwork)
+ return;
+
+ mNetwork->connect(mServer);
+ MessageOut outMsg(CMSG_SERVER_VERSION_REQUEST);
+}
+
+bool LoginHandler::isConnected()
+{
+ if (!mNetwork)
+ return false;
+
+ return mVersionResponse && mNetwork->isConnected();
+}
+
+void LoginHandler::disconnect()
+{
+ if (mNetwork && mNetwork->getServer() == mServer)
+ mNetwork->disconnect();
+}
+
+void LoginHandler::changePassword(const std::string &username A_UNUSED,
+ const std::string &oldPassword,
+ const std::string &newPassword)
+{
+ MessageOut outMsg(CMSG_CHAR_PASSWORD_CHANGE);
+ outMsg.writeStringNoLog(oldPassword, 24);
+ outMsg.writeStringNoLog(newPassword, 24);
+}
+
+void LoginHandler::sendLoginRegister(const std::string &username,
+ const std::string &password)
+{
+ MessageOut outMsg(0x0064);
+ outMsg.writeInt32(0); // client version
+ outMsg.writeString(username, 24);
+ outMsg.writeStringNoLog(password, 24);
+
+ /*
+ * eAthena calls the last byte "client version 2", but it isn't used at
+ * at all. We're retasking it, as a bit mask:
+ * 0 - can handle the 0x63 "update host" packet
+ * 1 - defaults to the first char-server (instead of the last)
+ */
+ outMsg.writeInt8(0x03);
+}
+
+ServerInfo *LoginHandler::getCharServer()
+{
+ return &charServer;
+}
+
+void LoginHandler::requestUpdateHosts()
+{
+ MessageOut outMsg(CMSG_SEND_CLIENT_INFO);
+ outMsg.writeInt8(CLIENT_PROTOCOL_VERSION);
+ outMsg.writeInt8(0); // unused
+}
+
+void LoginHandler::processServerVersion(Net::MessageIn &msg)
+{
+ char b1 = msg.readInt8(); // -1
+ char b2 = msg.readInt8(); // E
+ char b3 = msg.readInt8(); // V
+ char b4 = msg.readInt8(); // L
+ if (b1 == -1 && b2 == 'E' && b3 == 'V' && b4 == 'L')
+ {
+ unsigned int options = msg.readInt8();
+ mRegistrationEnabled = options;
+ msg.skip(2); // 0 unused
+ serverVersion = msg.readInt8();
+ if (serverVersion >= 5)
+ requestUpdateHosts();
+ }
+ else
+ {
+ unsigned int options = msg.readInt32();
+ mRegistrationEnabled = options;
+ serverVersion = 0;
+ }
+ logger->log("Server version: %d", serverVersion);
+ if (serverVersion < 5)
+ {
+ if (Client::getState() != STATE_LOGIN)
+ Client::setState(STATE_LOGIN);
+ }
+
+ // Leave this last
+ mVersionResponse = true;
+}
+
+void LoginHandler::processUpdateHost2(Net::MessageIn &msg)
+{
+ int len;
+
+ len = msg.readInt16() - 4;
+ std::string updateHost = msg.readString(len);
+
+ splitToStringVector(loginData.updateHosts, updateHost, '|');
+ for (StringVectIter it = loginData.updateHosts.begin(),
+ it_end = loginData.updateHosts.end(); it != it_end; ++ it)
+ {
+ if (!checkPath(*it))
+ {
+ logger->log1("Warning: incorrect update server name");
+ loginData.updateHosts.clear();
+ break;
+ }
+ }
+
+ logger->log("Received update hosts \"%s\" from login server.",
+ updateHost.c_str());
+
+ if (Client::getState() == STATE_PRE_LOGIN)
+ Client::setState(STATE_LOGIN);
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/loginhandler.h b/src/net/eathena/loginhandler.h
new file mode 100644
index 000000000..602518782
--- /dev/null
+++ b/src/net/eathena/loginhandler.h
@@ -0,0 +1,81 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_TMWA_LOGINHANDLER_H
+#define NET_TMWA_LOGINHANDLER_H
+
+#include "net/loginhandler.h"
+
+#include "net/ea/loginhandler.h"
+
+#include "net/eathena/messagehandler.h"
+
+#include "net/ea/token.h"
+
+#include <string>
+
+class LoginData;
+
+namespace EAthena
+{
+
+class LoginHandler : public MessageHandler, public Ea::LoginHandler
+{
+ public:
+ LoginHandler();
+
+ ~LoginHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void connect();
+
+ bool isConnected();
+
+ void disconnect();
+
+ int supportedOptionalActions() const
+ { return SetGenderOnRegister; }
+
+ unsigned int getMaxPasswordLength() const
+ { return 25; }
+
+ void changePassword(const std::string &username,
+ const std::string &oldPassword,
+ const std::string &newPassword);
+
+ ServerInfo *getCharServer();
+
+ void processServerVersion(Net::MessageIn &msg);
+
+ void requestUpdateHosts();
+
+ void processUpdateHost2(Net::MessageIn &msg);
+
+ private:
+ void sendLoginRegister(const std::string &username,
+ const std::string &password);
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_LOGINHANDLER_H
diff --git a/src/net/eathena/messagehandler.cpp b/src/net/eathena/messagehandler.cpp
new file mode 100644
index 000000000..4dd5c1c61
--- /dev/null
+++ b/src/net/eathena/messagehandler.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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/messagehandler.h"
+
+#include "net/eathena/network.h"
+
+#include <cassert>
+
+#include "debug.h"
+
+namespace EAthena
+{
+
+MessageHandler::MessageHandler()
+ : mNetwork(nullptr)
+{
+}
+
+MessageHandler::~MessageHandler()
+{
+ if (mNetwork)
+ mNetwork->unregisterHandler(this);
+}
+
+void MessageHandler::setNetwork(Network *network)
+{
+ assert(!(network && mNetwork));
+ mNetwork = network;
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/messagehandler.h b/src/net/eathena/messagehandler.h
new file mode 100644
index 000000000..f494c5181
--- /dev/null
+++ b/src/net/eathena/messagehandler.h
@@ -0,0 +1,60 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_MESSAGEHANDLER_H
+#define NET_EATHENA_MESSAGEHANDLER_H
+
+#include "net/messagehandler.h"
+#include "net/messagein.h"
+
+#include "net/eathena/messageout.h"
+
+#include <SDL_types.h>
+
+#include <memory>
+
+namespace EAthena
+{
+
+class Network;
+
+/**
+ * \ingroup Network
+ */
+class MessageHandler : public Net::MessageHandler
+{
+ public:
+ MessageHandler();
+
+ ~MessageHandler();
+
+ void setNetwork(Network *network);
+
+ protected:
+ Network *mNetwork;
+};
+
+typedef const std::auto_ptr<MessageHandler> MessageHandlerPtr;
+
+}
+
+#endif // NET_EATHENA_MESSAGEHANDLER_H
diff --git a/src/net/eathena/messagein.cpp b/src/net/eathena/messagein.cpp
new file mode 100644
index 000000000..a5a71a535
--- /dev/null
+++ b/src/net/eathena/messagein.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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/messagein.h"
+
+#include "net/packetcounters.h"
+
+#include "logger.h"
+
+#include "utils/stringutils.h"
+
+#include <SDL.h>
+#include <SDL_endian.h>
+
+#include "debug.h"
+
+namespace EAthena
+{
+
+MessageIn::MessageIn(const char *data, unsigned int length):
+ Net::MessageIn(data, length)
+{
+ // Read the message ID
+ mId = readInt16();
+}
+
+int16_t MessageIn::readInt16()
+{
+ int16_t value = -1;
+ if (mPos + 2 <= mLength)
+ {
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ int16_t swap;
+ memcpy(&swap, mData + mPos, sizeof(int16_t));
+ value = SDL_Swap16(swap);
+#else
+ memcpy(&value, mData + mPos, sizeof(int16_t));
+#endif
+ }
+ mPos += 2;
+ PacketCounters::incInBytes(2);
+ DEBUGLOG("readInt16: " + toString(static_cast<int>(value)));
+ return value;
+}
+
+int MessageIn::readInt32()
+{
+ int32_t value = -1;
+ if (mPos + 4 <= mLength)
+ {
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ int32_t swap;
+ memcpy(&swap, mData + mPos, sizeof(int32_t));
+ value = SDL_Swap32(swap);
+#else
+ memcpy(&value, mData + mPos, sizeof(int32_t));
+#endif
+ }
+ mPos += 4;
+ PacketCounters::incInBytes(4);
+ DEBUGLOG(strprintf("readInt32: %u", value));
+ return value;
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/messagein.h b/src/net/eathena/messagein.h
new file mode 100644
index 000000000..a9cf0ce0f
--- /dev/null
+++ b/src/net/eathena/messagein.h
@@ -0,0 +1,53 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_MESSAGEIN_H
+#define NET_EATHENA_MESSAGEIN_H
+
+#include "net/messagein.h"
+
+#include <SDL_types.h>
+#include <string>
+
+namespace EAthena
+{
+
+/**
+ * Used for parsing an incoming message.
+ *
+ * \ingroup Network
+ */
+ class MessageIn : public Net::MessageIn
+{
+ public:
+ /**
+ * Constructor.
+ */
+ MessageIn(const char *data, unsigned int length);
+
+ int16_t readInt16(); /**< Reads a short. */
+ int readInt32(); /**< Reads a long. */
+};
+
+}
+
+#endif // NET_EATHENA_MESSAGEIN_H
diff --git a/src/net/eathena/messageout.cpp b/src/net/eathena/messageout.cpp
new file mode 100644
index 000000000..795807a3f
--- /dev/null
+++ b/src/net/eathena/messageout.cpp
@@ -0,0 +1,147 @@
+/*
+ * 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/messageout.h"
+
+#include "net/packetcounters.h"
+
+#include "net/eathena/network.h"
+
+#include "logger.h"
+
+#include "utils/stringutils.h"
+
+#include <SDL.h>
+#include <SDL_endian.h>
+
+#include <cstring>
+#include <string>
+
+#include "debug.h"
+
+namespace EAthena
+{
+
+MessageOut::MessageOut(short id):
+ Net::MessageOut(id)
+{
+ mNetwork = EAthena::Network::instance();
+ mData = mNetwork->mOutBuffer + mNetwork->mOutSize;
+
+ writeInt16(id);
+}
+
+void MessageOut::expand(size_t bytes)
+{
+ mNetwork->mOutSize += static_cast<unsigned>(bytes);
+ PacketCounters::incOutBytes(static_cast<int>(bytes));
+}
+
+void MessageOut::writeInt16(int16_t value)
+{
+ DEBUGLOG("writeInt16: " + toString(static_cast<int>(value)));
+ expand(2);
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ int16_t swap = SDL_Swap16(value);
+ memcpy(mData + mPos, &swap, sizeof(int16_t));
+#else
+ memcpy(mData + mPos, &value, sizeof(int16_t));
+#endif
+ mPos += 2;
+ PacketCounters::incOutBytes(2);
+}
+
+void MessageOut::writeInt32(int32_t value)
+{
+ DEBUGLOG("writeInt32: " + toString(value));
+ expand(4);
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ int32_t swap = SDL_Swap32(value);
+ memcpy(mData + mPos, &swap, sizeof(int32_t));
+#else
+ memcpy(mData + mPos, &value, sizeof(int32_t));
+#endif
+ mPos += 4;
+ PacketCounters::incOutBytes(4);
+}
+
+#define LOBYTE(w) (static_cast<unsigned char>(w))
+#define HIBYTE(w) (static_cast<unsigned char>(( \
+static_cast<unsigned short>(w)) >> 8))
+
+void MessageOut::writeCoordinates(unsigned short x, unsigned short y,
+ unsigned char direction)
+{
+ DEBUGLOG(strprintf("writeCoordinates: %u,%u %u", x, y, direction));
+ char *data = mData + mPos;
+ mNetwork->mOutSize += 3;
+ mPos += 3;
+
+ short temp;
+ temp = x;
+ temp <<= 6;
+ data[0] = 0;
+ data[1] = 1;
+ data[2] = 2;
+ data[0] = HIBYTE(temp);
+ data[1] = static_cast<unsigned char>(temp);
+ temp = y;
+ temp <<= 4;
+ data[1] |= HIBYTE(temp);
+ data[2] = LOBYTE(temp);
+
+ // Translate direction to eAthena format
+ switch (direction)
+ {
+ case 1:
+ direction = 0;
+ break;
+ case 3:
+ direction = 1;
+ break;
+ case 2:
+ direction = 2;
+ break;
+ case 6:
+ direction = 3;
+ break;
+ case 4:
+ direction = 4;
+ break;
+ case 12:
+ direction = 5;
+ break;
+ case 8:
+ direction = 6;
+ break;
+ case 9:
+ direction = 7;
+ break;
+ default:
+ // OOPSIE! Impossible or unknown
+ direction = static_cast<unsigned char>(-1);
+ }
+ data[2] |= direction;
+ PacketCounters::incOutBytes(3);
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/messageout.h b/src/net/eathena/messageout.h
new file mode 100644
index 000000000..c01962221
--- /dev/null
+++ b/src/net/eathena/messageout.h
@@ -0,0 +1,70 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_MESSAGEOUT_H
+#define NET_EATHENA_MESSAGEOUT_H
+
+#include "net/messageout.h"
+
+#include <iosfwd>
+#include <SDL_types.h>
+
+namespace EAthena
+{
+
+class Network;
+
+/**
+ * Used for building an outgoing message.
+ *
+ * \ingroup Network
+ */
+class MessageOut : public Net::MessageOut
+{
+ public:
+ /**
+ * Constructor.
+ */
+ MessageOut(short id);
+
+ void writeInt16(int16_t value); /**< Writes a short. */
+
+ void writeInt32(int32_t value); /**< Writes a long. */
+
+ /**
+ * Encodes coordinates and direction in 3 bytes.
+ */
+ void writeCoordinates(unsigned short x, unsigned short y,
+ unsigned char direction);
+
+ void resetPos()
+ { mPos = 0; }
+
+ private:
+ void expand(size_t size);
+
+ Network *mNetwork;
+};
+
+}
+
+#endif // NET_EATHENA_MESSAGEOUT_H
diff --git a/src/net/eathena/network.cpp b/src/net/eathena/network.cpp
new file mode 100644
index 000000000..17514d025
--- /dev/null
+++ b/src/net/eathena/network.cpp
@@ -0,0 +1,520 @@
+/*
+ * 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/network.h"
+
+#include "configuration.h"
+#include "logger.h"
+
+#include "net/messagehandler.h"
+#include "net/messagein.h"
+
+#include "net/eathena/protocol.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include <assert.h>
+#include <sstream>
+
+#include "debug.h"
+
+namespace EAthena
+{
+
+/** Warning: buffers and other variables are shared,
+ so there can be only one connection active at a time */
+
+short packet_lengths[] =
+{
+ 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// #0x0040
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 50, 3, -1, 55, 17, 3, 37, 46, -1, 23, -1, 3, 108, 3, 2,
+ 3, 28, 19, 11, 3, -1, 9, 5, 55, 53, 58, 60, 44, 2, 6, 6,
+// #0x0080
+ 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 23, -1, -1, -1, 0,
+ 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6,
+ 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6,
+ 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3,
+// #0x00C0
+ 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 6, 2, 27,
+ 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1,
+ 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2,
+ 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10,
+// #0x0100
+ 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1,
+ 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16,
+ 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1,
+ 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26,
+// #0x0140
+ 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6,
+110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42,
+ -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14, 186, 182,
+ 14, 30, 10, 3, -1, 6, 106, -1, 4, 5, 4, -1, 6, 7, -1, -1,
+// #0x0180
+ 6, 3, 106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6,
+ 90, 86, 24, 6, 30, 102, 9, 4, 8, 4, 14, 10, 4, 6, 2, 6,
+ 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4,
+ 11, 7, -1, 67, 12, 18, 114, 6, 3, 6, 26, 26, 26, 26, 2, 3,
+// #0x01C0
+ 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 0, 9, 9, 29, 6, 28,
+ 8, 14, 10, 35, 6, 8, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6,
+ 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1,
+ -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10,
+// #0x0200
+ 26, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 19, 10, 0, 0, 0,
+ 2, -1, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -1, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// #0x0240
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// #0x0280
+ 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 0, 0, 0, 0, 0, 0,
+// #0x02C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// #0x0300
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+const unsigned int BUFFER_SIZE = 655360;
+
+int networkThread(void *data)
+{
+ Network *network = static_cast<Network*>(data);
+
+ if (!network || !network->realConnect())
+ return -1;
+
+ network->receive();
+
+ return 0;
+}
+
+Network *Network::mInstance = nullptr;
+
+Network::Network() :
+ mSocket(nullptr),
+ mInBuffer(new char[BUFFER_SIZE]),
+ mOutBuffer(new char[BUFFER_SIZE]),
+ mInSize(0),
+ mOutSize(0),
+ mToSkip(0),
+ mState(IDLE),
+ mWorkerThread(nullptr)
+{
+ SDLNet_Init();
+
+ mMutex = SDL_CreateMutex();
+ mInstance = this;
+}
+
+Network::~Network()
+{
+ clearHandlers();
+
+ if (mState != IDLE && mState != NET_ERROR)
+ disconnect();
+
+ SDL_DestroyMutex(mMutex);
+ mMutex = nullptr;
+ mInstance = nullptr;
+
+ delete []mInBuffer;
+ delete []mOutBuffer;
+
+ SDLNet_Quit();
+}
+
+bool Network::connect(ServerInfo server)
+{
+ if (mState != IDLE && mState != NET_ERROR)
+ {
+ logger->log1("Tried to connect an already connected socket!");
+ assert(false);
+ return false;
+ }
+
+ if (server.hostname.empty())
+ {
+ setError(_("Empty address given to Network::connect()!"));
+ return false;
+ }
+
+ logger->log("Network::Connecting to %s:%i",
+ server.hostname.c_str(), server.port);
+
+ mServer.hostname = server.hostname;
+ mServer.port = server.port;
+
+ // Reset to sane values
+ mOutSize = 0;
+ mInSize = 0;
+ mToSkip = 0;
+
+ mState = CONNECTING;
+ mWorkerThread = SDL_CreateThread(networkThread, this);
+ if (!mWorkerThread)
+ {
+ setError("Unable to create network worker thread");
+ return false;
+ }
+
+ return true;
+}
+
+void Network::disconnect()
+{
+ mState = IDLE;
+
+ if (mWorkerThread && SDL_GetThreadID(mWorkerThread))
+ {
+ SDL_WaitThread(mWorkerThread, nullptr);
+ mWorkerThread = nullptr;
+ }
+
+ if (mSocket)
+ {
+ // need call SDLNet_TCP_DelSocket?
+ SDLNet_TCP_Close(mSocket);
+ mSocket = nullptr;
+ int sleep = config.getIntValue("networksleep");
+ if (sleep > 0)
+ SDL_Delay(sleep);
+ }
+}
+
+void Network::registerHandler(MessageHandler *handler)
+{
+ if (!handler)
+ return;
+
+ for (const uint16_t *i = handler->handledMessages; *i; ++i)
+ mMessageHandlers[*i] = handler;
+
+ handler->setNetwork(this);
+}
+
+void Network::unregisterHandler(MessageHandler *handler)
+{
+ if (!handler)
+ return;
+
+ for (const uint16_t *i = handler->handledMessages; *i; ++i)
+ mMessageHandlers.erase(*i);
+
+ handler->setNetwork(nullptr);
+}
+
+void Network::clearHandlers()
+{
+ for (MessageHandlerIterator i = mMessageHandlers.begin();
+ i != mMessageHandlers.end(); ++i)
+ {
+ if (i->second)
+ i->second->setNetwork(nullptr);
+ }
+ mMessageHandlers.clear();
+}
+
+void Network::dispatchMessages()
+{
+ while (messageReady())
+ {
+ MessageIn msg = getNextMessage();
+
+ MessageHandlerIterator iter = mMessageHandlers.find(msg.getId());
+
+ if (msg.getLength() == 0)
+ logger->safeError("Zero length packet received. Exiting.");
+
+ if (iter != mMessageHandlers.end())
+ {
+ if (iter->second)
+ iter->second->handleMessage(msg);
+ }
+ else
+ {
+ logger->log("Unhandled packet: %x", msg.getId());
+ }
+
+ skip(msg.getLength());
+ }
+}
+
+void Network::flush()
+{
+ if (!mOutSize || mState != CONNECTED)
+ return;
+
+ int ret;
+
+ SDL_mutexP(mMutex);
+ ret = SDLNet_TCP_Send(mSocket, mOutBuffer, mOutSize);
+ DEBUGLOG("Send " + toString(mOutSize) + " bytes");
+ if (ret < static_cast<int>(mOutSize))
+ {
+ setError("Error in SDLNet_TCP_Send(): " +
+ std::string(SDLNet_GetError()));
+ }
+ mOutSize = 0;
+ SDL_mutexV(mMutex);
+}
+
+void Network::skip(int len)
+{
+ SDL_mutexP(mMutex);
+ mToSkip += len;
+ if (!mInSize)
+ {
+ SDL_mutexV(mMutex);
+ return;
+ }
+
+ if (mInSize >= mToSkip)
+ {
+ mInSize -= mToSkip;
+ memmove(mInBuffer, mInBuffer + mToSkip, mInSize);
+ mToSkip = 0;
+ }
+ else
+ {
+ mToSkip -= mInSize;
+ mInSize = 0;
+ }
+ SDL_mutexV(mMutex);
+}
+
+bool Network::messageReady()
+{
+ int len = -1;
+
+ SDL_mutexP(mMutex);
+ if (mInSize >= 2)
+ {
+ int msgId = readWord(0);
+ if (msgId == SMSG_SERVER_VERSION_RESPONSE)
+ len = 10;
+ else if (msgId == SMSG_UPDATE_HOST2)
+ len = -1;
+ else
+ len = packet_lengths[msgId];
+
+ if (len == -1 && mInSize > 4)
+ len = readWord(2);
+ }
+
+ bool ret = (mInSize >= static_cast<unsigned int>(len));
+ SDL_mutexV(mMutex);
+
+ return ret;
+}
+
+MessageIn Network::getNextMessage()
+{
+ while (!messageReady())
+ {
+ if (mState == NET_ERROR)
+ break;
+ }
+
+ SDL_mutexP(mMutex);
+ int msgId = readWord(0);
+ int len;
+ if (msgId == SMSG_SERVER_VERSION_RESPONSE)
+ len = 10;
+ else if (msgId == SMSG_UPDATE_HOST2)
+ len = -1;
+ else
+ len = packet_lengths[msgId];
+
+ if (len == -1)
+ len = readWord(2);
+
+#ifdef ENABLEDEBUGLOG
+ logger->dlog(strprintf("Received packet 0x%x of length %d\n",
+ msgId, len));
+#endif
+
+ MessageIn msg(mInBuffer, len);
+ SDL_mutexV(mMutex);
+
+ return msg;
+}
+
+bool Network::realConnect()
+{
+ IPaddress ipAddress;
+
+ if (SDLNet_ResolveHost(&ipAddress, mServer.hostname.c_str(),
+ mServer.port) == -1)
+ {
+ std::string errorMessage = _("Unable to resolve host \"") +
+ mServer.hostname + "\"";
+ setError(errorMessage);
+ logger->log("SDLNet_ResolveHost: %s", errorMessage.c_str());
+ return false;
+ }
+
+ mState = CONNECTING;
+
+ mSocket = SDLNet_TCP_Open(&ipAddress);
+ if (!mSocket)
+ {
+ logger->log("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError());
+ setError(SDLNet_GetError());
+ return false;
+ }
+
+ logger->log("Network::Started session with %s:%i",
+ ipToString(ipAddress.host), ipAddress.port);
+
+ mState = CONNECTED;
+
+ return true;
+}
+
+void Network::receive()
+{
+ SDLNet_SocketSet set;
+
+ if (!(set = SDLNet_AllocSocketSet(1)))
+ {
+ setError("Error in SDLNet_AllocSocketSet(): " +
+ std::string(SDLNet_GetError()));
+ return;
+ }
+
+ if (SDLNet_TCP_AddSocket(set, mSocket) == -1)
+ {
+ setError("Error in SDLNet_AddSocket(): " +
+ std::string(SDLNet_GetError()));
+ }
+
+ while (mState == CONNECTED)
+ {
+ // TODO Try to get this to block all the time while still being able
+ // to escape the loop
+ int numReady = SDLNet_CheckSockets(set, (static_cast<uint32_t>(500)));
+ int ret;
+ switch (numReady)
+ {
+ case -1:
+ logger->log1("Error: SDLNet_CheckSockets");
+ // FALLTHROUGH
+ case 0:
+ break;
+
+ case 1:
+ // Receive data from the socket
+ SDL_mutexP(mMutex);
+ ret = SDLNet_TCP_Recv(mSocket, mInBuffer + mInSize,
+ BUFFER_SIZE - mInSize);
+
+ if (!ret)
+ {
+ // We got disconnected
+ mState = IDLE;
+ logger->log1("Disconnected.");
+ }
+ else if (ret < 0)
+ {
+ setError(_("Connection to server terminated. ") +
+ std::string(SDLNet_GetError()));
+ }
+ else
+ {
+// DEBUGLOG("Receive " + toString(ret) + " bytes");
+ mInSize += ret;
+ if (mToSkip)
+ {
+ if (mInSize >= mToSkip)
+ {
+ mInSize -= mToSkip;
+ memmove(mInBuffer, mInBuffer + mToSkip, mInSize);
+ mToSkip = 0;
+ }
+ else
+ {
+ mToSkip -= mInSize;
+ mInSize = 0;
+ }
+ }
+ }
+ SDL_mutexV(mMutex);
+ break;
+
+ default:
+ // more than one socket is ready..
+ // this should not happen since we only listen once socket.
+ std::stringstream errorStream;
+ errorStream << "Error in SDLNet_TCP_Recv(), " << numReady
+ << " sockets are ready: " << SDLNet_GetError();
+ setError(errorStream.str());
+ break;
+ }
+ }
+
+ if (SDLNet_TCP_DelSocket(set, mSocket) == -1)
+ logger->log("Error in SDLNet_DelSocket(): %s", SDLNet_GetError());
+
+ SDLNet_FreeSocketSet(set);
+}
+
+Network *Network::instance()
+{
+ return mInstance;
+}
+
+void Network::setError(const std::string &error)
+{
+ logger->log("Network error: %s", error.c_str());
+ mError = error;
+ mState = NET_ERROR;
+}
+
+uint16_t Network::readWord(int pos)
+{
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ return SDL_Swap16((*(uint16_t*)(mInBuffer + (pos))));
+#else
+ return (*reinterpret_cast<uint16_t*>(mInBuffer + (pos)));
+#endif
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/network.h b/src/net/eathena/network.h
new file mode 100644
index 000000000..82e8fcc88
--- /dev/null
+++ b/src/net/eathena/network.h
@@ -0,0 +1,138 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_NETWORK_H
+#define NET_EATHENA_NETWORK_H
+
+#include "net/serverinfo.h"
+
+#include "net/eathena/messagehandler.h"
+#include "net/eathena/messagein.h"
+#include "net/eathena/messageout.h"
+
+#include <SDL_net.h>
+#include <SDL_thread.h>
+
+#include <map>
+#include <string>
+
+/**
+ * Protocol version, reported to the eAthena char and mapserver who can adjust
+ * the protocol accordingly.
+ */
+#define CLIENT_PROTOCOL_VERSION 5
+#define CLIENT_TMW_PROTOCOL_VERSION 1
+
+namespace EAthena
+{
+
+class Network
+{
+ public:
+ Network();
+
+ ~Network();
+
+ bool connect(ServerInfo server);
+
+ void disconnect();
+
+ ServerInfo getServer() const
+ { return mServer; }
+
+ void registerHandler(MessageHandler *handler);
+
+ void unregisterHandler(MessageHandler *handler);
+
+ void clearHandlers();
+
+ int getState() const
+ { return mState; }
+
+ const std::string &getError() const
+ { return mError; }
+
+ bool isConnected() const
+ { return mState == CONNECTED; }
+
+ int getInSize() const
+ { return mInSize; }
+
+ void skip(int len);
+
+ bool messageReady();
+
+ MessageIn getNextMessage();
+
+ void dispatchMessages();
+
+ void flush();
+
+ // ERROR replaced by NET_ERROR because already defined in Windows
+ enum
+ {
+ IDLE = 0,
+ CONNECTED,
+ CONNECTING,
+ DATA,
+ NET_ERROR
+ };
+
+ protected:
+ friend int networkThread(void *data);
+ friend class MessageOut;
+
+ static Network *instance();
+
+ void setError(const std::string &error);
+
+ uint16_t readWord(int pos);
+
+ bool realConnect();
+
+ void receive();
+
+ TCPsocket mSocket;
+
+ ServerInfo mServer;
+
+ char *mInBuffer, *mOutBuffer;
+ unsigned int mInSize, mOutSize;
+
+ unsigned int mToSkip;
+
+ int mState;
+ std::string mError;
+
+ SDL_Thread *mWorkerThread;
+ SDL_mutex *mMutex;
+
+ typedef std::map<uint16_t, MessageHandler*> MessageHandlers;
+ typedef MessageHandlers::iterator MessageHandlerIterator;
+ MessageHandlers mMessageHandlers;
+
+ static Network *mInstance;
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_NETWORK_H
diff --git a/src/net/eathena/npchandler.cpp b/src/net/eathena/npchandler.cpp
new file mode 100644
index 000000000..8b64248e4
--- /dev/null
+++ b/src/net/eathena/npchandler.cpp
@@ -0,0 +1,309 @@
+/*
+ * 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/npchandler.h"
+
+#include "localplayer.h"
+
+#include "gui/npcdialog.h"
+#include "gui/viewport.h"
+
+#include "net/messagein.h"
+#include "net/net.h"
+#include "net/npchandler.h"
+
+#include "net/eathena/protocol.h"
+
+#include "net/ea/eaprotocol.h"
+
+#include "utils/langs.h"
+
+#include "debug.h"
+
+extern Net::NpcHandler *npcHandler;
+
+namespace EAthena
+{
+
+NpcHandler::NpcHandler() :
+ mRequestLang(false)
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_NPC_CHOICE,
+ SMSG_NPC_MESSAGE,
+ SMSG_NPC_NEXT,
+ SMSG_NPC_CLOSE,
+ SMSG_NPC_INT_INPUT,
+ SMSG_NPC_STR_INPUT,
+ SMSG_NPC_COMMAND,
+ 0
+ };
+ handledMessages = _messages;
+ npcHandler = this;
+}
+
+void NpcHandler::handleMessage(Net::MessageIn &msg)
+{
+ int npcId = getNpc(msg, msg.getId() == SMSG_NPC_CHOICE
+ || msg.getId() == SMSG_NPC_MESSAGE);
+
+ if (msg.getId() != SMSG_NPC_STR_INPUT)
+ mRequestLang = false;
+
+ switch (msg.getId())
+ {
+ case SMSG_NPC_CHOICE:
+ processNpcChoice(msg);
+ break;
+
+ case SMSG_NPC_MESSAGE:
+ processNpcMessage(msg);
+ break;
+
+ case SMSG_NPC_CLOSE:
+ processNpcClose(msg);
+ break;
+
+ case SMSG_NPC_NEXT:
+ processNpcNext(msg);
+ break;
+
+ case SMSG_NPC_INT_INPUT:
+ processNpcIntInput(msg);
+ break;
+
+ case SMSG_NPC_STR_INPUT:
+ if (mRequestLang)
+ processLangReuqest(msg, npcId);
+ else
+ processNpcStrInput(msg);
+ break;
+
+ case SMSG_NPC_COMMAND:
+ processNpcCommand(msg, npcId);
+ break;
+
+ default:
+ break;
+ }
+
+ mDialog = nullptr;
+}
+
+void NpcHandler::talk(int npcId)
+{
+ MessageOut outMsg(CMSG_NPC_TALK);
+ outMsg.writeInt32(npcId);
+ outMsg.writeInt8(0); // Unused
+}
+
+void NpcHandler::nextDialog(int npcId)
+{
+ MessageOut outMsg(CMSG_NPC_NEXT_REQUEST);
+ outMsg.writeInt32(npcId);
+}
+
+void NpcHandler::closeDialog(int npcId)
+{
+ MessageOut outMsg(CMSG_NPC_CLOSE);
+ outMsg.writeInt32(npcId);
+
+ NpcDialogs::iterator it = mNpcDialogs.find(npcId);
+ if (it != mNpcDialogs.end())
+ {
+ if ((*it).second.dialog)
+ (*it).second.dialog->close();
+ if ((*it).second.dialog == mDialog)
+ mDialog = nullptr;
+ mNpcDialogs.erase(it);
+ }
+}
+
+void NpcHandler::listInput(int npcId, unsigned char value)
+{
+ MessageOut outMsg(CMSG_NPC_LIST_CHOICE);
+ outMsg.writeInt32(npcId);
+ outMsg.writeInt8(value);
+}
+
+void NpcHandler::integerInput(int npcId, int value)
+{
+ MessageOut outMsg(CMSG_NPC_INT_RESPONSE);
+ outMsg.writeInt32(npcId);
+ outMsg.writeInt32(value);
+}
+
+void NpcHandler::stringInput(int npcId, const std::string &value)
+{
+ MessageOut outMsg(CMSG_NPC_STR_RESPONSE);
+ outMsg.writeInt16(static_cast<int16_t>(value.length() + 9));
+ outMsg.writeInt32(npcId);
+ outMsg.writeString(value, static_cast<int>(value.length()));
+ outMsg.writeInt8(0); // Prevent problems with string reading
+}
+
+void NpcHandler::buy(int beingId)
+{
+ MessageOut outMsg(CMSG_NPC_BUY_SELL_REQUEST);
+ outMsg.writeInt32(beingId);
+ outMsg.writeInt8(0); // Buy
+}
+
+void NpcHandler::sell(int beingId)
+{
+ MessageOut outMsg(CMSG_NPC_BUY_SELL_REQUEST);
+ outMsg.writeInt32(beingId);
+ outMsg.writeInt8(1); // Sell
+}
+
+void NpcHandler::buyItem(int beingId A_UNUSED, int itemId,
+ unsigned char color, int amount)
+{
+ MessageOut outMsg(CMSG_NPC_BUY_REQUEST);
+ if (serverVersion > 0)
+ {
+ outMsg.writeInt16(10); // One item (length of packet)
+ outMsg.writeInt16(static_cast<int16_t>(amount));
+ outMsg.writeInt16(static_cast<int16_t>(itemId));
+ outMsg.writeInt8(color);
+ outMsg.writeInt8(0);
+ }
+ else
+ {
+ outMsg.writeInt16(8); // One item (length of packet)
+ outMsg.writeInt16(static_cast<int16_t>(amount));
+ outMsg.writeInt16(static_cast<int16_t>(itemId));
+ }
+}
+
+void NpcHandler::sellItem(int beingId A_UNUSED, int itemId, int amount)
+{
+ MessageOut outMsg(CMSG_NPC_SELL_REQUEST);
+ outMsg.writeInt16(8); // One item (length of packet)
+ outMsg.writeInt16(static_cast<int16_t>(itemId + INVENTORY_OFFSET));
+ outMsg.writeInt16(static_cast<int16_t>(amount));
+}
+
+int NpcHandler::getNpc(Net::MessageIn &msg, bool haveLength)
+{
+ if (haveLength)
+ msg.readInt16(); // length
+
+ const int npcId = msg.readInt32();
+
+ NpcDialogs::const_iterator diag = mNpcDialogs.find(npcId);
+ mDialog = nullptr;
+
+ if (diag == mNpcDialogs.end())
+ {
+ // Empty dialogs don't help
+ if (msg.getId() == SMSG_NPC_CLOSE)
+ {
+ closeDialog(npcId);
+ return npcId;
+ }
+ else if (msg.getId() == SMSG_NPC_NEXT)
+ {
+ nextDialog(npcId);
+ return npcId;
+ }
+ else
+ {
+ mDialog = new NpcDialog(npcId);
+ mDialog->saveCamera();
+ if (player_node)
+ player_node->stopWalking(false);
+ Wrapper wrap;
+ wrap.dialog = mDialog;
+ mNpcDialogs[npcId] = wrap;
+ }
+ }
+ else
+ {
+ if (mDialog && mDialog != diag->second.dialog)
+ mDialog->restoreCamera();
+ mDialog = diag->second.dialog;
+ if (mDialog)
+ mDialog->saveCamera();
+ }
+ return npcId;
+}
+
+void NpcHandler::processNpcCommand(Net::MessageIn &msg, int npcId)
+{
+ const int cmd = msg.readInt16();
+ switch (cmd)
+ {
+ case 0:
+ mRequestLang = true;
+ break;
+
+ case 1:
+ if (viewport)
+ viewport->moveCameraToActor(npcId);
+ break;
+
+ case 2:
+ if (viewport)
+ {
+ const int id = msg.readInt32();
+ const int x = msg.readInt16();
+ const int y = msg.readInt16();
+ if (!id)
+ viewport->moveCameraToPosition(x, y);
+ else
+ viewport->moveCameraToActor(id, x, y);
+ }
+ break;
+
+ case 3:
+ if (viewport)
+ viewport->returnCamera();
+ break;
+
+ case 4:
+ if (viewport)
+ {
+ msg.readInt32(); // id
+ const int x = msg.readInt16();
+ const int y = msg.readInt16();
+ viewport->moveCameraRelative(x, y);
+ }
+ break;
+ case 5:
+ closeDialog(npcId);
+ break;
+
+ default:
+ logger->log("unknown npc command: %d", cmd);
+ break;
+ }
+}
+
+void NpcHandler::processLangReuqest(Net::MessageIn &msg A_UNUSED, int npcId)
+{
+ mRequestLang = false;
+ stringInput(npcId, getLangSimple());
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/npchandler.h b/src/net/eathena/npchandler.h
new file mode 100644
index 000000000..855450f4f
--- /dev/null
+++ b/src/net/eathena/npchandler.h
@@ -0,0 +1,79 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_NPCHANDLER_H
+#define NET_EATHENA_NPCHANDLER_H
+
+#include "net/net.h"
+#include "net/npchandler.h"
+
+#include "net/ea/npchandler.h"
+
+#include "net/eathena/messagehandler.h"
+
+#include <map>
+
+class NpcDialog;
+
+namespace EAthena
+{
+
+class NpcHandler : public MessageHandler, public Ea::NpcHandler
+{
+ public:
+ NpcHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void talk(int npcId);
+
+ void nextDialog(int npcId);
+
+ void closeDialog(int npcId);
+
+ void listInput(int npcId, unsigned char value);
+
+ void integerInput(int npcId, int value);
+
+ void stringInput(int npcId, const std::string &value);
+
+ void buy(int beingId);
+
+ void sell(int beingId);
+
+ void buyItem(int beingId, int itemId, unsigned char color, int amount);
+
+ void sellItem(int beingId, int itemId, int amount);
+
+ int getNpc(Net::MessageIn &msg, bool haveLength);
+
+ void processNpcCommand(Net::MessageIn &msg, int npcId);
+
+ void processLangReuqest(Net::MessageIn &msg, int npcId);
+
+ private:
+ bool mRequestLang;
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_NPCHANDLER_H
diff --git a/src/net/eathena/partyhandler.cpp b/src/net/eathena/partyhandler.cpp
new file mode 100644
index 000000000..2f525c9d5
--- /dev/null
+++ b/src/net/eathena/partyhandler.cpp
@@ -0,0 +1,210 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net>
+ * 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/partyhandler.h"
+
+#include "actorspritemanager.h"
+#include "localplayer.h"
+#include "logger.h"
+
+#include "net/messagein.h"
+
+#include "net/eathena/protocol.h"
+
+#include "net/eathena/gui/partytab.h"
+
+#include "utils/gettext.h"
+
+#include "debug.h"
+
+extern Net::PartyHandler *partyHandler;
+
+namespace EAthena
+{
+
+PartyHandler::PartyHandler() :
+ Ea::PartyHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_PARTY_CREATE,
+ SMSG_PARTY_INFO,
+ SMSG_PARTY_INVITE_RESPONSE,
+ SMSG_PARTY_INVITED,
+ SMSG_PARTY_SETTINGS,
+ SMSG_PARTY_MOVE,
+ SMSG_PARTY_LEAVE,
+ SMSG_PARTY_UPDATE_HP,
+ SMSG_PARTY_UPDATE_COORDS,
+ SMSG_PARTY_MESSAGE,
+ 0
+ };
+ handledMessages = _messages;
+ partyHandler = this;
+}
+
+PartyHandler::~PartyHandler()
+{
+}
+
+void PartyHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_PARTY_CREATE:
+ processPartyCreate(msg);
+ break;
+ case SMSG_PARTY_INFO:
+ processPartyInfo(msg);
+ break;
+ case SMSG_PARTY_INVITE_RESPONSE:
+ processPartyInviteResponse(msg);
+ break;
+ case SMSG_PARTY_INVITED:
+ processPartyInvited(msg);
+ break;
+ case SMSG_PARTY_SETTINGS:
+ processPartySettings(msg);
+ break;
+ case SMSG_PARTY_MOVE:
+ processPartyMove(msg);
+ break;
+ case SMSG_PARTY_LEAVE:
+ processPartyLeave(msg);
+ break;
+ case SMSG_PARTY_UPDATE_HP:
+ processPartyUpdateHp(msg);
+ break;
+ case SMSG_PARTY_UPDATE_COORDS:
+ processPartyUpdateCoords(msg);
+ break;
+ case SMSG_PARTY_MESSAGE:
+ processPartyMessage(msg);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void PartyHandler::create(const std::string &name)
+{
+ MessageOut outMsg(CMSG_PARTY_CREATE);
+ outMsg.writeString(name.substr(0, 23), 24);
+}
+
+void PartyHandler::invite(Being *being)
+{
+ if (being)
+ {
+ MessageOut outMsg(CMSG_PARTY_INVITE);
+ outMsg.writeInt32(being->getId());
+ }
+}
+
+void PartyHandler::invite(const std::string &name)
+{
+ if (!actorSpriteManager)
+ return;
+
+ Being* being = actorSpriteManager->findBeingByName(name, Being::PLAYER);
+ if (being)
+ {
+ MessageOut outMsg(CMSG_PARTY_INVITE);
+ outMsg.writeInt32(being->getId());
+ }
+}
+
+void PartyHandler::inviteResponse(const std::string &inviter A_UNUSED,
+ bool accept)
+{
+ if (player_node)
+ {
+ MessageOut outMsg(CMSG_PARTY_INVITED);
+ outMsg.writeInt32(player_node->getId());
+ outMsg.writeInt32(accept ? 1 : 0);
+ }
+}
+
+void PartyHandler::leave()
+{
+ MessageOut outMsg(CMSG_PARTY_LEAVE);
+}
+
+void PartyHandler::kick(Being *being)
+{
+ if (being)
+ {
+ MessageOut outMsg(CMSG_PARTY_KICK);
+ outMsg.writeInt32(being->getId());
+ outMsg.writeString("", 24); //Unused
+ }
+}
+
+void PartyHandler::kick(const std::string &name)
+{
+ if (!Ea::taParty)
+ return;
+
+ PartyMember *m = Ea::taParty->getMember(name);
+ if (!m)
+ {
+ if (Ea::partyTab)
+ {
+ Ea::partyTab->chatLog(strprintf(_("%s is not in your party!"),
+ name.c_str()), BY_SERVER);
+ }
+ return;
+ }
+
+ MessageOut outMsg(CMSG_PARTY_KICK);
+ outMsg.writeInt32(m->getID());
+ outMsg.writeString(name, 24); //Unused
+}
+
+void PartyHandler::chat(const std::string &text)
+{
+ MessageOut outMsg(CMSG_PARTY_MESSAGE);
+ outMsg.writeInt16(static_cast<int16_t>(text.length() + 4));
+ outMsg.writeString(text, static_cast<int>(text.length()));
+}
+
+void PartyHandler::setShareExperience(PartyShare share)
+{
+ if (share == PARTY_SHARE_NOT_POSSIBLE)
+ return;
+
+ MessageOut outMsg(CMSG_PARTY_SETTINGS);
+ outMsg.writeInt16(share);
+ outMsg.writeInt16(mShareItems);
+}
+
+void PartyHandler::setShareItems(PartyShare share)
+{
+ if (share == PARTY_SHARE_NOT_POSSIBLE)
+ return;
+
+ MessageOut outMsg(CMSG_PARTY_SETTINGS);
+ outMsg.writeInt16(mShareExp);
+ outMsg.writeInt16(share);
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/partyhandler.h b/src/net/eathena/partyhandler.h
new file mode 100644
index 000000000..b39f17a33
--- /dev/null
+++ b/src/net/eathena/partyhandler.h
@@ -0,0 +1,69 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net>
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_PARTYHANDLER_H
+#define NET_EATHENA_PARTYHANDLER_H
+
+#include "net/net.h"
+#include "net/partyhandler.h"
+
+#include "net/eathena/messagehandler.h"
+
+#include "net/ea/partyhandler.h"
+
+#include "party.h"
+
+namespace EAthena
+{
+
+class PartyHandler : public MessageHandler, public Ea::PartyHandler
+{
+ public:
+ PartyHandler();
+
+ ~PartyHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void create(const std::string &name = "");
+
+ void invite(Being *being);
+
+ void invite(const std::string &name);
+
+ void inviteResponse(const std::string &inviter, bool accept);
+
+ void leave();
+
+ void kick(Being *being);
+
+ void kick(const std::string &name);
+
+ void chat(const std::string &text);
+
+ void setShareExperience(PartyShare share);
+
+ void setShareItems(PartyShare share);
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_PARTYHANDLER_H
diff --git a/src/net/eathena/playerhandler.cpp b/src/net/eathena/playerhandler.cpp
new file mode 100644
index 000000000..a96df306d
--- /dev/null
+++ b/src/net/eathena/playerhandler.cpp
@@ -0,0 +1,313 @@
+/*
+ * 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/playerhandler.h"
+
+#include "configuration.h"
+#include "client.h"
+#include "logger.h"
+
+#include "net/messagein.h"
+
+#include "net/eathena/protocol.h"
+#include "net/eathena/npchandler.h"
+#include "net/eathena/inventoryhandler.h"
+
+#include "gui/whoisonline.h"
+
+#include "debug.h"
+
+extern Net::PlayerHandler *playerHandler;
+
+namespace EAthena
+{
+
+PlayerHandler::PlayerHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_WALK_RESPONSE,
+ SMSG_PLAYER_WARP,
+ SMSG_PLAYER_STAT_UPDATE_1,
+ SMSG_PLAYER_STAT_UPDATE_2,
+ SMSG_PLAYER_STAT_UPDATE_3,
+ SMSG_PLAYER_STAT_UPDATE_4,
+ SMSG_PLAYER_STAT_UPDATE_5,
+ SMSG_PLAYER_STAT_UPDATE_6,
+ SMSG_PLAYER_ARROW_MESSAGE,
+ SMSG_ONLINE_LIST,
+ SMSG_PLAYER_SHORTCUTS,
+ SMSG_PLAYER_SHOW_EQUIP,
+ 0
+ };
+ handledMessages = _messages;
+ playerHandler = this;
+}
+
+void PlayerHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_WALK_RESPONSE:
+ processWalkResponse(msg);
+ break;
+
+ case SMSG_PLAYER_WARP:
+ processPlayerWarp(msg);
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_1:
+ processPlayerStatUpdate1(msg);
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_2:
+ processPlayerStatUpdate2(msg);
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_3: // Update a base attribute
+ processPlayerStatUpdate3(msg);
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_4: // Attribute increase ack
+ processPlayerStatUpdate4(msg);
+ break;
+
+ // Updates stats and status points
+ case SMSG_PLAYER_STAT_UPDATE_5:
+ processPlayerStatUpdate5(msg);
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_6:
+ processPlayerStatUpdate6(msg);
+ break;
+
+ case SMSG_PLAYER_ARROW_MESSAGE:
+ processPlayerArrowMessage(msg);
+ break;
+
+ case SMSG_ONLINE_LIST:
+ processOnlineList(msg);
+ break;
+
+ case SMSG_PLAYER_SHORTCUTS:
+ processPlayerShortcuts(msg);
+ break;
+
+ case SMSG_PLAYER_SHOW_EQUIP:
+ processPlayerShowEquip(msg);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void PlayerHandler::attack(int id, bool keep)
+{
+ MessageOut outMsg(CMSG_PLAYER_ATTACK);
+ outMsg.writeInt32(id);
+ if (keep)
+ outMsg.writeInt8(7);
+ else
+ outMsg.writeInt8(0);
+}
+
+void PlayerHandler::stopAttack()
+{
+ MessageOut outMsg(CMSG_PLAYER_STOP_ATTACK);
+}
+
+void PlayerHandler::emote(uint8_t emoteId)
+{
+ MessageOut outMsg(CMSG_PLAYER_EMOTE);
+ outMsg.writeInt8(emoteId);
+}
+
+void PlayerHandler::increaseAttribute(int attr)
+{
+ if (attr >= STR && attr <= LUK)
+ {
+ MessageOut outMsg(CMSG_STAT_UPDATE_REQUEST);
+ outMsg.writeInt16(static_cast<int16_t>(attr));
+ outMsg.writeInt8(1);
+ }
+}
+
+void PlayerHandler::increaseSkill(unsigned short skillId)
+{
+ if (PlayerInfo::getAttribute(SKILL_POINTS) <= 0)
+ return;
+
+ MessageOut outMsg(CMSG_SKILL_LEVELUP_REQUEST);
+ outMsg.writeInt16(skillId);
+}
+
+void PlayerHandler::pickUp(FloorItem *floorItem)
+{
+ if (!floorItem)
+ return;
+
+ MessageOut outMsg(CMSG_ITEM_PICKUP);
+ outMsg.writeInt32(floorItem->getId());
+ EAthena::InventoryHandler *handler =
+ static_cast<EAthena::InventoryHandler*>(Net::getInventoryHandler());
+ if (handler)
+ handler->pushPickup(floorItem->getId());
+}
+
+void PlayerHandler::setDirection(char direction)
+{
+ MessageOut outMsg(CMSG_PLAYER_CHANGE_DIR);
+ outMsg.writeInt16(0);
+ outMsg.writeInt8(direction);
+}
+
+void PlayerHandler::setDestination(int x, int y, int direction)
+{
+ MessageOut outMsg(CMSG_PLAYER_CHANGE_DEST);
+ outMsg.writeCoordinates(static_cast<short unsigned int>(x),
+ static_cast<short unsigned int>(y),
+ static_cast<unsigned char>(direction));
+}
+
+void PlayerHandler::changeAction(Being::Action action)
+{
+ char type;
+ switch (action)
+ {
+ case Being::SIT:
+ type = 2;
+ break;
+ case Being::STAND:
+ type = 3;
+ break;
+ default:
+ case Being::MOVE:
+ case Being::ATTACK:
+ case Being::DEAD:
+ case Being::HURT:
+ case Being::SPAWN:
+ return;
+ }
+
+ MessageOut outMsg(CMSG_PLAYER_CHANGE_ACT);
+ outMsg.writeInt32(0);
+ outMsg.writeInt8(type);
+}
+
+void PlayerHandler::respawn()
+{
+ MessageOut outMsg(CMSG_PLAYER_RESTART);
+ outMsg.writeInt8(0);
+}
+
+void PlayerHandler::requestOnlineList()
+{
+ MessageOut outMsg(CMSG_ONLINE_LIST);
+}
+
+void PlayerHandler::processOnlineList(Net::MessageIn &msg)
+{
+ if (!whoIsOnline)
+ return;
+
+ int size = msg.readInt16() - 4;
+ std::vector<OnlinePlayer*> arr;
+
+ if (!size)
+ {
+ if (whoIsOnline)
+ whoIsOnline->loadList(arr);
+ return;
+ }
+
+ char *start = reinterpret_cast<char*>(msg.readBytes(size));
+ if (!start)
+ return;
+
+ char *buf = start;
+
+ int addVal = 1;
+ if (serverVersion >= 4)
+ addVal = 3;
+
+ while (buf - start + 1 < size && *(buf + addVal))
+ {
+ unsigned char status = 255;
+ unsigned char ver = 0;
+ unsigned char level = 0;
+ if (serverVersion >= 4)
+ {
+ status = *buf;
+ buf ++;
+ level = *buf;
+ buf ++;
+ ver = *buf;
+ }
+ buf ++;
+
+ int gender = GENDER_UNSPECIFIED;
+ if (serverVersion >= 4)
+ {
+ if (config.getBoolValue("showgender"))
+ {
+ if (status & Being::FLAG_GENDER_MALE)
+ gender = GENDER_MALE;
+ else if (status & Being::FLAG_GENDER_OTHER)
+ gender = GENDER_OTHER;
+ else
+ gender = GENDER_FEMALE;
+ }
+ }
+ arr.push_back(new OnlinePlayer(static_cast<char*>(buf),
+ status, level, gender, ver));
+ buf += strlen(buf) + 1;
+ }
+
+ if (whoIsOnline)
+ whoIsOnline->loadList(arr);
+ delete [] start;
+}
+
+void PlayerHandler::updateStatus(uint8_t status)
+{
+ MessageOut outMsg(CMSG_SET_STATUS);
+ outMsg.writeInt8(status);
+ outMsg.writeInt8(0);
+}
+
+void PlayerHandler::processPlayerShortcuts(Net::MessageIn &msg)
+{
+ for (int f = 0; f < 27; f ++)
+ {
+ msg.readInt8(); // type 0: item, 1: skill
+ msg.readInt32(); // item or skill id
+ msg.readInt16(); // lvl
+ }
+}
+
+void PlayerHandler::processPlayerShowEquip(Net::MessageIn &msg)
+{
+ msg.readInt8(); // show equip
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/playerhandler.h b/src/net/eathena/playerhandler.h
new file mode 100644
index 000000000..9b0ba215c
--- /dev/null
+++ b/src/net/eathena/playerhandler.h
@@ -0,0 +1,66 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_PLAYERHANDLER_H
+#define NET_EATHENA_PLAYERHANDLER_H
+
+#include "net/net.h"
+#include "net/playerhandler.h"
+
+#include "net/ea/playerhandler.h"
+
+#include "net/eathena/messagehandler.h"
+
+namespace EAthena
+{
+
+class PlayerHandler : public MessageHandler, public Ea::PlayerHandler
+{
+ public:
+ PlayerHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void attack(int id, bool keep = false);
+ void stopAttack();
+ void emote(uint8_t emoteId);
+
+ void increaseAttribute(int attr);
+ void increaseSkill(unsigned short skillId);
+
+ void pickUp(FloorItem *floorItem);
+ void setDirection(char direction);
+ void setDestination(int x, int y, int direction = -1);
+ void changeAction(Being::Action action);
+ void processOnlineList(Net::MessageIn &msg);
+ void requestOnlineList();
+ void updateStatus(uint8_t status);
+
+ void processPlayerShortcuts(Net::MessageIn &msg);
+ void processPlayerShowEquip(Net::MessageIn &msg);
+
+ void respawn();
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_PLAYERHANDLER_H
diff --git a/src/net/eathena/protocol.h b/src/net/eathena/protocol.h
new file mode 100644
index 000000000..f26a07131
--- /dev/null
+++ b/src/net/eathena/protocol.h
@@ -0,0 +1,347 @@
+/*
+ * 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/>.
+ */
+
+#ifndef EATHENA_PROTOCOL_H
+#define EATHENA_PROTOCOL_H
+
+enum
+{
+ JOB = 0xa,
+
+ STR = 0xd,
+ AGI,
+ VIT,
+ INT,
+ DEX,
+ LUK,
+
+ ATK,
+ DEF,
+ MATK,
+ MDEF,
+ HIT,
+ FLEE,
+ CRIT
+
+// KARMA,
+// MANNER
+};
+
+enum
+{
+ SPRITE_BASE = 0,
+ SPRITE_SHOE,
+ SPRITE_BOTTOMCLOTHES,
+ SPRITE_TOPCLOTHES,
+ SPRITE_MISC1,
+ SPRITE_MISC2,
+ SPRITE_HAIR,
+ SPRITE_HAT,
+ SPRITE_CAPE,
+ SPRITE_GLOVES,
+ SPRITE_WEAPON,
+ SPRITE_SHIELD,
+ SPRITE_EVOL1,
+ SPRITE_EVOL2,
+ SPRITE_VECTOREND
+};
+
+/*********************************
+ * Packets from server to client *
+ *********************************/
+#define SMSG_SERVER_VERSION_RESPONSE 0x7531
+
+#define SMSG_SERVER_PING 0x007f /**< Contains server tick */
+#define SMSG_CONNECTION_PROBLEM 0x0081
+
+#define SMSG_UPDATE_HOST 0x0063 /**< Custom update host packet */
+#define SMSG_LOGIN_DATA 0x0069
+#define SMSG_LOGIN_ERROR 0x006a
+
+#define SMSG_CHAR_LOGIN 0x006b
+#define SMSG_CHAR_LOGIN_ERROR 0x006c
+#define SMSG_CHAR_CREATE_SUCCEEDED 0x006d
+#define SMSG_CHAR_CREATE_SUCCEEDED2 0x0221
+
+#define SMSG_CHAR_CREATE_FAILED 0x006e
+#define SMSG_CHAR_DELETE_SUCCEEDED 0x006f
+#define SMSG_CHAR_DELETE_FAILED 0x0070
+#define SMSG_CHAR_MAP_INFO 0x0071
+#define SMSG_CHAR_PASSWORD_RESPONSE 0x0062 /**< Custom packet reply \
+ to password change request */
+
+#define SMSG_CHAR_SWITCH_RESPONSE 0x00b3
+#define SMSG_CHANGE_MAP_SERVER 0x0092
+
+#define SMSG_MAP_LOGIN_SUCCESS 0x0073 /**< Contains starting location */
+#define SMSG_MAP_QUIT_RESPONSE 0x018b
+#define SMSG_PLAYER_UPDATE_1 0x01d8
+#define SMSG_PLAYER_UPDATE_2 0x01d9
+#define SMSG_PLAYER_MOVE 0x01da /**< A nearby player moves */
+#define SMSG_PLAYER_STOP 0x0088 /**< Stop walking, set position */
+#define SMSG_PLAYER_MOVE_TO_ATTACK 0x0139 /**< Move to within attack range */
+#define SMSG_PLAYER_STAT_UPDATE_1 0x00b0
+#define SMSG_PLAYER_STAT_UPDATE_2 0x00b1
+#define SMSG_PLAYER_STAT_UPDATE_3 0x0141
+#define SMSG_PLAYER_STAT_UPDATE_4 0x00bc
+#define SMSG_PLAYER_STAT_UPDATE_5 0x00bd
+#define SMSG_PLAYER_STAT_UPDATE_6 0x00be
+#define SMSG_WHO_ANSWER 0x00c2
+#define SMSG_PLAYER_WARP 0x0091 /**< Warp player to map/location */
+#define SMSG_PLAYER_INVENTORY 0x01ee
+#define SMSG_PLAYER_INVENTORY_ADD 0x00a0
+#define SMSG_PLAYER_INVENTORY_REMOVE 0x00af
+#define SMSG_PLAYER_INVENTORY_USE 0x01c8
+#define SMSG_PLAYER_EQUIPMENT 0x00a4
+#define SMSG_PLAYER_EQUIP 0x00aa
+#define SMSG_PLAYER_UNEQUIP 0x00ac
+#define SMSG_PLAYER_ATTACK_RANGE 0x013a
+#define SMSG_PLAYER_ARROW_EQUIP 0x013c
+#define SMSG_PLAYER_ARROW_MESSAGE 0x013b
+#define SMSG_PLAYER_SKILLS 0x010f
+#define SMSG_PLAYER_SKILL_UP 0x010e
+#define SMSG_SKILL_FAILED 0x0110
+#define SMSG_SKILL_DAMAGE 0x01de
+#define SMSG_ITEM_USE_RESPONSE 0x00a8
+#define SMSG_ITEM_VISIBLE 0x009d /**< An item is on the floor */
+#define SMSG_ITEM_DROPPED 0x009e /**< An item is dropped */
+#define SMSG_ITEM_REMOVE 0x00a1 /**< An item disappers */
+#define SMSG_BEING_VISIBLE 0x0078
+#define SMSG_BEING_MOVE 0x007b /**< A nearby monster moves */
+#define SMSG_BEING_SPAWN 0x007c /**< A being spawns nearby */
+#define SMSG_BEING_MOVE2 0x0086 /**< New eAthena being moves */
+#define SMSG_BEING_REMOVE 0x0080
+#define SMSG_BEING_CHANGE_LOOKS 0x00c3
+#define SMSG_BEING_CHANGE_LOOKS2 0x01d7 /**< Same as 0x00c3, \
+ but 16 bit ID */
+#define SMSG_BEING_SELFEFFECT 0x019b
+#define SMSG_BEING_EMOTION 0x00c0
+#define SMSG_BEING_ACTION 0x008a /**< Attack, sit, stand up, ... */
+#define SMSG_BEING_CHAT 0x008d /**< A being talks */
+#define SMSG_BEING_NAME_RESPONSE 0x0095 /**< Has to be requested */
+#define SMSG_BEING_NAME_RESPONSE2 0x0220 /**< Has to be requested */
+#define SMSG_BEING_CHANGE_DIRECTION 0x009c
+#define SMSG_BEING_RESURRECT 0x0148
+
+#define SMSG_PLAYER_STATUS_CHANGE 0x0119
+#define SMSG_PLAYER_GUILD_PARTY_INFO 0x0195
+#define SMSG_BEING_STATUS_CHANGE 0x0196
+
+#define SMSG_NPC_MESSAGE 0x00b4
+#define SMSG_NPC_NEXT 0x00b5
+#define SMSG_NPC_CLOSE 0x00b6
+#define SMSG_NPC_CHOICE 0x00b7 /**< Display a choice */
+#define SMSG_NPC_BUY_SELL_CHOICE 0x00c4
+#define SMSG_NPC_BUY 0x00c6
+#define SMSG_NPC_SELL 0x00c7
+#define SMSG_NPC_BUY_RESPONSE 0x00ca
+#define SMSG_NPC_SELL_RESPONSE 0x00cb
+#define SMSG_NPC_INT_INPUT 0x0142 /**< Integer input */
+#define SMSG_NPC_STR_INPUT 0x01d4 /**< String input */
+#define SMSG_PLAYER_CHAT 0x008e /**< Player talks */
+#define SMSG_WHISPER 0x0097 /**< Whisper Recieved */
+#define SMSG_WHISPER_RESPONSE 0x0098
+#define SMSG_GM_CHAT 0x009a /**< GM announce */
+#define SMSG_WALK_RESPONSE 0x0087
+
+#define SMSG_TRADE_REQUEST 0x00e5 /**< Receiving a request \
+ to trade */
+#define SMSG_TRADE_RESPONSE 0x00e7
+#define SMSG_TRADE_ITEM_ADD 0x00e9
+#define SMSG_TRADE_ITEM_ADD_RESPONSE 0x01b1 /**< Not standard eAthena! */
+#define SMSG_TRADE_OK 0x00ec
+#define SMSG_TRADE_CANCEL 0x00ee
+#define SMSG_TRADE_COMPLETE 0x00f0
+
+#define SMSG_PARTY_CREATE 0x00fa
+#define SMSG_PARTY_INFO 0x00fb
+#define SMSG_PARTY_INVITE_RESPONSE 0x00fd
+#define SMSG_PARTY_INVITED 0x00fe
+#define SMSG_PARTY_SETTINGS 0x0101
+#define SMSG_PARTY_MOVE 0x0104
+#define SMSG_PARTY_LEAVE 0x0105
+#define SMSG_PARTY_UPDATE_HP 0x0106
+#define SMSG_PARTY_UPDATE_COORDS 0x0107
+#define SMSG_PARTY_MESSAGE 0x0109
+
+#define SMSG_PLAYER_STORAGE_ITEMS 0x01f0 /**< Item list for storage */
+#define SMSG_PLAYER_STORAGE_EQUIP 0x00a6 /**< Equipment list for storage */
+#define SMSG_PLAYER_STORAGE_STATUS 0x00f2 /**< Slots used and total slots */
+#define SMSG_PLAYER_STORAGE_ADD 0x00f4 /**< Add item/equip to storage */
+#define SMSG_PLAYER_STORAGE_REMOVE 0x00f6 /**< Remove item/equip \
+ from storage */
+#define SMSG_PLAYER_STORAGE_CLOSE 0x00f8 /**< Storage access closed */
+
+#define SMSG_ADMIN_KICK_ACK 0x00cd
+
+#define SMSG_GUILD_CREATE_RESPONSE 0x0167
+#define SMSG_GUILD_POSITION_INFO 0x016c
+#define SMSG_GUILD_MEMBER_LOGIN 0x016d
+#define SMSG_GUILD_MASTER_OR_MEMBER 0x014e
+#define SMSG_GUILD_BASIC_INFO 0x01b6
+#define SMSG_GUILD_ALIANCE_INFO 0x014c
+#define SMSG_GUILD_MEMBER_LIST 0x0154
+#define SMSG_GUILD_POS_NAME_LIST 0x0166
+#define SMSG_GUILD_POS_INFO_LIST 0x0160
+#define SMSG_GUILD_POSITION_CHANGED 0x0174
+#define SMSG_GUILD_MEMBER_POS_CHANGE 0x0156
+#define SMSG_GUILD_EMBLEM 0x0152
+#define SMSG_GUILD_SKILL_INFO 0x0162
+#define SMSG_GUILD_NOTICE 0x016f
+#define SMSG_GUILD_INVITE 0x016a
+#define SMSG_GUILD_INVITE_ACK 0x0169
+#define SMSG_GUILD_LEAVE 0x015a
+#define SMSG_GUILD_EXPULSION 0x015c
+#define SMSG_GUILD_EXPULSION_LIST 0x0163
+#define SMSG_GUILD_MESSAGE 0x017f
+#define SMSG_GUILD_SKILL_UP 0x010e
+#define SMSG_GUILD_REQ_ALLIANCE 0x0171
+#define SMSG_GUILD_REQ_ALLIANCE_ACK 0x0173
+#define SMSG_GUILD_DEL_ALLIANCE 0x0184
+#define SMSG_GUILD_OPPOSITION_ACK 0x0181
+#define SMSG_GUILD_BROKEN 0x015e
+
+#define SMSG_MVP 0x010c
+
+/**********************************
+ * Packets from client to server *
+ **********************************/
+#define CMSG_SERVER_VERSION_REQUEST 0x7530
+
+#define CMSG_CHAR_PASSWORD_CHANGE 0x0061 /**< Custom change \
+ password packet */
+#define CMSG_CHAR_SERVER_CONNECT 0x0065
+#define CMSG_CHAR_SELECT 0x0066
+#define CMSG_CHAR_CREATE 0x0067
+#define CMSG_CHAR_DELETE 0x0068
+
+#define CMSG_MAP_SERVER_CONNECT 0x0072
+#define CMSG_CLIENT_PING 0x007e /**< Send to server with tick */
+#define CMSG_MAP_LOADED 0x007d
+#define CMSG_CLIENT_QUIT 0x018A
+
+#define CMSG_CHAT_MESSAGE 0x008c
+#define CMSG_CHAT_WHISPER 0x0096
+#define CMSG_CHAT_ANNOUNCE 0x0099
+#define CMSG_CHAT_WHO 0x00c1
+
+#define CMSG_SKILL_LEVELUP_REQUEST 0x0112
+#define CMSG_STAT_UPDATE_REQUEST 0x00bb
+#define CMSG_SKILL_USE_BEING 0x0113
+#define CMSG_SKILL_USE_POSITION 0x0116
+// Variant of 0x116 with 80 char string at end (unsure of use)
+#define CMSG_SKILL_USE_POSITION_MORE 0x0190
+#define CMSG_SKILL_USE_MAP 0x011b
+
+#define CMSG_PLAYER_INVENTORY_USE 0x00a7
+#define CMSG_PLAYER_INVENTORY_DROP 0x00a2
+#define CMSG_PLAYER_EQUIP 0x00a9
+#define CMSG_PLAYER_UNEQUIP 0x00ab
+
+#define CMSG_ITEM_PICKUP 0x009f
+#define CMSG_PLAYER_CHANGE_DIR 0x009b
+#define CMSG_PLAYER_CHANGE_DEST 0x0085
+#define CMSG_PLAYER_CHANGE_ACT 0x0089
+#define CMSG_PLAYER_RESTART 0x00b2
+#define CMSG_PLAYER_EMOTE 0x00bf
+#define CMSG_PLAYER_ATTACK 0x0089
+#define CMSG_PLAYER_STOP_ATTACK 0x0118
+#define CMSG_WHO_REQUEST 0x00c1
+
+#define CMSG_NPC_TALK 0x0090
+#define CMSG_NPC_NEXT_REQUEST 0x00b9
+#define CMSG_NPC_CLOSE 0x0146
+#define CMSG_NPC_LIST_CHOICE 0x00b8
+#define CMSG_NPC_INT_RESPONSE 0x0143
+#define CMSG_NPC_STR_RESPONSE 0x01d5
+#define CMSG_NPC_BUY_SELL_REQUEST 0x00c5
+#define CMSG_NPC_BUY_REQUEST 0x00c8
+#define CMSG_NPC_SELL_REQUEST 0x00c9
+
+#define CMSG_TRADE_REQUEST 0x00e4
+#define CMSG_TRADE_RESPONSE 0x00e6
+#define CMSG_TRADE_ITEM_ADD_REQUEST 0x00e8
+#define CMSG_TRADE_CANCEL_REQUEST 0x00ed
+#define CMSG_TRADE_ADD_COMPLETE 0x00eb
+#define CMSG_TRADE_OK 0x00ef
+
+#define CMSG_PARTY_CREATE 0x00f9
+#define CMSG_PARTY_INVITE 0x00fc
+#define CMSG_PARTY_INVITED 0x00ff
+#define CMSG_PARTY_LEAVE 0x0100
+#define CMSG_PARTY_SETTINGS 0x0102
+#define CMSG_PARTY_KICK 0x0103
+#define CMSG_PARTY_MESSAGE 0x0108
+
+#define CMSG_MOVE_TO_STORAGE 0x00f3 /** Move item to storage */
+#define CSMG_MOVE_FROM_STORAGE 0x00f5 /** Remove item from storage */
+#define CMSG_CLOSE_STORAGE 0x00f7 /** Request storage close */
+
+#define CMSG_ADMIN_ANNOUNCE 0x0099
+#define CMSG_ADMIN_LOCAL_ANNOUNCE 0x019C
+#define CMSG_ADMIN_HIDE 0x019D
+#define CMSG_ADMIN_KICK 0x00CC
+#define CMSG_ADMIN_MUTE 0x0149
+
+#define CMSG_GUILD_CHECK_MASTER 0x014d
+#define CMSG_GUILD_REQUEST_INFO 0x014f
+#define CMSG_GUILD_REQUEST_EMBLEM 0x0151
+#define CMSG_GUILD_CHANGE_EMBLEM 0x0153
+#define CMSG_GUILD_CHANGE_MEMBER_POS 0x0155
+#define CMSG_GUILD_LEAVE 0x0159
+#define CMSG_GUILD_EXPULSION 0x015b
+#define CMSG_GUILD_BREAK 0x015d
+#define CMSG_GUILD_CHANGE_POS_INFO 0x0161
+#define CMSG_GUILD_CREATE 0x0165
+#define CMSG_GUILD_INVITE 0x0168
+#define CMSG_GUILD_INVITE_REPLY 0x016b
+#define CMSG_GUILD_CHANGE_NOTICE 0x016e
+#define CMSG_GUILD_ALLIANCE_REQUEST 0x0170
+#define CMSG_GUILD_ALLIANCE_REPLY 0x0172
+#define CMSG_GUILD_MESSAGE 0x017e
+#define CMSG_GUILD_OPPOSITION 0x0180
+#define CMSG_GUILD_ALLIANCE_DELETE 0x0183
+
+#define CMSG_SOLVE_CHAR_NAME 0x0193
+#define SMSG_SOLVE_CHAR_NAME 0x0194
+#define CMSG_CLIENT_DISCONNECT 0x7532
+#define SMSG_SKILL_CASTING 0x013e
+#define SMSG_SKILL_CAST_CANCEL 0x01b9
+#define SMSG_SKILL_NO_DAMAGE 0x011a
+
+#define SMSG_BEING_IP_RESPONSE 0x020c
+#define SMSG_PVP_MAP_MODE 0x0199
+#define SMSG_PVP_SET 0x019a
+#define CMSG_IGNORE_ALL 0x00d0
+#define SMSG_IGNORE_ALL_RESPONSE 0x00d2
+#define CMSG_ONLINE_LIST 0x0210
+#define SMSG_ONLINE_LIST 0x0211
+#define SMSG_NPC_COMMAND 0x0212
+#define CMSG_SET_STATUS 0x0213
+
+#define CMSG_SEND_CLIENT_INFO 0x7533
+#define SMSG_UPDATE_HOST2 0x7534
+
+#define SMSG_MAP_CHAR_ID 0x0283
+#define SMSG_PLAYER_SHORTCUTS 0x02b9
+#define SMSG_PLAYER_SHOW_EQUIP 0x02da
+#endif
diff --git a/src/net/eathena/specialhandler.cpp b/src/net/eathena/specialhandler.cpp
new file mode 100644
index 000000000..f2f969ff7
--- /dev/null
+++ b/src/net/eathena/specialhandler.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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/specialhandler.h"
+
+#include "logger.h"
+
+#include "net/messagein.h"
+
+#include "net/eathena/protocol.h"
+
+#include "debug.h"
+
+extern Net::SpecialHandler *specialHandler;
+
+namespace EAthena
+{
+
+SpecialHandler::SpecialHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_PLAYER_SKILLS,
+ SMSG_SKILL_FAILED,
+ SMSG_PLAYER_SKILL_UP,
+ 0
+ };
+ handledMessages = _messages;
+ specialHandler = this;
+}
+
+void SpecialHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_PLAYER_SKILLS:
+ processPlayerSkills(msg);
+ break;
+
+ case SMSG_PLAYER_SKILL_UP:
+ processPlayerSkillUp(msg);
+ break;
+
+ case SMSG_SKILL_FAILED:
+ processSkillFailed(msg);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void SpecialHandler::useBeing(int id, int level, int beingId)
+{
+ MessageOut outMsg(CMSG_SKILL_USE_BEING);
+ outMsg.writeInt16(static_cast<int16_t>(id));
+ outMsg.writeInt16(static_cast<int16_t>(level));
+ outMsg.writeInt32(beingId);
+}
+
+void SpecialHandler::usePos(int id, int level, int x, int y)
+{
+ MessageOut outMsg(CMSG_SKILL_USE_POSITION);
+ outMsg.writeInt16(static_cast<int16_t>(level));
+ outMsg.writeInt16(static_cast<int16_t>(id));
+ outMsg.writeInt16(static_cast<int16_t>(x));
+ outMsg.writeInt16(static_cast<int16_t>(y));
+}
+
+void SpecialHandler::useMap(int id, const std::string &map)
+{
+ MessageOut outMsg(CMSG_SKILL_USE_MAP);
+ outMsg.writeInt16(static_cast<int16_t>(id));
+ outMsg.writeString(map, 16);
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/specialhandler.h b/src/net/eathena/specialhandler.h
new file mode 100644
index 000000000..fe76204f2
--- /dev/null
+++ b/src/net/eathena/specialhandler.h
@@ -0,0 +1,52 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_SKILLHANDLER_H
+#define NET_EATHENA_SKILLHANDLER_H
+
+#include "net/net.h"
+#include "net/specialhandler.h"
+
+#include "net/ea/specialhandler.h"
+
+#include "net/eathena/messagehandler.h"
+
+namespace EAthena
+{
+
+class SpecialHandler : public MessageHandler, public Ea::SpecialHandler
+{
+ public:
+ SpecialHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void useBeing(int id, int level, int beingId);
+
+ void usePos(int id, int level, int x, int y);
+
+ void useMap(int id, const std::string &map);
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_SKILLHANDLER_H
diff --git a/src/net/eathena/tradehandler.cpp b/src/net/eathena/tradehandler.cpp
new file mode 100644
index 000000000..66bb20b29
--- /dev/null
+++ b/src/net/eathena/tradehandler.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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/tradehandler.h"
+
+#include "item.h"
+#include "logger.h"
+#include "playerinfo.h"
+
+#include "net/messagein.h"
+
+#include "net/eathena/protocol.h"
+
+#include "net/ea/eaprotocol.h"
+
+#include "debug.h"
+
+extern Net::TradeHandler *tradeHandler;
+
+namespace EAthena
+{
+
+TradeHandler::TradeHandler()
+{
+ static const uint16_t _messages[] =
+ {
+ SMSG_TRADE_REQUEST,
+ SMSG_TRADE_RESPONSE,
+ SMSG_TRADE_ITEM_ADD,
+ SMSG_TRADE_ITEM_ADD_RESPONSE,
+ SMSG_TRADE_OK,
+ SMSG_TRADE_CANCEL,
+ SMSG_TRADE_COMPLETE,
+ 0
+ };
+ handledMessages = _messages;
+ tradeHandler = this;
+}
+
+
+void TradeHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_TRADE_REQUEST:
+ processTradeRequest(msg);
+ break;
+
+ case SMSG_TRADE_RESPONSE:
+ processTradeResponse(msg);
+ break;
+
+ case SMSG_TRADE_ITEM_ADD:
+ processTradeItemAdd(msg);
+ break;
+
+ case SMSG_TRADE_ITEM_ADD_RESPONSE:
+ processTradeItemAddResponse(msg);
+ break;
+
+ case SMSG_TRADE_OK:
+ processTradeOk(msg);
+ break;
+
+ case SMSG_TRADE_CANCEL:
+ processTradeCancel(msg);
+ break;
+
+ case SMSG_TRADE_COMPLETE:
+ processTradeComplete(msg);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void TradeHandler::request(Being *being)
+{
+ if (!being)
+ return;
+
+ MessageOut outMsg(CMSG_TRADE_REQUEST);
+ outMsg.writeInt32(being->getId());
+}
+
+void TradeHandler::respond(bool accept)
+{
+ if (!accept)
+ PlayerInfo::setTrading(false);
+
+ MessageOut outMsg(CMSG_TRADE_RESPONSE);
+ outMsg.writeInt8(accept ? 3 : 4);
+}
+
+void TradeHandler::addItem(Item *item, int amount)
+{
+ if (!item)
+ return;
+
+ MessageOut outMsg(CMSG_TRADE_ITEM_ADD_REQUEST);
+ outMsg.writeInt16(static_cast<int16_t>(
+ item->getInvIndex() + INVENTORY_OFFSET));
+ outMsg.writeInt32(amount);
+}
+
+void TradeHandler::setMoney(int amount)
+{
+ MessageOut outMsg(CMSG_TRADE_ITEM_ADD_REQUEST);
+ outMsg.writeInt16(0);
+ outMsg.writeInt32(amount);
+}
+
+void TradeHandler::confirm()
+{
+ MessageOut outMsg(CMSG_TRADE_ADD_COMPLETE);
+}
+
+void TradeHandler::finish()
+{
+ MessageOut outMsg(CMSG_TRADE_OK);
+}
+
+void TradeHandler::cancel()
+{
+ MessageOut outMsg(CMSG_TRADE_CANCEL_REQUEST);
+}
+
+} // namespace EAthena
diff --git a/src/net/eathena/tradehandler.h b/src/net/eathena/tradehandler.h
new file mode 100644
index 000000000..ec5393340
--- /dev/null
+++ b/src/net/eathena/tradehandler.h
@@ -0,0 +1,60 @@
+/*
+ * 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/>.
+ */
+
+#ifndef NET_EATHENA_TRADEHANDLER_H
+#define NET_EATHENA_TRADEHANDLER_H
+
+#include "net/net.h"
+#include "net/tradehandler.h"
+
+#include "net/ea/tradehandler.h"
+
+#include "net/eathena/messagehandler.h"
+
+namespace EAthena
+{
+
+class TradeHandler : public MessageHandler, public Ea::TradeHandler
+{
+ public:
+ TradeHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void request(Being *being);
+
+ void respond(bool accept);
+
+ void addItem(Item *item, int amount);
+
+ void setMoney(int amount);
+
+ void confirm();
+
+ void finish();
+
+ void cancel();
+};
+
+} // namespace EAthena
+
+#endif // NET_EATHENA_TRADEHANDLER_H