diff options
author | Andrei Karas <akaras@inbox.ru> | 2011-01-02 01:48:38 +0200 |
---|---|---|
committer | Andrei Karas <akaras@inbox.ru> | 2011-01-02 02:41:24 +0200 |
commit | 3eeae12c498d1a4dbe969462d2ba841f77ee3ccb (patch) | |
tree | ff8eab35e732bc0749fc11677c8873a7b3a58704 /src/net/manaserv | |
download | plus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.gz plus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.bz2 plus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.xz plus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.zip |
Initial commit.
This code based on mana client http://www.gitorious.org/mana/mana
and my private repository.
Diffstat (limited to 'src/net/manaserv')
50 files changed, 7415 insertions, 0 deletions
diff --git a/src/net/manaserv/adminhandler.cpp b/src/net/manaserv/adminhandler.cpp new file mode 100644 index 000000000..cd71f00a2 --- /dev/null +++ b/src/net/manaserv/adminhandler.cpp @@ -0,0 +1,93 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/adminhandler.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +extern Net::AdminHandler *adminHandler; + +namespace ManaServ +{ + +extern Connection *chatServerConnection; + +AdminHandler::AdminHandler() +{ + adminHandler = this; +} + +void AdminHandler::announce(const std::string &text) +{ + MessageOut msg(PCMSG_ANNOUNCE); + msg.writeString(text); + chatServerConnection->send(msg); +} + +void AdminHandler::localAnnounce(const std::string &text _UNUSED_) +{ + // TODO +} + +void AdminHandler::hide(bool hide _UNUSED_) +{ + // TODO +} + +void AdminHandler::kick(int playerId _UNUSED_) +{ + // TODO +} + +void AdminHandler::kick(const std::string &name _UNUSED_) +{ + // TODO +} + +void AdminHandler::ban(int playerId _UNUSED_) +{ + // TODO +} + +void AdminHandler::ban(const std::string &name _UNUSED_) +{ + // TODO +} + +void AdminHandler::unban(int playerId _UNUSED_) +{ + // TODO +} + +void AdminHandler::unban(const std::string &name _UNUSED_) +{ + // TODO +} + +void AdminHandler::mute(int playerId _UNUSED_, int type _UNUSED_, + int limit _UNUSED_) +{ + // TODO +} + +} // namespace ManaServ diff --git a/src/net/manaserv/adminhandler.h b/src/net/manaserv/adminhandler.h new file mode 100644 index 000000000..596ccbccd --- /dev/null +++ b/src/net/manaserv/adminhandler.h @@ -0,0 +1,64 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_ADMINHANDLER_H +#define NET_MANASERV_ADMINHANDLER_H + +#include "net/adminhandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class AdminHandler : public Net::AdminHandler +{ + public: + AdminHandler(); + + void announce(const std::string &text); + + void localAnnounce(const std::string &text); + + void hide(bool hide); + + void kick(int playerId); + + void kick(const std::string &name); + + void ban(int playerId); + + void ban(const std::string &name); + + void unban(int playerId); + + void unban(const std::string &name); + + void mute(int playerId, int type, int limit); +}; + +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/attributes.cpp b/src/net/manaserv/attributes.cpp new file mode 100644 index 000000000..7cc0e053d --- /dev/null +++ b/src/net/manaserv/attributes.cpp @@ -0,0 +1,411 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/attributes.h" + +#include "log.h" +#include "playerinfo.h" + +#include "gui/statuswindow.h" + +#include "resources/itemdb.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" +#include "utils/xml.h" + +#include <list> +#include <map> + +#define DEFAULT_ATTRIBUTESDB_FILE "attributes.xml" +#define DEFAULT_POINTS 60 +#define DEFAULT_MIN_PTS 1 +#define DEFAULT_MAX_PTS 20 + +namespace ManaServ +{ +namespace Attributes +{ + typedef struct + { + unsigned int id; + std::string name; + std::string description; + /** Whether the attribute value can be modified by the player */ + bool modifiable; + /**< Attribute scope. */ + std::string scope; + /** The playerInfo core Id the attribute is linked with or -1 if not */ + int playerInfoId; + } Attribute; + + /** Map for attributes. */ + typedef std::map<unsigned int, Attribute> AttributeMap; + static AttributeMap attributes; + + /** tags = effects on attributes. */ + typedef std::map< std::string, std::string > TagMap; + static TagMap tags; + + /** List of modifiable attribute names used at character's creation. */ + static std::vector<std::string> attributeLabels; + + /** Characters creation points. */ + static unsigned int creationPoints = 0; + static unsigned int attributeMinimum = 0; + static unsigned int attributeMaximum = 0; + + unsigned int getCreationPoints() + { return creationPoints; } + + unsigned int getAttributeMinimum() + { return attributeMinimum; } + + unsigned int getAttributeMaximum() + { return attributeMaximum; } + + std::vector<std::string>& getLabels() + { return attributeLabels; } + + /** + * Fills the list of base attribute labels. + */ + static void fillLabels() + { + // Fill up the modifiable attribute label list. + attributeLabels.clear(); + AttributeMap::const_iterator it, it_end; + for (it = attributes.begin(), it_end = attributes.end(); + it != it_end; it++) + { + if (it->second.modifiable && (it->second.scope == "character" + || it->second.scope == "being")) + { + attributeLabels.push_back(it->second.name + ":"); + } + } + } + + /** + * Fills the list of base attribute labels. + */ + static int getPlayerInfoIdFromAttrType(std::string attrType) + { + toLower(attrType); + if (attrType == "level") + return ::LEVEL; + else if (attrType == "hp") + return ::HP; + else if (attrType == "max-hp") + return ::MAX_HP; + else if (attrType == "mp") + return ::MP; + else if (attrType == "max-mp") + return ::MAX_MP; + else if (attrType == "exp") + return ::EXP; + else if (attrType == "exp-needed") + return ::EXP_NEEDED; + else if (attrType == "money") + return ::MONEY; + else if (attrType == "total-weight") + return ::TOTAL_WEIGHT; + else if (attrType == "max-weight") + return ::MAX_WEIGHT; + else if (attrType == "skill-points") + return ::SKILL_POINTS; + else if (attrType == "char-points") + return ::CHAR_POINTS; + else if (attrType == "corr-points") + return ::CORR_POINTS; + else if (attrType == "none") + return -2; // Used to hide the attribute display. + + return -1; // Not linked to a playerinfo stat. + } + + int getPlayerInfoIdFromAttrId(int attrId) + { + AttributeMap::const_iterator it = attributes.find(attrId); + + if (it != attributes.end()) + return it->second.playerInfoId; + + return -1; + } + + static void loadBuiltins() + { + { + Attribute a; + a.id = 16; + a.name = _("Strength"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("str", _("Strength %+.1f"))); + } + + { + Attribute a; + a.id = 17; + a.name = _("Agility"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("agi", _("Agility %+.1f"))); + } + + { + Attribute a; + a.id = 18; + a.name = _("Dexterity"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("dex", _("Dexterity %+.1f"))); + } + + { + Attribute a; + a.id = 19; + a.name = _("Vitality"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("vit", _("Vitality %+.1f"))); + } + + { + Attribute a; + a.id = 20; + a.name = _("Intelligence"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("int", _("Intelligence %+.1f"))); + } + + { + Attribute a; + a.id = 21; + a.name = _("Willpower"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("wil", _("Willpower %+.1f"))); + } + } + + void load() + { + logger->log("Initializing attributes database..."); + + XML::Document doc(DEFAULT_ATTRIBUTESDB_FILE); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "attributes")) + { + logger->log("Attributes: Error while loading " + DEFAULT_ATTRIBUTESDB_FILE ". Using Built-ins."); + loadBuiltins(); + fillLabels(); + return; + } + + for_each_xml_child_node(node, rootNode) + { + if (xmlStrEqual(node->name, BAD_CAST "attribute")) + { + int id = XML::getProperty(node, "id", 0); + + if (!id) + { + logger->log("Attributes: Invalid or missing stat ID in " + DEFAULT_ATTRIBUTESDB_FILE "!"); + continue; + } + else if (attributes.find(id) != attributes.end()) + { + logger->log("Attributes: Redefinition of stat ID %d", id); + } + + std::string name = XML::getProperty(node, "name", ""); + + if (name.empty()) + { + logger->log("Attributes: Invalid or missing stat name in " + DEFAULT_ATTRIBUTESDB_FILE "!"); + continue; + } + + // Create the attribute. + Attribute a; + a.id = id; + a.name = name; + a.description = XML::getProperty(node, "desc", ""); + a.modifiable = XML::getBoolProperty(node, "modifiable", false); + a.scope = XML::getProperty(node, "scope", "none"); + a.playerInfoId = getPlayerInfoIdFromAttrType( + XML::getProperty(node, "player-info", "")); + + attributes[id] = a; + + unsigned int count = 0; + for_each_xml_child_node(effectNode, node) + { + if (!xmlStrEqual(effectNode->name, BAD_CAST "modifier")) + continue; + ++count; + std::string tag = XML::getProperty(effectNode, "tag", ""); + if (tag.empty()) + { + if (name.empty()) + { + logger->log("Attribute modifier in attribute" + " %u:%s: Empty name definition " + "on empty tag definition, skipping.", + a.id, a.name.c_str()); + --count; + continue; + } + tag = name.substr(0, name.size() > 3 + ? 3 : name.size()); + tag = toLower(tag) + toString(count); + } + + std::string effect = XML::getProperty( + effectNode, "effect", ""); + + if (effect.empty()) + { + if (name.empty()) + { + logger->log("Attribute modifier in attribute" + " %u:%s: Empty name definition " + "on empty effect definition, skipping.", + a.id, a.name.c_str()); + --count; + continue; + } + else + { + effect = name + " %+f"; + } + } + tags.insert(std::make_pair(tag, effect)); + } + logger->log("Found %d tags for attribute %d.", count, id); + + }// End attribute + else if (xmlStrEqual(node->name, BAD_CAST "points")) + { + creationPoints = XML::getProperty( + node, "start", DEFAULT_POINTS); + attributeMinimum = XML::getProperty( + node, "minimum", DEFAULT_MIN_PTS); + attributeMaximum = XML::getProperty( + node, "maximum", DEFAULT_MAX_PTS); + logger->log("Loaded points: start: %i, min: %i, max: %i.", + creationPoints, attributeMinimum, attributeMaximum); + } + else + { + continue; + } + } + logger->log("Found %d tags for %d attributes.", int(tags.size()), + int(attributes.size())); + + fillLabels(); + + // Sanity checks on starting points + float modifiableAttributeCount = (float) attributeLabels.size(); + float averageValue = 1; + if (modifiableAttributeCount) + averageValue = ((float) creationPoints) / modifiableAttributeCount; + + if (averageValue > attributeMaximum || averageValue < attributeMinimum + || creationPoints < 1) + { + logger->log("Attributes: Character's point values make " + "the character's creation impossible. " + "Switch back to defaults."); + creationPoints = DEFAULT_POINTS; + attributeMinimum = DEFAULT_MIN_PTS; + attributeMaximum = DEFAULT_MAX_PTS; + } + } + + void unload() + { + attributes.clear(); + } + + void informItemDB() + { + std::list<ItemDB::Stat> dbStats; + + TagMap::const_iterator it, it_end; + for (it = tags.begin(), it_end = tags.end(); it != it_end; ++it) + dbStats.push_back(ItemDB::Stat(it->first, it->second)); + + ItemDB::setStatsList(dbStats); + } + + void informStatusWindow() + { + if (!statusWindow) + return; + + AttributeMap::const_iterator it, it_end; + for (it = attributes.begin(), it_end = attributes.end(); + it != it_end; it++) + { + if (it->second.playerInfoId == -1 + && (it->second.scope == "character" + || it->second.scope == "being")) + { + statusWindow->addAttribute(it->second.id, it->second.name, + it->second.modifiable, it->second.description); + } + } + } + +} // namespace Attributes +} // namespace ManaServ diff --git a/src/net/manaserv/attributes.h b/src/net/manaserv/attributes.h new file mode 100644 index 000000000..216bae411 --- /dev/null +++ b/src/net/manaserv/attributes.h @@ -0,0 +1,72 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_ATTRIBUTES_H +#define NET_MANASERV_ATTRIBUTES_H + +#include <string> +#include <vector> + +namespace ManaServ +{ + +namespace Attributes +{ + void load(); + + void unload(); + + void informItemDB(); + + void informStatusWindow(); + + /** + * Returns the list of base attribute labels. + */ + std::vector<std::string>& getLabels(); + + /** + * Give back the corresponding playerinfo Id from the attribute id + * defined in the xml file. + */ + int getPlayerInfoIdFromAttrId(int attrId); + + /** + * Give the attribute points given to a character + * at its creation. + */ + unsigned int getCreationPoints(); + + /** + * Give the minimum attribute point possible + * at character's creation. + */ + unsigned int getAttributeMinimum(); + + /** + * Give the maximum attribute point possible + * at character's creation. + */ + unsigned int getAttributeMaximum(); + +} // namespace Attributes +} // namespace ManaServ + +#endif // NET_MANASERV_ATTRIBUTES_H diff --git a/src/net/manaserv/beinghandler.cpp b/src/net/manaserv/beinghandler.cpp new file mode 100644 index 000000000..ae6a417e5 --- /dev/null +++ b/src/net/manaserv/beinghandler.cpp @@ -0,0 +1,385 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/beinghandler.h" + +#include "actorspritemanager.h" +#include "being.h" +#include "client.h" +#include "game.h" +#include "localplayer.h" +#include "log.h" +#include "particle.h" + +#include "gui/okdialog.h" + +#include "net/messagein.h" +#include "net/net.h" + +#include "net/manaserv/playerhandler.h" +#include "net/manaserv/protocol.h" + +#include "resources/colordb.h" + +#include "utils/gettext.h" + +extern Net::BeingHandler *beingHandler; + +namespace ManaServ +{ + +BeingHandler::BeingHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_BEING_ATTACK, + GPMSG_BEING_ENTER, + GPMSG_BEING_LEAVE, + GPMSG_BEINGS_MOVE, + GPMSG_BEINGS_DAMAGE, + GPMSG_BEING_ACTION_CHANGE, + GPMSG_BEING_LOOKS_CHANGE, + GPMSG_BEING_DIR_CHANGE, + 0 + }; + handledMessages = _messages; + beingHandler = this; +} + +void BeingHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_BEING_ENTER: + handleBeingEnterMessage(msg); + break; + case GPMSG_BEING_LEAVE: + handleBeingLeaveMessage(msg); + break; + case GPMSG_BEINGS_MOVE: + handleBeingsMoveMessage(msg); + break; + case GPMSG_BEING_ATTACK: + handleBeingAttackMessage(msg); + break; + case GPMSG_BEINGS_DAMAGE: + handleBeingsDamageMessage(msg); + break; + case GPMSG_BEING_ACTION_CHANGE: + handleBeingActionChangeMessage(msg); + break; + case GPMSG_BEING_LOOKS_CHANGE: + handleBeingLooksChangeMessage(msg); + break; + case GPMSG_BEING_DIR_CHANGE: + handleBeingDirChangeMessage(msg); + break; + default: + break; + } +} + +Vector BeingHandler::giveSpeedInPixelsPerTicks(float speedInTilesPerSeconds) +{ + Vector speedInTicks; + Game *game = Game::instance(); + Map *map = 0; + if (game) + { + map = game->getCurrentMap(); + if (map) + { + speedInTicks.x = speedInTilesPerSeconds + * (float)map->getTileWidth() + / 1000 * (float) MILLISECONDS_IN_A_TICK; + speedInTicks.y = speedInTilesPerSeconds + * (float)map->getTileHeight() + / 1000 * (float) MILLISECONDS_IN_A_TICK; + } + } + + if (!game || !map) + { + speedInTicks.x = speedInTicks.y = 0; + logger->log1("Manaserv::BeingHandler: Speed wasn't given back" + " because game/Map not initialized."); + } + // We don't use z for now. + speedInTicks.z = 0; + + return speedInTicks; +} + +static void handleLooks(Being *being, Net::MessageIn &msg) +{ + // Order of sent slots. Has to be in sync with the server code. + static int const nb_slots = 4; + static int const slots[nb_slots] = + { + SPRITE_WEAPON, + SPRITE_HAT, + SPRITE_TOPCLOTHES, + SPRITE_BOTTOMCLOTHES + }; + + int mask = msg.readInt8(); + + if (mask & (1 << 7)) + { + // The equipment has to be cleared first. + for (int i = 0; i < nb_slots; ++i) + being->setSprite(slots[i], 0); + } + + // Fill slots enumerated by the bitmask. + for (int i = 0; i < nb_slots; ++i) + { + if (!(mask & (1 << i))) continue; + int id = msg.readInt16(); + being->setSprite(slots[i], id, "", (slots[i] == SPRITE_WEAPON)); + } +} + +void BeingHandler::handleBeingEnterMessage(Net::MessageIn &msg) +{ + int type = msg.readInt8(); + int id = msg.readInt16(); + Being::Action action = (Being::Action)msg.readInt8(); + int px = msg.readInt16(); + int py = msg.readInt16(); + Being *being; + + switch (type) + { + case OBJECT_CHARACTER: + { + std::string name = msg.readString(); + if (player_node->getName() == name) + { + being = player_node; + being->setId(id); + } + else + { + being = actorSpriteManager->createBeing(id, + ActorSprite::PLAYER, 0); + being->setName(name); + } + int hs = msg.readInt8(), hc = msg.readInt8(); + being->setSprite(SPRITE_HAIR, hs * -1, ColorDB::get(hc)); + being->setGender(msg.readInt8() == GENDER_MALE ? + GENDER_MALE : GENDER_FEMALE); + handleLooks(being, msg); + } break; + + case OBJECT_MONSTER: + case OBJECT_NPC: + { + int subtype = msg.readInt16(); + being = actorSpriteManager->createBeing(id, type == OBJECT_MONSTER + ? ActorSprite::MONSTER : ActorSprite::NPC, subtype); + std::string name = msg.readString(); + if (name.length() > 0) being->setName(name); + } break; + + default: + return; + } + + being->setPosition(px, py); + being->setDestination(px, py); + being->setAction(action); +} + +void BeingHandler::handleBeingLeaveMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being) + return; + + actorSpriteManager->destroy(being); +} + +void BeingHandler::handleBeingsMoveMessage(Net::MessageIn &msg) +{ + while (msg.getUnreadLength()) + { + int id = msg.readInt16(); + int flags = msg.readInt8(); + Being *being = actorSpriteManager->findBeing(id); + int sx = 0; + int sy = 0; + int speed = 0; + + if (flags & MOVING_POSITION) + { + sx = msg.readInt16(); + sy = msg.readInt16(); + speed = msg.readInt8(); + } + if (!being || !(flags & (MOVING_POSITION | MOVING_DESTINATION))) + { + continue; + } + if (speed) + { + /* + * The being's speed is transfered in tiles per second * 10 + * to keep it transferable in a Byte. + * We set it back to tiles per second and in a float. + * Then, we translate it in pixels per ticks, to correspond + * with the Being::logic() function calls + * @see MILLISECONDS_IN_A_TICK + */ + being->setWalkSpeed( + giveSpeedInPixelsPerTicks((float) speed / 10)); + } + + // Ignore messages from the server for the local player + if (being == player_node) + continue; + + if (flags & MOVING_POSITION) + { + being->setDestination(sx, sy); + } + } +} + +void BeingHandler::handleBeingAttackMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + const int direction = msg.readInt8(); + const int attackType = msg.readInt8(); + + if (!being) + return; + + switch (direction) + { + case DIRECTION_UP: being->setDirection(Being::UP); break; + case DIRECTION_DOWN: being->setDirection(Being::DOWN); break; + case DIRECTION_LEFT: being->setDirection(Being::LEFT); break; + case DIRECTION_RIGHT: being->setDirection(Being::RIGHT); break; + default: break; + } + + being->setAction(Being::ATTACK, attackType); +} + +void BeingHandler::handleBeingsDamageMessage(Net::MessageIn &msg) +{ + while (msg.getUnreadLength()) + { + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + int damage = msg.readInt16(); + if (being) + { + being->takeDamage(0, damage, Being::HIT); + } + } +} + +void BeingHandler::handleBeingActionChangeMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + Being::Action action = (Being::Action) msg.readInt8(); + if (!being) + return; + + being->setAction(action); + + if (action == Being::DEAD && being == player_node) + { + static char const *const deadMsg[] = + { + _("You are dead."), + _("We regret to inform you that your character was killed in " + "battle."), + _("You are not that alive anymore."), + _("The cold hands of the grim reaper are grabbing for your soul."), + _("Game Over!"), + _("No, kids. Your character did not really die. It... err... " + "went to a better place."), + _("Your plan of breaking your enemies weapon by bashing it with " + "your throat failed."), + _("I guess this did not run too well."), + _("Do you want your possessions identified?"), // Nethack reference + _("Sadly, no trace of you was ever found..."), // Secret of Mana + // reference + _("Annihilated."), // Final Fantasy VI reference + _("Looks like you got your head handed to you."), // Earthbound + // reference + _("You screwed up again, dump your body down the tubes and get " + "you another one.") // Leisure Suit Larry 1 Reference + + }; + std::string message(deadMsg[rand() % 13]); + message.append(std::string(" ") + _("Press OK to respawn.")); + OkDialog *dlg = new OkDialog(_("You Died"), message, false); + dlg->addActionListener(&(ManaServ::respawnListener)); + } +} + +void BeingHandler::handleBeingLooksChangeMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being || being->getType() != ActorSprite::PLAYER) + return; + handleLooks(being, msg); + if (msg.getUnreadLength()) + { + int style = msg.readInt16(); + int color = msg.readInt16(); + being->setSprite(SPRITE_HAIR, style * -1, ColorDB::get(color)); + } +} + +void BeingHandler::handleBeingDirChangeMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being) + return; + int data = msg.readInt8(); + + // The direction for the player's character is handled on client side. + if (being != player_node) + { + switch (data) + { + case DIRECTION_UP: being->setDirection(Being::UP); break; + case DIRECTION_DOWN: being->setDirection(Being::DOWN); break; + case DIRECTION_LEFT: being->setDirection(Being::LEFT); break; + case DIRECTION_RIGHT: being->setDirection(Being::RIGHT); break; + default: break; + } + } +} + +void BeingHandler::requestNameById(int id _UNUSED_) +{ +} + +void BeingHandler::undress(Being *being _UNUSED_) +{ +} + +} // namespace ManaServ diff --git a/src/net/manaserv/beinghandler.h b/src/net/manaserv/beinghandler.h new file mode 100644 index 000000000..7ed657520 --- /dev/null +++ b/src/net/manaserv/beinghandler.h @@ -0,0 +1,73 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_BEINGHANDLER_H +#define NET_MANASERV_BEINGHANDLER_H + +#include "net/manaserv/messagehandler.h" + +#include "net/beinghandler.h" + +#include "vector.h" +#include "map.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class BeingHandler : public MessageHandler, public Net::BeingHandler +{ + public: + BeingHandler(); + + void handleMessage(Net::MessageIn &msg); + + /** + * Translate a given speed in tiles per seconds + * into pixels per ticks. + * Used to optimize Being::logic() calls. + * @see MILLISECONDS_IN_A_TICKS + */ + static Vector giveSpeedInPixelsPerTicks(float speedInTilesPerSeconds); + + void requestNameById(int id); + + void undress(Being *being); + + private: + void handleBeingAttackMessage(Net::MessageIn &msg); + void handleBeingEnterMessage(Net::MessageIn &msg); + void handleBeingLeaveMessage(Net::MessageIn &msg); + void handleBeingsMoveMessage(Net::MessageIn &msg); + void handleBeingsDamageMessage(Net::MessageIn &msg); + void handleBeingActionChangeMessage(Net::MessageIn &msg); + void handleBeingLooksChangeMessage(Net::MessageIn &msg); + void handleBeingDirChangeMessage(Net::MessageIn &msg); +}; + +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/buysellhandler.cpp b/src/net/manaserv/buysellhandler.cpp new file mode 100644 index 000000000..dbfdfd46d --- /dev/null +++ b/src/net/manaserv/buysellhandler.cpp @@ -0,0 +1,132 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/buysellhandler.h" + +#include "actorspritemanager.h" +#include "item.h" +#include "localplayer.h" +#include "playerinfo.h" +#include "shopitem.h" + +#include "gui/buy.h" +#include "gui/chat.h" +#include "gui/sell.h" + +#include "net/messagein.h" +#include "net/net.h" + +#include "net/manaserv/protocol.h" + + +extern Net::BuySellHandler *buySellHandler; + +namespace ManaServ +{ + +BuySellHandler::BuySellHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_NPC_BUY, + GPMSG_NPC_SELL, + 0 + }; + handledMessages = _messages; + buySellHandler = this; +} + +void BuySellHandler::handleMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being || being->getType() != ActorSprite::NPC) + { + return; + } + + int npcId = being->getId(); + + switch (msg.getId()) + { + case GPMSG_NPC_BUY: + { + BuyDialog* dialog = new BuyDialog(npcId); + + dialog->reset(); + dialog->setMoney(PlayerInfo::getAttribute(MONEY)); + + while (msg.getUnreadLength()) + { + int itemId = msg.readInt16(); + int amount = msg.readInt16(); + int value = msg.readInt16(); + dialog->addItem(itemId, amount, value); + } + break; + } + + case GPMSG_NPC_SELL: + { + SellDialog* dialog = new SellDialog(npcId); + + dialog->reset(); + dialog->setMoney(PlayerInfo::getAttribute(MONEY)); + + while (msg.getUnreadLength()) + { + int itemId = msg.readInt16(); + int amount = msg.readInt16(); + int value = msg.readInt16(); + dialog->addItem(new Item(itemId, amount, false), value); + } + break; + } + + default: + break; + } +} + +void BuySellHandler::requestSellList(std::string nick _UNUSED_) +{ + // TODO +} + +void BuySellHandler::requestBuyList(std::string nick _UNUSED_) +{ + // TODO +} + +void BuySellHandler::sendBuyRequest(std::string nick _UNUSED_, + ShopItem* item _UNUSED_, + int amount _UNUSED_) +{ + // TODO +} + +void BuySellHandler::sendSellRequest(std::string nick _UNUSED_, + ShopItem* item _UNUSED_, + int amount _UNUSED_) +{ + // TODO +} + +} // namespace ManaServ diff --git a/src/net/manaserv/buysellhandler.h b/src/net/manaserv/buysellhandler.h new file mode 100644 index 000000000..0ceebe61f --- /dev/null +++ b/src/net/manaserv/buysellhandler.h @@ -0,0 +1,57 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_BUYSELLHANDLER_H +#define NET_MANASERV_BUYSELLHANDLER_H + +#include "net/manaserv/messagehandler.h" + +#include "net/buysellhandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class BuySellHandler : public MessageHandler, public Net::BuySellHandler +{ + public: + BuySellHandler(); + + void handleMessage(Net::MessageIn &msg); + + void requestSellList(std::string nick); + + void requestBuyList(std::string nick); + + void sendBuyRequest(std::string nick, ShopItem* item, int amount); + + void sendSellRequest(std::string nick, ShopItem* item, int amount); + +}; + +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/charhandler.cpp b/src/net/manaserv/charhandler.cpp new file mode 100644 index 000000000..dd5430c03 --- /dev/null +++ b/src/net/manaserv/charhandler.cpp @@ -0,0 +1,406 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/charhandler.h" + +#include "client.h" +#include "localplayer.h" +#include "log.h" + +#include "gui/charcreatedialog.h" +#include "gui/okdialog.h" + +#include "net/logindata.h" +#include "net/loginhandler.h" +#include "net/net.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/gamehandler.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" +#include "net/manaserv/attributes.h" + +#include "resources/colordb.h" + +#include "utils/dtor.h" +#include "utils/gettext.h" + +extern Net::CharHandler *charHandler; +extern ManaServ::GameHandler *gameHandler; + +namespace ManaServ +{ + +extern Connection *accountServerConnection; +extern Connection *gameServerConnection; +extern Connection *chatServerConnection; +extern std::string netToken; +extern ServerInfo gameServer; +extern ServerInfo chatServer; + +CharHandler::CharHandler() +{ + static const Uint16 _messages[] = + { + APMSG_CHAR_CREATE_RESPONSE, + APMSG_CHAR_DELETE_RESPONSE, + APMSG_CHAR_INFO, + APMSG_CHAR_SELECT_RESPONSE, + 0 + }; + handledMessages = _messages; + charHandler = this; +} + +CharHandler::~CharHandler() +{ + clear(); +} + +void CharHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case APMSG_CHAR_CREATE_RESPONSE: + handleCharacterCreateResponse(msg); + break; + + case APMSG_CHAR_DELETE_RESPONSE: + handleCharacterDeleteResponse(msg); + break; + + case APMSG_CHAR_INFO: + handleCharacterInfo(msg); + break; + + case APMSG_CHAR_SELECT_RESPONSE: + handleCharacterSelectResponse(msg); + break; + + default: + break; + } +} + +void CharHandler::handleCharacterInfo(Net::MessageIn &msg) +{ + CachedCharacterInfo info; + info.slot = msg.readInt8(); + info.name = msg.readString(); + info.gender = msg.readInt8() == GENDER_MALE ? GENDER_MALE : + GENDER_FEMALE; + info.hairStyle = msg.readInt8(); + info.hairColor = msg.readInt8(); + info.level = msg.readInt16(); + info.characterPoints = msg.readInt16(); + info.correctionPoints = msg.readInt16(); + + + while (msg.getUnreadLength() > 0) + { + int id = msg.readInt32(); + CachedAttrbiute attr; + attr.base = msg.readInt32() / 256.0; + attr.mod = msg.readInt32() / 256.0; + + info.attribute[id] = attr; + } + + mCachedCharacterInfos.push_back(info); + + updateCharacters(); +} + +void CharHandler::handleCharacterCreateResponse(Net::MessageIn &msg) +{ + const int errMsg = msg.readInt8(); + + if (errMsg != ERRMSG_OK) + { + // Character creation failed + std::string errorMessage = ""; + switch (errMsg) + { + case ERRMSG_NO_LOGIN: + errorMessage = _("Not logged in."); + break; + case CREATE_TOO_MUCH_CHARACTERS: + errorMessage = _("No empty slot."); + break; + case ERRMSG_INVALID_ARGUMENT: + errorMessage = _("Invalid name."); + break; + case CREATE_EXISTS_NAME: + errorMessage = _("Character's name already exists."); + break; + case CREATE_INVALID_HAIRSTYLE: + errorMessage = _("Invalid hairstyle."); + break; + case CREATE_INVALID_HAIRCOLOR: + errorMessage = _("Invalid hair color."); + break; + case CREATE_INVALID_GENDER: + errorMessage = _("Invalid gender."); + break; + case CREATE_ATTRIBUTES_TOO_HIGH: + errorMessage = _("Character's stats are too high."); + break; + case CREATE_ATTRIBUTES_TOO_LOW: + errorMessage = _("Character's stats are too low."); + break; + case CREATE_ATTRIBUTES_OUT_OF_RANGE: + errorMessage = strprintf( _("At least one stat" + "is out of the permitted range: (%u - %u)."), + Attributes::getAttributeMinimum(), + Attributes::getAttributeMaximum()); + break; + case CREATE_INVALID_SLOT: + errorMessage = _("Invalid slot number."); + break; + default: + errorMessage = _("Unknown error."); + break; + } + new OkDialog(_("Error"), errorMessage); + + if (mCharCreateDialog) + mCharCreateDialog->unlock(); + } + else + { + // Close the character create dialog + if (mCharCreateDialog) + { + mCharCreateDialog->scheduleDelete(); + mCharCreateDialog = 0; + } + } +} + +void CharHandler::handleCharacterDeleteResponse(Net::MessageIn &msg) +{ + int errMsg = msg.readInt8(); + if (errMsg == ERRMSG_OK) + { + // Character deletion successful + delete mSelectedCharacter; + mCharacters.remove(mSelectedCharacter); +// mSelectedCharacter = 0; + updateCharSelectDialog(); + new OkDialog(_("Info"), _("Player deleted.")); + } + else + { + // Character deletion failed + std::string errorMessage = ""; + switch (errMsg) + { + case ERRMSG_NO_LOGIN: + errorMessage = _("Not logged in."); + break; + case ERRMSG_INVALID_ARGUMENT: + errorMessage = _("Selection out of range."); + break; + default: + errorMessage = strprintf(_("Unknown error (%d)."), errMsg); + } + new OkDialog(_("Error"), errorMessage); + } + mSelectedCharacter = 0; + unlockCharSelectDialog(); +} + +void CharHandler::handleCharacterSelectResponse(Net::MessageIn &msg) +{ + int errMsg = msg.readInt8(); + + if (errMsg == ERRMSG_OK) + { + netToken = msg.readString(32); + + gameServer.hostname.assign(msg.readString()); + gameServer.port = msg.readInt16(); + + chatServer.hostname.assign(msg.readString()); + chatServer.port = msg.readInt16(); + + logger->log("Game server: %s:%d", gameServer.hostname.c_str(), + gameServer.port); + logger->log("Chat server: %s:%d", chatServer.hostname.c_str(), + chatServer.port); + + // Prevent the selected local player from being deleted + player_node = mSelectedCharacter->dummy; + PlayerInfo::setBackend(mSelectedCharacter->data); + mSelectedCharacter->dummy = 0; + + Client::setState(STATE_CONNECT_GAME); + } + else if (errMsg == ERRMSG_FAILURE) + { + errorMessage = _("No gameservers are available."); + delete_all(mCharacters); + mCharacters.clear(); + Client::setState(STATE_ERROR); + } +} + +void CharHandler::setCharSelectDialog(CharSelectDialog *window) +{ + mCharSelectDialog = window; + updateCharacters(); +} + +void CharHandler::setCharCreateDialog(CharCreateDialog *window) +{ + mCharCreateDialog = window; + + if (!mCharCreateDialog) + return; + + mCharCreateDialog->setAttributes(Attributes::getLabels(), + Attributes::getCreationPoints(), + Attributes::getAttributeMinimum(), + Attributes::getAttributeMaximum()); +} + +void CharHandler::requestCharacters() +{ + if (!accountServerConnection->isConnected()) + { + Net::getLoginHandler()->connect(); + } + else + { + // The characters are already there, continue to character selection + Client::setState(STATE_CHAR_SELECT); + } +} + +void CharHandler::chooseCharacter(Net::Character *character) +{ + mSelectedCharacter = character; + + MessageOut msg(PAMSG_CHAR_SELECT); + msg.writeInt8(mSelectedCharacter->slot); + accountServerConnection->send(msg); +} + +void CharHandler::newCharacter(const std::string &name, + int slot, + bool gender, + int hairstyle, + int hairColor, + const std::vector<int> &stats) +{ + MessageOut msg(PAMSG_CHAR_CREATE); + + msg.writeString(name); + msg.writeInt8(hairstyle); + msg.writeInt8(hairColor); + msg.writeInt8(gender); + msg.writeInt8(slot); + + std::vector<int>::const_iterator it, it_end; + for (it = stats.begin(), it_end = stats.end(); it != it_end; it++) + msg.writeInt16((*it)); + + accountServerConnection->send(msg); +} + +void CharHandler::deleteCharacter(Net::Character *character) +{ + mSelectedCharacter = character; + + MessageOut msg(PAMSG_CHAR_DELETE); + msg.writeInt8(mSelectedCharacter->slot); + accountServerConnection->send(msg); +} + +void CharHandler::switchCharacter() +{ + gameHandler->quit(true); +} + +unsigned int CharHandler::baseSprite() const +{ + return SPRITE_BASE; +} + +unsigned int CharHandler::hairSprite() const +{ + return SPRITE_HAIR; +} + +unsigned int CharHandler::maxSprite() const +{ + return SPRITE_VECTOREND; +} + +void CharHandler::updateCharacters() +{ + // Delete previous characters + delete_all(mCharacters); + mCharacters.clear(); + + if (!mCharSelectDialog) + return; + + // Create new characters and initialize them from the cached infos + for (unsigned i = 0; i < mCachedCharacterInfos.size(); ++i) + { + const CachedCharacterInfo &info = mCachedCharacterInfos.at(i); + + Net::Character *character = new Net::Character; + character->slot = info.slot; + LocalPlayer *player = character->dummy = new LocalPlayer; + player->setName(info.name); + player->setGender(info.gender); + player->setSprite(SPRITE_HAIR, info.hairStyle * -1, + ColorDB::get(info.hairColor)); + character->data.mAttributes[LEVEL] = info.level; + character->data.mAttributes[CHAR_POINTS] = info.characterPoints; + character->data.mAttributes[CORR_POINTS] = info.correctionPoints; + + for (CachedAttributes::const_iterator it = info.attribute.begin(), + it_end = info.attribute.end(); it != it_end; it++) + { + character->data.mStats[i].base = it->second.base; + character->data.mStats[i].mod = it->second.mod; + } + + mCharacters.push_back(character); + } + + updateCharSelectDialog(); +} + +void CharHandler::clear() +{ + setCharCreateDialog(0); + setCharSelectDialog(0); + + mCachedCharacterInfos.clear(); + updateCharacters(); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/charhandler.h b/src/net/manaserv/charhandler.h new file mode 100644 index 000000000..512cca451 --- /dev/null +++ b/src/net/manaserv/charhandler.h @@ -0,0 +1,119 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_CHARSERVERHANDLER_H +#define NET_MANASERV_CHARSERVERHANDLER_H + +#include "gui/charselectdialog.h" + +#include "net/charhandler.h" + +#include "net/manaserv/messagehandler.h" + +#include <map.h> + +class LoginData; + +namespace ManaServ +{ + +/** + * Deals with incoming messages related to character selection. + */ +class CharHandler : public MessageHandler, public Net::CharHandler +{ + public: + CharHandler(); + + ~CharHandler(); + + void handleMessage(Net::MessageIn &msg); + + void setCharSelectDialog(CharSelectDialog *window); + + /** + * Sets the character create dialog. The handler will clean up this + * dialog when a new character is succesfully created, and will unlock + * the dialog when a new character failed to be created. + */ + void setCharCreateDialog(CharCreateDialog *window); + + void requestCharacters(); + + void chooseCharacter(Net::Character *character); + + void newCharacter(const std::string &name, int slot, + bool gender, int hairstyle, int hairColor, + const std::vector<int> &stats); + + void deleteCharacter(Net::Character *character); + + void switchCharacter(); + + unsigned int baseSprite() const; + + unsigned int hairSprite() const; + + unsigned int maxSprite() const; + + void clear(); + + private: + /** + * Character information needs to be cached since we receive it before + * we have loaded the dynamic data, so we can't resolve load any + * sprites yet. + */ + struct CachedAttrbiute + { + double base; + double mod; + }; + + typedef std::map<int, CachedAttrbiute> CachedAttributes; + + struct CachedCharacterInfo + { + int slot; + std::string name; + Gender gender; + int hairStyle; + int hairColor; + int level; + int characterPoints; + int correctionPoints; + CachedAttributes attribute; + }; + + void handleCharacterInfo(Net::MessageIn &msg); + void handleCharacterCreateResponse(Net::MessageIn &msg); + void handleCharacterDeleteResponse(Net::MessageIn &msg); + void handleCharacterSelectResponse(Net::MessageIn &msg); + + void updateCharacters(); + + /** Cached character information */ + std::vector<CachedCharacterInfo> mCachedCharacterInfos; +}; + +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/chathandler.cpp b/src/net/manaserv/chathandler.cpp new file mode 100644 index 000000000..5e588c583 --- /dev/null +++ b/src/net/manaserv/chathandler.cpp @@ -0,0 +1,472 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/chathandler.h" + +#include "actorspritemanager.h" +#include "being.h" +#include "client.h" +#include "channel.h" +#include "channelmanager.h" + +#include "gui/chat.h" + +#include "gui/widgets/channeltab.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include <string> +#include <iostream> + +extern Being *player_node; + +extern Net::ChatHandler *chatHandler; + +namespace ManaServ +{ + +extern Connection *chatServerConnection; +extern Connection *gameServerConnection; +extern std::string netToken; +extern ServerInfo chatServer; + +ChatHandler::ChatHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_SAY, + CPMSG_ENTER_CHANNEL_RESPONSE, + CPMSG_LIST_CHANNELS_RESPONSE, + CPMSG_PUBMSG, + CPMSG_ANNOUNCEMENT, + CPMSG_PRIVMSG, + CPMSG_QUIT_CHANNEL_RESPONSE, + CPMSG_LIST_CHANNELUSERS_RESPONSE, + CPMSG_CHANNEL_EVENT, + CPMSG_WHO_RESPONSE, + CPMSG_DISCONNECT_RESPONSE, + 0 + }; + handledMessages = _messages; + chatHandler = this; +} + +void ChatHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_SAY: + handleGameChatMessage(msg); + break; + + case CPMSG_ENTER_CHANNEL_RESPONSE: + handleEnterChannelResponse(msg); + break; + + case CPMSG_LIST_CHANNELS_RESPONSE: + handleListChannelsResponse(msg); + break; + + case CPMSG_PRIVMSG: + handlePrivateMessage(msg); + break; + + case CPMSG_ANNOUNCEMENT: + handleAnnouncement(msg); + break; + + case CPMSG_PUBMSG: + handleChatMessage(msg); + break; + + case CPMSG_QUIT_CHANNEL_RESPONSE: + handleQuitChannelResponse(msg); + break; + + case CPMSG_LIST_CHANNELUSERS_RESPONSE: + handleListChannelUsersResponse(msg); + break; + + case CPMSG_CHANNEL_EVENT: + handleChannelEvent(msg); + break; + + case CPMSG_WHO_RESPONSE: + handleWhoResponse(msg); + break; + case CPMSG_DISCONNECT_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful logout + if (errMsg == ERRMSG_OK) + { + // TODO: Handle logout + } + else + { + switch (errMsg) + { + case ERRMSG_NO_LOGIN: + errorMessage = "Chatserver: Not logged in"; + break; + default: + errorMessage = "Chatserver: Unknown error"; + break; + } + Client::setState(STATE_ERROR); + } + } + break; + default: + break; + } +} + +void ChatHandler::handleGameChatMessage(Net::MessageIn &msg) +{ + short id = msg.readInt16(); + std::string chatMsg = msg.readString(); + + if (id == 0) + { + localChatTab->chatLog(chatMsg, BY_SERVER); + return; + } + + Being *being = actorSpriteManager->findBeing(id); + + std::string mes; + if (being) + { + mes = being->getName() + " : " + chatMsg; + being->setSpeech(chatMsg, SPEECH_TIME); + } + else + mes = "Unknown : " + chatMsg; + + localChatTab->chatLog(mes, being == player_node + ? BY_PLAYER : BY_OTHER); +} + +void ChatHandler::handleEnterChannelResponse(Net::MessageIn &msg) +{ + if (msg.readInt8() == ERRMSG_OK) + { + short channelId = msg.readInt16(); + std::string channelName = msg.readString(); + std::string announcement = msg.readString(); + Channel *channel = new Channel(channelId, channelName, announcement); + channelManager->addChannel(channel); + ChatTab *tab = channel->getTab(); + tab->chatLog(strprintf(_("Topic: %s"), announcement.c_str()), + BY_CHANNEL); + + std::string user; + std::string userModes; + tab->chatLog(_("Players in this channel:"), BY_CHANNEL); + while (msg.getUnreadLength()) + { + user = msg.readString(); + if (user == "") + return; + userModes = msg.readString(); + if (userModes.find('o') != std::string::npos) + { + user = "@" + user; + } + tab->chatLog(user, BY_CHANNEL); + } + + } + else + { + localChatTab->chatLog(_("Error joining channel."), BY_SERVER); + } +} + +void ChatHandler::handleListChannelsResponse(Net::MessageIn &msg) +{ + localChatTab->chatLog(_("Listing channels."), BY_SERVER); + while (msg.getUnreadLength()) + { + std::string channelName = msg.readString(); + if (channelName == "") + return; + std::ostringstream numUsers; + numUsers << msg.readInt16(); + channelName += " - "; + channelName += numUsers.str(); + localChatTab->chatLog(channelName, BY_SERVER); + } + localChatTab->chatLog(_("End of channel list."), BY_SERVER); +} + +void ChatHandler::handlePrivateMessage(Net::MessageIn &msg) +{ + std::string userNick = msg.readString(); + std::string chatMsg = msg.readString(); + + chatWindow->whisper(userNick, chatMsg); +} + +void ChatHandler::handleAnnouncement(Net::MessageIn &msg) +{ + std::string chatMsg = msg.readString(); + localChatTab->chatLog(chatMsg, BY_GM); +} + +void ChatHandler::handleChatMessage(Net::MessageIn &msg) +{ + short channelId = msg.readInt16(); + std::string userNick = msg.readString(); + std::string chatMsg = msg.readString(); + + Channel *channel = channelManager->findById(channelId); + channel->getTab()->chatLog(userNick, chatMsg); +} + +void ChatHandler::handleQuitChannelResponse(Net::MessageIn &msg) +{ + if (msg.readInt8() == ERRMSG_OK) + { + short channelId = msg.readInt16(); + Channel *channel = channelManager->findById(channelId); + channelManager->removeChannel(channel); + } +} + +void ChatHandler::handleListChannelUsersResponse(Net::MessageIn &msg) +{ + std::string channelName = msg.readString(); + std::string userNick; + std::string userModes; + Channel *channel = channelManager->findByName(channelName); + channel->getTab()->chatLog(_("Players in this channel:"), + BY_CHANNEL); + while (msg.getUnreadLength()) + { + userNick = msg.readString(); + if (userNick == "") + { + break; + } + userModes = msg.readString(); + if (userModes.find('o') != std::string::npos) + { + userNick = "@" + userNick; + } + localChatTab->chatLog(userNick, BY_CHANNEL, channel); + } +} + +void ChatHandler::handleChannelEvent(Net::MessageIn &msg) +{ + short channelId = msg.readInt16(); + char eventId = msg.readInt8(); + std::string line = msg.readString(); + Channel *channel = channelManager->findById(channelId); + + if (channel) + { + switch(eventId) + { + case CHAT_EVENT_NEW_PLAYER: + channel->getTab()->chatLog(strprintf(_("%s entered the " + "channel."), line.c_str()), BY_CHANNEL); + break; + + case CHAT_EVENT_LEAVING_PLAYER: + channel->getTab()->chatLog(strprintf(_("%s left the channel."), + line.c_str()), BY_CHANNEL); + break; + + case CHAT_EVENT_TOPIC_CHANGE: + channel->getTab()->chatLog(strprintf(_("Topic: %s"), + line.c_str()), BY_CHANNEL); + break; + + case CHAT_EVENT_MODE_CHANGE: + { + int first = line.find(":"); + int second = line.find(":", first + 1); + std::string user1 = line.substr(0, first); + std::string user2 = line.substr(first + 1, second); + std::string mode = line.substr(second + 1, line.length()); + channel->getTab()->chatLog(strprintf(_("%s has set mode %s " + "on user %s."), user1.c_str(), mode.c_str(), + user2.c_str()), BY_CHANNEL); + } + break; + + case CHAT_EVENT_KICKED_PLAYER: + { + int first = line.find(":"); + std::string user1 = line.substr(0, first); + std::string user2 = line.substr(first + 1, line.length()); + channel->getTab()->chatLog(strprintf(_("%s has kicked %s."), + user1.c_str(), user2.c_str()), BY_CHANNEL); + } + break; + + default: + channel->getTab()->chatLog(_("Unknown channel event."), + BY_CHANNEL); + } + } +} + +void ChatHandler::handleWhoResponse(Net::MessageIn &msg) +{ + std::string userNick; + + while (msg.getUnreadLength()) + { + userNick = msg.readString(); + if (userNick == "") + break; + localChatTab->chatLog(userNick, BY_SERVER); + } +} + +void ChatHandler::connect() +{ + MessageOut msg(PCMSG_CONNECT); + msg.writeString(netToken, 32); + chatServerConnection->send(msg); +} + +bool ChatHandler::isConnected() +{ + return chatServerConnection->isConnected(); +} + +void ChatHandler::disconnect() +{ + chatServerConnection->disconnect(); +} + +void ChatHandler::talk(const std::string &text) +{ + MessageOut msg(PGMSG_SAY); + msg.writeString(text); + gameServerConnection->send(msg); +} + +void ChatHandler::talkRaw(const std::string &text) +{ + MessageOut msg(PGMSG_SAY); + msg.writeString(text); + gameServerConnection->send(msg); +} + +void ChatHandler::me(const std::string &text _UNUSED_) +{ + // TODO +} + +void ChatHandler::privateMessage(const std::string &recipient, + const std::string &text) +{ + MessageOut msg(PCMSG_PRIVMSG); + msg.writeString(recipient); + msg.writeString(text); + chatServerConnection->send(msg); +} + +void ChatHandler::channelList() +{ + MessageOut msg(PCMSG_LIST_CHANNELS); + chatServerConnection->send(msg); +} + +void ChatHandler::enterChannel(const std::string &channel, + const std::string &password) +{ + MessageOut msg(PCMSG_ENTER_CHANNEL); + msg.writeString(channel); + msg.writeString(password); + chatServerConnection->send(msg); +} + +void ChatHandler::quitChannel(int channelId) +{ + MessageOut msg(PCMSG_QUIT_CHANNEL); + msg.writeInt16(channelId); + chatServerConnection->send(msg); +} + +void ChatHandler::sendToChannel(int channelId, const std::string &text) +{ + MessageOut msg(PCMSG_CHAT); + msg.writeString(text); + msg.writeInt16(channelId); + chatServerConnection->send(msg); +} + +void ChatHandler::userList(const std::string &channel) +{ + MessageOut msg(PCMSG_LIST_CHANNELUSERS); + msg.writeString(channel); + chatServerConnection->send(msg); +} + +void ChatHandler::setChannelTopic(int channelId, const std::string &text) +{ + MessageOut msg(PCMSG_TOPIC_CHANGE); + msg.writeInt16(channelId); + msg.writeString(text); + chatServerConnection->send(msg); +} + +void ChatHandler::setUserMode(int channelId, const std::string &name, int mode) +{ + MessageOut msg(PCMSG_USER_MODE); + msg.writeInt16(channelId); + msg.writeString(name); + msg.writeInt8(mode); + chatServerConnection->send(msg); +} + +void ChatHandler::kickUser(int channelId, const std::string &name) +{ + MessageOut msg(PCMSG_KICK_USER); + msg.writeInt16(channelId); + msg.writeString(name); + chatServerConnection->send(msg); +} + +void ChatHandler::who() +{ + MessageOut msg(PCMSG_WHO); + chatServerConnection->send(msg); +} + +void ChatHandler::sendRaw(const std::string &args _UNUSED_) +{ + +} +} // namespace ManaServ diff --git a/src/net/manaserv/chathandler.h b/src/net/manaserv/chathandler.h new file mode 100644 index 000000000..8ffa6d28d --- /dev/null +++ b/src/net/manaserv/chathandler.h @@ -0,0 +1,139 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_CHATHANDLER_H +#define NET_MANASERV_CHATHANDLER_H + +#include "net/chathandler.h" +#include "net/serverinfo.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class ChatHandler : public MessageHandler, public Net::ChatHandler +{ + public: + ChatHandler(); + + /** + * Handle the given message appropriately. + */ + void handleMessage(Net::MessageIn &msg); + + void connect(); + + bool isConnected(); + + void disconnect(); + + void talk(const std::string &text); + + void talkRaw(const std::string &text); + + void me(const std::string &text); + + void privateMessage(const std::string &recipient, + const std::string &text); + + void channelList(); + + void enterChannel(const std::string &channel, + const std::string &password); + + void quitChannel(int channelId); + + void sendToChannel(int channelId, const std::string &text); + + void userList(const std::string &channel); + + void setChannelTopic(int channelId, const std::string &text); + + void setUserMode(int channelId, const std::string &name, int mode); + + void kickUser(int channelId, const std::string &name); + + void who(); + + void sendRaw(const std::string &args); + + private: + /** + * Handle chat messages sent from the game server. + */ + void handleGameChatMessage(Net::MessageIn &msg); + + /** + * Handle channel entry responses. + */ + void handleEnterChannelResponse(Net::MessageIn &msg); + + /** + * Handle list channels responses. + */ + void handleListChannelsResponse(Net::MessageIn &msg); + + /** + * Handle private messages. + */ + void handlePrivateMessage(Net::MessageIn &msg); + + /** + * Handle announcements. + */ + void handleAnnouncement(Net::MessageIn &msg); + + /** + * Handle chat messages. + */ + void handleChatMessage(Net::MessageIn &msg); + + /** + * Handle quit channel responses. + */ + void handleQuitChannelResponse(Net::MessageIn &msg); + + /** + * Handle list channel users responses. + */ + void handleListChannelUsersResponse(Net::MessageIn &msg); + + /** + * Handle channel events. + */ + void handleChannelEvent(Net::MessageIn &msg); + + /** + * Handle who responses. + */ + void handleWhoResponse(Net::MessageIn &msg); +}; + +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/connection.cpp b/src/net/manaserv/connection.cpp new file mode 100644 index 000000000..944d8a94c --- /dev/null +++ b/src/net/manaserv/connection.cpp @@ -0,0 +1,113 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/connection.h" + +#include "log.h" + +#include "net/manaserv/internal.h" +#include "net/manaserv/messageout.h" + +#include <string> + +namespace ManaServ +{ + +Connection::Connection(ENetHost *client): + mConnection(0), mClient(client) +{ + mPort = 0; + connections++; +} + +Connection::~Connection() +{ + connections--; +} + +bool Connection::connect(const std::string &address, short port) +{ + logger->log("Net::Connection::connect(%s, %i)", address.c_str(), port); + + if (address.empty()) + { + logger->log1("Net::Connection::connect() got empty address!"); + mState = NET_ERROR; + return false; + } + + ENetAddress enetAddress; + + enet_address_set_host(&enetAddress, address.c_str()); + enetAddress.port = port; + + // Initiate the connection, allocating channel 0. +#if defined(ENET_VERSION) && ENET_VERSION >= ENET_CUTOFF + mConnection = enet_host_connect(mClient, &enetAddress, 1, 0); +#else + mConnection = enet_host_connect(mClient, &enetAddress, 1); +#endif + + if (!mConnection) + { + logger->log1("Unable to initiate connection to the server."); + mState = NET_ERROR; + return false; + } + + mPort = port; + + return true; +} + +void Connection::disconnect() +{ + if (!mConnection) + return; + + enet_peer_disconnect(mConnection, 0); + enet_host_flush(mClient); + enet_peer_reset(mConnection); + + mConnection = 0; +} + +bool Connection::isConnected() +{ + return (mConnection) ? + (mConnection->state == ENET_PEER_STATE_CONNECTED) : false; +} + +void Connection::send(const ManaServ::MessageOut &msg) +{ + if (!isConnected()) + { + logger->log1("Warning: cannot send message to not connected server!"); + return; + } + + ENetPacket *packet = enet_packet_create(msg.getData(), + msg.getDataSize(), + ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(mConnection, 0, packet); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/connection.h b/src/net/manaserv/connection.h new file mode 100644 index 000000000..260a177ee --- /dev/null +++ b/src/net/manaserv/connection.h @@ -0,0 +1,89 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_CONNECTION_H +#define NET_MANASERV_CONNECTION_H + +#include "enet/enet.h" + +#include <iosfwd> + +#ifdef ENET_VERSION_CREATE +#define ENET_CUTOFF ENET_VERSION_CREATE(1, 3, 0) +#else +#define ENET_CUTOFF 0xFFFFFFFF +#endif + +namespace ManaServ +{ + class MessageOut; + + /** + * \ingroup Network + */ + class Connection + { + public: + enum State + { + OK = 0, + NET_ERROR + }; + + ~Connection(); + + /** + * Connects to the given server with the specified address and port. + * This method is non-blocking, use isConnected to check whether the + * server is connected. + */ + bool connect(const std::string &address, short port); + + /** + * Disconnects from the given server. + */ + void disconnect(); + + State getState() + { return mState; } + + /** + * Returns whether the server is connected. + */ + bool isConnected(); + + /** + * Sends a message. + */ + void send(const ManaServ::MessageOut &msg); + + private: + friend Connection *ManaServ::getConnection(); + Connection(ENetHost *client); + + short mPort; + ENetPeer *mConnection; + ENetHost *mClient; + State mState; + }; +} + +#endif // NET_MANASERV_CONNECTION_H diff --git a/src/net/manaserv/defines.h b/src/net/manaserv/defines.h new file mode 100644 index 000000000..f09175f65 --- /dev/null +++ b/src/net/manaserv/defines.h @@ -0,0 +1,77 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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 MANASERV_DEFINES_H +#define MANASERV_DEFINES_H + +/** + * Attributes used during combat. Available to all the beings. + */ +enum +{ + BASE_ATTR_BEGIN = 0, + BASE_ATTR_PHY_ATK_MIN = BASE_ATTR_BEGIN, + BASE_ATTR_PHY_ATK_DELTA, + /**< Physical attack power. */ + BASE_ATTR_MAG_ATK, /**< Magical attack power. */ + BASE_ATTR_PHY_RES, /**< Resistance to physical damage. */ + BASE_ATTR_MAG_RES, /**< Resistance to magical damage. */ + BASE_ATTR_EVADE, /**< Ability to avoid hits. */ + BASE_ATTR_HIT, /**< Ability to hit stuff. */ + BASE_ATTR_HP, /**< Hit Points (Base value: maximum, + Modded value: current) */ + BASE_ATTR_HP_REGEN, /**< number of HP regenerated every 10 game ticks */ + BASE_ATTR_END, + BASE_ATTR_NB = BASE_ATTR_END - BASE_ATTR_BEGIN, + + BASE_ELEM_BEGIN = BASE_ATTR_END, + BASE_ELEM_NEUTRAL = BASE_ELEM_BEGIN, + BASE_ELEM_FIRE, + BASE_ELEM_WATER, + BASE_ELEM_EARTH, + BASE_ELEM_AIR, + BASE_ELEM_SACRED, + BASE_ELEM_DEATH, + BASE_ELEM_END, + BASE_ELEM_NB = BASE_ELEM_END - BASE_ELEM_BEGIN, + + NB_BEING_ATTRIBUTES = BASE_ELEM_END +}; + +/** + * Attributes of characters. Used to derive being attributes. + */ +enum +{ + CHAR_ATTR_BEGIN = NB_BEING_ATTRIBUTES, + CHAR_ATTR_STRENGTH = CHAR_ATTR_BEGIN, + CHAR_ATTR_AGILITY, + CHAR_ATTR_DEXTERITY, + CHAR_ATTR_VITALITY, + CHAR_ATTR_INTELLIGENCE, + CHAR_ATTR_WILLPOWER, + CHAR_ATTR_END, + CHAR_ATTR_NB = CHAR_ATTR_END - CHAR_ATTR_BEGIN, + + NB_CHARACTER_ATTRIBUTES = CHAR_ATTR_END +}; + +#endif // MANASERV_DEFINES_H diff --git a/src/net/manaserv/effecthandler.cpp b/src/net/manaserv/effecthandler.cpp new file mode 100644 index 000000000..5e0083744 --- /dev/null +++ b/src/net/manaserv/effecthandler.cpp @@ -0,0 +1,80 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/effecthandler.h" + +#include "actorspritemanager.h" +#include "effectmanager.h" +#include "log.h" + +#include "net/messagein.h" + +#include "net/manaserv/protocol.h" + +namespace ManaServ +{ + +EffectHandler::EffectHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_CREATE_EFFECT_POS, + GPMSG_CREATE_EFFECT_BEING, + 0 + }; + handledMessages = _messages; +} + +void EffectHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_CREATE_EFFECT_POS: + handleCreateEffectPos(msg); + break; + case GPMSG_CREATE_EFFECT_BEING: + handleCreateEffectBeing(msg); + break; + default: + break; + } +} + +void EffectHandler::handleCreateEffectPos(Net::MessageIn &msg) +{ + int id = msg.readInt16(); + Uint16 x = msg.readInt16(); + Uint16 y = msg.readInt16(); + effectManager->trigger(id, x, y); +} + +void EffectHandler::handleCreateEffectBeing(Net::MessageIn &msg) +{ + int eid = msg.readInt16(); + int bid = msg.readInt16(); + Being* b = actorSpriteManager->findBeing(bid); + if (b) + effectManager->trigger(eid, b); + else + logger->log("Warning: CreateEffect called for unknown being #%d", bid); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/effecthandler.h b/src/net/manaserv/effecthandler.h new file mode 100644 index 000000000..5f6c07580 --- /dev/null +++ b/src/net/manaserv/effecthandler.h @@ -0,0 +1,44 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_EFFECTSHANDLER_H +#define NET_MANASERV_EFFECTSHANDLER_H + +#include "net/manaserv/messagehandler.h" + +namespace ManaServ +{ + +class EffectHandler : public MessageHandler +{ + public: + EffectHandler(); + + void handleMessage(Net::MessageIn &msg); + + private: + void handleCreateEffectPos(Net::MessageIn &msg); + void handleCreateEffectBeing(Net::MessageIn &msg); +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_EFFECTSHANDLER_H diff --git a/src/net/manaserv/gamehandler.cpp b/src/net/manaserv/gamehandler.cpp new file mode 100644 index 000000000..9ca6c0d6b --- /dev/null +++ b/src/net/manaserv/gamehandler.cpp @@ -0,0 +1,154 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/gamehandler.h" + +#include "client.h" +#include "localplayer.h" + +#include "net/manaserv/chathandler.h" +#include "net/manaserv/connection.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +extern Net::GameHandler *gameHandler; + +extern ManaServ::ChatHandler *chatHandler; + +namespace ManaServ +{ + +extern Connection *chatServerConnection; +extern Connection *gameServerConnection; +extern std::string netToken; +extern ServerInfo gameServer; +extern ServerInfo chatServer; + +GameHandler::GameHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_DISCONNECT_RESPONSE, + 0 + }; + handledMessages = _messages; + gameHandler = this; +} + +void GameHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_DISCONNECT_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful logout + if (errMsg == ERRMSG_OK) + { + netToken = msg.readString(32); + + if (!netToken.empty()) + { + Client::setState(STATE_SWITCH_CHARACTER); + } + else + { + // TODO: Handle logout + } + } + // Logout failed + else + { + switch (errMsg) + { + case ERRMSG_NO_LOGIN: + errorMessage = "Gameserver: Not logged in"; + break; + default: + errorMessage = "Gameserver: Unknown error"; + break; + } + Client::setState(STATE_ERROR); + } + } + break; + default: + break; + } +} + +void GameHandler::connect() +{ + gameServerConnection->connect(gameServer.hostname, gameServer.port); + + // Will already be connected if we just changed gameservers + if (!chatServerConnection->isConnected()) + chatServerConnection->connect(chatServer.hostname, chatServer.port); +} + +bool GameHandler::isConnected() +{ + return gameServerConnection->isConnected() && + chatHandler->isConnected(); +} + +void GameHandler::disconnect() +{ + gameServerConnection->disconnect(); + // No need if we're just changing gameservers + if (Client::getState() != STATE_CHANGE_MAP) + chatHandler->disconnect(); +} + +void GameHandler::who() +{ + // TODO +} + +void GameHandler::quit(bool reconnectAccount) +{ + MessageOut msg(PGMSG_DISCONNECT); + msg.writeInt8((unsigned char) reconnectAccount); + gameServerConnection->send(msg); +} + +void GameHandler::ping(int tick _UNUSED_) +{ + // TODO +} + +void GameHandler::gameLoading() +{ + MessageOut msg(PGMSG_CONNECT); + msg.writeString(netToken, 32); + gameServerConnection->send(msg); + + chatHandler->connect(); + + // Attack range from item DB + player_node->setAttackRange(-1); +} + +void GameHandler::disconnect2() +{ +} + +} // namespace ManaServ diff --git a/src/net/manaserv/gamehandler.h b/src/net/manaserv/gamehandler.h new file mode 100644 index 000000000..246a3c736 --- /dev/null +++ b/src/net/manaserv/gamehandler.h @@ -0,0 +1,74 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_MAPHANDLER_H +#define NET_MANASERV_MAPHANDLER_H + +#include "net/gamehandler.h" +#include "net/serverinfo.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class GameHandler : public MessageHandler, public Net::GameHandler +{ + public: + GameHandler(); + + void handleMessage(Net::MessageIn &msg); + + void connect(); + + bool isConnected(); + + void disconnect(); + + void who(); + + void quit(bool reconnectAccount); + + void quit() { quit(false); } + + void ping(int tick); + + bool removeDeadBeings() const { return false; } + + void clear(); + + void gameLoading(); + + /** The ManaServ protocol doesn't use the MP status bar. */ + bool canUseMagicBar() const { return false; } + + void disconnect2(); +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_MAPHANDLER_H diff --git a/src/net/manaserv/generalhandler.cpp b/src/net/manaserv/generalhandler.cpp new file mode 100644 index 000000000..acf731dec --- /dev/null +++ b/src/net/manaserv/generalhandler.cpp @@ -0,0 +1,211 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/generalhandler.h" + +#include "client.h" + +#include "gui/changeemaildialog.h" +#include "gui/charselectdialog.h" +#include "gui/inventorywindow.h" +#include "gui/register.h" +#include "gui/skilldialog.h" +#include "gui/specialswindow.h" + +#include "net/manaserv/beinghandler.h" +#include "net/manaserv/buysellhandler.h" +#include "net/manaserv/charhandler.h" +#include "net/manaserv/chathandler.h" +#include "net/manaserv/connection.h" +#include "net/manaserv/effecthandler.h" +#include "net/manaserv/gamehandler.h" +#include "net/manaserv/guildhandler.h" +#include "net/manaserv/inventoryhandler.h" +#include "net/manaserv/itemhandler.h" +#include "net/manaserv/loginhandler.h" +#include "net/manaserv/network.h" +#include "net/manaserv/npchandler.h" +#include "net/manaserv/partyhandler.h" +#include "net/manaserv/playerhandler.h" +#include "net/manaserv/specialhandler.h" +#include "net/manaserv/attributes.h" +#include "net/manaserv/tradehandler.h" + +#include "utils/gettext.h" + +#include <list> + +extern Net::GeneralHandler *generalHandler; + +extern ManaServ::LoginHandler *loginHandler; + +namespace ManaServ +{ + +Connection *accountServerConnection = 0; +Connection *chatServerConnection = 0; +Connection *gameServerConnection = 0; +std::string netToken = ""; +ServerInfo gameServer; +ServerInfo chatServer; + +GeneralHandler::GeneralHandler(): + mBeingHandler(new BeingHandler), + mBuySellHandler(new BuySellHandler), + mCharHandler(new CharHandler), + mChatHandler(new ChatHandler), + mEffectHandler(new EffectHandler), + mGameHandler(new GameHandler), + mGuildHandler(new GuildHandler), + mInventoryHandler(new InventoryHandler), + mItemHandler(new ItemHandler), + mLoginHandler(new LoginHandler), + mNpcHandler(new NpcHandler), + mPartyHandler(new PartyHandler), + mPlayerHandler(new PlayerHandler), + mTradeHandler(new TradeHandler), + mSpecialHandler(new SpecialHandler) +{ + initialize(); + + accountServerConnection = getConnection(); + gameServerConnection = getConnection(); + chatServerConnection = getConnection(); + + generalHandler = this; + + listen(CHANNEL_CLIENT); + listen(CHANNEL_GAME); +} + +void GeneralHandler::load() +{ + registerHandler(mBeingHandler.get()); + registerHandler(mBuySellHandler.get()); + registerHandler(mCharHandler.get()); + registerHandler(mChatHandler.get()); + registerHandler(mEffectHandler.get()); + registerHandler(mGameHandler.get()); + registerHandler(mGuildHandler.get()); + registerHandler(mInventoryHandler.get()); + registerHandler(mItemHandler.get()); + registerHandler(mLoginHandler.get()); + registerHandler(mNpcHandler.get()); + registerHandler(mPartyHandler.get()); + registerHandler(mPlayerHandler.get()); + registerHandler(mTradeHandler.get()); +} + +void GeneralHandler::reload() +{ + static_cast<CharHandler*>(Net::getCharHandler())->clear(); + + if (accountServerConnection) + accountServerConnection->disconnect(); + + if (gameServerConnection) + gameServerConnection->disconnect(); + + if (chatServerConnection) + chatServerConnection->disconnect(); + + netToken.clear(); + gameServer.clear(); + chatServer.clear(); + + Attributes::unload(); + Attributes::load(); + Attributes::informItemDB(); +} + +void GeneralHandler::unload() +{ + clearHandlers(); + + if (accountServerConnection) + accountServerConnection->disconnect(); + if (gameServerConnection) + gameServerConnection->disconnect(); + if (chatServerConnection) + chatServerConnection->disconnect(); + + delete accountServerConnection; + accountServerConnection = 0; + delete gameServerConnection; + gameServerConnection = 0; + delete chatServerConnection; + chatServerConnection = 0; + + Attributes::unload(); + finalize(); +} + +void GeneralHandler::flushNetwork() +{ + flush(); + + if (Client::getState() == STATE_SWITCH_CHARACTER && + Net::getLoginHandler()->isConnected()) + { + loginHandler->reconnect(); + Client::setState(STATE_GET_CHARACTERS); + } +} + +void GeneralHandler::clearHandlers() +{ + clearNetworkHandlers(); +} + +void GeneralHandler::event(Channels channel, + const Mana::Event &event) +{ + if (channel == CHANNEL_CLIENT) + { + int newState = event.getInt("newState"); + + if (newState == STATE_GAME) + { + GameHandler *game = static_cast<GameHandler*>( + Net::getGameHandler()); + game->gameLoading(); + } + else if (newState == STATE_LOAD_DATA) + { + Attributes::load(); + Attributes::informItemDB(); + } + } + else if (channel == CHANNEL_GAME) + { + if (event.getName() == EVENT_GUIWINDOWSLOADED) + { + inventoryWindow->setSplitAllowed(true); + skillDialog->loadSkills("mana-skills.xml"); + + PlayerInfo::setAttribute(EXP_NEEDED, 100); + + Attributes::informStatusWindow(); + } + } +} + +} // namespace ManaServ diff --git a/src/net/manaserv/generalhandler.h b/src/net/manaserv/generalhandler.h new file mode 100644 index 000000000..944a3d305 --- /dev/null +++ b/src/net/manaserv/generalhandler.h @@ -0,0 +1,78 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_GENERALHANDLER_H +#define NET_MANASERV_GENERALHANDLER_H + +#include "listener.h" + +#include "net/generalhandler.h" +#include "net/net.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class GeneralHandler : public Net::GeneralHandler, public Mana::Listener +{ + public: + GeneralHandler(); + + void load(); + + void reload(); + + void unload(); + + void flushNetwork(); + + void clearHandlers(); + + void event(Channels channel, const Mana::Event &event); + + protected: + MessageHandlerPtr mBeingHandler; + MessageHandlerPtr mBuySellHandler; + MessageHandlerPtr mCharHandler; + MessageHandlerPtr mChatHandler; + MessageHandlerPtr mEffectHandler; + MessageHandlerPtr mGameHandler; + MessageHandlerPtr mGuildHandler; + MessageHandlerPtr mInventoryHandler; + MessageHandlerPtr mItemHandler; + MessageHandlerPtr mLoginHandler; + MessageHandlerPtr mNpcHandler; + MessageHandlerPtr mPartyHandler; + MessageHandlerPtr mPlayerHandler; + MessageHandlerPtr mTradeHandler; + MessageHandlerPtr mSpecialHandler; +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_GENERALHANDLER_H diff --git a/src/net/manaserv/guildhandler.cpp b/src/net/manaserv/guildhandler.cpp new file mode 100644 index 000000000..26af3eff1 --- /dev/null +++ b/src/net/manaserv/guildhandler.cpp @@ -0,0 +1,360 @@ +/* + * The Mana Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/guildhandler.h" + +#include "event.h" +#include "guild.h" +#include "log.h" +#include "localplayer.h" +#include "channel.h" +#include "channelmanager.h" + +#include "gui/widgets/channeltab.h" +#include "gui/chat.h" +#include "gui/socialwindow.h" + +#include "net/messagein.h" +#include "net/net.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include <iostream> + +extern Net::GuildHandler *guildHandler; + +namespace ManaServ +{ + +extern Connection *chatServerConnection; + +GuildHandler::GuildHandler() +{ + static const Uint16 _messages[] = + { + CPMSG_GUILD_CREATE_RESPONSE, + CPMSG_GUILD_INVITE_RESPONSE, + CPMSG_GUILD_ACCEPT_RESPONSE, + CPMSG_GUILD_GET_MEMBERS_RESPONSE, + CPMSG_GUILD_UPDATE_LIST, + CPMSG_GUILD_INVITED, + CPMSG_GUILD_REJOIN, + CPMSG_GUILD_QUIT_RESPONSE, + 0 + }; + handledMessages = _messages; + + guildHandler = this; +} + +void GuildHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case CPMSG_GUILD_CREATE_RESPONSE: + { + logger->log1("Received CPMSG_GUILD_CREATE_RESPONSE"); + if (msg.readInt8() == ERRMSG_OK) + { + // TODO - Acknowledge guild was created + SERVER_NOTICE(_("Guild created.")) + joinedGuild(msg); + } + else + { + SERVER_NOTICE(_("Error creating guild.")) + } + } break; + + case CPMSG_GUILD_INVITE_RESPONSE: + { + logger->log1("Received CPMSG_GUILD_INVITE_RESPONSE"); + if (msg.readInt8() == ERRMSG_OK) + { + // TODO - Acknowledge invite was sent + SERVER_NOTICE(_("Invite sent.")) + } + } break; + + case CPMSG_GUILD_ACCEPT_RESPONSE: + { + logger->log1("Received CPMSG_GUILD_ACCEPT_RESPONSE"); + if (msg.readInt8() == ERRMSG_OK) + { + // TODO - Acknowledge accepted into guild + joinedGuild(msg); + } + } break; + + case CPMSG_GUILD_GET_MEMBERS_RESPONSE: + { + logger->log1("Received CPMSG_GUILD_GET_MEMBERS_RESPONSE"); + if (msg.readInt8() == ERRMSG_OK) + { + std::string name; + bool online; + Guild *guild; + GuildMember *member; + + short guildId = msg.readInt16(); + guild = player_node->getGuild(guildId); + + if (!guild) + return; + + guild->clearMembers(); + + while (msg.getUnreadLength()) + { + name = msg.readString(); + online = msg.readInt8(); + if (name != "") + { + member = guild->addMember(name); + member->setOnline(online); + } + } + } + } break; + + case CPMSG_GUILD_UPDATE_LIST: + { + logger->log1("Received CPMSG_GUILD_UPDATE_LIST"); + short guildId = msg.readInt16(); + std::string name = msg.readString(); + char eventId = msg.readInt8(); + GuildMember *member; + + Guild *guild = player_node->getGuild(guildId); + if (guild) + { + switch(eventId) + { + case GUILD_EVENT_NEW_PLAYER: + member = guild->addMember(name); + member->setOnline(true); + break; + + case GUILD_EVENT_LEAVING_PLAYER: + guild->removeMember(name); + break; + + case GUILD_EVENT_ONLINE_PLAYER: + member = guild->getMember(name); + if (member) + { + member->setOnline(true); + } + break; + + case GUILD_EVENT_OFFLINE_PLAYER: + member = guild->getMember(name); + if (member) + { + member->setOnline(false); + } + break; + + default: + logger->log1("Invalid guild event"); + } + } + } break; + + case CPMSG_GUILD_INVITED: + { + logger->log1("Received CPMSG_GUILD_INVITED"); + std::string inviterName = msg.readString(); + std::string guildName = msg.readString(); + int guildId = msg.readInt16(); + + // Open a dialog asking if the player accepts joining the guild. + socialWindow->showGuildInvite(guildName, guildId, inviterName); + } break; + + case CPMSG_GUILD_PROMOTE_MEMBER_RESPONSE: + { + logger->log1("Received CPMSG_GUILD_PROMOTE_MEMBER_RESPONSE"); + + if (msg.readInt8() == ERRMSG_OK) + { + // promotion succeeded + SERVER_NOTICE(_("Member was promoted successfully.")) + } + else + { + // promotion failed + SERVER_NOTICE(_("Failed to promote member.")) + } + } + + case CPMSG_GUILD_REJOIN: + { + logger->log1("Received CPMSG_GUILD_REJOIN"); + + joinedGuild(msg); + } break; + + case CPMSG_GUILD_QUIT_RESPONSE: + { + logger->log1("Received CPMSG_GUILD_QUIT_RESPONSE"); + + if (msg.readInt8() == ERRMSG_OK) + { + // Must remove tab first, as it wont find the guild + // name after its removed from the player + int guildId = msg.readInt16(); + Guild *guild = player_node->getGuild(guildId); + if (guild) + { + Channel *channel = channelManager->findByName( + guild->getName()); + channelManager->removeChannel(channel); + player_node->removeGuild(guildId); + } + } + } break; + default: break; + } +} + +void GuildHandler::joinedGuild(Net::MessageIn &msg) +{ + std::string guildName = msg.readString(); + short guildId = msg.readInt16(); + short permissions = msg.readInt16(); + short channelId = msg.readInt16(); + std::string announcement = msg.readString(); + + // Add guild to player + Guild *guild = Guild::getGuild(guildId); + guild->setName(guildName); + guild->setRights(permissions); + player_node->addGuild(guild); + Net::getGuildHandler()->memberList(guildId); + + // Automatically create the guild channel + // COMMENT: Should this go here?? + Channel *channel = new Channel(channelId, guildName, announcement); + channelManager->addChannel(channel); + channel->getTab()->chatLog(strprintf(_("Topic: %s"), announcement.c_str()), + BY_CHANNEL); +} + +void GuildHandler::create(const std::string &name) +{ + MessageOut msg(PCMSG_GUILD_CREATE); + msg.writeString(name); + chatServerConnection->send(msg); +} + +void GuildHandler::invite(int guildId, const std::string &name) +{ + MessageOut msg(PCMSG_GUILD_INVITE); + msg.writeInt16(guildId); + msg.writeString(name); + chatServerConnection->send(msg); +} + +void GuildHandler::invite(int guildId, Being *being) +{ + invite(guildId, being->getName()); +} + +void GuildHandler::inviteResponse(int guildId _UNUSED_, bool response _UNUSED_) +{ + /*MessageOut msg(PCMSG_GUILD_ACCEPT); + msg.writeString(name); + chatServerConnection->send(msg);*/ +} + +void GuildHandler::leave(int guildId) +{ + MessageOut msg(PCMSG_GUILD_QUIT); + msg.writeInt16(guildId); + chatServerConnection->send(msg); +} + +void GuildHandler::kick(GuildMember *member _UNUSED_, + std::string reason _UNUSED_) +{ + // TODO +} + +void GuildHandler::chat(int guildId _UNUSED_, const std::string &text _UNUSED_) +{ + // TODO +} + +void GuildHandler::memberList(int guildId) +{ + MessageOut msg(PCMSG_GUILD_GET_MEMBERS); + msg.writeInt16(guildId); + chatServerConnection->send(msg); +} + +void GuildHandler::info(int guildId _UNUSED_) +{ + // TODO +} + +void GuildHandler::changeMemberPostion(GuildMember *member _UNUSED_, + int level _UNUSED_) +{ + /*MessageOut msg(PCMSG_GUILD_PROMOTE_MEMBER); + msg.writeInt16(guildId); + msg.writeString(name); + msg.writeInt8(level); + chatServerConnection->send(msg);*/ +} + +void GuildHandler::requestAlliance(int guildId _UNUSED_, + int otherGuildId _UNUSED_) +{ + // TODO +} + +void GuildHandler::requestAllianceResponse(int guildId _UNUSED_, + int otherGuildId _UNUSED_, + bool response _UNUSED_) +{ + // TODO +} + +void GuildHandler::endAlliance(int guildId _UNUSED_, int otherGuildId _UNUSED_) +{ + // TODO +} + +void GuildHandler::changeNotice(int guildId _UNUSED_, + std::string msg1 _UNUSED_, + std::string msg2 _UNUSED_) +{ + // TODO +} + +} // namespace ManaServ diff --git a/src/net/manaserv/guildhandler.h b/src/net/manaserv/guildhandler.h new file mode 100644 index 000000000..18862398b --- /dev/null +++ b/src/net/manaserv/guildhandler.h @@ -0,0 +1,84 @@ +/* + * The Mana Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_GUILDHANDLER_H +#define NET_MANASERV_GUILDHANDLER_H + +#include "net/guildhandler.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class GuildHandler : public Net::GuildHandler, public MessageHandler +{ +public: + GuildHandler(); + + bool isSupported() + { return true; } + + void handleMessage(Net::MessageIn &msg); + + void create(const std::string &name); + + void invite(int guildId, const std::string &name); + + void invite(int guidId, Being *being); + + void inviteResponse(int guidId, 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 _UNUSED_); + + void changeMemberPostion(GuildMember *member, int level); + + void requestAlliance(int guildId, int otherGuildId); + + void requestAllianceResponse(int guildId, int otherGuildId, + bool response); + + void endAlliance(int guildId, int otherGuildId); + + void changeNotice(int guildId, std::string msg1, + std::string msg2); + +protected: + void joinedGuild(Net::MessageIn &msg); +}; + +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/internal.cpp b/src/net/manaserv/internal.cpp new file mode 100644 index 000000000..fcba3fb40 --- /dev/null +++ b/src/net/manaserv/internal.cpp @@ -0,0 +1,27 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/internal.h" + +namespace ManaServ +{ + int connections = 0; +} diff --git a/src/net/manaserv/internal.h b/src/net/manaserv/internal.h new file mode 100644 index 000000000..f600c207e --- /dev/null +++ b/src/net/manaserv/internal.h @@ -0,0 +1,30 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_INTERNAL_H +#define NET_MANASERV_INTERNAL_H + +namespace ManaServ +{ + extern int connections; +} + +#endif diff --git a/src/net/manaserv/inventoryhandler.cpp b/src/net/manaserv/inventoryhandler.cpp new file mode 100644 index 000000000..1d7736f1a --- /dev/null +++ b/src/net/manaserv/inventoryhandler.cpp @@ -0,0 +1,219 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/inventoryhandler.h" + +#include "equipment.h" +#include "inventory.h" +#include "item.h" +#include "itemshortcut.h" +#include "localplayer.h" +#include "playerinfo.h" + +#include "gui/chat.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +#include "resources/iteminfo.h" + +#include "log.h" // <<< REMOVE ME! + +extern Net::InventoryHandler *inventoryHandler; + +namespace ManaServ +{ + +extern Connection *gameServerConnection; + +InventoryHandler::InventoryHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_INVENTORY_FULL, + GPMSG_INVENTORY, + GPMSG_EQUIP, + 0 + }; + handledMessages = _messages; + inventoryHandler = this; +} + +void InventoryHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_INVENTORY_FULL: + { + PlayerInfo::clearInventory(); + PlayerInfo::getEquipment()->setBackend(&mEquips); + int count = msg.readInt16(); + while (count--) + { + unsigned int slot = msg.readInt16(); + int id = msg.readInt16(); + unsigned int amount = msg.readInt16(); + PlayerInfo::setInventoryItem(slot, id, amount, 0); + } + while (msg.getUnreadLength()) + { + unsigned int slot = msg.readInt8(); + unsigned int ref = msg.readInt16(); + + mEquips.addEquipment(slot, ref); + } + } + break; + + case GPMSG_INVENTORY: + while (msg.getUnreadLength()) + { + unsigned int slot = msg.readInt16(); + int id = msg.readInt16(); + unsigned int amount = id ? msg.readInt16() : 0; + PlayerInfo::setInventoryItem(slot, id, amount, 0); + } + break; + + case GPMSG_EQUIP: + while (msg.getUnreadLength()) + { + unsigned int ref = msg.readInt16(); + int count = msg.readInt8(); + while (count--) + { + unsigned int slot = msg.readInt8(); + unsigned int used = msg.readInt8(); + + mEquips.setEquipment(slot, used, ref); + } + } + break; + default: + break; + } +} + +void InventoryHandler::equipItem(const Item *item) +{ + MessageOut msg(PGMSG_EQUIP); + msg.writeInt8(item->getInvIndex()); + gameServerConnection->send(msg); +} + +void InventoryHandler::unequipItem(const Item *item) +{ + MessageOut msg(PGMSG_UNEQUIP); + msg.writeInt8(item->getInvIndex()); + gameServerConnection->send(msg); + +/* + // Tidy equipment directly to avoid weapon still shown bug, for instance + int equipSlot = item->getInvIndex(); + logger->log("Unequipping %d", equipSlot); + mEquips.setEquipment(equipSlot, 0); +*/ +} + +void InventoryHandler::useItem(const Item *item) +{ + MessageOut msg(PGMSG_USE_ITEM); + msg.writeInt8(item->getInvIndex()); + gameServerConnection->send(msg); +} + +void InventoryHandler::dropItem(const Item *item, int amount) +{ + MessageOut msg(PGMSG_DROP); + msg.writeInt8(item->getInvIndex()); + msg.writeInt8(amount); + gameServerConnection->send(msg); +} + +bool InventoryHandler::canSplit(const Item *item) +{ + return item && !item->isEquipment() && item->getQuantity() > 1; +} + +void InventoryHandler::splitItem(const Item *item, int amount) +{ + int newIndex = PlayerInfo::getInventory()->getFreeSlot(); + if (newIndex > Inventory::NO_SLOT_INDEX) + { + MessageOut msg(PGMSG_MOVE_ITEM); + msg.writeInt8(item->getInvIndex()); + msg.writeInt8(newIndex); + msg.writeInt8(amount); + gameServerConnection->send(msg); + } +} + +void InventoryHandler::moveItem(int oldIndex, int newIndex) +{ + if (oldIndex == newIndex) + return; + + MessageOut msg(PGMSG_MOVE_ITEM); + msg.writeInt8(oldIndex); + msg.writeInt8(newIndex); + msg.writeInt8(PlayerInfo::getInventory()->getItem(oldIndex) + ->getQuantity()); + gameServerConnection->send(msg); +} + +void InventoryHandler::openStorage(int type _UNUSED_) +{ + // TODO +} + +void InventoryHandler::closeStorage(int type _UNUSED_) +{ + // TODO +} + +void InventoryHandler::moveItem(int source _UNUSED_, int slot _UNUSED_, + int amount _UNUSED_, int destination _UNUSED_) +{ + // TODO +} + +size_t InventoryHandler::getSize(int type) const +{ + switch (type) + { + case Inventory::INVENTORY: + case Inventory::TRADE: + return 50; + case Inventory::STORAGE: + return 300; + default: + return 0; + } +} + +int InventoryHandler::convertFromServerSlot(int eAthenaSlot) +{ + return eAthenaSlot; +} + +} // namespace ManaServ diff --git a/src/net/manaserv/inventoryhandler.h b/src/net/manaserv/inventoryhandler.h new file mode 100644 index 000000000..07d168ce7 --- /dev/null +++ b/src/net/manaserv/inventoryhandler.h @@ -0,0 +1,109 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_INVENTORYHANDLER_H +#define NET_MANASERV_INVENTORYHANDLER_H + +#include "equipment.h" + +#include "net/inventoryhandler.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class EquipBackend : public Equipment::Backend +{ + public: + EquipBackend() + { memset(mEquipment, 0, sizeof(mEquipment)); } + + Item *getEquipment(int index) const + { return mEquipment[index]; } + + void clear() + { + for (int i = 0; i < EQUIPMENT_SIZE; ++i) + delete mEquipment[i]; + + std::fill_n(mEquipment, EQUIPMENT_SIZE, (Item*) 0); + } + + void setEquipment(unsigned int slot, unsigned int used, int reference) + { + printf("Equip: %d at %dx%d\n", reference, slot, used); + } + + void addEquipment(unsigned int slot, int reference) + { + printf("Equip: %d at %d\n", reference, slot); + } + + private: + Item *mEquipment[EQUIPMENT_SIZE]; +}; + +class InventoryHandler : public MessageHandler, Net::InventoryHandler +{ + public: + 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); + + bool canSplit(const Item *item); + + void splitItem(const Item *item, int amount); + + void moveItem(int oldIndex, int newIndex); + + void openStorage(int type); + + void closeStorage(int type); + + void moveItem(int source, int slot, int amount, + int destination); + + size_t getSize(int type) const; + + int convertFromServerSlot(int eAthenaSlot); + + private: + EquipBackend mEquips; +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_INVENTORYHANDLER_H diff --git a/src/net/manaserv/itemhandler.cpp b/src/net/manaserv/itemhandler.cpp new file mode 100644 index 000000000..73320ecfb --- /dev/null +++ b/src/net/manaserv/itemhandler.cpp @@ -0,0 +1,90 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/itemhandler.h" + +#include "actorspritemanager.h" + +#include "net/manaserv/protocol.h" +#include "net/manaserv/messagein.h" + +#include "game.h" +#include "map.h" +#include "log.h" + +namespace ManaServ +{ + +ItemHandler::ItemHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_ITEMS, + GPMSG_ITEM_APPEAR, + 0 + }; + handledMessages = _messages; +} + +void ItemHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_ITEM_APPEAR: + case GPMSG_ITEMS: + { + while (msg.getUnreadLength()) + { + int itemId = msg.readInt16(); + int x = msg.readInt16(); + int y = msg.readInt16(); + int id = (x << 16) | y; // dummy id + + if (itemId) + { + if (Game *game = Game::instance()) + { + if (Map *map = game->getCurrentMap()) + { + actorSpriteManager->createItem(id, itemId, + x / map->getTileWidth(), + y / map->getTileHeight(), + 0); + } + else + { + logger->log( + "ItemHandler: An item wasn't created " + "because of Game/Map not initialized..."); + } + } + } + else if (FloorItem *item = actorSpriteManager->findItem(id)) + { + actorSpriteManager->destroy(item); + } + } + } break; + default: break; + } +} + +} // namespace ManaServ diff --git a/src/net/manaserv/itemhandler.h b/src/net/manaserv/itemhandler.h new file mode 100644 index 000000000..58f3695fd --- /dev/null +++ b/src/net/manaserv/itemhandler.h @@ -0,0 +1,40 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_ITEMHANDLER_H +#define NET_MANASERV_ITEMHANDLER_H + +#include "net/manaserv/messagehandler.h" + +namespace ManaServ +{ + +class ItemHandler : public MessageHandler +{ + public: + ItemHandler(); + + void handleMessage(Net::MessageIn &msg); +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_ITEMHANDLER_H diff --git a/src/net/manaserv/loginhandler.cpp b/src/net/manaserv/loginhandler.cpp new file mode 100644 index 000000000..1588d762d --- /dev/null +++ b/src/net/manaserv/loginhandler.cpp @@ -0,0 +1,479 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/loginhandler.h" + +#include "client.h" +#include "log.h" + +#include "net/logindata.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +#include "utils/gettext.h" +#include "utils/sha256.h" + +extern Net::LoginHandler *loginHandler; + +namespace ManaServ +{ + +extern Connection *accountServerConnection; +extern std::string netToken; + +LoginHandler::LoginHandler() +{ + static const Uint16 _messages[] = + { + APMSG_LOGIN_RESPONSE, + APMSG_REGISTER_RESPONSE, + APMSG_RECONNECT_RESPONSE, + APMSG_PASSWORD_CHANGE_RESPONSE, + APMSG_EMAIL_CHANGE_RESPONSE, + APMSG_LOGOUT_RESPONSE, + APMSG_UNREGISTER_RESPONSE, + APMSG_REGISTER_INFO_RESPONSE, + 0 + }; + handledMessages = _messages; + loginHandler = this; +} + +void LoginHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case APMSG_LOGIN_RESPONSE: + handleLoginResponse(msg); + break; + + case APMSG_REGISTER_RESPONSE: + handleRegisterResponse(msg); + break; + + case APMSG_RECONNECT_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful login + if (errMsg == ERRMSG_OK) + { + Client::setState(STATE_CHAR_SELECT); + } + // Login failed + else + { + switch (errMsg) + { + case ERRMSG_INVALID_ARGUMENT: + errorMessage = _("Wrong magic_token."); + break; + case ERRMSG_FAILURE: + errorMessage = _("Already logged in."); + break; + case LOGIN_BANNED: + errorMessage = _("Account banned."); + break; + default: + errorMessage = _("Unknown error."); + break; + } + Client::setState(STATE_ERROR); + } + } + break; + + case APMSG_PASSWORD_CHANGE_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful pass change + if (errMsg == ERRMSG_OK) + { + Client::setState(STATE_CHANGEPASSWORD_SUCCESS); + } + // pass change failed + else + { + switch (errMsg) + { + case ERRMSG_INVALID_ARGUMENT: + errorMessage = _("New password incorrect."); + break; + case ERRMSG_FAILURE: + errorMessage = _("Old password incorrect."); + break; + case ERRMSG_NO_LOGIN: + errorMessage = + _("Account not connected. Please login first."); + break; + default: + errorMessage = _("Unknown error."); + break; + } + Client::setState(STATE_ACCOUNTCHANGE_ERROR); + } + } + break; + + case APMSG_EMAIL_CHANGE_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful pass change + if (errMsg == ERRMSG_OK) + { + Client::setState(STATE_CHANGEEMAIL_SUCCESS); + } + // pass change failed + else + { + switch (errMsg) + { + case ERRMSG_INVALID_ARGUMENT: + errorMessage = _("New email address incorrect."); + break; + case ERRMSG_FAILURE: + errorMessage = _("Old email address incorrect."); + break; + case ERRMSG_NO_LOGIN: + errorMessage = + _("Account not connected. Please login first."); + break; + case ERRMSG_EMAIL_ALREADY_EXISTS: + errorMessage = + _("The new email address already exists."); + break; + default: + errorMessage = _("Unknown error."); + break; + } + Client::setState(STATE_ACCOUNTCHANGE_ERROR); + } + } + break; + case APMSG_LOGOUT_RESPONSE: + { + int errMsg = msg.readInt8(); + + // Successful logout + if (errMsg == ERRMSG_OK) + { + // TODO: handle logout + } + // Logout failed + else + { + switch (errMsg) + { + case ERRMSG_NO_LOGIN: + errorMessage = "Accountserver: Not logged in"; + break; + default: + errorMessage = "Accountserver: Unknown error"; + break; + } + Client::setState(STATE_ERROR); + } + } + break; + case APMSG_UNREGISTER_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful unregistration + if (errMsg == ERRMSG_OK) + { + Client::setState(STATE_UNREGISTER); + } + // Unregistration failed + else + { + switch (errMsg) + { + case ERRMSG_INVALID_ARGUMENT: + errorMessage = + "Accountserver: Wrong username or password"; + break; + default: + errorMessage = "Accountserver: Unknown error"; + break; + } + Client::setState(STATE_ACCOUNTCHANGE_ERROR); + } + } + break; + + case APMSG_REGISTER_INFO_RESPONSE: + { + int allowed = msg.readInt8(); + + if (allowed) + { + mMinUserNameLength = msg.readInt8(); + mMaxUserNameLength = msg.readInt8(); + std::string captchaURL = msg.readString(); + std::string captchaInstructions = msg.readString(); + + printf("%s: %s\n", captchaURL.c_str(), captchaInstructions.c_str()); + + Client::setState(STATE_REGISTER); + } + else + { + errorMessage = msg.readString(); + + if (errorMessage.empty()) + errorMessage = _("Client registration is not allowed. " + "Please contact server administration."); + Client::setState(STATE_LOGIN_ERROR); + } + } + break; + default: + break; + } +} + +void LoginHandler::handleLoginResponse(Net::MessageIn &msg) +{ + const int errMsg = msg.readInt8(); + + if (errMsg == ERRMSG_OK) + { + readServerInfo(msg); + // No worlds atm, but future use :-D + Client::setState(STATE_WORLD_SELECT); + } + else + { + switch (errMsg) + { + case LOGIN_INVALID_VERSION: + errorMessage = _("Client version is too old."); + break; + case ERRMSG_INVALID_ARGUMENT: + errorMessage = _("Wrong username or password."); + break; + case ERRMSG_FAILURE: + errorMessage = _("Already logged in."); + break; + case LOGIN_BANNED: + errorMessage = _("Account banned"); + break; + case LOGIN_INVALID_TIME: + errorMessage = _("Login attempt too soon after previous " + "attempt."); + break; + default: + errorMessage = _("Unknown error."); + break; + } + Client::setState(STATE_LOGIN_ERROR); + } +} + +void LoginHandler::handleRegisterResponse(Net::MessageIn &msg) +{ + const int errMsg = msg.readInt8(); + + if (errMsg == ERRMSG_OK) + { + readServerInfo(msg); + Client::setState(STATE_WORLD_SELECT); + } + else + { + switch (errMsg) + { + case REGISTER_INVALID_VERSION: + errorMessage = _("Client version is too old."); + break; + case ERRMSG_INVALID_ARGUMENT: + errorMessage = _("Wrong username, password or email address."); + break; + case REGISTER_EXISTS_USERNAME: + errorMessage = _("Username already exists."); + break; + case REGISTER_EXISTS_EMAIL: + errorMessage = _("Email address already exists."); + break; + case REGISTER_CAPTCHA_WRONG: + errorMessage = _("You took too long with the captcha or your " + "response was incorrect."); + break; + default: + errorMessage = _("Unknown error."); + break; + } + Client::setState(STATE_LOGIN_ERROR); + } +} + +void LoginHandler::readServerInfo(Net::MessageIn &msg) +{ + // Safety check for outdated manaserv versions (remove me later) + if (msg.getUnreadLength() == 0) + return; + + // Set the update host when included in the message + const std::string updateHost = msg.readString(); + if (!updateHost.empty()) + mLoginData->updateHost = updateHost; + else + logger->log1("Warning: server does not have an update host set!"); + + // Read the client data folder for dynamic data loading. + // This is only used by the QT client. + msg.readString(); + + // Read the number of character slots + mLoginData->characterSlots = msg.readInt8(); +} + +void LoginHandler::connect() +{ + accountServerConnection->connect(mServer.hostname, mServer.port); +} + +bool LoginHandler::isConnected() +{ + return accountServerConnection->isConnected(); +} + +void LoginHandler::disconnect() +{ + accountServerConnection->disconnect(); + + if (Client::getState() == STATE_CONNECT_GAME) + { + Client::setState(STATE_GAME); + } +} + +bool LoginHandler::isRegistrationEnabled() +{ + return true; +} + +void LoginHandler::getRegistrationDetails() +{ + MessageOut msg(PAMSG_REQUEST_REGISTER_INFO); + accountServerConnection->send(msg); +} + +unsigned int LoginHandler::getMinUserNameLength() const +{ + return mMinUserNameLength; +} + +unsigned int LoginHandler::getMaxUserNameLength() const +{ + return mMaxUserNameLength; +} + +void LoginHandler::loginAccount(LoginData *loginData) +{ + mLoginData = loginData; + + MessageOut msg(PAMSG_LOGIN); + + msg.writeInt32(0); // client version + msg.writeString(loginData->username); + msg.writeString(sha256(loginData->username + loginData->password)); + + accountServerConnection->send(msg); +} + +void LoginHandler::logout() +{ + MessageOut msg(PAMSG_LOGOUT); + accountServerConnection->send(msg); +} + +void LoginHandler::changeEmail(const std::string &email) +{ + MessageOut msg(PAMSG_EMAIL_CHANGE); + + // Email is sent clearly so the server can validate the data. + // Encryption is assumed server-side. + msg.writeString(email); + + accountServerConnection->send(msg); +} + +void LoginHandler::changePassword(const std::string &username, + const std::string &oldPassword, + const std::string &newPassword) +{ + MessageOut msg(PAMSG_PASSWORD_CHANGE); + + // Change password using SHA2 encryption + msg.writeString(sha256(username + oldPassword)); + msg.writeString(sha256(username + newPassword)); + + accountServerConnection->send(msg); +} + +void LoginHandler::chooseServer(unsigned int server _UNUSED_) +{ + // TODO +} + +void LoginHandler::registerAccount(LoginData *loginData) +{ + mLoginData = loginData; + + MessageOut msg(PAMSG_REGISTER); + + msg.writeInt32(0); // client version + msg.writeString(loginData->username); + // Use a hashed password for privacy reasons + msg.writeString(sha256(loginData->username + loginData->password)); + msg.writeString(loginData->email); + msg.writeString(loginData->captchaResponse); + + accountServerConnection->send(msg); +} + +void LoginHandler::unregisterAccount(const std::string &username, + const std::string &password) +{ + MessageOut msg(PAMSG_UNREGISTER); + + msg.writeString(username); + msg.writeString(sha256(username + password)); + + accountServerConnection->send(msg); +} + +Worlds LoginHandler::getWorlds() const +{ + return Worlds(); +} + +void LoginHandler::reconnect() +{ + MessageOut msg(PAMSG_RECONNECT); + msg.writeString(netToken, 32); + accountServerConnection->send(msg); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/loginhandler.h b/src/net/manaserv/loginhandler.h new file mode 100644 index 000000000..1c47184a7 --- /dev/null +++ b/src/net/manaserv/loginhandler.h @@ -0,0 +1,99 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_LOGINHANDLER_H +#define NET_MANASERV_LOGINHANDLER_H + +#include "net/loginhandler.h" +#include "net/serverinfo.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +class LoginData; + +namespace ManaServ +{ + +class LoginHandler : public MessageHandler, public Net::LoginHandler +{ + public: + LoginHandler(); + + void handleMessage(Net::MessageIn &msg); + + void connect(); + + bool isConnected(); + + void disconnect(); + + int supportedOptionalActions() const + { return Unregister | ChangeEmail | SetEmailOnRegister; } + + bool isRegistrationEnabled(); + + void getRegistrationDetails(); + + unsigned int getMinUserNameLength() const; + + unsigned int getMaxUserNameLength() const; + + void loginAccount(LoginData *loginData); + + void logout(); + + void changeEmail(const std::string &email); + + void changePassword(const std::string &username, + const std::string &oldPassword, + const std::string &newPassword); + + void chooseServer(unsigned int server); + + void registerAccount(LoginData *loginData); + + void unregisterAccount(const std::string &username, + const std::string &password); + + Worlds getWorlds() const; + + void reconnect(); + + private: + void handleLoginResponse(Net::MessageIn &msg); + void handleRegisterResponse(Net::MessageIn &msg); + + void readServerInfo(Net::MessageIn &msg); + + LoginData *mLoginData; + unsigned int mMinUserNameLength; + unsigned int mMaxUserNameLength; +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_LOGINHANDLER_H diff --git a/src/net/manaserv/messagehandler.cpp b/src/net/manaserv/messagehandler.cpp new file mode 100644 index 000000000..2f4d53dc6 --- /dev/null +++ b/src/net/manaserv/messagehandler.cpp @@ -0,0 +1,36 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/messagehandler.h" + +#include "net/manaserv/network.h" + +#include <cassert> + +namespace ManaServ +{ + +MessageHandler::~MessageHandler() +{ + unregisterHandler(this); +} + +} diff --git a/src/net/manaserv/messagehandler.h b/src/net/manaserv/messagehandler.h new file mode 100644 index 000000000..2181438f3 --- /dev/null +++ b/src/net/manaserv/messagehandler.h @@ -0,0 +1,44 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_MESSAGEHANDLER_H +#define NET_MANASERV_MESSAGEHANDLER_H + +#include "net/messagehandler.h" + +namespace ManaServ +{ + +/** + * \ingroup Network + */ +class MessageHandler : public Net::MessageHandler +{ + public: + ~MessageHandler(); + +}; + +typedef const std::auto_ptr<MessageHandler> MessageHandlerPtr; + +} + +#endif // NET_MANASERV_MESSAGEHANDLER_H diff --git a/src/net/manaserv/messagein.cpp b/src/net/manaserv/messagein.cpp new file mode 100644 index 000000000..458aae740 --- /dev/null +++ b/src/net/manaserv/messagein.cpp @@ -0,0 +1,62 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/messagein.h" + +#include "enet/enet.h" + +namespace ManaServ +{ + +MessageIn::MessageIn(const char *data, unsigned int length): + Net::MessageIn(data, length) +{ + // Read the message ID + mId = readInt16(); +} + +Sint16 MessageIn::readInt16() +{ + Sint16 value = -1; + if (mPos + 2 <= mLength) + { + uint16_t t; + memcpy(&t, mData + mPos, 2); + value = (unsigned short) ENET_NET_TO_HOST_16(t); + } + mPos += 2; + return value; +} + +int MessageIn::readInt32() +{ + int value = -1; + if (mPos + 4 <= mLength) + { + uint32_t t; + memcpy(&t, mData + mPos, 4); + value = ENET_NET_TO_HOST_32(t); + } + mPos += 4; + return value; +} + +} diff --git a/src/net/manaserv/messagein.h b/src/net/manaserv/messagein.h new file mode 100644 index 000000000..e3528e5bf --- /dev/null +++ b/src/net/manaserv/messagein.h @@ -0,0 +1,49 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_MESSAGEIN_H +#define NET_MANASERV_MESSAGEIN_H + +#include "net/messagein.h" + +namespace ManaServ +{ + +/** + * Used for parsing an incoming message. + * + * \ingroup Network + */ +class MessageIn : public Net::MessageIn +{ + public: + /** + * Constructor. + */ + MessageIn(const char *data, unsigned int length); + + Sint16 readInt16(); /**< Reads a short. */ + int readInt32(); /**< Reads a long. */ +}; + +} + +#endif // NET_MANASERV_MESSAGEIN_H diff --git a/src/net/manaserv/messageout.cpp b/src/net/manaserv/messageout.cpp new file mode 100644 index 000000000..f4856e640 --- /dev/null +++ b/src/net/manaserv/messageout.cpp @@ -0,0 +1,65 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/messageout.h" + +#include "enet/enet.h" + +#include <cstring> +#include <string> + +namespace ManaServ +{ + +MessageOut::MessageOut(short id): + Net::MessageOut(id) +{ + writeInt16(id); +} + +MessageOut::~MessageOut() +{ + free(mData); +} + +void MessageOut::expand(size_t bytes) +{ + mData = (char*)realloc(mData, mPos + bytes); + mDataSize = mPos + bytes; +} + +void MessageOut::writeInt16(Sint16 value) +{ + expand(2); + uint16_t t = ENET_HOST_TO_NET_16(value); + memcpy(mData + mPos, &t, 2); + mPos += 2; +} + +void MessageOut::writeInt32(Sint32 value) +{ + expand(4); + uint32_t t = ENET_HOST_TO_NET_32(value); + memcpy(mData + mPos, &t, 4); + mPos += 4; +} + +} // namespace ManaServ diff --git a/src/net/manaserv/messageout.h b/src/net/manaserv/messageout.h new file mode 100644 index 000000000..00ae069de --- /dev/null +++ b/src/net/manaserv/messageout.h @@ -0,0 +1,59 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_MESSAGEOUT_H +#define NET_MANASERV_MESSAGEOUT_H + +#include "net/messageout.h" + +namespace ManaServ +{ + +class MessageOut : public Net::MessageOut +{ + public: + /** + * Constructor. + */ + MessageOut(short id); + + /** + * Destructor. + */ + ~MessageOut(); + + void writeInt16(Sint16 value); /**< Writes a short. */ + void writeInt32(Sint32 value); /**< Writes a long. */ + + protected: + /** + * Expand the packet data to be able to hold more data. + * + * NOTE: For performance enhancements this method could allocate extra + * memory in advance instead of expanding size every time more data is + * added. + */ + void expand(size_t size); +}; + +} + +#endif // NET_MANASERV_MESSAGEOUT_H diff --git a/src/net/manaserv/network.cpp b/src/net/manaserv/network.cpp new file mode 100644 index 000000000..c8389bb82 --- /dev/null +++ b/src/net/manaserv/network.cpp @@ -0,0 +1,178 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/network.h" + +#include "log.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/internal.h" +#include "net/manaserv/messagehandler.h" +#include "net/manaserv/messagein.h" + +#include "enet/enet.h" + +#include <map> + +/** + * The local host which is shared for all outgoing connections. + */ +namespace +{ + ENetHost *client; +} + +namespace ManaServ +{ + +typedef std::map<unsigned short, MessageHandler*> MessageHandlers; +typedef MessageHandlers::iterator MessageHandlerIterator; +static MessageHandlers mMessageHandlers; + +void initialize() +{ + if (enet_initialize()) + { + logger->error("Failed to initialize ENet."); + } + +#if defined(ENET_VERSION) && ENET_VERSION >= ENET_CUTOFF + client = enet_host_create(NULL, 3, 0, 0, 0); +#else + client = enet_host_create(NULL, 3, 0, 0); +#endif + + if (!client) + { + logger->error("Failed to create the local host."); + } +} + +void finalize() +{ + if (!client) + return; // Wasn't initialized at all + + if (connections) + { + logger->error("Tried to shutdown the network subsystem while there " + "are network connections left!"); + } + + clearNetworkHandlers(); + enet_deinitialize(); +} + +Connection *getConnection() +{ + if (!client) + { + logger->error("Tried to instantiate a network object before " + "initializing the network subsystem!"); + } + + return new Connection(client); +} + +void registerHandler(MessageHandler *handler) +{ + for (const Uint16 *i = handler->handledMessages; *i; i++) + { + mMessageHandlers[*i] = handler; + } +} + +void unregisterHandler(MessageHandler *handler) +{ + for (const Uint16 *i = handler->handledMessages; *i; i++) + { + mMessageHandlers.erase(*i); + } +} + +void clearNetworkHandlers() +{ + mMessageHandlers.clear(); +} + + +/** + * Dispatches a message to the appropriate message handler and + * destroys it afterwards. + */ +namespace +{ + void dispatchMessage(ENetPacket *packet) + { + MessageIn msg((const char *)packet->data, packet->dataLength); + + MessageHandlerIterator iter = mMessageHandlers.find(msg.getId()); + + if (iter != mMessageHandlers.end()) + { + //logger->log("Received packet %x (%i B)", + // msg.getId(), msg.getLength()); + iter->second->handleMessage(msg); + } + else + { + logger->log("Unhandled packet %x (%i B)", + msg.getId(), msg.getLength()); + } + + // Clean up the packet now that we're done using it. + enet_packet_destroy(packet); + } +} + +void flush() +{ + ENetEvent event; + + // Check if there are any new events + while (enet_host_service(client, &event, 0) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + logger->log("Connected to port %d.", event.peer->address.port); + // Store any relevant server information here. + event.peer->data = 0; + break; + + case ENET_EVENT_TYPE_RECEIVE: + dispatchMessage(event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + logger->log1("Disconnected."); + // Reset the server information. + event.peer->data = 0; + break; + + case ENET_EVENT_TYPE_NONE: + default: + break; + } + } +} + +} diff --git a/src/net/manaserv/network.h b/src/net/manaserv/network.h new file mode 100644 index 000000000..149f484e5 --- /dev/null +++ b/src/net/manaserv/network.h @@ -0,0 +1,75 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_NETWORK_H +#define NET_MANASERV_NETWORK_H + +#include <iosfwd> + +/** + * \ingroup Network + */ +namespace ManaServ +{ + class MessageHandler; + class MessageOut; + + class Connection; + + /** + * Initializes the network subsystem. + */ + void initialize(); + + /** + * Finalizes the network subsystem. + */ + void finalize(); + + /** + * Returns a new Connection object. Should be deleted by the caller. + */ + Connection *getConnection(); + + /** + * Registers a message handler. A message handler handles a certain + * subset of incoming messages. + */ + void registerHandler(MessageHandler *handler); + + /** + * Unregisters a message handler. + */ + void unregisterHandler(MessageHandler *handler); + + /** + * Clears all registered message handlers. + */ + void clearNetworkHandlers(); + + /* + * Handles all events and dispatches incoming messages to the + * registered handlers + */ + void flush(); +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/npchandler.cpp b/src/net/manaserv/npchandler.cpp new file mode 100644 index 000000000..6d4010e83 --- /dev/null +++ b/src/net/manaserv/npchandler.cpp @@ -0,0 +1,237 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/npchandler.h" + +#include "actorspritemanager.h" + +#include "gui/npcdialog.h" +#include "gui/npcpostdialog.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +extern Net::NpcHandler *npcHandler; + +namespace ManaServ +{ + +extern Connection *gameServerConnection; + +NpcHandler::NpcHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_NPC_CHOICE, + GPMSG_NPC_POST, + GPMSG_NPC_MESSAGE, + GPMSG_NPC_ERROR, + GPMSG_NPC_CLOSE, + GPMSG_NPC_NUMBER, + GPMSG_NPC_STRING, + 0 + }; + handledMessages = _messages; + npcHandler = this; +} + +void NpcHandler::handleMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being || being->getType() != ActorSprite::NPC) + { + return; + } + + int npcId = being->getId(); + NpcDialogs::iterator diag = mNpcDialogs.find(npcId); + NpcDialog *dialog; + + if (diag == mNpcDialogs.end()) + { + if (msg.getId() == GPMSG_NPC_ERROR || msg.getId() == GPMSG_NPC_CLOSE) + return; // Dialog is pointless in these cases + + dialog = new NpcDialog(npcId); + Wrapper wrap; + wrap.dialog = dialog; + mNpcDialogs[npcId] = wrap; + } + else + { + dialog = diag->second.dialog; + } + + switch (msg.getId()) + { + case GPMSG_NPC_CHOICE: + dialog->choiceRequest(); + while (msg.getUnreadLength()) + { + dialog->addChoice(msg.readString()); + } + break; + + case GPMSG_NPC_NUMBER: + { + int min_num = msg.readInt32(); + int max_num = msg.readInt32(); + dialog->integerRequest(msg.readInt32(), min_num, max_num); + break; + } + + case GPMSG_NPC_STRING: + dialog->textRequest(""); + break; + + case GPMSG_NPC_POST: + { + new NpcPostDialog(npcId); + break; + } + + case GPMSG_NPC_ERROR: + dialog->close(); + if (diag != mNpcDialogs.end()) + { + mNpcDialogs.erase(diag); + } + break; + + case GPMSG_NPC_MESSAGE: + dialog->addText(msg.readString(msg.getUnreadLength())); + dialog->showNextButton(); + break; + + case GPMSG_NPC_CLOSE: + dialog->showCloseButton(); + break; + + default: + break; + } +} + +void NpcHandler::talk(int npcId) +{ + MessageOut msg(PGMSG_NPC_TALK); + msg.writeInt16(npcId); + gameServerConnection->send(msg); +} + +void NpcHandler::nextDialog(int npcId) +{ + MessageOut msg(PGMSG_NPC_TALK_NEXT); + msg.writeInt16(npcId); + gameServerConnection->send(msg); +} + +void NpcHandler::closeDialog(int npcId) +{ + MessageOut msg(PGMSG_NPC_TALK_NEXT); + msg.writeInt16(npcId); + gameServerConnection->send(msg); + + NpcDialogs::iterator it = mNpcDialogs.find(npcId); + if (it != mNpcDialogs.end()) + { + (*it).second.dialog->close(); + mNpcDialogs.erase(it); + } +} + +void NpcHandler::listInput(int npcId, unsigned char value) +{ + MessageOut msg(PGMSG_NPC_SELECT); + msg.writeInt16(npcId); + msg.writeInt8(value); + gameServerConnection->send(msg); +} + +void NpcHandler::integerInput(int npcId, int value) +{ + MessageOut msg(PGMSG_NPC_NUMBER); + msg.writeInt16(npcId); + msg.writeInt32(value); + gameServerConnection->send(msg); +} + +void NpcHandler::stringInput(int npcId, const std::string &value) +{ + MessageOut msg(PGMSG_NPC_STRING); + msg.writeInt16(npcId); + msg.writeString(value); + gameServerConnection->send(msg); +} + +void NpcHandler::sendLetter(int npcId _UNUSED_, const std::string &recipient, + const std::string &text) +{ + MessageOut msg(PGMSG_NPC_POST_SEND); + msg.writeString(recipient); + msg.writeString(text); + gameServerConnection->send(msg); +} + +void NpcHandler::startShopping(int beingId _UNUSED_) +{ + // TODO +} + +void NpcHandler::buy(int beingId _UNUSED_) +{ + // TODO +} + +void NpcHandler::sell(int beingId _UNUSED_) +{ + // TODO +} + +void NpcHandler::buyItem(int beingId _UNUSED_, int itemId, int amount) +{ + MessageOut msg(PGMSG_NPC_BUYSELL); + msg.writeInt16(itemId); + msg.writeInt16(amount); + gameServerConnection->send(msg); +} + +void NpcHandler::sellItem(int beingId _UNUSED_, int itemId, int amount) +{ + MessageOut msg(PGMSG_NPC_BUYSELL); + msg.writeInt16(itemId); + msg.writeInt16(amount); + gameServerConnection->send(msg); +} + +void NpcHandler::endShopping(int beingId _UNUSED_) +{ + // TODO +} + +void NpcHandler::clearDialogs() +{ + mNpcDialogs.clear(); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/npchandler.h b/src/net/manaserv/npchandler.h new file mode 100644 index 000000000..5f47ee388 --- /dev/null +++ b/src/net/manaserv/npchandler.h @@ -0,0 +1,89 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_NPCHANDLER_H +#define NET_MANASERV_NPCHANDLER_H + +#include "net/npchandler.h" + +#include "net/manaserv/messagehandler.h" + +#include <map> + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +class NpcDialog; + +namespace ManaServ +{ + +class NpcHandler : public MessageHandler, public Net::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 sendLetter(int npcId, const std::string &recipient, + const std::string &text); + + void startShopping(int beingId); + + void buy(int beingId); + + void sell(int beingId); + + void buyItem(int beingId, int itemId, int amount); + + void sellItem(int beingId, int itemId, int amount); + + void endShopping(int beingId); + + void clearDialogs(); + + private: + typedef struct + { + NpcDialog* dialog; + } Wrapper; + typedef std::map<int, Wrapper> NpcDialogs; + NpcDialogs mNpcDialogs; +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_NPCHANDLER_H diff --git a/src/net/manaserv/partyhandler.cpp b/src/net/manaserv/partyhandler.cpp new file mode 100644 index 000000000..3aec20b6b --- /dev/null +++ b/src/net/manaserv/partyhandler.cpp @@ -0,0 +1,197 @@ +/* + * The Mana Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/partyhandler.h" + +#include "event.h" +#include "log.h" +#include "localplayer.h" + +#include "gui/socialwindow.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include <iostream> + +#define PARTY_ID 1 + +extern Net::PartyHandler *partyHandler; + +namespace ManaServ +{ + +extern Connection *chatServerConnection; + +PartyHandler::PartyHandler(): + mParty(Party::getParty(PARTY_ID)) +{ + static const Uint16 _messages[] = + { + CPMSG_PARTY_INVITE_RESPONSE, + CPMSG_PARTY_INVITED, + CPMSG_PARTY_ACCEPT_INVITE_RESPONSE, + CPMSG_PARTY_QUIT_RESPONSE, + CPMSG_PARTY_NEW_MEMBER, + CPMSG_PARTY_MEMBER_LEFT, + CPMSG_PARTY_REJECTED, + 0 + }; + handledMessages = _messages; + partyHandler = this; +} + +void PartyHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case CPMSG_PARTY_INVITE_RESPONSE: + { + if (msg.readInt8() == ERRMSG_OK) + { + + } + } break; + + case CPMSG_PARTY_INVITED: + { + socialWindow->showPartyInvite(msg.readString()); + } break; + + case CPMSG_PARTY_ACCEPT_INVITE_RESPONSE: + { + if (msg.readInt8() == ERRMSG_OK) + { + // + SERVER_NOTICE(_("Joined party.")); + } + } + + case CPMSG_PARTY_QUIT_RESPONSE: + { + if (msg.readInt8() == ERRMSG_OK) + { + mParty->clearMembers(); + player_node->setParty(NULL); + } + } break; + + case CPMSG_PARTY_NEW_MEMBER: + { + int id = msg.readInt16(); // being id + std::string name = msg.readString(); + + SERVER_NOTICE(strprintf(_("%s joined the party."), + name.c_str())); + + if (id == player_node->getId()) + player_node->setParty(mParty); + + mParty->addMember(id, name); + } break; + + case CPMSG_PARTY_MEMBER_LEFT: + { + mParty->removeMember(msg.readString()); + } break; + + case CPMSG_PARTY_REJECTED: + { + std::string name = msg.readString(); + SERVER_NOTICE(strprintf( + _("%s rejected your invite."), name.c_str())); + } break; + default: + break; + } +} + +void PartyHandler::create(const std::string &name _UNUSED_) +{ + // TODO +} + +void PartyHandler::join(int partyId _UNUSED_) +{ + // TODO +} + +void PartyHandler::invite(Being *being) +{ + invite(being->getName()); +} + +void PartyHandler::invite(const std::string &name) +{ + MessageOut msg(PCMSG_PARTY_INVITE); + + msg.writeString(name); + + chatServerConnection->send(msg); +} + +void PartyHandler::inviteResponse(const std::string &inviter, bool accept) +{ + MessageOut msg = MessageOut(accept ? PCMSG_PARTY_ACCEPT_INVITE : + PCMSG_PARTY_REJECT_INVITE); + + msg.writeString(inviter); + + chatServerConnection->send(msg); +} + +void PartyHandler::leave() +{ + MessageOut msg(PCMSG_PARTY_QUIT); + + chatServerConnection->send(msg); +} + +void PartyHandler::kick(Being *being _UNUSED_) +{ + // TODO +} + +void PartyHandler::kick(const std::string &name _UNUSED_) +{ + // TODO +} + +void PartyHandler::chat(const std::string &text _UNUSED_) +{ + // TODO +} + +void PartyHandler::requestPartyMembers() +{ + //MessageOut msg(PCMSG_GUILD_GET_MEMBERS); + + //msg.writeInt16(guildId); + + //chatServerConnection->send(msg); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/partyhandler.h b/src/net/manaserv/partyhandler.h new file mode 100644 index 000000000..b2c8ce49e --- /dev/null +++ b/src/net/manaserv/partyhandler.h @@ -0,0 +1,82 @@ +/* + * The Mana Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_PARTYHANDLER_H +#define NET_MANASERV_PARTYHANDLER_H + +#include "net/partyhandler.h" + +#include "net/manaserv/messagehandler.h" + +#include "party.h" + +#include <string> + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class PartyHandler : public MessageHandler, public Net::PartyHandler +{ +public: + PartyHandler(); + + void handleMessage(Net::MessageIn &msg); + + void create(const std::string &name = ""); + + void join(int partyId); + + 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 requestPartyMembers(); + + PartyShare getShareExperience() { return PARTY_SHARE_NO; } + + void setShareExperience(PartyShare share _UNUSED_) {} + + PartyShare getShareItems() { return PARTY_SHARE_NO; } + + void setShareItems(PartyShare share _UNUSED_) {} +private: + Party *mParty; +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_PARTYHANDLER_H diff --git a/src/net/manaserv/playerhandler.cpp b/src/net/manaserv/playerhandler.cpp new file mode 100644 index 000000000..3af82486c --- /dev/null +++ b/src/net/manaserv/playerhandler.cpp @@ -0,0 +1,440 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/playerhandler.h" +#include "net/manaserv/beinghandler.h" + +#include "client.h" +#include "effectmanager.h" +#include "game.h" +#include "localplayer.h" +#include "log.h" +#include "particle.h" +#include "playerinfo.h" +#include "configuration.h" + +#include "gui/chat.h" +#include "gui/gui.h" +#include "gui/okdialog.h" +#include "gui/viewport.h" + +#include "net/net.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/defines.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/npchandler.h" +#include "net/manaserv/protocol.h" +#include "net/manaserv/attributes.h" + +/** + * Max. distance we are willing to scroll after a teleport; + * everything beyond will reset the port hard. + * 32 is the nominal tile width/height. + * @todo: Make this parameter read from config. + */ +static const int MAP_TELEPORT_SCROLL_DISTANCE = 8 * 32; + +extern Net::PlayerHandler *playerHandler; + +namespace ManaServ +{ + +void RespawnRequestListener::action(const gcn::ActionEvent &event _UNUSED_) +{ + Net::getPlayerHandler()->respawn(); + + ManaServ::NpcHandler *handler = + static_cast<ManaServ::NpcHandler*>(Net::getNpcHandler()); + handler->clearDialogs(); +} + +extern Connection *gameServerConnection; + +PlayerHandler::PlayerHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_PLAYER_MAP_CHANGE, + GPMSG_PLAYER_SERVER_CHANGE, + GPMSG_PLAYER_ATTRIBUTE_CHANGE, + GPMSG_PLAYER_EXP_CHANGE, + GPMSG_LEVELUP, + GPMSG_LEVEL_PROGRESS, + GPMSG_RAISE_ATTRIBUTE_RESPONSE, + GPMSG_LOWER_ATTRIBUTE_RESPONSE, + GPMSG_SPECIAL_STATUS, + 0 + }; + handledMessages = _messages; + playerHandler = this; +} + +void PlayerHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_PLAYER_MAP_CHANGE: + handleMapChangeMessage(msg); + break; + + case GPMSG_PLAYER_SERVER_CHANGE: + { // TODO: Implement reconnecting to another game server + std::string token = msg.readString(32); + std::string address = msg.readString(); + int port = msg.readInt16(); + logger->log("Changing server to %s:%d", address.c_str(), port); + } break; + + case GPMSG_PLAYER_ATTRIBUTE_CHANGE: + { + logger->log1("ATTRIBUTE UPDATE:"); + while (msg.getUnreadLength()) + { + int attrId = msg.readInt16(); + double base = msg.readInt32() / 256.0; + double value = msg.readInt32() / 256.0; + + // Set the core player attribute the stat + // depending on attribute link. + int playerInfoId = + Attributes::getPlayerInfoIdFromAttrId(attrId); + if (playerInfoId > -1) + { + PlayerInfo::setAttribute(playerInfoId, value); + } + else + { + PlayerInfo::setStatBase(attrId, base); + PlayerInfo::setStatMod(attrId, value - base); + } + } + } break; + + case GPMSG_PLAYER_EXP_CHANGE: + { + logger->log1("EXP Update"); + while (msg.getUnreadLength()) + { + int skill = msg.readInt16(); + int current = msg.readInt32(); + int next = msg.readInt32(); + + PlayerInfo::setStatExperience(skill, current, next); + } + } break; + + case GPMSG_LEVELUP: + { + PlayerInfo::setAttribute(LEVEL, msg.readInt16()); + PlayerInfo::setAttribute(CHAR_POINTS, msg.readInt16()); + PlayerInfo::setAttribute(CORR_POINTS, msg.readInt16()); + Particle* effect = particleEngine->addEffect( + paths.getStringValue("particles") + + paths.getStringValue("levelUpEffectFile") + , 0, 0); + player_node->controlParticle(effect); + } break; + + + case GPMSG_LEVEL_PROGRESS: + { + PlayerInfo::setAttribute(EXP, msg.readInt8()); + } break; + + + case GPMSG_RAISE_ATTRIBUTE_RESPONSE: + { + int errCode = msg.readInt8(); + int attrNum = msg.readInt16(); + switch (errCode) + { + case ATTRIBMOD_OK: + { + // feel(acknowledgment); + } break; + case ATTRIBMOD_INVALID_ATTRIBUTE: + { + logger->log("Warning: Server denied increase of attribute" + " %d (unknown attribute) ", attrNum); + } break; + case ATTRIBMOD_NO_POINTS_LEFT: + { + // when the server says "you got no points" it + // has to be correct. The server is always right! + // undo attribute change and set points to 0 + logger->log("Warning: Server denied increase of attribute" + " %d (no points left) ", attrNum); + int attrValue = PlayerInfo::getStatBase(attrNum) - 1; + PlayerInfo::setAttribute(CHAR_POINTS, 0); + PlayerInfo::setStatBase(attrNum, attrValue); + } break; + case ATTRIBMOD_DENIED: + { + // undo attribute change + logger->log("Warning: Server denied increase of attribute" + " %d (reason unknown) ", attrNum); + int points = PlayerInfo::getAttribute(CHAR_POINTS) - 1; + PlayerInfo::setAttribute(CHAR_POINTS, points); + + int attrValue = PlayerInfo::getStatBase(attrNum) - 1; + PlayerInfo::setStatBase(attrNum, attrValue); + } break; + default: + break; + } + } break; + + case GPMSG_LOWER_ATTRIBUTE_RESPONSE: + { + int errCode = msg.readInt8(); + int attrNum = msg.readInt16(); + switch (errCode) + { + case ATTRIBMOD_OK: + { + // feel(acknowledgment); + } break; + case ATTRIBMOD_INVALID_ATTRIBUTE: + { + logger->log("Warning: Server denied reduction of attribute" + " %d (unknown attribute) ", attrNum); + } break; + case ATTRIBMOD_NO_POINTS_LEFT: + { + // when the server says "you got no points" it + // has to be correct. The server is always right! + // undo attribute change and set points to 0 + logger->log("Warning: Server denied reduction of attribute" + " %d (no points left) ", attrNum); + int attrValue = PlayerInfo::getStatBase(attrNum) + 1; + PlayerInfo::setAttribute(CHAR_POINTS, 0); + PlayerInfo::setAttribute(CORR_POINTS, 0); + PlayerInfo::setStatBase(attrNum, attrValue); + break; + } break; + case ATTRIBMOD_DENIED: + { + // undo attribute change + logger->log("Warning: Server denied reduction of attribute" + " %d (reason unknown) ", attrNum); + int charaPoints = PlayerInfo::getAttribute( + CHAR_POINTS) - 1; + + PlayerInfo::setAttribute(CHAR_POINTS, charaPoints); + + int correctPoints = PlayerInfo::getAttribute(CORR_POINTS) + + 1; + + PlayerInfo::setAttribute(CORR_POINTS, correctPoints); + + int attrValue = PlayerInfo::getStatBase(attrNum) + 1; + PlayerInfo::setStatBase(attrNum, attrValue); + } break; + default: + break; + } + + } break; + + case GPMSG_SPECIAL_STATUS : + { + while (msg.getUnreadLength()) + { + // { B specialID, L current, L max, L recharge } + int id = msg.readInt8(); + int current = msg.readInt32(); + int max = msg.readInt32(); + int recharge = msg.readInt32(); + PlayerInfo::setSpecialStatus(id, current, max, recharge); + } + } break; + /* + case SMSG_PLAYER_ARROW_MESSAGE: + { + Sint16 type = msg.readInt16(); + + switch (type) + { + case 0: + localChatTab->chatLog(_("Equip arrows first."), + BY_SERVER); + break; + default: + logger->log("0x013b: Unhandled message %i", type); + break; + } + } + break; + */ + default: + break; + } +} + +void PlayerHandler::handleMapChangeMessage(Net::MessageIn &msg) +{ + const std::string mapName = msg.readString(); + const unsigned short x = msg.readInt16(); + const unsigned short y = msg.readInt16(); + + Game *game = Game::instance(); + const bool sameMap = (game->getCurrentMapName() == mapName); + + logger->log("Changing map to %s (%d, %d)", mapName.c_str(), x, y); + + // Switch the actual map, deleting the previous one + game->changeMap(mapName); + + const Vector &playerPos = player_node->getPosition(); + float scrollOffsetX = 0.0f; + float scrollOffsetY = 0.0f; + + /* Scroll if neccessary */ + if (!sameMap + || (abs(x - (int) playerPos.x) > MAP_TELEPORT_SCROLL_DISTANCE) + || (abs(y - (int) playerPos.y) > MAP_TELEPORT_SCROLL_DISTANCE)) + { + scrollOffsetX = x - (int) playerPos.x; + scrollOffsetY = y - (int) playerPos.y; + } + + player_node->setAction(Being::STAND); + player_node->setPosition(x, y); + player_node->setDestination(x, y); + + logger->log("Adjust scrolling by %d,%d", (int) scrollOffsetX, + (int) scrollOffsetY); + viewport->scrollBy(scrollOffsetX, scrollOffsetY); +} + +void PlayerHandler::attack(int id, bool keep _UNUSED_) +{ + MessageOut msg(PGMSG_ATTACK); + msg.writeInt16(id); + gameServerConnection->send(msg); +} + +void PlayerHandler::stopAttack() +{ + +} + +void PlayerHandler::emote(int emoteId _UNUSED_) +{ + // TODO +} + +void PlayerHandler::increaseAttribute(int attr) +{ + MessageOut msg(PGMSG_RAISE_ATTRIBUTE); + msg.writeInt16(attr); + gameServerConnection->send(msg); +} + +void PlayerHandler::decreaseAttribute(int attr) +{ + MessageOut msg(PGMSG_LOWER_ATTRIBUTE); + msg.writeInt16(attr); + gameServerConnection->send(msg); +} + +void PlayerHandler::increaseSkill(int skillId _UNUSED_) +{ + // Not used atm +} + +void PlayerHandler::pickUp(FloorItem *floorItem) +{ + if (floorItem) + { + int id = floorItem->getId(); + MessageOut msg(PGMSG_PICKUP); + msg.writeInt16(id >> 16); + msg.writeInt16(id & 0xFFFF); + gameServerConnection->send(msg); + } +} + +void PlayerHandler::setDirection(char direction) +{ + MessageOut msg(PGMSG_DIRECTION_CHANGE); + msg.writeInt8(direction); + gameServerConnection->send(msg); +} + +void PlayerHandler::setDestination(int x, int y, int /* direction */) +{ + MessageOut msg(PGMSG_WALK); + msg.writeInt16(x); + msg.writeInt16(y); + gameServerConnection->send(msg); +} + +void PlayerHandler::changeAction(Being::Action action) +{ + player_node->setAction(action); + + MessageOut msg(PGMSG_ACTION_CHANGE); + msg.writeInt8(action); + gameServerConnection->send(msg); +} + +void PlayerHandler::respawn() +{ + MessageOut msg(PGMSG_RESPAWN); + gameServerConnection->send(msg); +} + +void PlayerHandler::ignorePlayer(const std::string &player _UNUSED_, + bool ignore _UNUSED_) +{ + // TODO +} + +void PlayerHandler::ignoreAll(bool ignore _UNUSED_) +{ + // TODO +} + +bool PlayerHandler::canUseMagic() +{ + return true; +} + +bool PlayerHandler::canCorrectAttributes() +{ + return true; +} + +int PlayerHandler::getJobLocation() +{ + return -1; +} + +Vector PlayerHandler::getDefaultWalkSpeed() +{ + // Return translation in pixels per ticks. + return ManaServ::BeingHandler::giveSpeedInPixelsPerTicks(6.0f); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/playerhandler.h b/src/net/manaserv/playerhandler.h new file mode 100644 index 000000000..ddd510714 --- /dev/null +++ b/src/net/manaserv/playerhandler.h @@ -0,0 +1,85 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_PLAYERHANDLER_H +#define NET_MANASERV_PLAYERHANDLER_H + +#include "net/playerhandler.h" + +#include "net/manaserv/messagehandler.h" + +#include <guichan/actionlistener.hpp> + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +struct RespawnRequestListener : public gcn::ActionListener +{ + void action(const gcn::ActionEvent &event); +}; + +static RespawnRequestListener respawnListener; + +class PlayerHandler : public MessageHandler, public Net::PlayerHandler +{ + public: + PlayerHandler(); + + void handleMessage(Net::MessageIn &msg); + + void attack(int id, bool keep = false); + void stopAttack(); + void emote(int emoteId); + + void increaseAttribute(int attr); + void decreaseAttribute(int attr); + void increaseSkill(int skillId); + + void pickUp(FloorItem *floorItem); + void setDirection(char direction); + void setDestination(int x, int y, int direction = -1); + void changeAction(Being::Action action); + + void respawn(); + + void ignorePlayer(const std::string &player, bool ignore); + void ignoreAll(bool ignore); + + bool canUseMagic(); + bool canCorrectAttributes(); + + int getJobLocation(); + + Vector getDefaultWalkSpeed(); + + private: + void handleMapChangeMessage(Net::MessageIn &msg); +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_PLAYERHANDLER_H diff --git a/src/net/manaserv/protocol.h b/src/net/manaserv/protocol.h new file mode 100644 index 000000000..2b5efd69c --- /dev/null +++ b/src/net/manaserv/protocol.h @@ -0,0 +1,392 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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 MANASERV_PROTOCOL_H +#define MANASERV_PROTOCOL_H + +/** + * Enumerated type for communicated messages: + * + * - PAMSG_*: from client to account server + * - APMSG_*: from account server to client + * - PCMSG_*: from client to chat server + * - CPMSG_*: from chat server to client + * - PGMSG_*: from client to game server + * - GPMSG_*: from game server to client + * - GAMSG_*: from game server to account server + * + * Components: B byte, W word, D double word, S variable-size string + * C tile-based coordinates (B*3) + * + * Hosts: P (player's client), A (account server), C (char server), + * G (game server) + * + * TODO - Document specific error codes for each packet + */ +enum { + // Login/Register + PAMSG_REGISTER = 0x0000, // D version, S username, S password, S email, S captcha response + APMSG_REGISTER_RESPONSE = 0x0002, // B error, S updatehost, S Client data URL, B Character slots + PAMSG_UNREGISTER = 0x0003, // S username, S password + APMSG_UNREGISTER_RESPONSE = 0x0004, // B error + PAMSG_REQUEST_REGISTER_INFO = 0x0005, // + APMSG_REGISTER_INFO_RESPONSE = 0x0006, // B byte registration Allowed, byte minNameLength, byte maxNameLength, string captchaURL, string captchaInstructions + PAMSG_LOGIN = 0x0010, // D version, S username, S password + APMSG_LOGIN_RESPONSE = 0x0012, // B error, S updatehost, S Client data URL, B Character slots + PAMSG_LOGOUT = 0x0013, // - + APMSG_LOGOUT_RESPONSE = 0x0014, // B error + PAMSG_CHAR_CREATE = 0x0020, // S name, B hair style, B hair color, B gender, B slot, W*6 stats + APMSG_CHAR_CREATE_RESPONSE = 0x0021, // B error + PAMSG_CHAR_DELETE = 0x0022, // B slot + APMSG_CHAR_DELETE_RESPONSE = 0x0023, // B error + // B slot, S name, B gender, B hair style, B hair color, W level, + // W character points, W correction points, + // {D attr id, D base value (in 1/256ths) D mod value (in 256ths) }* + APMSG_CHAR_INFO = 0x0024, // ^ + PAMSG_CHAR_SELECT = 0x0026, // B slot + APMSG_CHAR_SELECT_RESPONSE = 0x0027, // B error, B*32 token, S game address, W game port, S chat address, W chat port + PAMSG_EMAIL_CHANGE = 0x0030, // S email + APMSG_EMAIL_CHANGE_RESPONSE = 0x0031, // B error + PAMSG_PASSWORD_CHANGE = 0x0034, // S old password, S new password + APMSG_PASSWORD_CHANGE_RESPONSE = 0x0035, // B error + + PGMSG_CONNECT = 0x0050, // B*32 token + GPMSG_CONNECT_RESPONSE = 0x0051, // B error + PCMSG_CONNECT = 0x0053, // B*32 token + CPMSG_CONNECT_RESPONSE = 0x0054, // B error + + PGMSG_DISCONNECT = 0x0060, // B reconnect account + GPMSG_DISCONNECT_RESPONSE = 0x0061, // B error, B*32 token + PCMSG_DISCONNECT = 0x0063, // - + CPMSG_DISCONNECT_RESPONSE = 0x0064, // B error + + PAMSG_RECONNECT = 0x0065, // B*32 token + APMSG_RECONNECT_RESPONSE = 0x0066, // B error + + // Game + GPMSG_PLAYER_MAP_CHANGE = 0x0100, // S filename, W x, W y + GPMSG_PLAYER_SERVER_CHANGE = 0x0101, // B*32 token, S game address, W game port + PGMSG_PICKUP = 0x0110, // W*2 position + PGMSG_DROP = 0x0111, // B slot, B amount + PGMSG_EQUIP = 0x0112, // B slot + PGMSG_UNEQUIP = 0x0113, // B slot + PGMSG_MOVE_ITEM = 0x0114, // B slot1, B slot2, B amount + GPMSG_INVENTORY = 0x0120, // { W slot, W item id [, W amount] (if item id is nonzero) }* + GPMSG_INVENTORY_FULL = 0x0121, // W inventory slot count { W slot, W itemId, W amount } { B equip slot, W invy slot}* + GPMSG_EQUIP = 0x0122, // { W Invy slot, B equip slot type count { B equip slot, B number used} }* + GPMSG_PLAYER_ATTRIBUTE_CHANGE = 0x0130, // { W attribute, D base value (in 1/256ths), D modified value (in 1/256ths)}* + GPMSG_PLAYER_EXP_CHANGE = 0x0140, // { W skill, D exp got, D exp needed }* + GPMSG_LEVELUP = 0x0150, // W new level, W character points, W correction points + GPMSG_LEVEL_PROGRESS = 0x0151, // B percent completed to next levelup + PGMSG_RAISE_ATTRIBUTE = 0x0160, // W attribute + GPMSG_RAISE_ATTRIBUTE_RESPONSE = 0x0161, // B error, W attribute + PGMSG_LOWER_ATTRIBUTE = 0x0170, // W attribute + GPMSG_LOWER_ATTRIBUTE_RESPONSE = 0x0171, // B error, W attribute + PGMSG_RESPAWN = 0x0180, // - + GPMSG_BEING_ENTER = 0x0200, // B type, W being id, B action, W*2 position + // character: S name, B hair style, B hair color, B gender, B item bitmask, { W item id }* + // monster: W type id + // npc: W type id + GPMSG_BEING_LEAVE = 0x0201, // W being id + GPMSG_ITEM_APPEAR = 0x0202, // W item id, W*2 position + GPMSG_BEING_LOOKS_CHANGE = 0x0210, // W weapon, W hat, W top clothes, W bottom clothes + PGMSG_WALK = 0x0260, // W*2 destination + PGMSG_ACTION_CHANGE = 0x0270, // B Action + GPMSG_BEING_ACTION_CHANGE = 0x0271, // W being id, B action + PGMSG_DIRECTION_CHANGE = 0x0272, // B Direction + GPMSG_BEING_DIR_CHANGE = 0x0273, // W being id, B direction + GPMSG_BEING_HEALTH_CHANGE = 0x0274, // W being id, W hp, W max hp + GPMSG_BEINGS_MOVE = 0x0280, // { W being id, B flags [, W*2 position, B speed] }* + GPMSG_ITEMS = 0x0281, // { W item id, W*2 position }* + PGMSG_ATTACK = 0x0290, // W being id + GPMSG_BEING_ATTACK = 0x0291, // W being id, B direction, B attacktype + PGMSG_USE_SPECIAL = 0x0292, // B specialID + GPMSG_SPECIAL_STATUS = 0x0293, // { B specialID, D current, D max, D recharge } + PGMSG_SAY = 0x02A0, // S text + GPMSG_SAY = 0x02A1, // W being id, S text + GPMSG_NPC_CHOICE = 0x02B0, // W being id, { S text }* + GPMSG_NPC_MESSAGE = 0x02B1, // W being id, B* text + PGMSG_NPC_TALK = 0x02B2, // W being id + PGMSG_NPC_TALK_NEXT = 0x02B3, // W being id + PGMSG_NPC_SELECT = 0x02B4, // W being id, B choice + GPMSG_NPC_BUY = 0x02B5, // W being id, { W item id, W amount, W cost }* + GPMSG_NPC_SELL = 0x02B6, // W being id, { W item id, W amount, W cost }* + PGMSG_NPC_BUYSELL = 0x02B7, // W item id, W amount + GPMSG_NPC_ERROR = 0x02B8, // B error + GPMSG_NPC_CLOSE = 0x02B9, // W being id + GPMSG_NPC_POST = 0x02D0, // W being id + PGMSG_NPC_POST_SEND = 0x02D1, // W being id, { S name, S text, W item id } + GPMSG_NPC_POST_GET = 0x02D2, // W being id, { S name, S text, W item id } + PGMSG_NPC_NUMBER = 0x02D3, // W being id, D number + PGMSG_NPC_STRING = 0x02D4, // W being id, S string + GPMSG_NPC_NUMBER = 0x02D5, // W being id, D max, D min, D default + GPMSG_NPC_STRING = 0x02D6, // W being id + PGMSG_TRADE_REQUEST = 0x02C0, // W being id + GPMSG_TRADE_REQUEST = 0x02C1, // W being id + GPMSG_TRADE_START = 0x02C2, // - + GPMSG_TRADE_COMPLETE = 0x02C3, // - + PGMSG_TRADE_CANCEL = 0x02C4, // - + GPMSG_TRADE_CANCEL = 0x02C5, // - + PGMSG_TRADE_AGREED = 0x02C6, // - + GPMSG_TRADE_AGREED = 0x02C7, // - + PGMSG_TRADE_CONFIRM = 0x02C8, // - + GPMSG_TRADE_CONFIRM = 0x02C9, // - + PGMSG_TRADE_ADD_ITEM = 0x02CA, // B slot, B amount + GPMSG_TRADE_ADD_ITEM = 0x02CB, // W item id, B amount + PGMSG_TRADE_SET_MONEY = 0x02CC, // D amount + GPMSG_TRADE_SET_MONEY = 0x02CD, // D amount + GPMSG_TRADE_BOTH_CONFIRM = 0x02CE, // - + PGMSG_USE_ITEM = 0x0300, // B slot + GPMSG_USE_RESPONSE = 0x0301, // B error + GPMSG_BEINGS_DAMAGE = 0x0310, // { W being id, W amount }* + GPMSG_CREATE_EFFECT_POS = 0x0320, // W effect id, W*2 position + GPMSG_CREATE_EFFECT_BEING = 0x0321, // W effect id, W BeingID + + // Guild + PCMSG_GUILD_CREATE = 0x0350, // S name + CPMSG_GUILD_CREATE_RESPONSE = 0x0351, // B error, W guild, B rights, W channel + PCMSG_GUILD_INVITE = 0x0352, // W id, S name + CPMSG_GUILD_INVITE_RESPONSE = 0x0353, // B error + PCMSG_GUILD_ACCEPT = 0x0354, // W id + CPMSG_GUILD_ACCEPT_RESPONSE = 0x0355, // B error, W guild, B rights, W channel + PCMSG_GUILD_GET_MEMBERS = 0x0356, // W id + CPMSG_GUILD_GET_MEMBERS_RESPONSE = 0x0357, // S names, B online + CPMSG_GUILD_UPDATE_LIST = 0x0358, // W id, S name, B event + PCMSG_GUILD_QUIT = 0x0360, // W id + CPMSG_GUILD_QUIT_RESPONSE = 0x0361, // B error + PCMSG_GUILD_PROMOTE_MEMBER = 0x0365, // W guild, S name, B rights + CPMSG_GUILD_PROMOTE_MEMBER_RESPONSE = 0x0366, // B error + PCMSG_GUILD_KICK_MEMBER = 0x0370, // W guild, S name + CPMSG_GUILD_KICK_MEMBER_RESPONSE = 0x0371, // B error + + CPMSG_GUILD_INVITED = 0x0388, // S char name, S guild name, W id + CPMSG_GUILD_REJOIN = 0x0389, // S name, W guild, W rights, W channel, S announce + + // Party + PCMSG_PARTY_INVITE = 0x03A0, // S name + CPMSG_PARTY_INVITE_RESPONSE = 0x03A1, // B error, S name + CPMSG_PARTY_INVITED = 0x03A2, // S name + PCMSG_PARTY_ACCEPT_INVITE = 0x03A5, // S name + CPMSG_PARTY_ACCEPT_INVITE_RESPONSE = 0x03A6, // B error, { S name } + PCMSG_PARTY_REJECT_INVITE = 0x03A7, // S name + CPMSG_PARTY_REJECTED = 0x03A8, // S name + PCMSG_PARTY_QUIT = 0x03AA, // - + CPMSG_PARTY_QUIT_RESPONSE = 0x03AB, // B error + CPMSG_PARTY_NEW_MEMBER = 0x03B0, // W being id, S name + CPMSG_PARTY_MEMBER_LEFT = 0x03B1, // W being id + + // Chat + CPMSG_ERROR = 0x0401, // B error + CPMSG_ANNOUNCEMENT = 0x0402, // S text + CPMSG_PRIVMSG = 0x0403, // S user, S text + CPMSG_PUBMSG = 0x0404, // W channel, S user, S text + PCMSG_CHAT = 0x0410, // S text, W channel + PCMSG_ANNOUNCE = 0x0411, // S text + PCMSG_PRIVMSG = 0x0412, // S user, S text + PCMSG_WHO = 0x0415, // - + CPMSG_WHO_RESPONSE = 0x0416, // { S user } + + // -- Channeling + CPMSG_CHANNEL_EVENT = 0x0430, // W channel, B event, S info + PCMSG_ENTER_CHANNEL = 0x0440, // S channel, S password + CPMSG_ENTER_CHANNEL_RESPONSE = 0x0441, // B error, W id, S name, S topic, S userlist + PCMSG_QUIT_CHANNEL = 0x0443, // W channel id + CPMSG_QUIT_CHANNEL_RESPONSE = 0x0444, // B error, W channel id + PCMSG_LIST_CHANNELS = 0x0445, // - + CPMSG_LIST_CHANNELS_RESPONSE = 0x0446, // S names, W number of users + PCMSG_LIST_CHANNELUSERS = 0x0460, // S channel + CPMSG_LIST_CHANNELUSERS_RESPONSE = 0x0461, // S channel, { S user, B mode } + PCMSG_TOPIC_CHANGE = 0x0462, // W channel id, S topic + // -- User modes + PCMSG_USER_MODE = 0x0465, // W channel id, S name, B mode + PCMSG_KICK_USER = 0x0466, // W channel id, S name + + // Inter-server + GAMSG_REGISTER = 0x0500, // S address, W port, S password, D items db revision, { W map id }* + AGMSG_REGISTER_RESPONSE = 0x0501, // C item version, C password response + AGMSG_ACTIVE_MAP = 0x0502, // W map id + AGMSG_PLAYER_ENTER = 0x0510, // B*32 token, D id, S name, serialised character data + GAMSG_PLAYER_DATA = 0x0520, // D id, serialised character data + GAMSG_REDIRECT = 0x0530, // D id + AGMSG_REDIRECT_RESPONSE = 0x0531, // D id, B*32 token, S game address, W game port + GAMSG_PLAYER_RECONNECT = 0x0532, // D id, B*32 token + GAMSG_PLAYER_SYNC = 0x0533, // serialised sync data + GAMSG_SET_QUEST = 0x0540, // D id, S name, S value + GAMSG_GET_QUEST = 0x0541, // D id, S name + AGMSG_GET_QUEST_RESPONSE = 0x0542, // D id, S name, S value + GAMSG_BAN_PLAYER = 0x0550, // D id, W duration + GAMSG_CHANGE_PLAYER_LEVEL = 0x0555, // D id, W level + GAMSG_CHANGE_ACCOUNT_LEVEL = 0x0556, // D id, W level + GAMSG_STATISTICS = 0x0560, // { W map id, W thing nb, W monster nb, W player nb, { D character id }* }* + CGMSG_CHANGED_PARTY = 0x0590, // D character id, D party id + GCMSG_REQUEST_POST = 0x05A0, // D character id + CGMSG_POST_RESPONSE = 0x05A1, // D receiver id, { S sender name, S letter, W num attachments { W attachment item id, W quantity } } + GCMSG_STORE_POST = 0x05A5, // D sender id, S receiver name, S letter, { W attachment item id, W quantity } + CGMSG_STORE_POST_RESPONSE = 0x05A6, // D id, B error + GAMSG_TRANSACTION = 0x0600, // D character id, D action, S message + + XXMSG_INVALID = 0x7FFF +}; + +// Generic return values + +enum { + ERRMSG_OK = 0, // everything is fine + ERRMSG_FAILURE, // the action failed + ERRMSG_NO_LOGIN, // the user is not yet logged + ERRMSG_NO_CHARACTER_SELECTED, // the user needs a character + ERRMSG_INSUFFICIENT_RIGHTS, // the user is not privileged + ERRMSG_INVALID_ARGUMENT, // part of the received message was invalid + ERRMSG_EMAIL_ALREADY_EXISTS, // The Email Address already exists + ERRMSG_ALREADY_TAKEN, // name used was already taken + ERRMSG_SERVER_FULL, // the server is overloaded + ERRMSG_TIME_OUT, // data failed to arrive in due time + ERRMSG_LIMIT_REACHED // limit reached +}; + +// used in AGMSG_REGISTER_RESPONSE to show state of item db +enum { + DATA_VERSION_OK = 0x00, + DATA_VERSION_OUTDATED = 0x01 +}; + +// used in AGMSG_REGISTER_RESPNSE to show if password was accepted +enum { + PASSWORD_OK = 0x00, + PASSWORD_BAD = 0x01 +}; + +// used to identify part of sync message +enum { + SYNC_CHARACTER_POINTS = 0x01, // D charId, D charPoints, D corrPoints + SYNC_CHARACTER_ATTRIBUTE = 0x02, // D charId, D attrId, DF base, DF mod + SYNC_CHARACTER_SKILL = 0x03, // D charId, B skillId, D skill value + SYNC_ONLINE_STATUS = 0x04, // D charId, B 0x00 = offline, 0x01 = online + SYNC_END_OF_BUFFER = 0xFF // shows, that the buffer ends here. +}; + +// Login specific return values +enum { + LOGIN_INVALID_VERSION = 0x40, // the user is using an incompatible protocol + LOGIN_INVALID_TIME = 0x50, // the user tried logging in too fast + LOGIN_BANNED // the user is currently banned +}; + +// Account register specific return values +enum { + REGISTER_INVALID_VERSION = 0x40, // the user is using an incompatible protocol + REGISTER_EXISTS_USERNAME, // there already is an account with this username + REGISTER_EXISTS_EMAIL, // there already is an account with this email address + REGISTER_CAPTCHA_WRONG // user didn't solve the captcha correctly +}; + +// Character creation specific return values +enum { + CREATE_INVALID_HAIRSTYLE = 0x40, + CREATE_INVALID_HAIRCOLOR, + CREATE_INVALID_GENDER, + CREATE_ATTRIBUTES_TOO_HIGH, + CREATE_ATTRIBUTES_TOO_LOW, + CREATE_ATTRIBUTES_OUT_OF_RANGE, + CREATE_EXISTS_NAME, + CREATE_TOO_MUCH_CHARACTERS, + CREATE_INVALID_SLOT +}; + +// Character attribute modification specific return value +enum AttribmodResponseCode { + ATTRIBMOD_OK = ERRMSG_OK, + ATTRIBMOD_INVALID_ATTRIBUTE = 0x40, + ATTRIBMOD_NO_POINTS_LEFT, + ATTRIBMOD_DENIED +}; + +// Object type enumeration +enum ThingType +{ + // A simple item. + OBJECT_ITEM = 0, + // An item that toggle map/quest actions (doors, switchs, ...) + // and can speak (map panels). + OBJECT_ACTOR, + // Non-Playable-Character is an actor capable of movement and maybe actions. + OBJECT_NPC, + // A monster (moving actor with AI. Should be able to toggle map/quest + // actions, too). + OBJECT_MONSTER, + // A normal being. + OBJECT_CHARACTER, + // A effect to be shown. + OBJECT_EFFECT, + // Server-only object. + OBJECT_OTHER +}; + +// Moving object flags +enum { + // Payload contains the current position. + MOVING_POSITION = 1, + // Payload contains the destination. + MOVING_DESTINATION = 2 +}; + +// Email change specific return values +enum { + EMAILCHG_EXISTS_EMAIL = 0x40 +}; + +// Chat errors return values +enum { + CHAT_USING_BAD_WORDS = 0x40, + CHAT_UNHANDLED_COMMAND +}; + +// Chat channels event values +enum { + CHAT_EVENT_NEW_PLAYER = 0, + CHAT_EVENT_LEAVING_PLAYER, + CHAT_EVENT_TOPIC_CHANGE, + CHAT_EVENT_MODE_CHANGE, + CHAT_EVENT_KICKED_PLAYER +}; + +// Guild member event values +enum { + GUILD_EVENT_NEW_PLAYER = 0, + GUILD_EVENT_LEAVING_PLAYER, + GUILD_EVENT_ONLINE_PLAYER, + GUILD_EVENT_OFFLINE_PLAYER +}; + + +enum +{ + SPRITE_BASE = 0, + SPRITE_SHOE, + SPRITE_BOTTOMCLOTHES, + SPRITE_TOPCLOTHES, + SPRITE_HAIR, + SPRITE_HAT, + SPRITE_WEAPON, + SPRITE_VECTOREND +}; + +#endif // MANASERV_PROTOCOL_H diff --git a/src/net/manaserv/specialhandler.cpp b/src/net/manaserv/specialhandler.cpp new file mode 100644 index 000000000..8508c9b56 --- /dev/null +++ b/src/net/manaserv/specialhandler.cpp @@ -0,0 +1,70 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/specialhandler.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +extern Net::SpecialHandler *specialHandler; + +namespace ManaServ +{ + +extern Connection *gameServerConnection; + +SpecialHandler::SpecialHandler() +{ + specialHandler = this; +} + +void SpecialHandler::handleMessage(Net::MessageIn &msg _UNUSED_) +{ + // TODO +} + +void SpecialHandler::use(int id) +{ + MessageOut msg(PGMSG_USE_SPECIAL); + msg.writeInt8(id); + gameServerConnection->send(msg); +} + +void SpecialHandler::use(int id _UNUSED_, int level _UNUSED_, + int beingId _UNUSED_) +{ + // TODO +} + +void SpecialHandler::use(int id _UNUSED_, int level _UNUSED_, int x _UNUSED_, + int y _UNUSED_) +{ + // TODO +} + +void SpecialHandler::use(int id _UNUSED_, const std::string &map _UNUSED_) +{ + // TODO +} + +} // namespace ManaServ diff --git a/src/net/manaserv/specialhandler.h b/src/net/manaserv/specialhandler.h new file mode 100644 index 000000000..1f48688bf --- /dev/null +++ b/src/net/manaserv/specialhandler.h @@ -0,0 +1,56 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_SKILLHANDLER_H +#define NET_MANASERV_SKILLHANDLER_H + +#include "net/specialhandler.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class SpecialHandler : public MessageHandler, public Net::SpecialHandler +{ + public: + SpecialHandler(); + + void handleMessage(Net::MessageIn &msg); + + void use(int id); + + void use(int id, int level, int beingId); + + void use(int id, int level, int x, int y); + + void use(int id, const std::string &map); +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_SKILLHANDLER_H diff --git a/src/net/manaserv/tradehandler.cpp b/src/net/manaserv/tradehandler.cpp new file mode 100644 index 000000000..5a9fdfabd --- /dev/null +++ b/src/net/manaserv/tradehandler.cpp @@ -0,0 +1,237 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/manaserv/tradehandler.h" + +#include "actorspritemanager.h" +#include "event.h" +#include "item.h" +#include "localplayer.h" +#include "playerinfo.h" +#include "playerrelations.h" + +#include "gui/confirmdialog.h" +#include "gui/trade.h" + +#include "net/net.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +extern std::string tradePartnerName; +int tradePartnerID; + +extern Net::TradeHandler *tradeHandler; + +namespace ManaServ +{ + +extern Connection *gameServerConnection; + +/** + * Listener for request trade dialogs + */ +namespace +{ + struct RequestTradeListener : public gcn::ActionListener + { + void action(const gcn::ActionEvent &event) + { + if (event.getId() == "yes") + { + ManaServ::MessageOut msg(PGMSG_TRADE_REQUEST); + msg.writeInt16(tradePartnerID); + gameServerConnection->send(msg); + } + else if (event.getId() == "ignore") + { + player_relations.ignoreTrade(tradePartnerName); + Net::getTradeHandler()->cancel(); + } + else + { + Net::getTradeHandler()->cancel(); + } + } + } listener; +} + +TradeHandler::TradeHandler(): + mAcceptTradeRequests(true) +{ + static const Uint16 _messages[] = + { + GPMSG_TRADE_REQUEST, + GPMSG_TRADE_CANCEL, + GPMSG_TRADE_START, + GPMSG_TRADE_COMPLETE, + GPMSG_TRADE_AGREED, + GPMSG_TRADE_BOTH_CONFIRM, + GPMSG_TRADE_CONFIRM, + GPMSG_TRADE_ADD_ITEM, + GPMSG_TRADE_SET_MONEY, + 0 + }; + handledMessages = _messages; + tradeHandler = this; +} + +void TradeHandler::setAcceptTradeRequests(bool acceptTradeRequests) +{ + mAcceptTradeRequests = acceptTradeRequests; + if (mAcceptTradeRequests) + SERVER_NOTICE(_("Accepting incoming trade requests.")) + else + SERVER_NOTICE(_("Ignoring incoming trade requests.")) +} + +void TradeHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_TRADE_REQUEST: + { + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being || !mAcceptTradeRequests) + { + respond(false); + break; + } + PlayerInfo::setTrading(true); + tradePartnerName = being->getName(); + tradePartnerID = being->getId(); + ConfirmDialog *dlg = new ConfirmDialog(_("Request for Trade"), + strprintf(_("%s wants to trade with you, do you accept?"), + tradePartnerName.c_str()), true); + dlg->addActionListener(&listener); + } break; + + case GPMSG_TRADE_ADD_ITEM: + { + int type = msg.readInt16(); + int amount = msg.readInt8(); + tradeWindow->addItem(type, false, amount, 0); + } break; + + case GPMSG_TRADE_SET_MONEY: + tradeWindow->setMoney(msg.readInt32()); + break; + + case GPMSG_TRADE_START: + tradeWindow->reset(); + tradeWindow->setCaption(strprintf(_("Trading with %s"), + tradePartnerName.c_str())); + tradeWindow->setVisible(true); + break; + + case GPMSG_TRADE_BOTH_CONFIRM: + tradeWindow->receivedOk(false); + break; + + case GPMSG_TRADE_AGREED: + tradeWindow->receivedOk(false); + break; + + case GPMSG_TRADE_CANCEL: + SERVER_NOTICE(_("Trade canceled.")) + tradeWindow->setVisible(false); + tradeWindow->reset(); + PlayerInfo::setTrading(false); + break; + + case GPMSG_TRADE_COMPLETE: + SERVER_NOTICE(_("Trade completed.")) + tradeWindow->setVisible(false); + tradeWindow->reset(); + PlayerInfo::setTrading(false); + break; + + default: + break; + } +} + +void TradeHandler::request(Being *being) +{ + tradePartnerName = being->getName(); + tradePartnerID = being->getId(); + + MessageOut msg(PGMSG_TRADE_REQUEST); + msg.writeInt16(tradePartnerID); + gameServerConnection->send(msg); +} + +void TradeHandler::respond(bool accept) +{ + MessageOut msg(accept ? PGMSG_TRADE_REQUEST : PGMSG_TRADE_CANCEL); + gameServerConnection->send(msg); + + if (!accept) + PlayerInfo::setTrading(false); +} + +void TradeHandler::addItem(Item *item, int amount) +{ + MessageOut msg(PGMSG_TRADE_ADD_ITEM); + msg.writeInt8(item->getInvIndex()); + msg.writeInt8(amount); + gameServerConnection->send(msg); + + tradeWindow->addItem(item->getId(), true, amount, 0); + item->increaseQuantity(-amount); +} + +void TradeHandler::removeItem(int slotNum _UNUSED_, int amount _UNUSED_) +{ + // TODO +} + +void TradeHandler::setMoney(int amount) +{ + MessageOut msg(PGMSG_TRADE_SET_MONEY); + msg.writeInt32(amount); + gameServerConnection->send(msg); +} + +void TradeHandler::confirm() +{ + MessageOut msg(PGMSG_TRADE_CONFIRM); + gameServerConnection->send(msg); +} + +void TradeHandler::finish() +{ + MessageOut msg(PGMSG_TRADE_AGREED); + gameServerConnection->send(msg); +} + +void TradeHandler::cancel() +{ + MessageOut msg(PGMSG_TRADE_CANCEL); + gameServerConnection->send(msg); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/tradehandler.h b/src/net/manaserv/tradehandler.h new file mode 100644 index 000000000..3bbf15470 --- /dev/null +++ b/src/net/manaserv/tradehandler.h @@ -0,0 +1,82 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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_MANASERV_TRADEHANDLER_H +#define NET_MANASERV_TRADEHANDLER_H + +#include "net/tradehandler.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class TradeHandler : public MessageHandler, public Net::TradeHandler +{ + public: + TradeHandler(); + + void handleMessage(Net::MessageIn &msg); + + /** + * Returns whether trade requests are accepted. + * + * @see setAcceptTradeRequests + */ + bool acceptTradeRequests() const + { return mAcceptTradeRequests; } + + /** + * Sets whether trade requests are accepted. When set to false, trade + * requests are automatically denied. When true, a popup will ask the + * player whether he wants to trade. + */ + void setAcceptTradeRequests(bool acceptTradeRequests); + + void request(Being *being); + + void respond(bool accept); + + void addItem(Item *item, int amount); + + void removeItem(int slotNum, int amount); + + void setMoney(int amount); + + void confirm(); + + void finish(); + + void cancel(); + + private: + bool mAcceptTradeRequests; +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_TRADEHANDLER_H |