summaryrefslogtreecommitdiff
path: root/src/net/manaserv
diff options
context:
space:
mode:
authorAndrei Karas <akaras@inbox.ru>2012-03-04 16:32:40 +0300
committerAndrei Karas <akaras@inbox.ru>2012-03-04 16:33:19 +0300
commit8fd450f698f8277eb8348da914d3dcbc11890ea6 (patch)
treeebaddac087d5e5b29f23a2aa01dd2758c3b8f68a /src/net/manaserv
parentc6d7f7ed3d6d7827b820670faf6c48f0689fd5c2 (diff)
parentf74202392745923f9ce372a6bdcd0a45db6bcd08 (diff)
downloadplus-8fd450f698f8277eb8348da914d3dcbc11890ea6.tar.gz
plus-8fd450f698f8277eb8348da914d3dcbc11890ea6.tar.bz2
plus-8fd450f698f8277eb8348da914d3dcbc11890ea6.tar.xz
plus-8fd450f698f8277eb8348da914d3dcbc11890ea6.zip
Merge branch 'master' into stripped
Conflicts: configure.ac data/Makefile.am data/fonts/liberationsans-bold.ttf data/fonts/liberationsans.ttf data/fonts/liberationsansmono-bold.ttf data/fonts/liberationsansmono.ttf data/themes/redandblack/CMakeLists.txt data/themes/redandblack/Makefile.am po/cs.po po/de.po po/es.po po/fi.po po/fr.po po/id.po po/ja.po po/manaplus.pot po/nl_BE.po po/pl.po po/pt.po po/pt_BR.po po/ru.po po/zh_CN.po src/guichan/color.cpp src/guichan/defaultfont.cpp src/guichan/focushandler.cpp src/guichan/include/guichan/actionlistener.hpp src/guichan/include/guichan/deathlistener.hpp src/guichan/include/guichan/focushandler.hpp src/guichan/include/guichan/focuslistener.hpp src/guichan/include/guichan/graphics.hpp src/guichan/include/guichan/keylistener.hpp src/guichan/include/guichan/listmodel.hpp src/guichan/include/guichan/mouselistener.hpp src/guichan/include/guichan/sdl/sdlpixel.hpp src/guichan/include/guichan/widget.hpp src/guichan/include/guichan/widgetlistener.hpp src/guichan/include/guichan/widgets/listbox.hpp src/guichan/include/guichan/widgets/slider.hpp src/guichan/sdl/sdlgraphics.cpp src/guichan/sdl/sdlimage.cpp src/guichan/widgets/scrollarea.cpp src/guichan/widgets/slider.cpp src/guichan/widgets/tabbedarea.cpp src/guichan/widgets/textbox.cpp src/guichan/widgets/window.cpp src/net/manaserv/attributes.cpp src/net/manaserv/beinghandler.cpp src/net/manaserv/charhandler.cpp src/net/manaserv/gamehandler.h src/net/manaserv/inventoryhandler.cpp src/net/manaserv/inventoryhandler.h src/net/manaserv/itemhandler.cpp src/net/manaserv/loginhandler.cpp src/net/manaserv/network.cpp
Diffstat (limited to 'src/net/manaserv')
-rw-r--r--src/net/manaserv/attributes.cpp412
-rw-r--r--src/net/manaserv/beinghandler.cpp388
-rw-r--r--src/net/manaserv/charhandler.cpp407
-rw-r--r--src/net/manaserv/gamehandler.h72
-rw-r--r--src/net/manaserv/inventoryhandler.cpp220
-rw-r--r--src/net/manaserv/inventoryhandler.h104
-rw-r--r--src/net/manaserv/itemhandler.cpp91
-rw-r--r--src/net/manaserv/loginhandler.cpp499
-rw-r--r--src/net/manaserv/network.cpp177
9 files changed, 2370 insertions, 0 deletions
diff --git a/src/net/manaserv/attributes.cpp b/src/net/manaserv/attributes.cpp
new file mode 100644
index 000000000..662032e29
--- /dev/null
+++ b/src/net/manaserv/attributes.cpp
@@ -0,0 +1,412 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2010 The Mana Developers
+ * Copyright (C) 2011-2012 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/manaserv/attributes.h"
+
+#include "logger.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 || !xmlNameEqual(rootNode, "attributes"))
+ {
+ logger->log("Attributes: Error while loading "
+ DEFAULT_ATTRIBUTESDB_FILE ". Using Built-ins.");
+ loadBuiltins();
+ fillLabels();
+ return;
+ }
+
+ for_each_xml_child_node(node, rootNode)
+ {
+ if (xmlNameEqual(node, "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 (!xmlNameEqual(effectNode, "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 (xmlNameEqual(node, "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::vector<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/beinghandler.cpp b/src/net/manaserv/beinghandler.cpp
new file mode 100644
index 000000000..9468baaa1
--- /dev/null
+++ b/src/net/manaserv/beinghandler.cpp
@@ -0,0 +1,388 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2012 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/manaserv/beinghandler.h"
+
+#include "actorspritemanager.h"
+#include "being.h"
+#include "client.h"
+#include "game.h"
+#include "localplayer.h"
+#include "logger.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, "", 1, (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::getHairColor(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, DIALOG_OK, 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::getHairColor(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 A_UNUSED)
+{
+}
+
+void BeingHandler::undress(Being *being A_UNUSED)
+{
+}
+
+} // namespace ManaServ
diff --git a/src/net/manaserv/charhandler.cpp b/src/net/manaserv/charhandler.cpp
new file mode 100644
index 000000000..7977df909
--- /dev/null
+++ b/src/net/manaserv/charhandler.cpp
@@ -0,0 +1,407 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2012 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/manaserv/charhandler.h"
+
+#include "client.h"
+#include "localplayer.h"
+#include "logger.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, DIALOG_ERROR);
+
+ 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, DIALOG_ERROR);
+ }
+ 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, unsigned char race,
+ 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::getHairColor(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/gamehandler.h b/src/net/manaserv/gamehandler.h
new file mode 100644
index 000000000..2019755ea
--- /dev/null
+++ b/src/net/manaserv/gamehandler.h
@@ -0,0 +1,72 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2012 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NET_MANASERV_MAPHANDLER_H
+#define NET_MANASERV_MAPHANDLER_H
+
+#include "net/gamehandler.h"
+#include "net/serverinfo.h"
+
+#include "net/manaserv/messagehandler.h"
+
+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/inventoryhandler.cpp b/src/net/manaserv/inventoryhandler.cpp
new file mode 100644
index 000000000..2c9c84c76
--- /dev/null
+++ b/src/net/manaserv/inventoryhandler.cpp
@@ -0,0 +1,220 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2012 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/manaserv/inventoryhandler.h"
+
+#include "equipment.h"
+#include "inventory.h"
+#include "item.h"
+#include "itemshortcut.h"
+#include "localplayer.h"
+#include "playerinfo.h"
+
+#include "gui/chatwindow.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 "logger.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) const
+{
+ 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 A_UNUSED)
+{
+ // TODO
+}
+
+void InventoryHandler::closeStorage(int type A_UNUSED)
+{
+ // TODO
+}
+
+void InventoryHandler::moveItem2(int source A_UNUSED, int slot A_UNUSED,
+ int amount A_UNUSED, int destination A_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) const
+{
+ return eAthenaSlot;
+}
+
+} // namespace ManaServ
diff --git a/src/net/manaserv/inventoryhandler.h b/src/net/manaserv/inventoryhandler.h
new file mode 100644
index 000000000..af340399e
--- /dev/null
+++ b/src/net/manaserv/inventoryhandler.h
@@ -0,0 +1,104 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2012 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NET_MANASERV_INVENTORYHANDLER_H
+#define NET_MANASERV_INVENTORYHANDLER_H
+
+#include "equipment.h"
+
+#include "net/inventoryhandler.h"
+
+#include "net/manaserv/messagehandler.h"
+
+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) const;
+
+ void splitItem(const Item *item, int amount);
+
+ void moveItem(int oldIndex, int newIndex);
+
+ void openStorage(int type);
+
+ void closeStorage(int type);
+
+ void moveItem2(int source, int slot, int amount,
+ int destination);
+
+ size_t getSize(int type) const;
+
+ int convertFromServerSlot(int eAthenaSlot) const;
+
+ 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..8ebe965ec
--- /dev/null
+++ b/src/net/manaserv/itemhandler.cpp
@@ -0,0 +1,91 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2012 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/manaserv/itemhandler.h"
+
+#include "actorspritemanager.h"
+
+#include "net/manaserv/protocol.h"
+#include "net/manaserv/messagein.h"
+
+#include "game.h"
+#include "map.h"
+#include "logger.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, 1, 0, 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/loginhandler.cpp b/src/net/manaserv/loginhandler.cpp
new file mode 100644
index 000000000..f93d8e44c
--- /dev/null
+++ b/src/net/manaserv/loginhandler.cpp
@@ -0,0 +1,499 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2012 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/manaserv/loginhandler.h"
+
+#include "client.h"
+#include "logger.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/paths.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;
+ mMinUserNameLength = 4;
+ mMaxUserNameLength = 10;
+ mLoginData = 0;
+}
+
+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
+ std::string updateHost = msg.readString();
+ if (!updateHost.empty())
+ {
+ if (!checkPath(updateHost))
+ {
+ logger->log1("Warning: incorrect update server name");
+ updateHost = "";
+ }
+ 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 A_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);
+}
+
+void LoginHandler::clearWorlds()
+{
+
+}
+
+} // namespace ManaServ
diff --git a/src/net/manaserv/network.cpp b/src/net/manaserv/network.cpp
new file mode 100644
index 000000000..05041c56a
--- /dev/null
+++ b/src/net/manaserv/network.cpp
@@ -0,0 +1,177 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2012 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/manaserv/network.h"
+
+#include "logger.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>
+
+#include "debug.h"
+
+/**
+ * The local host which is shared for all outgoing connections.
+ */
+namespace
+{
+ ENetHost *client;
+}
+
+namespace ManaServ
+{
+
+typedef std::map<unsigned short, MessageHandler*> MessageHandlers;
+typedef MessageHandlers::const_iterator MessageHandlerIterator;
+static MessageHandlers mMessageHandlers;
+
+void initialize()
+{
+ if (enet_initialize())
+ {
+ logger->safeError("Failed to initialize ENet.");
+ }
+
+#if defined(ENET_VERSION) && ENET_VERSION >= ENET_CUTOFF
+ client = enet_host_create(nullptr, 3, 0, 0, 0);
+#else
+ client = enet_host_create(nullptr, 3, 0, 0);
+#endif
+
+ if (!client)
+ {
+ logger->safeError("Failed to create the local host.");
+ }
+}
+
+void finalize()
+{
+ if (!client)
+ return; // Wasn't initialized at all
+
+ if (connections)
+ {
+ logger->safeError("Tried to shutdown the network subsystem "
+ "while there are network connections left!");
+ }
+
+ clearNetworkHandlers();
+ enet_deinitialize();
+}
+
+Connection *getConnection()
+{
+ if (!client)
+ {
+ logger->safeError("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;
+ }
+ }
+}
+
+}