diff options
author | Andrei Karas <akaras@inbox.ru> | 2012-06-24 19:34:08 +0300 |
---|---|---|
committer | Andrei Karas <akaras@inbox.ru> | 2012-06-24 21:41:20 +0300 |
commit | 3b537e109df901df3da4c706f827c8d3d39a7238 (patch) | |
tree | ae5a83f71954aaeb45ce3f8d609e6f4a8dc0678c /src/net | |
parent | 4ef35e9ef757da1db724c5d784048601144c934c (diff) | |
download | mv-3b537e109df901df3da4c706f827c8d3d39a7238.tar.gz mv-3b537e109df901df3da4c706f827c8d3d39a7238.tar.bz2 mv-3b537e109df901df3da4c706f827c8d3d39a7238.tar.xz mv-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')
48 files changed, 6896 insertions, 8 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 diff --git a/src/net/net.cpp b/src/net/net.cpp index 75bc35b72..bfa46d086 100644 --- a/src/net/net.cpp +++ b/src/net/net.cpp @@ -42,6 +42,10 @@ #include "net/tmwa/generalhandler.h" +#ifdef EATHENA_SUPPORT +#include "net/eathena/generalhandler.h" +#endif + #ifdef MANASERV_SUPPORT #include "net/manaserv/generalhandler.h" #endif @@ -165,11 +169,20 @@ void connectToServer(const ServerInfo &server) case ServerInfo::EVOL: new TmwAthena::GeneralHandler; break; + case ServerInfo::EATHENA: +#ifdef EATHENA_SUPPORT + new EAthena::GeneralHandler; +#else + new TmwAthena::GeneralHandler; +#endif + break; case ServerInfo::MANASERV: #ifdef MANASERV_SUPPORT new ManaServ::GeneralHandler; - break; +#else + new TmwAthena::GeneralHandler; #endif + break; case ServerInfo::TMWATHENA: case ServerInfo::UNKNOWN: default: diff --git a/src/net/serverinfo.h b/src/net/serverinfo.h index d3c33d19f..a8480cf1e 100644 --- a/src/net/serverinfo.h +++ b/src/net/serverinfo.h @@ -36,7 +36,8 @@ public: UNKNOWN = 0, MANASERV, TMWATHENA, - EVOL + EVOL, + EATHENA, }; typedef std::pair<int, std::string> VersionString; @@ -106,9 +107,13 @@ public: return TMWATHENA; if (compareStrI(type, "evol") == 0) return EVOL; - // Used for backward compatibility +#ifdef EATHENA_SUPPORT + else if (compareStrI(type, "eathena") == 0) + return EATHENA; +#else else if (compareStrI(type, "eathena") == 0) return TMWATHENA; +#endif #ifdef MANASERV_SUPPORT else if (compareStrI(type, "manaserv") == 0) return MANASERV; diff --git a/src/net/tmwa/network.cpp b/src/net/tmwa/network.cpp index c25774d9b..c1020ebeb 100644 --- a/src/net/tmwa/network.cpp +++ b/src/net/tmwa/network.cpp @@ -38,6 +38,8 @@ #include "debug.h" +namespace TmwAthena +{ /** Warning: buffers and other variables are shared, so there can be only one connection active at a time */ @@ -90,9 +92,6 @@ short packet_lengths[] = const unsigned int BUFFER_SIZE = 655360; -namespace TmwAthena -{ - int networkThread(void *data) { Network *network = static_cast<Network*>(data); @@ -345,8 +344,9 @@ MessageIn Network::getNextMessage() if (len == -1) len = readWord(2); -#ifdef DEBUG - logger->log("Received packet 0x%x of length %d\n", msgId, len); +#ifdef ENABLEDEBUGLOG +// logger->dlog(strprintf("Received packet 0x%x of length %d\n", +// msgId, len)); #endif MessageIn msg(mInBuffer, len); |