summaryrefslogtreecommitdiff
path: root/src/net/manaserv
diff options
context:
space:
mode:
authorAndrei Karas <akaras@inbox.ru>2011-01-02 01:48:38 +0200
committerAndrei Karas <akaras@inbox.ru>2011-01-02 02:41:24 +0200
commit3eeae12c498d1a4dbe969462d2ba841f77ee3ccb (patch)
treeff8eab35e732bc0749fc11677c8873a7b3a58704 /src/net/manaserv
downloadmanaplus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.gz
manaplus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.bz2
manaplus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.xz
manaplus-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')
-rw-r--r--src/net/manaserv/adminhandler.cpp93
-rw-r--r--src/net/manaserv/adminhandler.h64
-rw-r--r--src/net/manaserv/attributes.cpp411
-rw-r--r--src/net/manaserv/attributes.h72
-rw-r--r--src/net/manaserv/beinghandler.cpp385
-rw-r--r--src/net/manaserv/beinghandler.h73
-rw-r--r--src/net/manaserv/buysellhandler.cpp132
-rw-r--r--src/net/manaserv/buysellhandler.h57
-rw-r--r--src/net/manaserv/charhandler.cpp406
-rw-r--r--src/net/manaserv/charhandler.h119
-rw-r--r--src/net/manaserv/chathandler.cpp472
-rw-r--r--src/net/manaserv/chathandler.h139
-rw-r--r--src/net/manaserv/connection.cpp113
-rw-r--r--src/net/manaserv/connection.h89
-rw-r--r--src/net/manaserv/defines.h77
-rw-r--r--src/net/manaserv/effecthandler.cpp80
-rw-r--r--src/net/manaserv/effecthandler.h44
-rw-r--r--src/net/manaserv/gamehandler.cpp154
-rw-r--r--src/net/manaserv/gamehandler.h74
-rw-r--r--src/net/manaserv/generalhandler.cpp211
-rw-r--r--src/net/manaserv/generalhandler.h78
-rw-r--r--src/net/manaserv/guildhandler.cpp360
-rw-r--r--src/net/manaserv/guildhandler.h84
-rw-r--r--src/net/manaserv/internal.cpp27
-rw-r--r--src/net/manaserv/internal.h30
-rw-r--r--src/net/manaserv/inventoryhandler.cpp219
-rw-r--r--src/net/manaserv/inventoryhandler.h109
-rw-r--r--src/net/manaserv/itemhandler.cpp90
-rw-r--r--src/net/manaserv/itemhandler.h40
-rw-r--r--src/net/manaserv/loginhandler.cpp479
-rw-r--r--src/net/manaserv/loginhandler.h99
-rw-r--r--src/net/manaserv/messagehandler.cpp36
-rw-r--r--src/net/manaserv/messagehandler.h44
-rw-r--r--src/net/manaserv/messagein.cpp62
-rw-r--r--src/net/manaserv/messagein.h49
-rw-r--r--src/net/manaserv/messageout.cpp65
-rw-r--r--src/net/manaserv/messageout.h59
-rw-r--r--src/net/manaserv/network.cpp178
-rw-r--r--src/net/manaserv/network.h75
-rw-r--r--src/net/manaserv/npchandler.cpp237
-rw-r--r--src/net/manaserv/npchandler.h89
-rw-r--r--src/net/manaserv/partyhandler.cpp197
-rw-r--r--src/net/manaserv/partyhandler.h82
-rw-r--r--src/net/manaserv/playerhandler.cpp440
-rw-r--r--src/net/manaserv/playerhandler.h85
-rw-r--r--src/net/manaserv/protocol.h392
-rw-r--r--src/net/manaserv/specialhandler.cpp70
-rw-r--r--src/net/manaserv/specialhandler.h56
-rw-r--r--src/net/manaserv/tradehandler.cpp237
-rw-r--r--src/net/manaserv/tradehandler.h82
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