summaryrefslogtreecommitdiff
path: root/src/net
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
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')
-rw-r--r--src/net/adminhandler.h60
-rw-r--r--src/net/beinghandler.h43
-rw-r--r--src/net/buysellhandler.h47
-rw-r--r--src/net/charhandler.cpp37
-rw-r--r--src/net/charhandler.h112
-rw-r--r--src/net/chathandler.h71
-rw-r--r--src/net/download.cpp355
-rw-r--r--src/net/download.h123
-rw-r--r--src/net/gamehandler.h62
-rw-r--r--src/net/generalhandler.h50
-rw-r--r--src/net/guildhandler.h76
-rw-r--r--src/net/inventoryhandler.h72
-rw-r--r--src/net/logindata.h95
-rw-r--r--src/net/loginhandler.h112
-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
-rw-r--r--src/net/messagehandler.h50
-rw-r--r--src/net/messagein.cpp227
-rw-r--r--src/net/messagein.h118
-rw-r--r--src/net/messageout.cpp92
-rw-r--r--src/net/messageout.h91
-rw-r--r--src/net/net.cpp198
-rw-r--r--src/net/net.h81
-rw-r--r--src/net/npchandler.h66
-rw-r--r--src/net/packetcounters.cpp128
-rw-r--r--src/net/packetcounters.h55
-rw-r--r--src/net/partyhandler.h82
-rw-r--r--src/net/playerhandler.h75
-rw-r--r--src/net/serverinfo.h117
-rw-r--r--src/net/specialhandler.h45
-rw-r--r--src/net/tmwa/adminhandler.cpp135
-rw-r--r--src/net/tmwa/adminhandler.h69
-rw-r--r--src/net/tmwa/beinghandler.cpp1075
-rw-r--r--src/net/tmwa/beinghandler.h53
-rw-r--r--src/net/tmwa/buysellhandler.cpp231
-rw-r--r--src/net/tmwa/buysellhandler.h59
-rw-r--r--src/net/tmwa/charserverhandler.cpp386
-rw-r--r--src/net/tmwa/charserverhandler.h87
-rw-r--r--src/net/tmwa/chathandler.cpp552
-rw-r--r--src/net/tmwa/chathandler.h87
-rw-r--r--src/net/tmwa/gamehandler.cpp196
-rw-r--r--src/net/tmwa/gamehandler.h85
-rw-r--r--src/net/tmwa/generalhandler.cpp285
-rw-r--r--src/net/tmwa/generalhandler.h83
-rw-r--r--src/net/tmwa/gui/guildtab.cpp150
-rw-r--r--src/net/tmwa/gui/guildtab.h57
-rw-r--r--src/net/tmwa/gui/partytab.cpp242
-rw-r--r--src/net/tmwa/gui/partytab.h57
-rw-r--r--src/net/tmwa/guildhandler.cpp780
-rw-r--r--src/net/tmwa/guildhandler.h84
-rw-r--r--src/net/tmwa/inventoryhandler.cpp609
-rw-r--r--src/net/tmwa/inventoryhandler.h177
-rw-r--r--src/net/tmwa/itemhandler.cpp93
-rw-r--r--src/net/tmwa/itemhandler.h40
-rw-r--r--src/net/tmwa/loginhandler.cpp342
-rw-r--r--src/net/tmwa/loginhandler.h105
-rw-r--r--src/net/tmwa/messagehandler.cpp48
-rw-r--r--src/net/tmwa/messagehandler.h59
-rw-r--r--src/net/tmwa/messagein.cpp85
-rw-r--r--src/net/tmwa/messagein.h52
-rw-r--r--src/net/tmwa/messageout.cpp142
-rw-r--r--src/net/tmwa/messageout.h65
-rw-r--r--src/net/tmwa/network.cpp483
-rw-r--r--src/net/tmwa/network.h136
-rw-r--r--src/net/tmwa/npchandler.cpp249
-rw-r--r--src/net/tmwa/npchandler.h90
-rw-r--r--src/net/tmwa/partyhandler.cpp547
-rw-r--r--src/net/tmwa/partyhandler.h85
-rw-r--r--src/net/tmwa/playerhandler.cpp752
-rw-r--r--src/net/tmwa/playerhandler.h74
-rw-r--r--src/net/tmwa/protocol.h327
-rw-r--r--src/net/tmwa/specialhandler.cpp273
-rw-r--r--src/net/tmwa/specialhandler.h57
-rw-r--r--src/net/tmwa/token.h43
-rw-r--r--src/net/tmwa/tradehandler.cpp347
-rw-r--r--src/net/tmwa/tradehandler.h65
-rw-r--r--src/net/tradehandler.h67
-rw-r--r--src/net/worldinfo.h39
126 files changed, 20359 insertions, 0 deletions
diff --git a/src/net/adminhandler.h b/src/net/adminhandler.h
new file mode 100644
index 000000000..7c24554c9
--- /dev/null
+++ b/src/net/adminhandler.h
@@ -0,0 +1,60 @@
+/*
+ * 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 ADMINHANDLER_H
+#define ADMINHANDLER_H
+
+#include <iosfwd>
+
+namespace Net
+{
+
+class AdminHandler
+{
+ public:
+ virtual ~AdminHandler() {}
+
+ virtual void announce(const std::string &text) = 0;
+
+ virtual void localAnnounce(const std::string &text) = 0;
+
+ virtual void hide(bool hide) = 0;
+
+ virtual void kick(int playerId) = 0;
+
+ virtual void kick(const std::string &name) = 0;
+
+ virtual void ban(int playerId) = 0;
+
+ virtual void ban(const std::string &name) = 0;
+
+ virtual void unban(int playerId) = 0;
+
+ virtual void unban(const std::string &name) = 0;
+
+ virtual void mute(int playerId, int type, int limit) = 0;
+
+ // TODO
+};
+
+} // namespace Net
+
+#endif // ADMINHANDLER_H
diff --git a/src/net/beinghandler.h b/src/net/beinghandler.h
new file mode 100644
index 000000000..89f972e6d
--- /dev/null
+++ b/src/net/beinghandler.h
@@ -0,0 +1,43 @@
+/*
+ * The Mana World
+ * Copyright (C) 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BEINGHANDLER_H
+#define BEINGHANDLER_H
+
+#include "being.h"
+#include "net/messagein.h"
+
+namespace Net
+{
+
+class BeingHandler
+{
+ public:
+ virtual void handleMessage(Net::MessageIn &msg) = 0;
+
+ virtual void requestNameById(int id) = 0;
+
+ virtual void undress(Being *being) = 0;
+};
+
+} // namespace Net
+
+#endif // BEINGHANDLER_H
diff --git a/src/net/buysellhandler.h b/src/net/buysellhandler.h
new file mode 100644
index 000000000..88851e94e
--- /dev/null
+++ b/src/net/buysellhandler.h
@@ -0,0 +1,47 @@
+/*
+ * 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 BUYSELLHANDLER_H
+#define BUYSELLHANDLER_H
+
+#include "net/messagein.h"
+
+#include "being.h"
+#include "shopitem.h"
+
+namespace Net
+{
+
+class BuySellHandler
+{
+ public:
+ virtual void handleMessage(Net::MessageIn &msg) = 0;
+ virtual void requestSellList(std::string nick) = 0;
+ virtual void requestBuyList(std::string nick) = 0;
+ virtual void sendBuyRequest(std::string nick, ShopItem* item,
+ int amount) = 0;
+ virtual void sendSellRequest(std::string nick, ShopItem* item,
+ int amount) = 0;
+};
+
+} // namespace Net
+
+#endif // BUYSELLHANDLER_H
diff --git a/src/net/charhandler.cpp b/src/net/charhandler.cpp
new file mode 100644
index 000000000..699d930a2
--- /dev/null
+++ b/src/net/charhandler.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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/charhandler.h"
+
+#include "gui/charselectdialog.h"
+
+using namespace Net;
+
+void CharHandler::updateCharSelectDialog()
+{
+ if (mCharSelectDialog)
+ mCharSelectDialog->setCharacters(mCharacters);
+}
+
+void CharHandler::unlockCharSelectDialog()
+{
+ if (mCharSelectDialog)
+ mCharSelectDialog->unlock();
+}
diff --git a/src/net/charhandler.h b/src/net/charhandler.h
new file mode 100644
index 000000000..005d995c8
--- /dev/null
+++ b/src/net/charhandler.h
@@ -0,0 +1,112 @@
+/*
+ * 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 CHARHANDLER_H
+#define CHARHANDLER_H
+
+#include "localplayer.h"
+#include "logindata.h"
+#include "playerinfo.h"
+
+#include <iosfwd>
+#include <vector>
+
+class CharCreateDialog;
+class CharSelectDialog;
+
+namespace Net
+{
+
+/**
+ * A structure to hold information about a character.
+ */
+struct Character
+{
+ Character() :
+ slot(0),
+ dummy(0)
+ {
+ }
+
+ ~Character()
+ {
+ delete dummy;
+ dummy = 0;
+ }
+
+ int slot; /**< The index in the list of characters */
+ LocalPlayer *dummy; /**< A dummy representing this character */
+ PlayerInfoBackend data;
+};
+
+typedef std::list<Character*> Characters;
+
+class CharHandler
+{
+ public:
+ virtual ~CharHandler()
+ { }
+
+ virtual void setCharSelectDialog(CharSelectDialog *window) = 0;
+
+ virtual void setCharCreateDialog(CharCreateDialog *window) = 0;
+
+ virtual void requestCharacters() = 0;
+
+ virtual void chooseCharacter(Net::Character *character) = 0;
+
+ virtual void newCharacter(const std::string &name, int slot,
+ bool gender, int hairstyle, int hairColor,
+ const std::vector<int> &stats) = 0;
+
+ virtual void deleteCharacter(Net::Character *character) = 0;
+
+ virtual void switchCharacter() = 0;
+
+ virtual unsigned int baseSprite() const = 0;
+
+ virtual unsigned int hairSprite() const = 0;
+
+ virtual unsigned int maxSprite() const = 0;
+
+ protected:
+ CharHandler():
+ mSelectedCharacter(0),
+ mCharSelectDialog(0),
+ mCharCreateDialog(0)
+ {}
+
+ void updateCharSelectDialog();
+ void unlockCharSelectDialog();
+
+ /** The list of available characters. */
+ Net::Characters mCharacters;
+
+ /** The selected character. */
+ Net::Character *mSelectedCharacter;
+
+ CharSelectDialog *mCharSelectDialog;
+ CharCreateDialog *mCharCreateDialog;
+};
+
+} // namespace Net
+
+#endif // CHARHANDLER_H
diff --git a/src/net/chathandler.h b/src/net/chathandler.h
new file mode 100644
index 000000000..20938c78b
--- /dev/null
+++ b/src/net/chathandler.h
@@ -0,0 +1,71 @@
+/*
+ * 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 CHATHANDLER_H
+#define CHATHANDLER_H
+
+#include <iosfwd>
+
+namespace Net
+{
+class ChatHandler
+{
+ public:
+ virtual ~ChatHandler()
+ { }
+
+ virtual void talk(const std::string &text) = 0;
+
+ virtual void talkRaw(const std::string &text) = 0;
+
+ virtual void me(const std::string &text) = 0;
+
+ virtual void privateMessage(const std::string &recipient,
+ const std::string &text) = 0;
+
+ virtual void channelList() = 0;
+
+ virtual void enterChannel(const std::string &channel,
+ const std::string &password) = 0;
+
+ virtual void quitChannel(int channelId) = 0;
+
+ virtual void sendToChannel(int channelId, const std::string &text) = 0;
+
+ virtual void userList(const std::string &channel) = 0;
+
+ virtual void setChannelTopic(int channelId,
+ const std::string &text) = 0;
+
+ virtual void setUserMode(int channelId, const std::string &name,
+ int mode) = 0;
+
+ virtual void kickUser(int channelId, const std::string &name) = 0;
+
+ virtual void who() = 0;
+
+ virtual void sendRaw(const std::string &args) = 0;
+
+// virtual ~ChatHandler() {}
+};
+}
+
+#endif // CHATHANDLER_H
diff --git a/src/net/download.cpp b/src/net/download.cpp
new file mode 100644
index 000000000..2d391b783
--- /dev/null
+++ b/src/net/download.cpp
@@ -0,0 +1,355 @@
+/*
+ * The Mana Client
+ * 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/download.h"
+
+#include "configuration.h"
+#include "log.h"
+#include "main.h"
+
+#include "utils/stringutils.h"
+
+#include <curl/curl.h>
+
+#include <SDL.h>
+#include <SDL_thread.h>
+
+#include <zlib.h>
+
+const char *DOWNLOAD_ERROR_MESSAGE_THREAD
+ = "Could not create download thread!";
+
+/**
+ * Calculates the Alder-32 checksum for the given file.
+ */
+static unsigned long fadler32(FILE *file)
+{
+ // Obtain file size
+ fseek(file, 0, SEEK_END);
+ long fileSize = ftell(file);
+ rewind(file);
+
+ // Calculate Adler-32 checksum
+ char *buffer = static_cast<char*>(malloc(fileSize));
+ const size_t read = fread(buffer, 1, fileSize, file);
+ unsigned long adler = adler32(0L, Z_NULL, 0);
+ adler = adler32(static_cast<uInt>(adler), (Bytef*)buffer, read);
+ free(buffer);
+
+ return adler;
+}
+
+enum
+{
+ OPTIONS_NONE = 0,
+ OPTIONS_MEMORY = 1
+};
+
+namespace Net
+{
+
+Download::Download(void *ptr, const std::string &url,
+ DownloadUpdate updateFunction, bool ignoreError):
+ mPtr(ptr),
+ mUrl(url),
+ mFileName(""),
+ mWriteFunction(NULL),
+ mUpdateFunction(updateFunction),
+ mThread(NULL),
+ mCurl(NULL),
+ mHeaders(NULL),
+ mIgnoreError(ignoreError)
+{
+ mError = static_cast<char*>(malloc(CURL_ERROR_SIZE + 1));
+ mError[0] = 0;
+
+ mOptions.cancel = false;
+}
+
+Download::~Download()
+{
+ if (mHeaders)
+ curl_slist_free_all(mHeaders);
+
+ int status;
+ if (mThread && SDL_GetThreadID(mThread))
+ SDL_WaitThread(mThread, &status);
+ mThread = 0;
+ free(mError);
+}
+
+void Download::addHeader(const std::string &header)
+{
+ mHeaders = curl_slist_append(mHeaders, header.c_str());
+}
+
+void Download::noCache()
+{
+ addHeader("pragma: no-cache");
+ addHeader("Cache-Control: no-cache");
+}
+
+void Download::setFile(const std::string &filename, Sint64 adler32)
+{
+ mOptions.memoryWrite = false;
+ mFileName = filename;
+
+ if (adler32 > -1)
+ {
+ mAdler = static_cast<unsigned long>(adler32);
+ mOptions.checkAdler = true;
+ }
+ else
+ {
+ mOptions.checkAdler = false;
+ }
+}
+
+void Download::setWriteFunction(WriteFunction write)
+{
+ mOptions.memoryWrite = true;
+ mWriteFunction = write;
+}
+
+bool Download::start()
+{
+ logger->log("Starting download: %s", mUrl.c_str());
+
+ mThread = SDL_CreateThread(downloadThread, this);
+
+ if (!mThread)
+ {
+ logger->log1(DOWNLOAD_ERROR_MESSAGE_THREAD);
+ strcpy(mError, DOWNLOAD_ERROR_MESSAGE_THREAD);
+ mUpdateFunction(mPtr, DOWNLOAD_STATUS_THREAD_ERROR, 0, 0);
+ if (!mIgnoreError)
+ return false;
+ }
+
+ return true;
+}
+
+void Download::cancel()
+{
+ logger->log("Canceling download: %s", mUrl.c_str());
+
+ mOptions.cancel = true;
+ if (mThread && SDL_GetThreadID(mThread))
+ SDL_WaitThread(mThread, NULL);
+
+ mThread = NULL;
+}
+
+char *Download::getError()
+{
+ return mError;
+}
+
+int Download::downloadProgress(void *clientp, double dltotal, double dlnow,
+ double ultotal _UNUSED_, double ulnow _UNUSED_)
+{
+ Download *d = reinterpret_cast<Download*>(clientp);
+ if (!d)
+ return -5;
+
+ if (d->mOptions.cancel)
+ {
+ return d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_CANCELLED,
+ static_cast<size_t>(dltotal),
+ static_cast<size_t>(dlnow));
+ return -5;
+ }
+
+ return d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_IDLE,
+ static_cast<size_t>(dltotal),
+ static_cast<size_t>(dlnow));
+}
+
+int Download::downloadThread(void *ptr)
+{
+ int attempts = 0;
+ bool complete = false;
+ Download *d = reinterpret_cast<Download*>(ptr);
+ CURLcode res;
+ std::string outFilename;
+
+ if (!d)
+ return 0;
+
+ if (!d->mOptions.memoryWrite)
+ outFilename = d->mFileName + ".part";
+
+ while (attempts < 3 && !complete && !d->mOptions.cancel)
+ {
+ FILE *file = NULL;
+
+ d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_STARTING, 0, 0);
+
+ if (d->mOptions.cancel)
+ {
+ //need terminate thread?
+ d->mThread = NULL;
+ return 0;
+ }
+
+ d->mCurl = curl_easy_init();
+
+ if (d->mCurl && !d->mOptions.cancel)
+ {
+ logger->log("Downloading: %s", d->mUrl.c_str());
+
+ curl_easy_setopt(d->mCurl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt(d->mCurl, CURLOPT_HTTPHEADER, d->mHeaders);
+// curl_easy_setopt(d->mCurl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+
+ if (d->mOptions.memoryWrite)
+ {
+ curl_easy_setopt(d->mCurl, CURLOPT_FAILONERROR, 1);
+ curl_easy_setopt(d->mCurl, CURLOPT_WRITEFUNCTION,
+ d->mWriteFunction);
+ curl_easy_setopt(d->mCurl, CURLOPT_WRITEDATA, d->mPtr);
+ }
+ else
+ {
+ file = fopen(outFilename.c_str(), "w+b");
+ curl_easy_setopt(d->mCurl, CURLOPT_WRITEDATA, file);
+ }
+
+ curl_easy_setopt(d->mCurl, CURLOPT_USERAGENT,
+ strprintf(PACKAGE_EXTENDED_VERSION,
+ branding.getStringValue("appShort").c_str()).c_str());
+ curl_easy_setopt(d->mCurl, CURLOPT_ERRORBUFFER, d->mError);
+ curl_easy_setopt(d->mCurl, CURLOPT_URL, d->mUrl.c_str());
+ curl_easy_setopt(d->mCurl, CURLOPT_NOPROGRESS, 0);
+ curl_easy_setopt(d->mCurl, CURLOPT_PROGRESSFUNCTION,
+ downloadProgress);
+ curl_easy_setopt(d->mCurl, CURLOPT_PROGRESSDATA, ptr);
+ curl_easy_setopt(d->mCurl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(d->mCurl, CURLOPT_CONNECTTIMEOUT, 30);
+ curl_easy_setopt(d->mCurl, CURLOPT_TIMEOUT, 1800);
+
+ if ((res = curl_easy_perform(d->mCurl)) != 0
+ && !d->mOptions.cancel)
+ {
+ switch (res)
+ {
+ case CURLE_ABORTED_BY_CALLBACK:
+ d->mOptions.cancel = true;
+ break;
+ case CURLE_COULDNT_CONNECT:
+ default:
+ logger->log("curl error %d: %s host: %s",
+ res, d->mError, d->mUrl.c_str());
+ break;
+ }
+
+ if (d->mOptions.cancel)
+ break;
+
+ d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_ERROR, 0, 0);
+
+ if (!d->mOptions.memoryWrite)
+ {
+ fclose(file);
+ ::remove(outFilename.c_str());
+ }
+ attempts++;
+ continue;
+ }
+
+ curl_easy_cleanup(d->mCurl);
+ d->mCurl = 0;
+
+ if (!d->mOptions.memoryWrite)
+ {
+ // Don't check resources.xml checksum
+ if (d->mOptions.checkAdler)
+ {
+ unsigned long adler = fadler32(file);
+
+ if (d->mAdler != adler)
+ {
+ fclose(file);
+
+ // Remove the corrupted file
+ ::remove(d->mFileName.c_str());
+ logger->log("Checksum for file %s failed: (%lx/%lx)",
+ d->mFileName.c_str(),
+ adler, d->mAdler);
+ attempts++;
+ continue; // Bail out here to avoid the renaming
+ }
+ }
+ fclose(file);
+
+ // Any existing file with this name is deleted first, otherwise
+ // the rename will fail on Windows.
+ ::remove(d->mFileName.c_str());
+ ::rename(outFilename.c_str(), d->mFileName.c_str());
+
+ // Check if we can open it and no errors were encountered
+ // during renaming
+ file = fopen(d->mFileName.c_str(), "rb");
+ if (file)
+ {
+ fclose(file);
+ complete = true;
+ }
+ }
+ else
+ {
+ // It's stored in memory, we're done
+ complete = true;
+ }
+ }
+
+ if (d->mCurl)
+ {
+ curl_easy_cleanup(d->mCurl);
+ d->mCurl = 0;
+ }
+
+ if (d->mOptions.cancel)
+ {
+ //need ternibate thread?
+ d->mThread = NULL;
+ return 0;
+ }
+ attempts++;
+ }
+
+ if (d->mOptions.cancel)
+ {
+ // Nothing to do...
+ }
+ else if (!complete || attempts >= 3)
+ {
+ d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_ERROR, 0, 0);
+ }
+ else
+ {
+ d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_COMPLETE, 0, 0);
+ }
+
+ d->mThread = NULL;
+ return 0;
+}
+
+} // namespace Net
diff --git a/src/net/download.h b/src/net/download.h
new file mode 100644
index 000000000..11bca3238
--- /dev/null
+++ b/src/net/download.h
@@ -0,0 +1,123 @@
+/*
+ * The Mana Client
+ * 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_DOWNLOAD_H
+#define NET_DOWNLOAD_H
+
+#include <SDL_types.h>
+
+#include <stdio.h>
+#include <string>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+enum DownloadStatus
+{
+ DOWNLOAD_STATUS_CANCELLED = -3,
+ DOWNLOAD_STATUS_THREAD_ERROR = -2,
+ DOWNLOAD_STATUS_ERROR = -1,
+ DOWNLOAD_STATUS_STARTING = 0,
+ DOWNLOAD_STATUS_IDLE,
+ DOWNLOAD_STATUS_COMPLETE
+};
+
+typedef int (*DownloadUpdate)(void *ptr, DownloadStatus status,
+ size_t total, size_t remaining);
+
+// Matches what CURL expects
+typedef size_t (*WriteFunction)( void *ptr, size_t size, size_t nmemb,
+ void *stream);
+
+struct SDL_Thread;
+typedef void CURL;
+struct curl_slist;
+
+namespace Net
+{
+class Download
+{
+ public:
+ Download(void *ptr, const std::string &url,
+ DownloadUpdate updateFunction, bool ignoreError = false);
+
+ ~Download();
+
+ void addHeader(const std::string &header);
+
+ /**
+ * Convience method for adding no-cache headers.
+ */
+ void noCache();
+
+ void setFile(const std::string &filename, Sint64 adler32 = -1);
+
+ void setWriteFunction(WriteFunction write);
+
+ /**
+ * Starts the download thread.
+ * @returns true if thread was created
+ * false if the thread could not be made or download wasn't
+ * properly setup
+ */
+ bool start();
+
+ /**
+ * Cancels the download. Returns immediately, the cancelled status will
+ * be noted in the next avialable update call.
+ */
+ void cancel();
+
+ char *getError();
+
+ void setIgnoreError(bool n)
+ { mIgnoreError = n; }
+
+ private:
+ static int downloadThread(void *ptr);
+ static int downloadProgress(void *clientp, double dltotal,
+ double dlnow, double ultotal,
+ double ulnow);
+ void *mPtr;
+ std::string mUrl;
+ struct
+ {
+ unsigned cancel : 1;
+ unsigned memoryWrite: 1;
+ unsigned checkAdler: 1;
+ } mOptions;
+ std::string mFileName;
+ WriteFunction mWriteFunction;
+ unsigned long mAdler;
+ DownloadUpdate mUpdateFunction;
+ SDL_Thread *mThread;
+ CURL *mCurl;
+ curl_slist *mHeaders;
+ char *mError;
+ bool mIgnoreError;
+};
+
+} // namespace Net
+
+#endif // NET_DOWNLOAD_H
diff --git a/src/net/gamehandler.h b/src/net/gamehandler.h
new file mode 100644
index 000000000..a0208f6c7
--- /dev/null
+++ b/src/net/gamehandler.h
@@ -0,0 +1,62 @@
+/*
+ * 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 MAPHANDLER_H
+#define MAPHANDLER_H
+
+#include "logindata.h"
+
+#include <iosfwd>
+
+namespace Net
+{
+
+class GameHandler
+{
+ public:
+ virtual ~GameHandler()
+ {}
+
+ virtual void connect() = 0;
+
+ virtual bool isConnected() = 0;
+
+ virtual void disconnect() = 0;
+
+ virtual void who() = 0;
+
+ virtual void quit() = 0;
+
+ virtual void ping(int tick) = 0;
+
+ virtual bool removeDeadBeings() const = 0;
+
+ virtual void disconnect2() = 0;
+
+ /**
+ * Tells whether the protocol is using the MP status bar
+ */
+ virtual bool canUseMagicBar() const = 0;
+};
+
+} // namespace Net
+
+#endif // MAPHANDLER_H
diff --git a/src/net/generalhandler.h b/src/net/generalhandler.h
new file mode 100644
index 000000000..9454bc7e1
--- /dev/null
+++ b/src/net/generalhandler.h
@@ -0,0 +1,50 @@
+/*
+ * 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 "client.h"
+#include "main.h"
+
+#ifndef GENERALHANDLER_H
+#define GENERALHANDLER_H
+
+namespace Net
+{
+
+class GeneralHandler
+{
+ public:
+ virtual ~GeneralHandler()
+ { }
+
+ virtual void load() = 0;
+
+ virtual void reload() = 0;
+
+ virtual void unload() = 0;
+
+ virtual void flushNetwork() = 0;
+
+ virtual void clearHandlers() = 0;
+};
+
+} // namespace Net
+
+#endif // GENERALHANDLER_H
diff --git a/src/net/guildhandler.h b/src/net/guildhandler.h
new file mode 100644
index 000000000..6a269ff74
--- /dev/null
+++ b/src/net/guildhandler.h
@@ -0,0 +1,76 @@
+/*
+ * 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 GUILDHANDLER_H
+#define GUILDHANDLER_H
+
+#include "guild.h"
+
+#include <iosfwd>
+
+class Being;
+
+namespace Net
+{
+
+class GuildHandler
+{
+ public:
+ virtual ~GuildHandler()
+ { }
+
+ virtual bool isSupported()
+ { return false; }
+
+ virtual void create(const std::string &name) = 0;
+
+ virtual void invite(int guildId, const std::string &name) = 0;
+
+ virtual void invite(int guildId, Being *being) = 0;
+
+ virtual void inviteResponse(int guildId, bool response) = 0;
+
+ virtual void leave(int guildId) = 0;
+
+ virtual void kick(GuildMember *member, std::string reason = "") = 0;
+
+ virtual void chat(int guildId, const std::string &text) = 0;
+
+ virtual void memberList(int guildId) = 0;
+
+ virtual void info(int guildId) = 0;
+
+ virtual void changeMemberPostion(GuildMember *member, int level) = 0;
+
+ virtual void requestAlliance(int guildId, int otherGuildId) = 0;
+
+ virtual void requestAllianceResponse(int guildId, int otherGuildId,
+ bool response) = 0;
+
+ virtual void endAlliance(int guildId, int otherGuildId) = 0;
+
+ virtual void changeNotice(int guildId, std::string msg1,
+ std::string msg2) = 0;
+};
+
+}
+
+#endif // GUILDHANDLER_H
diff --git a/src/net/inventoryhandler.h b/src/net/inventoryhandler.h
new file mode 100644
index 000000000..18ec6968b
--- /dev/null
+++ b/src/net/inventoryhandler.h
@@ -0,0 +1,72 @@
+/*
+ * 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 INVENTORYHANDLER_H
+#define INVENTORYHANDLER_H
+
+#include "inventory.h"
+#include "item.h"
+
+#include <iosfwd>
+
+namespace Net
+{
+
+class InventoryHandler
+{
+ public:
+ virtual ~InventoryHandler()
+ { }
+
+ virtual void equipItem(const Item *item) = 0;
+
+ virtual void unequipItem(const Item *item) = 0;
+
+ virtual void useItem(const Item *item) = 0;
+
+ virtual void dropItem(const Item *item, int amount) = 0;
+
+ virtual bool canSplit(const Item *item) = 0;
+
+ virtual void splitItem(const Item *item, int amount) = 0;
+
+ virtual void moveItem(int oldIndex, int newIndex) = 0;
+
+ virtual void openStorage(int type) = 0;
+
+ virtual void closeStorage(int type) = 0;
+
+ //void changeCart() = 0;
+
+ virtual void moveItem(int source, int slot, int amount,
+ int destination) = 0;
+
+ // TODO: fix/remove me
+ virtual size_t getSize(int type) const = 0;
+
+ virtual int convertFromServerSlot(int eAthenaSlot) = 0;
+
+// virtual ~InventoryHandler() {}
+};
+
+} // namespace Net
+
+#endif // INVENTORYHANDLER_H
diff --git a/src/net/logindata.h b/src/net/logindata.h
new file mode 100644
index 000000000..5617cea32
--- /dev/null
+++ b/src/net/logindata.h
@@ -0,0 +1,95 @@
+/*
+ * 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 LOGINDATA_H
+#define LOGINDATA_H
+
+#include "being.h"
+
+#include "net/serverinfo.h"
+
+#include <string>
+
+class LoginData
+{
+ public:
+ LoginData():
+ username(""),
+ password(""),
+ newPassword(""),
+ updateHost(""),
+ updateType(0),
+ email(""),
+ captchaResponse(""),
+ gender(GENDER_UNSPECIFIED),
+ remember(false),
+ registerLogin(false)
+ {
+ resetCharacterSlots();
+ }
+
+ enum UpdateType
+ {
+ Upd_Normal = 0,
+ Upd_Close = 1,
+ Upd_Skip = 2,
+ Upd_Custom = 4
+ };
+
+ std::string username;
+ std::string password;
+ std::string newPassword;
+ std::string updateHost;
+ int updateType;
+
+ std::string email;
+ std::string captchaResponse;
+
+ Gender gender;
+
+ bool remember; /**< Whether to store the username. */
+ bool registerLogin; /**< Whether an account
+ is being registered. */
+
+ unsigned short characterSlots; /**< The number of character slots */
+
+ void clear()
+ {
+ username.clear();
+ password.clear();
+ newPassword.clear();
+ updateHost.clear();
+ updateType = Upd_Normal;
+ email.clear();
+ captchaResponse.clear();
+ gender = GENDER_UNSPECIFIED;
+ resetCharacterSlots();
+ }
+ /**
+ * Initialize character slots to 3 for TmwAthena compatibility
+ */
+ void resetCharacterSlots()
+ {
+ characterSlots = 3; // Default value, used for TmwAthena.
+ }
+};
+
+#endif // LOGINDATA_H
diff --git a/src/net/loginhandler.h b/src/net/loginhandler.h
new file mode 100644
index 000000000..5511e46a4
--- /dev/null
+++ b/src/net/loginhandler.h
@@ -0,0 +1,112 @@
+/*
+ * 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 LOGINHANDLER_H
+#define LOGINHANDLER_H
+
+#include "net/logindata.h"
+#include "net/serverinfo.h"
+#include "net/worldinfo.h"
+
+#include <iosfwd>
+#include <vector>
+
+namespace Net
+{
+
+class LoginHandler
+{
+ public:
+ /**
+ * This enum describes options specific to either eAthena or Manaserv.
+ * By querying for these flags, the GUI can adapt to the current
+ * server type dynamically.
+ */
+ enum OptionalAction
+ {
+ Unregister = 0x1,
+ ChangeEmail = 0x2,
+ SetEmailOnRegister = 0x4,
+ SetGenderOnRegister = 0x8
+ };
+
+ void setServer(const ServerInfo &server)
+ { mServer = server; }
+
+ ServerInfo getServer() const
+ { return mServer; }
+
+ virtual void connect() = 0;
+
+ virtual bool isConnected() = 0;
+
+ virtual void disconnect() = 0;
+
+ /**
+ * @see OptionalAction
+ */
+ virtual int supportedOptionalActions() const = 0;
+
+ virtual bool isRegistrationEnabled() = 0;
+
+ virtual void getRegistrationDetails() = 0;
+
+ virtual unsigned int getMinUserNameLength() const
+ { return 4; }
+
+ virtual unsigned int getMaxUserNameLength() const
+ { return 25; }
+
+ virtual unsigned int getMinPasswordLength() const
+ { return 4; }
+
+ virtual unsigned int getMaxPasswordLength() const
+ { return 255; }
+
+ virtual void loginAccount(LoginData *loginData) = 0;
+
+ virtual void logout() = 0;
+
+ virtual void changeEmail(const std::string &email) = 0;
+
+ virtual void changePassword(const std::string &username,
+ const std::string &oldPassword,
+ const std::string &newPassword) = 0;
+
+ virtual void chooseServer(unsigned int server) = 0;
+
+ virtual void registerAccount(LoginData *loginData) = 0;
+
+ virtual void unregisterAccount(const std::string &username,
+ const std::string &password) = 0;
+
+ virtual Worlds getWorlds() const = 0;
+
+ virtual ~LoginHandler ()
+ { }
+
+ protected:
+ ServerInfo mServer;
+};
+
+} // namespace Net
+
+#endif // LOGINHANDLER_H
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
diff --git a/src/net/messagehandler.h b/src/net/messagehandler.h
new file mode 100644
index 000000000..3a454b1f9
--- /dev/null
+++ b/src/net/messagehandler.h
@@ -0,0 +1,50 @@
+/*
+ * 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_MESSAGEHANDLER_H
+#define NET_MESSAGEHANDLER_H
+
+#include "net/messagein.h"
+
+#include <SDL_types.h>
+
+#include <memory>
+
+namespace Net
+{
+
+/**
+ * \ingroup Network
+ */
+class MessageHandler
+{
+ public:
+ const Uint16 *handledMessages;
+
+ virtual void handleMessage(MessageIn &msg) = 0;
+
+ virtual ~MessageHandler()
+ { }
+};
+
+}
+
+#endif // NET_MESSAGEHANDLER_H
diff --git a/src/net/messagein.cpp b/src/net/messagein.cpp
new file mode 100644
index 000000000..0ac391ee2
--- /dev/null
+++ b/src/net/messagein.cpp
@@ -0,0 +1,227 @@
+/*
+ * 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/messagein.h"
+
+#include "net/packetcounters.h"
+
+#include "log.h"
+
+#include "utils/stringutils.h"
+
+#define MAKEWORD(low, high) \
+ ((unsigned short)(((unsigned char)(low)) | \
+ ((unsigned short)((unsigned char)(high))) << 8))
+
+namespace Net
+{
+
+MessageIn::MessageIn(const char *data, unsigned int length):
+ mData(data),
+ mLength(length),
+ mPos(0)
+{
+ PacketCounters::incInPackets();
+ DEBUGLOG("MessageIn");
+}
+
+unsigned char MessageIn::readInt8()
+{
+ unsigned char value = -1;
+ if (mPos < mLength)
+ value = static_cast<unsigned char>(mData[mPos]);
+
+ mPos += 1;
+ PacketCounters::incInBytes(1);
+ DEBUGLOG("readInt8: " + toString(static_cast<int>(value)));
+ return value;
+}
+
+void MessageIn::readCoordinates(Uint16 &x, Uint16 &y)
+{
+ if (mPos + 3 <= mLength)
+ {
+ unsigned char const *p
+ = reinterpret_cast< unsigned char const * >(mData + mPos);
+ x = static_cast<short unsigned>(p[0] | ((p[1] & 0x07) << 8));
+ y = static_cast<short unsigned>((p[1] >> 3) | ((p[2] & 0x3F) << 5));
+ }
+ mPos += 3;
+ PacketCounters::incInBytes(3);
+ DEBUGLOG("readCoordinates: " + toString(static_cast<int>(x)) + ","
+ + toString(static_cast<int>(y)));
+}
+
+void MessageIn::readCoordinates(Uint16 &x, Uint16 &y, Uint8 &direction)
+{
+ if (mPos + 3 <= mLength)
+ {
+ const char *data = mData + mPos;
+ Sint16 temp;
+
+ temp = MAKEWORD(data[1] & 0x00c0, data[0] & 0x00ff);
+ x = static_cast<unsigned short>(temp >> 6);
+ temp = MAKEWORD(data[2] & 0x00f0, data[1] & 0x003f);
+ y = static_cast<unsigned short>(temp >> 4);
+
+ direction = data[2] & 0x000f;
+
+ // Translate from eAthena format
+ switch (direction)
+ {
+ case 0:
+ direction = 1;
+ break;
+ case 1:
+ direction = 3;
+ break;
+ case 2:
+ direction = 2;
+ break;
+ case 3:
+ direction = 6;
+ break;
+ case 4:
+ direction = 4;
+ break;
+ case 5:
+ direction = 12;
+ break;
+ case 6:
+ direction = 8;
+ break;
+ case 7:
+ direction = 9;
+ break;
+ default:
+ // OOPSIE! Impossible or unknown
+ direction = 0;
+ }
+ }
+ mPos += 3;
+ PacketCounters::incInBytes(3);
+ DEBUGLOG("readCoordinates: " + toString((int)x) + "," + toString((int)y));
+}
+
+void MessageIn::readCoordinatePair(Uint16 &srcX, Uint16 &srcY,
+ Uint16 &dstX, Uint16 &dstY)
+{
+ if (mPos + 5 <= mLength)
+ {
+ const char *data = mData + mPos;
+ Sint16 temp;
+
+ temp = MAKEWORD(data[3], data[2] & 0x000f);
+ dstX = static_cast<unsigned short>(temp >> 2);
+
+ dstY = MAKEWORD(data[4], data[3] & 0x0003);
+
+ temp = MAKEWORD(data[1], data[0]);
+ srcX = static_cast<unsigned short>(temp >> 6);
+
+ temp = MAKEWORD(data[2], data[1] & 0x003f);
+ srcY = static_cast<unsigned short>(temp >> 4);
+ }
+ mPos += 5;
+ DEBUGLOG("readCoordinatePair: " + toString((int)srcX) + ","
+ + toString((int)srcY) + " " + toString((int)dstX) + ","
+ + toString((int)dstY));
+ PacketCounters::incInBytes(5);
+}
+
+void MessageIn::skip(unsigned int length)
+{
+ mPos += length;
+ PacketCounters::incInBytes(length);
+ DEBUGLOG("skip: " + toString((int)length));
+}
+
+std::string MessageIn::readString(int length)
+{
+ // Get string length
+ if (length < 0)
+ length = readInt16();
+
+ // Make sure the string isn't erroneous
+ if (length < 0 || mPos + length > mLength)
+ {
+ mPos = mLength + 1;
+ DEBUGLOG("readString error");
+ return "";
+ }
+
+ // Read the string
+ char const *stringBeg = mData + mPos;
+ char const *stringEnd
+ = static_cast<char const *>(memchr(stringBeg, '\0', length));
+
+ std::string readString(stringBeg,
+ stringEnd ? stringEnd - stringBeg : length);
+ mPos += length;
+ PacketCounters::incInBytes(length);
+ DEBUGLOG("readString: " + readString);
+ return readString;
+}
+
+std::string MessageIn::readRawString(int length)
+{
+ // Get string length
+ if (length < 0)
+ length = readInt16();
+
+ // Make sure the string isn't erroneous
+ if (length < 0 || mPos + length > mLength)
+ {
+ mPos = mLength + 1;
+ return "";
+ }
+
+ // Read the string
+ char const *stringBeg = mData + mPos;
+ char const *stringEnd
+ = static_cast<char const *>(memchr(stringBeg, '\0', length));
+ std::string readString(stringBeg,
+ stringEnd ? stringEnd - stringBeg : length);
+
+ mPos += length;
+ PacketCounters::incInBytes(length);
+ DEBUGLOG("readString: " + readString);
+
+ if (stringEnd)
+ {
+ long len2 = length - (stringEnd - stringBeg) - 1;
+ char const *stringBeg2 = stringEnd + 1;
+ char const *stringEnd2
+ = static_cast<char const *>(memchr(stringBeg2, '\0', len2));
+ std::string hiddenPart = std::string(stringBeg2,
+ stringEnd2 ? stringEnd2 - stringBeg2 : len2);
+ if (hiddenPart.length() > 0)
+ {
+ DEBUGLOG("readString2: " + hiddenPart);
+
+ return readString + "|" + hiddenPart;
+ }
+ }
+
+ return readString;
+}
+
+}
diff --git a/src/net/messagein.h b/src/net/messagein.h
new file mode 100644
index 000000000..8781b3050
--- /dev/null
+++ b/src/net/messagein.h
@@ -0,0 +1,118 @@
+/*
+ * 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_MESSAGEIN_H
+#define NET_MESSAGEIN_H
+
+#include <SDL_types.h>
+
+#include <string>
+
+namespace Net
+{
+
+/**
+ * Used for parsing an incoming message.
+ *
+ * \ingroup Network
+ */
+class MessageIn
+{
+ public:
+ /**
+ * Returns the message ID.
+ */
+ int getId() const
+ { return mId; }
+
+ /**
+ * Returns the message length.
+ */
+ unsigned int getLength() const
+ { return mLength; }
+
+ /**
+ * Returns the length of unread data.
+ */
+ unsigned int getUnreadLength() const
+ { return mLength - mPos; }
+
+ virtual unsigned char readInt8(); /**< Reads a byte. */
+ virtual Sint16 readInt16() = 0; /**< Reads a short. */
+ virtual int readInt32() = 0; /**< Reads a long. */
+
+ /**
+ * Reads a 3-byte block containing tile-based coordinates. Used by
+ * manaserv.
+ */
+ virtual void readCoordinates(Uint16 &x, Uint16 &y);
+
+ /**
+ * Reads a special 3 byte block used by eAthena, containing x and y
+ * coordinates and direction.
+ */
+ virtual void readCoordinates(Uint16 &x, Uint16 &y, Uint8 &direction);
+
+ /**
+ * Reads a special 5 byte block used by eAthena, containing a source
+ * and destination coordinate pair.
+ */
+ virtual void readCoordinatePair(Uint16 &srcX, Uint16 &srcY,
+ Uint16 &dstX, Uint16 &dstY);
+
+ /**
+ * Skips a given number of bytes.
+ */
+ virtual void skip(unsigned int length);
+
+ /**
+ * Reads a string. If a length is not given (-1), it is assumed
+ * that the length of the string is stored in a short at the
+ * start of the string.
+ */
+ virtual std::string readString(int length = -1);
+
+ virtual std::string readRawString(int length);
+
+ virtual ~MessageIn()
+ { }
+
+ protected:
+ /**
+ * Constructor.
+ */
+ MessageIn(const char *data, unsigned int length);
+
+ const char *mData; /**< The message data. */
+ unsigned int mLength; /**< The length of the data. */
+ unsigned short mId; /**< The message ID. */
+
+ /**
+ * Actual position in the packet. From 0 to packet->length.
+ * A value bigger than packet->length means EOP was reached when
+ * reading it.
+ */
+ unsigned int mPos;
+};
+
+}
+
+#endif // NET_MESSAGEIN_H
diff --git a/src/net/messageout.cpp b/src/net/messageout.cpp
new file mode 100644
index 000000000..5152b73e2
--- /dev/null
+++ b/src/net/messageout.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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/messageout.h"
+
+#include "net/packetcounters.h"
+
+#include "log.h"
+
+#include "utils/stringutils.h"
+
+#include <cstring>
+#include <string>
+
+namespace Net
+{
+
+MessageOut::MessageOut(short id _UNUSED_):
+ mData(0),
+ mDataSize(0),
+ mPos(0)
+{
+ PacketCounters::incOutPackets();
+ DEBUGLOG("MessageOut");
+}
+
+void MessageOut::writeInt8(Sint8 value)
+{
+ DEBUGLOG("writeInt8: " + toString((int)value));
+ expand(1);
+ mData[mPos] = value;
+ mPos += 1;
+ PacketCounters::incOutBytes(1);
+}
+
+void MessageOut::writeString(const std::string &string, int length)
+{
+ DEBUGLOG("writeString: " + string);
+ int stringLength = static_cast<int>(string.length());
+ if (length < 0)
+ {
+ // Write the length at the start if not fixed
+ writeInt16(static_cast<short>(stringLength));
+ length = stringLength;
+ }
+ else if (length < stringLength)
+ {
+ // Make sure the length of the string is no longer than specified
+ stringLength = length;
+ }
+ expand(length);
+
+ // Write the actual string
+ memcpy(mData + mPos, string.c_str(), stringLength);
+
+ // Pad remaining space with zeros
+ if (length > stringLength)
+ memset(mData + mPos + stringLength, '\0', length - stringLength);
+
+ mPos += length;
+ PacketCounters::incOutBytes(length);
+}
+
+char *MessageOut::getData() const
+{
+ return mData;
+}
+
+unsigned int MessageOut::getDataSize() const
+{
+ return mDataSize;
+}
+
+}
diff --git a/src/net/messageout.h b/src/net/messageout.h
new file mode 100644
index 000000000..11a9ff552
--- /dev/null
+++ b/src/net/messageout.h
@@ -0,0 +1,91 @@
+/*
+ * 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_MESSAGEOUT_H
+#define NET_MESSAGEOUT_H
+
+#include <SDL_types.h>
+
+#include <iosfwd>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+namespace Net
+{
+
+/**
+ * Used for building an outgoing message.
+ *
+ * \ingroup Network
+ */
+class MessageOut
+{
+ public:
+ virtual void writeInt8(Sint8 value); /**< Writes a byte. */
+ virtual void writeInt16(Sint16 value) = 0; /**< Writes a short. */
+ virtual void writeInt32(Sint32 value) = 0; /**< Writes a long. */
+
+ /**
+ * Writes a string. If a fixed length is not given (-1), it is stored
+ * as a short at the start of the string.
+ */
+ virtual void writeString(const std::string &string, int length = -1);
+
+ /**
+ * Returns the content of the message.
+ */
+ virtual char *getData() const;
+
+ /**
+ * Returns the length of the data.
+ */
+ virtual unsigned int getDataSize() const;
+
+ virtual ~MessageOut()
+ { }
+
+ protected:
+ /**
+ * Constructor.
+ */
+ MessageOut(short id);
+
+ /**
+ * 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.
+ */
+ virtual void expand(size_t size) = 0;
+
+ char *mData; /**< Data building up. */
+ unsigned int mDataSize; /**< Size of data. */
+ unsigned int mPos; /**< Position in the data. */
+};
+
+}
+
+#endif // NET_MESSAGEOUT_H
diff --git a/src/net/net.cpp b/src/net/net.cpp
new file mode 100644
index 000000000..36e414643
--- /dev/null
+++ b/src/net/net.cpp
@@ -0,0 +1,198 @@
+/*
+ * 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/net.h"
+
+#include "main.h"
+
+#include "net/adminhandler.h"
+#include "net/beinghandler.h"
+#include "net/buysellhandler.h"
+#include "net/charhandler.h"
+#include "net/chathandler.h"
+#include "net/generalhandler.h"
+#include "net/guildhandler.h"
+#include "net/inventoryhandler.h"
+#include "net/loginhandler.h"
+#include "net/gamehandler.h"
+#include "net/npchandler.h"
+#include "net/partyhandler.h"
+#include "net/playerhandler.h"
+#include "net/specialhandler.h"
+#include "net/tradehandler.h"
+
+#include "net/tmwa/generalhandler.h"
+
+#include "net/manaserv/generalhandler.h"
+
+Net::AdminHandler *adminHandler = NULL;
+Net::CharHandler *charHandler = NULL;
+Net::ChatHandler *chatHandler = NULL;
+Net::GeneralHandler *generalHandler = NULL;
+Net::InventoryHandler *inventoryHandler = NULL;
+Net::LoginHandler *loginHandler = NULL;
+Net::GameHandler *gameHandler = NULL;
+Net::GuildHandler *guildHandler = NULL;
+Net::NpcHandler *npcHandler = NULL;
+Net::PartyHandler *partyHandler = NULL;
+Net::PlayerHandler *playerHandler = NULL;
+Net::SpecialHandler *specialHandler = NULL;
+Net::TradeHandler *tradeHandler = NULL;
+Net::BeingHandler *beingHandler = NULL;
+Net::BuySellHandler *buySellHandler = NULL;
+
+Net::AdminHandler *Net::getAdminHandler()
+{
+ return adminHandler;
+}
+
+Net::CharHandler *Net::getCharHandler()
+{
+ return charHandler;
+}
+
+Net::ChatHandler *Net::getChatHandler()
+{
+ return chatHandler;
+}
+
+Net::GameHandler *Net::getGameHandler()
+{
+ return gameHandler;
+}
+
+Net::GeneralHandler *Net::getGeneralHandler()
+{
+ return generalHandler;
+}
+
+Net::GuildHandler *Net::getGuildHandler()
+{
+ return guildHandler;
+}
+
+Net::InventoryHandler *Net::getInventoryHandler()
+{
+ return inventoryHandler;
+}
+
+Net::LoginHandler *Net::getLoginHandler()
+{
+ return loginHandler;
+}
+
+Net::NpcHandler *Net::getNpcHandler()
+{
+ return npcHandler;
+}
+
+Net::PartyHandler *Net::getPartyHandler()
+{
+ return partyHandler;
+}
+
+Net::PlayerHandler *Net::getPlayerHandler()
+{
+ return playerHandler;
+}
+
+Net::SpecialHandler *Net::getSpecialHandler()
+{
+ return specialHandler;
+}
+
+Net::TradeHandler *Net::getTradeHandler()
+{
+ return tradeHandler;
+}
+
+Net::BeingHandler *Net::getBeingHandler()
+{
+ return beingHandler;
+}
+
+Net::BuySellHandler *Net::getBuySellHandler()
+{
+ return buySellHandler;
+}
+
+namespace Net
+{
+ServerInfo::Type networkType = ServerInfo::UNKNOWN;
+
+void connectToServer(const ServerInfo &server)
+{
+ if (server.type == ServerInfo::UNKNOWN)
+ {
+ // TODO: Query the server about itself and choose the netcode based on
+ // that
+ }
+
+ if (networkType == server.type && getGeneralHandler() != NULL)
+ {
+ getGeneralHandler()->reload();
+ }
+ else
+ {
+ if (networkType != ServerInfo::UNKNOWN && getGeneralHandler() != NULL)
+ getGeneralHandler()->unload();
+
+ switch (server.type)
+ {
+ case ServerInfo::MANASERV:
+ new ManaServ::GeneralHandler;
+ break;
+
+ case ServerInfo::TMWATHENA:
+ new TmwAthena::GeneralHandler;
+ break;
+
+ default:
+ // Shouldn't happen...
+ break;
+ }
+
+ getGeneralHandler()->load();
+
+ networkType = server.type;
+ }
+
+ if (getLoginHandler())
+ {
+ getLoginHandler()->setServer(server);
+ getLoginHandler()->connect();
+ }
+}
+
+void unload()
+{
+ GeneralHandler *handler = getGeneralHandler();
+ if (handler)
+ handler->unload();
+}
+
+ServerInfo::Type getNetworkType()
+{
+ return networkType;
+}
+
+} // namespace Net
+
diff --git a/src/net/net.h b/src/net/net.h
new file mode 100644
index 000000000..95fe04f36
--- /dev/null
+++ b/src/net/net.h
@@ -0,0 +1,81 @@
+/*
+ * 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_H
+#define NET_H
+
+/**
+ * \namespace Net
+ *
+ * The network communication layer. It is composed of a host of interfaces that
+ * interact with different aspects of the game. They have different
+ * implementations depending on the type of server the client is connecting to.
+ */
+
+#include "net/serverinfo.h"
+
+namespace Net
+{
+
+class AdminHandler;
+class BeingHandler;
+class CharHandler;
+class ChatHandler;
+class GameHandler;
+class GeneralHandler;
+class GuildHandler;
+class InventoryHandler;
+class LoginHandler;
+class NpcHandler;
+class PartyHandler;
+class PlayerHandler;
+class SpecialHandler;
+class TradeHandler;
+class BuySellHandler;
+
+AdminHandler *getAdminHandler();
+BeingHandler *getBeingHandler();
+CharHandler *getCharHandler();
+ChatHandler *getChatHandler();
+GameHandler *getGameHandler();
+GeneralHandler *getGeneralHandler();
+GuildHandler *getGuildHandler();
+InventoryHandler *getInventoryHandler();
+LoginHandler *getLoginHandler();
+NpcHandler *getNpcHandler();
+PartyHandler *getPartyHandler();
+PlayerHandler *getPlayerHandler();
+SpecialHandler *getSpecialHandler();
+TradeHandler *getTradeHandler();
+BuySellHandler *getBuySellHandler();
+
+ServerInfo::Type getNetworkType();
+
+/**
+ * Handles server detection and connection
+ */
+void connectToServer(const ServerInfo &server);
+
+void unload();
+
+} // namespace Net
+
+#endif // NET_H
diff --git a/src/net/npchandler.h b/src/net/npchandler.h
new file mode 100644
index 000000000..1b08a83ec
--- /dev/null
+++ b/src/net/npchandler.h
@@ -0,0 +1,66 @@
+/*
+ * 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 NPCHANDLER_H
+#define NPCHANDLER_H
+
+#include <iosfwd>
+
+namespace Net
+{
+
+class NpcHandler
+{
+ public:
+ virtual ~NpcHandler()
+ { }
+
+ virtual void talk(int npcId) = 0;
+
+ virtual void nextDialog(int npcId) = 0;
+
+ virtual void closeDialog(int npcId) = 0;
+
+ virtual void listInput(int npcId, unsigned char value) = 0;
+
+ virtual void integerInput(int npcId, int value) = 0;
+
+ virtual void stringInput(int npcId, const std::string &value) = 0;
+
+ virtual void sendLetter(int npcId, const std::string &recipient,
+ const std::string &text) = 0;
+
+ virtual void startShopping(int beingId) = 0;
+
+ virtual void buy(int beingId) = 0;
+
+ virtual void sell(int beingId) = 0;
+
+ virtual void buyItem(int beingId, int itemId, int amount) = 0;
+
+ virtual void sellItem(int beingId, int itemId, int amount) = 0;
+
+ virtual void endShopping(int beingId) = 0;
+};
+
+} // namespace Net
+
+#endif // NPCHANDLER_H
diff --git a/src/net/packetcounters.cpp b/src/net/packetcounters.cpp
new file mode 100644
index 000000000..40ab72024
--- /dev/null
+++ b/src/net/packetcounters.cpp
@@ -0,0 +1,128 @@
+/*
+ * 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/packetcounters.h"
+
+extern volatile int cur_time;
+extern volatile bool runCounters;
+
+int PacketCounters::mInCurrentSec = 0;
+int PacketCounters::mInBytes = 0;
+int PacketCounters::mInBytesCalc = 0;
+int PacketCounters::mInPackets = 0;
+int PacketCounters::mInPacketsCalc = 0;
+int PacketCounters::mOutCurrentSec = 0;
+int PacketCounters::mOutBytes = 0;
+int PacketCounters::mOutBytesCalc = 0;
+int PacketCounters::mOutPackets = 0;
+int PacketCounters::mOutPacketsCalc = 0;
+
+void PacketCounters::incInBytes(int cnt)
+{
+ if (!runCounters)
+ return;
+
+ updateCounter(PacketCounters::mInCurrentSec, PacketCounters::mInBytesCalc,
+ PacketCounters::mInBytes);
+
+ PacketCounters::mInBytes += cnt;
+}
+
+void PacketCounters::incInPackets()
+{
+ if (!runCounters)
+ return;
+
+ updateCounter(PacketCounters::mInCurrentSec,
+ PacketCounters::mInPacketsCalc, PacketCounters::mInPackets);
+
+ PacketCounters::mInPackets ++;
+}
+
+int PacketCounters::getInBytes()
+{
+ return PacketCounters::mInBytesCalc;
+}
+
+int PacketCounters::getInPackets()
+{
+ return PacketCounters::mInPacketsCalc;
+}
+
+void PacketCounters::incOutBytes(int cnt)
+{
+ if (!runCounters)
+ return;
+
+ updateCounter(PacketCounters::mOutCurrentSec,
+ PacketCounters::mOutBytesCalc, PacketCounters::mOutBytes);
+
+ PacketCounters::mOutBytes += cnt;
+}
+
+void PacketCounters::incOutPackets()
+{
+ if (!runCounters)
+ return;
+
+ updateCounter(PacketCounters::mOutCurrentSec,
+ PacketCounters::mOutPacketsCalc,
+ PacketCounters::mOutPackets);
+
+ PacketCounters::mOutPackets ++;
+}
+
+int PacketCounters::getOutBytes()
+{
+ return PacketCounters::mOutBytesCalc;
+}
+
+int PacketCounters::getOutPackets()
+{
+ return PacketCounters::mOutPacketsCalc;
+}
+
+
+void PacketCounters::updateCounter(int &currentSec, int &calc, int &counter)
+{
+ int idx = cur_time % 60;
+ if (currentSec != idx)
+ {
+ currentSec = idx;
+ calc = counter;
+ counter = 0;
+ }
+}
+
+void PacketCounters::update()
+{
+ if (!runCounters)
+ return;
+
+ updateCounter(PacketCounters::mInCurrentSec, PacketCounters::mInBytesCalc,
+ PacketCounters::mInBytes);
+ updateCounter(PacketCounters::mInCurrentSec,
+ PacketCounters::mInPacketsCalc, PacketCounters::mInPackets);
+ updateCounter(PacketCounters::mOutCurrentSec,
+ PacketCounters::mOutBytesCalc, PacketCounters::mOutBytes);
+ updateCounter(PacketCounters::mOutCurrentSec,
+ PacketCounters::mOutPacketsCalc, PacketCounters::mOutPackets);
+}
diff --git a/src/net/packetcounters.h b/src/net/packetcounters.h
new file mode 100644
index 000000000..1e1aa7d83
--- /dev/null
+++ b/src/net/packetcounters.h
@@ -0,0 +1,55 @@
+/*
+ * 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 PACKETCOUNTERS_H
+#define PACKETCOUNTERS_H
+
+class PacketCounters
+{
+public:
+// PacketCounters();
+ static void incInBytes(int cnt);
+ static void incInPackets();
+ static int getInBytes();
+ static int getInPackets();
+ static void incOutBytes(int cnt);
+ static void incOutPackets();
+ static int getOutBytes();
+ static int getOutPackets();
+ static void update();
+
+ static int mInCurrentSec;
+ static int mInBytes;
+ static int mInBytesCalc;
+ static int mInPackets;
+ static int mInPacketsCalc;
+ static int mOutCurrentSec;
+ static int mOutBytes;
+ static int mOutBytesCalc;
+ static int mOutPackets;
+ static int mOutPacketsCalc;
+
+private:
+ static void updateCounter(int &currentSec, int &calc, int &counter);
+
+};
+
+#endif
diff --git a/src/net/partyhandler.h b/src/net/partyhandler.h
new file mode 100644
index 000000000..461334ece
--- /dev/null
+++ b/src/net/partyhandler.h
@@ -0,0 +1,82 @@
+/*
+ * 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 PARTYHANDLER_H
+#define PARTYHANDLER_H
+
+#include <string>
+
+class Being;
+
+enum PartyShare
+{
+ PARTY_SHARE_UNKNOWN = -1,
+ PARTY_SHARE_NO,
+ PARTY_SHARE,
+ PARTY_SHARE_NOT_POSSIBLE = 2
+};
+
+namespace Net
+{
+
+class PartyHandler
+{
+ public:
+ virtual ~PartyHandler()
+ { }
+
+ virtual void create(const std::string &name = "") = 0;
+
+ virtual void join(int partyId) = 0;
+
+ virtual void invite(Being *player) = 0;
+
+ virtual void invite(const std::string &name) = 0;
+
+ virtual void inviteResponse(const std::string &inviter,
+ bool accept) = 0;
+
+ virtual void leave() = 0;
+
+ virtual void kick(Being *player) = 0;
+
+ virtual void kick(const std::string &name) = 0;
+
+ virtual void chat(const std::string &text) = 0;
+
+ virtual void requestPartyMembers() = 0;
+
+ virtual PartyShare getShareExperience() = 0;
+
+ virtual void setShareExperience(PartyShare share) = 0;
+
+ virtual PartyShare getShareItems() = 0;
+
+ virtual void setShareItems(PartyShare share) = 0;
+
+ // virtual void options() = 0;
+
+ // virtual void message() = 0;
+};
+
+} // namespace Net
+
+#endif // PARTYHANDLER_H
diff --git a/src/net/playerhandler.h b/src/net/playerhandler.h
new file mode 100644
index 000000000..8fa84b38f
--- /dev/null
+++ b/src/net/playerhandler.h
@@ -0,0 +1,75 @@
+/*
+ * 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 PLAYERHANDLER_H
+#define PLAYERHANDLER_H
+
+#include "being.h"
+#include "flooritem.h"
+#include "localplayer.h"
+
+namespace Net
+{
+
+class PlayerHandler
+{
+ public:
+ virtual ~PlayerHandler()
+ { }
+
+ virtual void attack(int id, bool keep = false) = 0;
+
+ virtual void stopAttack() = 0;
+
+ virtual void emote(int emoteId) = 0;
+
+ virtual void increaseAttribute(int attr) = 0;
+
+ virtual void decreaseAttribute(int attr) = 0;
+
+ virtual void increaseSkill(int skillId) = 0;
+
+ virtual void pickUp(FloorItem *floorItem) = 0;
+
+ virtual void setDirection(char direction) = 0;
+
+ virtual void setDestination(int x, int y, int direction = -1) = 0;
+
+ virtual void changeAction(Being::Action action) = 0;
+
+ virtual void respawn() = 0;
+
+ virtual void ignorePlayer(const std::string &player, bool ignore) = 0;
+
+ virtual void ignoreAll(bool ignore) = 0;
+
+ virtual bool canUseMagic() = 0;
+
+ virtual bool canCorrectAttributes() = 0;
+
+ virtual int getJobLocation() = 0;
+
+ virtual Vector getDefaultWalkSpeed() = 0;
+};
+
+} // namespace Net
+
+#endif // PLAYERHANDLER_H
diff --git a/src/net/serverinfo.h b/src/net/serverinfo.h
new file mode 100644
index 000000000..113d8a9b2
--- /dev/null
+++ b/src/net/serverinfo.h
@@ -0,0 +1,117 @@
+/*
+ * 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 SERVERINFO_H
+#define SERVERINFO_H
+
+#include "utils/stringutils.h"
+
+#include <string>
+#include <vector>
+
+class ServerInfo
+{
+public:
+ enum Type
+ {
+ UNKNOWN = 0,
+ MANASERV,
+ TMWATHENA
+ };
+
+ typedef std::pair<int, std::string> VersionString;
+
+ Type type;
+ std::string name;
+ std::string hostname;
+ unsigned short port;
+
+ std::string description;
+ VersionString version;
+
+ bool save;
+
+ ServerInfo()
+ {
+ type = TMWATHENA;
+ port = 0;
+ save = false;
+ version.first = 0;
+ }
+
+ ServerInfo(const ServerInfo &info)
+ {
+ type = info.type;
+ name = info.name;
+ hostname = info.hostname;
+ port = info.port;
+ description = info.description;
+ version.first = info.version.first;
+ version.second = info.version.second;
+ save = info.save;
+ }
+
+ bool isValid() const
+ {
+ return !(hostname.empty() || port == 0 || type == UNKNOWN);
+ }
+
+ void clear()
+ {
+ type = UNKNOWN;
+ name.clear();
+ hostname.clear();
+ port = 0;
+ description.clear();
+ version.first = 0;
+ version.second.clear();
+ save = false;
+ }
+
+ bool operator==(const ServerInfo &other) const
+ {
+ return (type == other.type && hostname == other.hostname &&
+ port == other.port);
+ }
+
+ bool operator!=(const ServerInfo &other) const
+ {
+ return (type != other.type || hostname != other.hostname ||
+ port != other.port);
+ }
+
+ static Type parseType(const std::string &type)
+ {
+ if (compareStrI(type, "tmwathena") == 0)
+ return TMWATHENA;
+ // Used for backward compatibility
+ else if (compareStrI(type, "eathena") == 0)
+ return TMWATHENA;
+ else if (compareStrI(type, "manaserv") == 0)
+ return MANASERV;
+
+ return UNKNOWN;
+ }
+};
+
+typedef std::vector<ServerInfo> ServerInfos;
+
+#endif // SERVERINFO_H
diff --git a/src/net/specialhandler.h b/src/net/specialhandler.h
new file mode 100644
index 000000000..cc3a09356
--- /dev/null
+++ b/src/net/specialhandler.h
@@ -0,0 +1,45 @@
+/*
+ * 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 SPECIALHANDLER_H
+#define SPECIALHANDLER_H
+
+#include <iosfwd>
+
+namespace Net
+{
+class SpecialHandler
+{
+ public:
+ virtual ~SpecialHandler ()
+ { }
+
+ virtual void use(int id) = 0;
+
+ virtual void use(int id, int level, int beingId) = 0;
+
+ virtual void use(int id, int level, int x, int y) = 0;
+
+ virtual void use(int id, const std::string &map) = 0;
+};
+}
+
+#endif // SPECIALHANDLER_H
diff --git a/src/net/tmwa/adminhandler.cpp b/src/net/tmwa/adminhandler.cpp
new file mode 100644
index 000000000..114c9a897
--- /dev/null
+++ b/src/net/tmwa/adminhandler.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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/tmwa/adminhandler.h"
+
+#include "actorspritemanager.h"
+#include "being.h"
+#include "event.h"
+#include "game.h"
+#include "playerrelations.h"
+
+#include "net/chathandler.h"
+#include "net/net.h"
+
+#include "net/tmwa/protocol.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include <string>
+
+extern Net::AdminHandler *adminHandler;
+
+namespace TmwAthena
+{
+
+AdminHandler::AdminHandler()
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_ADMIN_KICK_ACK,
+ 0
+ };
+ handledMessages = _messages;
+ adminHandler = this;
+}
+
+void AdminHandler::handleMessage(Net::MessageIn &msg)
+{
+ int id;
+ switch (msg.getId())
+ {
+ case SMSG_ADMIN_KICK_ACK:
+ id = msg.readInt32();
+ if (id == 0)
+ SERVER_NOTICE(_("Kick failed!"))
+ else
+ SERVER_NOTICE(_("Kick succeeded!"))
+ default:
+ break;
+ }
+}
+
+void AdminHandler::announce(const std::string &text)
+{
+ MessageOut outMsg(CMSG_ADMIN_ANNOUNCE);
+ outMsg.writeInt16(static_cast<Sint16>(text.length() + 4));
+ outMsg.writeString(text, static_cast<int>(text.length()));
+}
+
+void AdminHandler::localAnnounce(const std::string &text)
+{
+ MessageOut outMsg(CMSG_ADMIN_LOCAL_ANNOUNCE);
+ outMsg.writeInt16(static_cast<Sint16>(text.length() + 4));
+ outMsg.writeString(text, static_cast<int>(text.length()));
+}
+
+void AdminHandler::hide(bool hide _UNUSED_)
+{
+ MessageOut outMsg(CMSG_ADMIN_HIDE);
+ outMsg.writeInt32(0); //unused
+}
+
+void AdminHandler::kick(int playerId)
+{
+ MessageOut outMsg(CMSG_ADMIN_KICK);
+ outMsg.writeInt32(playerId);
+}
+
+void AdminHandler::kick(const std::string &name)
+{
+ Net::getChatHandler()->talk("@kick " + name);
+}
+
+void AdminHandler::ban(int playerId _UNUSED_)
+{
+ // Not supported
+}
+
+void AdminHandler::ban(const std::string &name)
+{
+ Net::getChatHandler()->talk("@ban " + name);
+}
+
+void AdminHandler::unban(int playerId _UNUSED_)
+{
+ // Not supported
+}
+
+void AdminHandler::unban(const std::string &name)
+{
+ Net::getChatHandler()->talk("@unban " + name);
+}
+
+void AdminHandler::mute(int playerId _UNUSED_, int type _UNUSED_,
+ int limit _UNUSED_)
+{
+ return; // Still looking into this
+/*
+ MessageOut outMsg(CMSG_ADMIN_MUTE);
+ outMsg.writeInt32(playerId);
+ outMsg.writeInt8(type);
+ outMsg.writeInt16(limit);
+*/
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/adminhandler.h b/src/net/tmwa/adminhandler.h
new file mode 100644
index 000000000..a7febf441
--- /dev/null
+++ b/src/net/tmwa/adminhandler.h
@@ -0,0 +1,69 @@
+/*
+ * 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_TA_ADMINHANDLER_H
+#define NET_TA_ADMINHANDLER_H
+
+#include "net/adminhandler.h"
+#include "net/net.h"
+
+#include "net/tmwa/messagehandler.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+namespace TmwAthena
+{
+
+class AdminHandler : public MessageHandler, public Net::AdminHandler
+{
+ public:
+ AdminHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ 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 TmwAthena
+
+#endif // NET_TA_ADMINHANDLER_H
diff --git a/src/net/tmwa/beinghandler.cpp b/src/net/tmwa/beinghandler.cpp
new file mode 100644
index 000000000..ea4978a20
--- /dev/null
+++ b/src/net/tmwa/beinghandler.cpp
@@ -0,0 +1,1075 @@
+/*
+ * 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/tmwa/beinghandler.h"
+
+#include "actorspritemanager.h"
+#include "being.h"
+#include "client.h"
+#include "effectmanager.h"
+#include "guild.h"
+#include "keyboardconfig.h"
+#include "localplayer.h"
+#include "log.h"
+#include "party.h"
+#include "playerrelations.h"
+#include "configuration.h"
+
+#include "gui/botcheckerwindow.h"
+#include "gui/outfitwindow.h"
+#include "gui/socialwindow.h"
+#include "gui/killstats.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include "net/playerhandler.h"
+#include "net/net.h"
+
+#include "net/tmwa/protocol.h"
+
+#include "resources/colordb.h"
+
+#include <iostream>
+
+extern Net::BeingHandler *beingHandler;
+
+namespace TmwAthena
+{
+
+const int EMOTION_TIME = 500; /**< Duration of emotion icon */
+
+Being *createBeing(int id, short job);
+
+BeingHandler::BeingHandler(bool enableSync):
+ mSync(enableSync)
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_BEING_VISIBLE,
+ SMSG_BEING_MOVE,
+ SMSG_BEING_MOVE2,
+ SMSG_BEING_REMOVE,
+ SMSG_SKILL_DAMAGE,
+ SMSG_BEING_ACTION,
+ SMSG_BEING_SELFEFFECT,
+ SMSG_BEING_EMOTION,
+ SMSG_BEING_CHANGE_LOOKS,
+ SMSG_BEING_CHANGE_LOOKS2,
+ SMSG_BEING_NAME_RESPONSE,
+ SMSG_PLAYER_GUILD_PARTY_INFO,
+ SMSG_BEING_CHANGE_DIRECTION,
+ SMSG_PLAYER_UPDATE_1,
+ SMSG_PLAYER_UPDATE_2,
+ SMSG_PLAYER_MOVE,
+ SMSG_PLAYER_STOP,
+ SMSG_PLAYER_MOVE_TO_ATTACK,
+ SMSG_PLAYER_STATUS_CHANGE,
+ SMSG_BEING_STATUS_CHANGE,
+ SMSG_BEING_RESURRECT,
+ SMSG_SOLVE_CHAR_NAME,
+ SMSG_BEING_SPAWN,
+ SMSG_SKILL_CASTING,
+ SMSG_SKILL_CAST_CANCEL,
+ SMSG_SKILL_NO_DAMAGE,
+ SMSG_BEING_IP_RESPONSE,
+ SMSG_PVP_MAP_MODE,
+ SMSG_PVP_SET,
+ 0
+ };
+ handledMessages = _messages;
+ beingHandler = this;
+}
+
+Being *createBeing(int id, short job)
+{
+ if (!actorSpriteManager)
+ return 0;
+
+ ActorSprite::Type type = ActorSprite::UNKNOWN;
+ if (job <= 25 || (job >= 4001 && job <= 4049))
+ type = ActorSprite::PLAYER;
+ else if (job >= 46 && job <= 1000)
+ type = ActorSprite::NPC;
+ else if (job > 1000 && job <= 2000)
+ type = ActorSprite::MONSTER;
+ else if (job == 45)
+ type = ActorSprite::PORTAL;
+
+ Being *being = actorSpriteManager->createBeing(id, type, job);
+
+ if (type == ActorSprite::PLAYER || type == ActorSprite::NPC)
+ {
+ if (!being->updateFromCache())
+ {
+ MessageOut outMsg(0x0094);
+ outMsg.writeInt32(id); //readLong(2));
+ }
+ else
+ {
+ if (player_node)
+ player_node->checkNewName(being);
+ }
+ }
+ if (type == Being::PLAYER)
+ {
+ if (botCheckerWindow)
+ botCheckerWindow->updateList();
+ if (socialWindow)
+ socialWindow->updateActiveList();
+ }
+ return being;
+}
+
+void BeingHandler::requestNameById(int id)
+{
+ MessageOut outMsg(0x0094);
+ outMsg.writeInt32(id); //readLong(2));
+}
+
+void BeingHandler::handleMessage(Net::MessageIn &msg)
+{
+ if (!actorSpriteManager)
+ return;
+
+ int id;
+ short job, speed, gender;
+ Uint16 headTop, headMid, headBottom;
+ Uint16 shoes, gloves;
+ Uint16 weapon, shield;
+ Uint16 gmstatus;
+ int param1;
+ Uint16 stunMode;
+ int level;
+ Uint32 statusEffects;
+ int type, guild;
+ Uint16 status;
+ Being *srcBeing, *dstBeing;
+ int hairStyle, hairColor, flag;
+ int hp, maxHP, oldHP;
+
+ switch (msg.getId())
+ {
+ case SMSG_BEING_VISIBLE:
+ case SMSG_BEING_MOVE:
+ // Information about a being in range
+ id = msg.readInt32();
+ speed = msg.readInt16();
+ stunMode = msg.readInt16(); // opt1
+ statusEffects = msg.readInt16(); // opt2
+ statusEffects |= ((Uint32)msg.readInt16()) << 16; // option
+ job = msg.readInt16(); // class
+
+ dstBeing = actorSpriteManager->findBeing(id);
+
+ if (dstBeing && dstBeing->getType() == Being::MONSTER
+ && !dstBeing->isAlive())
+ {
+ actorSpriteManager->destroy(dstBeing);
+ actorSpriteManager->erase(dstBeing);
+ dstBeing = 0;
+ }
+
+ if (!dstBeing)
+ {
+ // Being with id >= 110000000 and job 0 are better
+ // known as ghosts, so don't create those.
+ if (job == 0 && id >= 110000000)
+ break;
+
+ if (actorSpriteManager->isBlocked(id) == true)
+ break;
+
+ dstBeing = createBeing(id, job);
+
+ if (!dstBeing)
+ break;
+
+ if (job == 1022 && killStats)
+ killStats->jackoAlive(dstBeing->getId());
+ }
+ else
+ {
+ // undeleting marked for deletion being
+ if (dstBeing->getType() == Being::NPC)
+ {
+ actorSpriteManager->undelete(dstBeing);
+ }
+ }
+
+ if (dstBeing->getType() == Being::PLAYER)
+ dstBeing->setMoveTime();
+
+ if (msg.getId() == SMSG_BEING_VISIBLE)
+ {
+ dstBeing->clearPath();
+ dstBeing->setActionTime(tick_time);
+ dstBeing->setAction(Being::STAND);
+ }
+
+
+ // Prevent division by 0 when calculating frame
+ if (speed == 0)
+ speed = 150;
+
+ dstBeing->setWalkSpeed(Vector(speed, speed, 0));
+ dstBeing->setSubtype(job);
+ if (dstBeing->getType() == ActorSprite::MONSTER && player_node)
+ player_node->checkNewName(dstBeing);
+
+ hairStyle = msg.readInt16();
+ weapon = msg.readInt16();
+ headBottom = msg.readInt16();
+
+ if (msg.getId() == SMSG_BEING_MOVE)
+ msg.readInt32(); // server tick
+
+ shield = msg.readInt16();
+ headTop = msg.readInt16();
+ headMid = msg.readInt16();
+ hairColor = msg.readInt16();
+ shoes = msg.readInt16(); // clothes color - "abused" as shoes
+
+ if (dstBeing->getType() == ActorSprite::MONSTER)
+ {
+ hp = msg.readInt32();
+ maxHP = msg.readInt32();
+ if (hp && maxHP)
+ {
+ oldHP = dstBeing->getHP();
+ if (!oldHP || oldHP > hp)
+ dstBeing->setHP(hp);
+ dstBeing->setMaxHP(maxHP);
+ }
+ gloves = 0;
+ guild = 0;
+ }
+ else
+ {
+ gloves = msg.readInt16(); // head dir - "abused" as gloves
+ guild = msg.readInt32(); // guild
+ msg.readInt16(); // guild emblem
+ }
+// logger->log("being guild: " + toString(guild));
+/*
+ if (guild == 0)
+ dstBeing->clearGuilds();
+ else
+ dstBeing->setGuild(Guild::getGuild(static_cast<short>(guild)));
+*/
+
+ msg.readInt16(); // manner
+ dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3
+ msg.readInt8(); // karma
+ gender = msg.readInt8();
+
+ if (dstBeing->getType() == ActorSprite::PLAYER)
+ {
+ dstBeing->setGender((gender == 0)
+ ? GENDER_FEMALE : GENDER_MALE);
+ // Set these after the gender, as the sprites may be gender-specific
+ dstBeing->setSprite(SPRITE_HAIR, hairStyle * -1,
+ ColorDB::get(hairColor));
+ dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom);
+ dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid);
+ dstBeing->setSprite(SPRITE_HAT, headTop);
+ dstBeing->setSprite(SPRITE_SHOE, shoes);
+ dstBeing->setSprite(SPRITE_GLOVES, gloves);
+ dstBeing->setSprite(SPRITE_WEAPON, weapon, "", true);
+ if (!config.getBoolValue("hideShield"))
+ dstBeing->setSprite(SPRITE_SHIELD, shield);
+ }
+
+ if (msg.getId() == SMSG_BEING_MOVE)
+ {
+ Uint16 srcX, srcY, dstX, dstY;
+ msg.readCoordinatePair(srcX, srcY, dstX, dstY);
+ dstBeing->setAction(Being::STAND);
+ dstBeing->setTileCoords(srcX, srcY);
+ dstBeing->setDestination(dstX, dstY);
+
+// if (player_node && player_node->getTarget() == dstBeing)
+// player_node->targetMoved();
+ }
+ else
+ {
+ Uint8 dir;
+ Uint16 x, y;
+ msg.readCoordinates(x, y, dir);
+ dstBeing->setTileCoords(x, y);
+
+ if (job == 45 && socialWindow && outfitWindow)
+ {
+ int num = socialWindow->getPortalIndex(x, y);
+ if (num >= 0)
+ {
+ dstBeing->setName(keyboard.getKeyShortString(
+ outfitWindow->keyName(num)));
+ }
+ else
+ {
+ dstBeing->setName("");
+ }
+ }
+
+ dstBeing->setDirection(getDirection(dir));
+ }
+
+ msg.readInt8(); // unknown
+ msg.readInt8(); // unknown
+// msg.readInt8(); // unknown / sit
+ msg.readInt16();
+
+ dstBeing->setStunMode(stunMode);
+ dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff);
+ dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
+ break;
+
+ case SMSG_BEING_MOVE2:
+ /*
+ * A simplified movement packet, used by the
+ * later versions of eAthena for both mobs and
+ * players
+ */
+ dstBeing = actorSpriteManager->findBeing(msg.readInt32());
+
+ /*
+ * This packet doesn't have enough info to actually
+ * create a new being, so if the being isn't found,
+ * we'll just pretend the packet didn't happen
+ */
+
+ if (!dstBeing)
+ break;
+
+ Uint16 srcX, srcY, dstX, dstY;
+ msg.readCoordinatePair(srcX, srcY, dstX, dstY);
+ msg.readInt32(); // Server tick
+
+ dstBeing->setAction(Being::STAND);
+ dstBeing->setTileCoords(srcX, srcY);
+ dstBeing->setDestination(dstX, dstY);
+ if (dstBeing->getType() == Being::PLAYER)
+ dstBeing->setMoveTime();
+
+// if (player_node && player_node->getTarget() == dstBeing)
+// {
+// logger->log("SMSG_BEING_MOVE2");
+// player_node->targetMoved();
+// }
+
+ break;
+
+ case SMSG_BEING_SPAWN:
+ // skipping this packet
+ msg.readInt32(); // id
+ msg.readInt16(); // speed
+ msg.readInt16(); // opt1
+ msg.readInt16(); // opt2
+ msg.readInt16(); // option
+ msg.readInt16(); // disguise
+ break;
+
+ case SMSG_BEING_REMOVE:
+ // A being should be removed or has died
+
+ id = msg.readInt32();
+ dstBeing = actorSpriteManager->findBeing(id);
+ if (!dstBeing)
+ break;
+
+ player_node->followMoveTo(dstBeing, player_node->getNextDestX(),
+ player_node->getNextDestY());
+
+ // If this is player's current target, clear it.
+ if (dstBeing == player_node->getTarget())
+ player_node->stopAttack();
+
+ if (msg.readInt8() == 1)
+ {
+ dstBeing->setAction(Being::DEAD);
+ if (dstBeing->getName() == "Jack O" && killStats)
+ killStats->jackoDead(id);
+ }
+ else
+ {
+ if (dstBeing->getType() == Being::PLAYER)
+ {
+ if (botCheckerWindow)
+ botCheckerWindow->updateList();
+ if (socialWindow)
+ socialWindow->updateActiveList();
+ }
+ actorSpriteManager->destroy(dstBeing);
+ }
+ break;
+
+ case SMSG_BEING_RESURRECT:
+ // A being changed mortality status
+
+ id = msg.readInt32();
+ dstBeing = actorSpriteManager->findBeing(id);
+ if (!dstBeing)
+ break;
+
+ // If this is player's current target, clear it.
+ if (dstBeing == player_node->getTarget())
+ player_node->stopAttack();
+
+ if (msg.readInt8() == 1)
+ dstBeing->setAction(Being::STAND);
+
+ break;
+
+ case SMSG_SKILL_DAMAGE:
+ msg.readInt16(); // Skill Id
+ srcBeing = actorSpriteManager->findBeing(msg.readInt32());
+ dstBeing = actorSpriteManager->findBeing(msg.readInt32());
+ msg.readInt32(); // Server tick
+ msg.readInt32(); // src speed
+ msg.readInt32(); // dst speed
+ param1 = msg.readInt32(); // Damage
+ msg.readInt16(); // Skill level
+ msg.readInt16(); // Div
+ msg.readInt8(); // Skill hit/type (?)
+ if (dstBeing)
+ {
+ // Perhaps a new skill attack type should be created and used?
+ dstBeing->takeDamage(srcBeing, param1, Being::HIT);
+ }
+ if (srcBeing)
+ srcBeing->handleAttack(dstBeing, param1, Being::HIT);
+ break;
+
+ case SMSG_BEING_ACTION:
+ srcBeing = actorSpriteManager->findBeing(msg.readInt32());
+ dstBeing = actorSpriteManager->findBeing(msg.readInt32());
+
+ msg.readInt32(); // server tick
+ msg.readInt32(); // src speed
+ msg.readInt32(); // dst speed
+ param1 = msg.readInt16();
+ msg.readInt16(); // param 2
+ type = msg.readInt8();
+ msg.readInt16(); // param 3
+
+ switch (type)
+ {
+ case Being::HIT: // Damage
+ case Being::CRITICAL: // Critical Damage
+ case Being::MULTI: // Critical Damage
+ case Being::REFLECT: // Reflected Damage
+ case Being::FLEE: // Lucky Dodge
+ if (dstBeing)
+ {
+ dstBeing->takeDamage(srcBeing, param1,
+ (Being::AttackType)type);
+ }
+ if (srcBeing)
+ {
+ srcBeing->handleAttack(dstBeing, param1,
+ (Being::AttackType)type);
+ if (srcBeing->getType() == Being::PLAYER)
+ srcBeing->setAttackTime();
+ }
+ break;
+
+ case 0x02: // Sit
+ if (srcBeing)
+ {
+ srcBeing->setAction(Being::SIT);
+ if (srcBeing->getType() == Being::PLAYER)
+ {
+ srcBeing->setMoveTime();
+ if (player_node)
+ {
+ player_node->imitateAction(
+ srcBeing, Being::SIT);
+ }
+ }
+ }
+ break;
+
+ case 0x03: // Stand up
+ if (srcBeing)
+ {
+ srcBeing->setAction(Being::STAND);
+ if (srcBeing->getType() == Being::PLAYER)
+ {
+ srcBeing->setMoveTime();
+ if (player_node)
+ {
+ player_node->imitateAction(
+ srcBeing, Being::STAND);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+/*
+ logger->log("QQQ1 SMSG_BEING_ACTION:");
+ if (srcBeing)
+ logger->log("srcBeing:" + toString(srcBeing->getId()));
+ if (dstBeing)
+ logger->log("dstBeing:" + toString(dstBeing->getId()));
+ logger->log("type: " + toString(type));
+*/
+ }
+ break;
+
+ case SMSG_BEING_SELFEFFECT:
+ {
+ if (!effectManager)
+ return;
+
+ id = (Uint32)msg.readInt32();
+ Being* being = actorSpriteManager->findBeing(id);
+ if (!being)
+ break;
+
+ int effectType = msg.readInt32();
+
+ effectManager->trigger(effectType, being);
+
+ if (being && effectType == 3
+ && being->getType() == Being::PLAYER
+ && socialWindow)
+ { //reset received damage
+ socialWindow->resetDamage(being->getName());
+ }
+
+ break;
+ }
+
+ case SMSG_BEING_EMOTION:
+ if (!player_node)
+ break;
+
+ if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32())))
+ break;
+
+ if (player_relations.hasPermission(dstBeing,
+ PlayerRelation::EMOTE))
+ {
+ unsigned char emote = msg.readInt8();
+ if (emote)
+ {
+ dstBeing->setEmote(emote, EMOTION_TIME);
+ player_node->imitateEmote(dstBeing, emote);
+ }
+ }
+ if (dstBeing->getType() == Being::PLAYER)
+ dstBeing->setOtherTime();
+
+ break;
+
+ case SMSG_BEING_CHANGE_LOOKS:
+ case SMSG_BEING_CHANGE_LOOKS2:
+ {
+ /*
+ * SMSG_BEING_CHANGE_LOOKS (0x00c3) and
+ * SMSG_BEING_CHANGE_LOOKS2 (0x01d7) do basically the same
+ * thing. The difference is that ...LOOKS carries a single
+ * 8 bit value, where ...LOOKS2 carries two 16 bit values.
+ *
+ * If type = 2, then the first 16 bit value is the weapon ID,
+ * and the second 16 bit value is the shield ID. If no
+ * shield is equipped, or type is not 2, then the second
+ * 16 bit value will be 0.
+ */
+
+ if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32())))
+ break;
+
+ int type = msg.readInt8();
+ int id = 0;
+ int id2 = 0;
+
+ if (msg.getId() == SMSG_BEING_CHANGE_LOOKS)
+ {
+ id = msg.readInt8();
+ }
+ else
+ { // SMSG_BEING_CHANGE_LOOKS2
+ id = msg.readInt16();
+ id2 = msg.readInt16();
+ }
+
+ if (dstBeing->getType() == Being::PLAYER)
+ dstBeing->setOtherTime();
+
+ if (!player_node)
+ break;
+
+ switch (type)
+ {
+ case 0: // change race
+ dstBeing->setSubtype(id);
+ break;
+ case 1: // eAthena LOOK_HAIR
+ dstBeing->setSpriteID(SPRITE_HAIR, id *-1);
+ break;
+ case 2: // Weapon ID in id, Shield ID in id2
+ dstBeing->setSprite(SPRITE_WEAPON, id, "", true);
+ if (!config.getBoolValue("hideShield"))
+ dstBeing->setSprite(SPRITE_SHIELD, id2);
+ player_node->imitateOutfit(dstBeing, SPRITE_SHIELD);
+ break;
+ case 3: // Change lower headgear for eAthena, pants for us
+ dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, id);
+ player_node->imitateOutfit(dstBeing, SPRITE_BOTTOMCLOTHES);
+ break;
+ case 4: // Change upper headgear for eAthena, hat for us
+ dstBeing->setSprite(SPRITE_HAT, id);
+ player_node->imitateOutfit(dstBeing, SPRITE_HAT);
+ break;
+ case 5: // Change middle headgear for eathena, armor for us
+ dstBeing->setSprite(SPRITE_TOPCLOTHES, id);
+ player_node->imitateOutfit(dstBeing, SPRITE_TOPCLOTHES);
+ break;
+ case 6: // eAthena LOOK_HAIR_COLOR
+ dstBeing->setSpriteColor(SPRITE_HAIR, ColorDB::get(id));
+ break;
+ case 8: // eAthena LOOK_SHIELD
+ if (!config.getBoolValue("hideShield"))
+ dstBeing->setSprite(SPRITE_SHIELD, id);
+ player_node->imitateOutfit(dstBeing, SPRITE_SHIELD);
+ break;
+ case 9: // eAthena LOOK_SHOES
+ dstBeing->setSprite(SPRITE_SHOE, id);
+ player_node->imitateOutfit(dstBeing, SPRITE_SHOE);
+ break;
+ case 10: // LOOK_GLOVES
+ dstBeing->setSprite(SPRITE_GLOVES, id);
+ player_node->imitateOutfit(dstBeing, SPRITE_GLOVES);
+ break;
+ case 11: // LOOK_CAPE
+ dstBeing->setSprite(SPRITE_CAPE, id);
+ player_node->imitateOutfit(dstBeing, SPRITE_CAPE);
+ break;
+ case 12:
+ dstBeing->setSprite(SPRITE_MISC1, id);
+ player_node->imitateOutfit(dstBeing, SPRITE_MISC1);
+ break;
+ case 13:
+ dstBeing->setSprite(SPRITE_MISC2, id);
+ player_node->imitateOutfit(dstBeing, SPRITE_MISC2);
+ break;
+ default:
+ logger->log("QQQ3 CHANGE_LOOKS: unsupported type: "
+ "%d, id: %d", type, id);
+ if (dstBeing)
+ {
+ logger->log("ID: " + toString(dstBeing->getId()));
+ logger->log("name: " + toString(dstBeing->getName()));
+ }
+ break;
+ }
+ }
+ break;
+
+ case SMSG_BEING_NAME_RESPONSE:
+ {
+ int beingId = msg.readInt32();
+ if ((dstBeing = actorSpriteManager->findBeing(beingId)))
+ {
+ if (beingId == player_node->getId())
+ {
+ player_node->pingResponse();
+ }
+ else
+ {
+ dstBeing->setName(msg.readString(24));
+ dstBeing->updateGuild();
+ dstBeing->addToCache();
+
+ if (dstBeing->getType() == Being::PLAYER)
+ dstBeing->updateColors();
+
+ if (player_node)
+ {
+ Party *party = player_node->getParty();
+ if (party && party->isMember(dstBeing->getId()))
+ {
+ PartyMember *member = party->getMember(
+ dstBeing->getId());
+
+ if (member)
+ member->setName(dstBeing->getName());
+ }
+ player_node->checkNewName(dstBeing);
+ }
+ }
+ }
+ }
+ break;
+ case SMSG_BEING_IP_RESPONSE:
+ {
+ if ((dstBeing = actorSpriteManager->findBeing(
+ msg.readInt32())))
+ {
+ dstBeing->setIp(ipToString(msg.readInt32()));
+ }
+ }
+ break;
+ case SMSG_SOLVE_CHAR_NAME:
+ {
+ logger->log1("SMSG_SOLVE_CHAR_NAME");
+ logger->log(toString(msg.readInt32()));
+ logger->log(msg.readString(24));
+ }
+ break;
+ case SMSG_PLAYER_GUILD_PARTY_INFO:
+ if ((dstBeing = actorSpriteManager->findBeing(msg.readInt32())))
+ {
+ dstBeing->setPartyName(msg.readString(24));
+ dstBeing->setGuildName(msg.readString(24));
+ dstBeing->setGuildPos(msg.readString(24));
+ dstBeing->addToCache();
+ msg.readString(24); // Discard this
+ }
+ break;
+ case SMSG_BEING_CHANGE_DIRECTION:
+ {
+ if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32())))
+ break;
+
+ msg.readInt16(); // unused
+
+ unsigned char dir = getDirection(msg.readInt8());
+ dstBeing->setDirection(dir);
+ if (player_node)
+ player_node->imitateDirection(dstBeing, dir);
+ break;
+ }
+ case SMSG_PLAYER_UPDATE_1:
+ case SMSG_PLAYER_UPDATE_2:
+ case SMSG_PLAYER_MOVE:
+ if (!actorSpriteManager || !player_node)
+ break;
+
+ // An update about a player, potentially including movement.
+ id = msg.readInt32();
+ speed = msg.readInt16();
+ stunMode = msg.readInt16(); // opt1; Aethyra use this as cape
+ statusEffects = msg.readInt16(); // opt2; Aethyra use this as misc1
+ statusEffects |= ((Uint32) msg.readInt16())
+ << 16; // status.options; Aethyra uses this as misc2
+ job = msg.readInt16();
+
+ dstBeing = actorSpriteManager->findBeing(id);
+
+ if (!dstBeing)
+ {
+ if (actorSpriteManager->isBlocked(id) == true)
+ break;
+
+ dstBeing = createBeing(id, job);
+
+ if (!dstBeing)
+ break;
+ }
+
+ if (Party *party = player_node->getParty())
+ {
+ if (party->isMember(id))
+ dstBeing->setParty(party);
+ }
+
+ dstBeing->setWalkSpeed(Vector(speed, speed, 0));
+ dstBeing->setSubtype(job);
+ hairStyle = msg.readInt16();
+ weapon = msg.readInt16();
+ shield = msg.readInt16();
+ headBottom = msg.readInt16();
+
+ if (msg.getId() == SMSG_PLAYER_MOVE)
+ msg.readInt32(); // server tick
+
+ headTop = msg.readInt16();
+ headMid = msg.readInt16();
+ hairColor = msg.readInt16();
+ shoes = msg.readInt16();
+ gloves = msg.readInt16(); //sd->head_dir
+ guild = msg.readInt32(); // guild
+
+ if (guild == 0)
+ dstBeing->clearGuilds();
+ else
+ dstBeing->setGuild(Guild::getGuild(static_cast<short>(guild)));
+
+ msg.readInt16(); // emblem
+ msg.readInt16(); // manner
+ dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3
+ msg.readInt8(); // karma
+ dstBeing->setGender((msg.readInt8() == 0)
+ ? GENDER_FEMALE : GENDER_MALE);
+
+ // Set these after the gender, as the sprites may be gender-specific
+ dstBeing->setSprite(SPRITE_WEAPON, weapon, "", true);
+ if (!config.getBoolValue("hideShield"))
+ dstBeing->setSprite(SPRITE_SHIELD, shield);
+ //dstBeing->setSprite(SPRITE_SHOE, shoes);
+ dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom);
+ dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid);
+ dstBeing->setSprite(SPRITE_HAT, headTop);
+ //dstBeing->setSprite(SPRITE_GLOVES, gloves);
+ //dstBeing->setSprite(SPRITE_CAPE, cape);
+ //dstBeing->setSprite(SPRITE_MISC1, misc1);
+ //dstBeing->setSprite(SPRITE_MISC2, misc2);
+ dstBeing->setSprite(SPRITE_HAIR, hairStyle * -1,
+ ColorDB::get(hairColor));
+
+ player_node->imitateOutfit(dstBeing);
+
+ if (msg.getId() == SMSG_PLAYER_MOVE)
+ {
+ Uint16 srcX, srcY, dstX, dstY;
+ msg.readCoordinatePair(srcX, srcY, dstX, dstY);
+
+ player_node->followMoveTo(dstBeing, srcX, srcY, dstX, dstY);
+
+ dstBeing->setTileCoords(srcX, srcY);
+ dstBeing->setDestination(dstX, dstY);
+
+ if (player_node->getCurrentAction() != Being::STAND)
+ player_node->imitateAction(dstBeing, Being::STAND);
+ if (player_node->getDirection() != dstBeing->getDirection())
+ {
+ player_node->imitateDirection(dstBeing,
+ dstBeing->getDirection());
+ }
+ }
+ else
+ {
+ Uint8 dir;
+ Uint16 x, y;
+ msg.readCoordinates(x, y, dir);
+ dstBeing->setTileCoords(x, y);
+ dir = getDirection(dir);
+ dstBeing->setDirection(dir);
+
+ player_node->imitateDirection(dstBeing, dir);
+ }
+
+ gmstatus = msg.readInt16();
+
+ if (gmstatus & 0x80)
+ dstBeing->setGM(true);
+
+ if (msg.getId() == SMSG_PLAYER_UPDATE_1)
+ {
+ int type = msg.readInt8();
+ switch (type)
+ {
+ case 0:
+ dstBeing->setAction(Being::STAND);
+ player_node->imitateAction(dstBeing, Being::STAND);
+ break;
+
+ case 1:
+ dstBeing->setAction(Being::DEAD);
+ break;
+
+ case 2:
+ dstBeing->setAction(Being::SIT);
+ player_node->imitateAction(dstBeing, Being::SIT);
+ break;
+
+ default:
+ //need set stay state?
+ logger->log("QQQ2 SMSG_PLAYER_UPDATE_1:"
+ + toString(id) + " " + toString(type));
+ if (dstBeing)
+ {
+ logger->log("dstBeing id:"
+ + toString(dstBeing->getId()));
+ logger->log("dstBeing name:"
+ + dstBeing->getName());
+ }
+ break;
+
+ }
+ }
+ else if (msg.getId() == SMSG_PLAYER_MOVE)
+ {
+ msg.readInt8(); // unknown
+ }
+
+ level = msg.readInt8(); // Lv
+ if (level)
+ dstBeing->setLevel(level);
+
+ msg.readInt8(); // unknown
+
+ dstBeing->setActionTime(tick_time);
+ dstBeing->reset();
+
+ dstBeing->setStunMode(stunMode);
+ dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff);
+ dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
+
+ if (msg.getId() == SMSG_PLAYER_MOVE
+ && dstBeing->getType() == Being::PLAYER)
+ {
+ dstBeing->setMoveTime();
+ }
+
+ break;
+ case SMSG_PLAYER_STOP:
+ /*
+ * Instruction from server to stop walking at x, y.
+ *
+ * Some people like having this enabled. Others absolutely
+ * despise it. So I'm setting to so that it only affects the
+ * local player if the person has set a key "EnableSync" to "1"
+ * in their config.xml file.
+ *
+ * This packet will be honored for all other beings, regardless
+ * of the config setting.
+ */
+
+ id = msg.readInt32();
+
+ if (mSync || id != player_node->getId())
+ {
+ dstBeing = actorSpriteManager->findBeing(id);
+ if (dstBeing)
+ {
+ Uint16 x, y;
+ x = msg.readInt16();
+ y = msg.readInt16();
+ dstBeing->setTileCoords(x, y);
+ if (dstBeing->getCurrentAction() == Being::MOVE)
+ dstBeing->setAction(Being::STAND);
+ }
+ }
+ break;
+
+ case SMSG_PLAYER_MOVE_TO_ATTACK:
+ /*
+ * This is an *advisory* message, telling the client that
+ * it needs to move the character before attacking
+ * a target (out of range, obstruction in line of fire).
+ * We can safely ignore this...
+ */
+ if (player_node)
+ player_node->fixAttackTarget();
+ break;
+
+ case SMSG_PLAYER_STATUS_CHANGE:
+ // Change in players' flags
+
+ id = msg.readInt32();
+ dstBeing = actorSpriteManager->findBeing(id);
+ if (!dstBeing)
+ break;
+
+ stunMode = msg.readInt16();
+ statusEffects = msg.readInt16();
+ statusEffects |= ((Uint32) msg.readInt16()) << 16;
+ msg.readInt8(); // Unused?
+
+ dstBeing->setStunMode(stunMode);
+ dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff);
+ dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
+ break;
+
+ case SMSG_BEING_STATUS_CHANGE:
+ // Status change
+ status = msg.readInt16();
+ id = msg.readInt32();
+ flag = msg.readInt8(); // 0: stop, 1: start
+
+ dstBeing = actorSpriteManager->findBeing(id);
+ if (dstBeing)
+ dstBeing->setStatusEffect(status, flag);
+ break;
+
+ case SMSG_SKILL_CASTING:
+ msg.readInt32(); // src id
+ msg.readInt32(); // dst id
+ msg.readInt16(); // dst x
+ msg.readInt16(); // dst y
+ msg.readInt16(); // skill num
+ msg.readInt32(); // skill get pl
+ msg.readInt32(); // cast time
+ break;
+
+ case SMSG_SKILL_CAST_CANCEL:
+ msg.readInt32(); // id
+ break;
+
+ case SMSG_SKILL_NO_DAMAGE:
+ msg.readInt16(); // skill id
+ msg.readInt16(); // heal
+ msg.readInt32(); // dst id
+ msg.readInt32(); // src id
+ msg.readInt8(); // fail
+ break;
+
+ case SMSG_PVP_MAP_MODE:
+ {
+ Game *game = Game::instance();
+ if (!game)
+ break;
+
+ Map *map = game->getCurrentMap();
+ if (map)
+ map->setPvpMode(msg.readInt16());
+ break;
+ }
+
+ case SMSG_PVP_SET:
+ {
+ int id = msg.readInt32(); // id
+ int rank = msg.readInt32(); // rank
+ msg.readInt32(); // num
+ dstBeing = actorSpriteManager->findBeing(id);
+ if (dstBeing)
+ dstBeing->setPvpRank(rank);
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+Uint8 BeingHandler::getDirection(Uint8 dir)
+{
+ if (dir == 0)
+ dir = 8;
+ return dir;
+}
+
+void BeingHandler::undress(Being *being)
+{
+ being->setSprite(SPRITE_BOTTOMCLOTHES, 0);
+ being->setSprite(SPRITE_TOPCLOTHES, 0);
+ being->setSprite(SPRITE_HAT, 0);
+ being->setSprite(SPRITE_SHOE, 0);
+ being->setSprite(SPRITE_GLOVES, 0);
+// being->setSprite(SPRITE_WEAPON, 0, "", true);
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/beinghandler.h b/src/net/tmwa/beinghandler.h
new file mode 100644
index 000000000..f16b35178
--- /dev/null
+++ b/src/net/tmwa/beinghandler.h
@@ -0,0 +1,53 @@
+/*
+ * 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_TA_BEINGHANDLER_H
+#define NET_TA_BEINGHANDLER_H
+
+#include "net/beinghandler.h"
+#include "net/net.h"
+
+#include "net/tmwa/messagehandler.h"
+
+namespace TmwAthena
+{
+
+class BeingHandler : public MessageHandler, public Net::BeingHandler
+{
+ public:
+ BeingHandler(bool enableSync);
+
+ virtual void handleMessage(Net::MessageIn &msg);
+
+ virtual void requestNameById(int id);
+
+ virtual void undress(Being *being);
+
+ Uint8 getDirection(Uint8 dir);
+
+ private:
+ // Should we honor server "Stop Walking" packets
+ bool mSync;
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_BEINGHANDLER_H
diff --git a/src/net/tmwa/buysellhandler.cpp b/src/net/tmwa/buysellhandler.cpp
new file mode 100644
index 000000000..bdc372238
--- /dev/null
+++ b/src/net/tmwa/buysellhandler.cpp
@@ -0,0 +1,231 @@
+/*
+ * 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/tmwa/buysellhandler.h"
+
+#include "actorspritemanager.h"
+#include "configuration.h"
+#include "event.h"
+#include "inventory.h"
+#include "item.h"
+#include "localplayer.h"
+#include "playerinfo.h"
+#include "shopitem.h"
+
+#include "gui/buy.h"
+#include "gui/buysell.h"
+#include "gui/sell.h"
+#include "gui/shopwindow.h"
+
+#include "gui/widgets/chattab.h"
+
+#include "net/chathandler.h"
+#include "net/messagein.h"
+#include "net/net.h"
+
+#include "net/tmwa/chathandler.h"
+#include "net/tmwa/protocol.h"
+
+#include "utils/gettext.h"
+
+extern Net::BuySellHandler *buySellHandler;
+
+namespace TmwAthena
+{
+
+BuySellHandler::BuySellHandler()
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_NPC_BUY_SELL_CHOICE,
+ SMSG_NPC_BUY,
+ SMSG_NPC_SELL,
+ SMSG_NPC_BUY_RESPONSE,
+ SMSG_NPC_SELL_RESPONSE,
+ 0
+ };
+ mNpcId = 0;
+ handledMessages = _messages;
+ buySellHandler = this;
+}
+
+void BuySellHandler::handleMessage(Net::MessageIn &msg)
+{
+ int n_items;
+
+ switch (msg.getId())
+ {
+ case SMSG_NPC_BUY_SELL_CHOICE:
+ if (!BuySellDialog::isActive())
+ {
+ mNpcId = msg.readInt32();
+ new BuySellDialog(mNpcId);
+ }
+ break;
+
+ case SMSG_NPC_BUY:
+ msg.readInt16(); // length
+ n_items = (msg.getLength() - 4) / 11;
+ mBuyDialog = new BuyDialog(mNpcId);
+ mBuyDialog->setMoney(PlayerInfo::getAttribute(MONEY));
+
+ for (int k = 0; k < n_items; k++)
+ {
+ int value = msg.readInt32();
+ msg.readInt32(); // DCvalue
+ msg.readInt8(); // type
+ int itemId = msg.readInt16();
+ mBuyDialog->addItem(itemId, 0, value);
+ }
+ break;
+
+ case SMSG_NPC_SELL:
+ msg.readInt16(); // length
+ n_items = (msg.getLength() - 4) / 10;
+ if (n_items > 0)
+ {
+ SellDialog *dialog = new SellDialog(mNpcId);
+ dialog->setMoney(PlayerInfo::getAttribute(MONEY));
+
+ for (int k = 0; k < n_items; k++)
+ {
+ int index = msg.readInt16() - INVENTORY_OFFSET;
+ int value = msg.readInt32();
+ msg.readInt32(); // OCvalue
+
+ Item *item = PlayerInfo::getInventory()->getItem(index);
+
+ if (item && !(item->isEquipped()))
+ dialog->addItem(item, value);
+ }
+ }
+ else
+ {
+ SERVER_NOTICE(_("Nothing to sell."))
+ }
+ break;
+
+ case SMSG_NPC_BUY_RESPONSE:
+ if (msg.readInt8() == 0)
+ {
+ SERVER_NOTICE(_("Thanks for buying."))
+ }
+ else
+ {
+ // Reset player money since buy dialog already assumed purchase
+ // would go fine
+ if (mBuyDialog)
+ mBuyDialog->setMoney(PlayerInfo::getAttribute(MONEY));
+ SERVER_NOTICE(_("Unable to buy."))
+ }
+ break;
+
+ case SMSG_NPC_SELL_RESPONSE:
+ if (msg.readInt8() == 0)
+ SERVER_NOTICE(_("Thanks for selling."))
+ else
+ SERVER_NOTICE(_("Unable to sell."))
+
+ break;
+ default:
+ break;
+ }
+
+}
+
+void BuySellHandler::requestSellList(std::string nick)
+{
+ if (nick.empty() != 0 || !shopWindow)
+ return;
+
+ std::string data = "!selllist " + toString(tick_time);
+ shopWindow->setAcceptPlayer(nick);
+
+ if (config.getBoolValue("hideShopMessages"))
+ {
+ Net::getChatHandler()->privateMessage(nick, data);
+ }
+ else
+ {
+ if (chatWindow)
+ chatWindow->whisper(nick, data, BY_PLAYER);
+ }
+//was true
+}
+
+void BuySellHandler::requestBuyList(std::string nick)
+{
+ if (nick.empty() || !shopWindow)
+ return;
+
+ std::string data = "!buylist " + toString(tick_time);
+ shopWindow->setAcceptPlayer(nick);
+
+ if (config.getBoolValue("hideShopMessages"))
+ {
+ Net::getChatHandler()->privateMessage(nick, data);
+ }
+ else
+ {
+ if (chatWindow)
+ chatWindow->whisper(nick, data, BY_PLAYER);
+ }
+//was true
+}
+
+void BuySellHandler::sendBuyRequest(std::string nick, ShopItem* item,
+ int amount)
+{
+ if (!chatWindow || nick.empty() || !item ||
+ amount < 1 || amount > item->getQuantity())
+ {
+ return;
+ }
+ std::string data = strprintf("!buyitem %d %d %d",
+ item->getId(), item->getPrice(), amount);
+
+ if (config.getBoolValue("hideShopMessages"))
+ Net::getChatHandler()->privateMessage(nick, data);
+ else
+ chatWindow->whisper(nick, data, BY_PLAYER);
+//was true
+}
+
+void BuySellHandler::sendSellRequest(std::string nick, ShopItem* item,
+ int amount)
+{
+ if (!chatWindow || nick.empty() || !item ||
+ amount < 1 || amount > item->getQuantity())
+ {
+ return;
+ }
+
+ std::string data = strprintf("!sellitem %d %d %d",
+ item->getId(), item->getPrice(), amount);
+
+ if (config.getBoolValue("hideShopMessages"))
+ Net::getChatHandler()->privateMessage(nick, data);
+ else
+ chatWindow->whisper(nick, data, BY_PLAYER);
+//was true
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/buysellhandler.h b/src/net/tmwa/buysellhandler.h
new file mode 100644
index 000000000..e9ae2b9ff
--- /dev/null
+++ b/src/net/tmwa/buysellhandler.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_TA_BUYSELLHANDLER_H
+#define NET_TA_BUYSELLHANDLER_H
+
+#include "net/buysellhandler.h"
+
+#include "being.h"
+
+#include "net/net.h"
+
+#include "net/tmwa/messagehandler.h"
+
+class BuyDialog;
+
+namespace TmwAthena
+{
+
+class BuySellHandler : public MessageHandler, public Net::BuySellHandler
+{
+ public:
+ BuySellHandler();
+
+ virtual void handleMessage(Net::MessageIn &msg);
+
+ virtual void requestSellList(std::string nick);
+ virtual void requestBuyList(std::string nick);
+ virtual void sendBuyRequest(std::string nick, ShopItem* item,
+ int amount);
+ virtual void sendSellRequest(std::string nick, ShopItem* item,
+ int amount);
+
+ private:
+ int mNpcId;
+ BuyDialog *mBuyDialog;
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_BUYSELLHANDLER_H
diff --git a/src/net/tmwa/charserverhandler.cpp b/src/net/tmwa/charserverhandler.cpp
new file mode 100644
index 000000000..e500f667b
--- /dev/null
+++ b/src/net/tmwa/charserverhandler.cpp
@@ -0,0 +1,386 @@
+/*
+ * 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/tmwa/charserverhandler.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "game.h"
+#include "log.h"
+
+#include "gui/charcreatedialog.h"
+#include "gui/okdialog.h"
+
+#include "net/logindata.h"
+#include "net/messagein.h"
+#include "net/messageout.h"
+#include "net/net.h"
+
+#include "net/tmwa/gamehandler.h"
+#include "net/tmwa/loginhandler.h"
+#include "net/tmwa/network.h"
+#include "net/tmwa/protocol.h"
+
+#include "resources/colordb.h"
+
+#include "utils/dtor.h"
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+extern Net::CharHandler *charHandler;
+
+namespace TmwAthena
+{
+
+extern ServerInfo charServer;
+extern ServerInfo mapServer;
+
+CharServerHandler::CharServerHandler()
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_CHAR_LOGIN,
+ SMSG_CHAR_LOGIN_ERROR,
+ SMSG_CHAR_CREATE_SUCCEEDED,
+ SMSG_CHAR_CREATE_FAILED,
+ SMSG_CHAR_DELETE_SUCCEEDED,
+ SMSG_CHAR_DELETE_FAILED,
+ SMSG_CHAR_MAP_INFO,
+ SMSG_CHANGE_MAP_SERVER,
+ 0
+ };
+ handledMessages = _messages;
+ charHandler = this;
+}
+
+void CharServerHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_CHAR_LOGIN:
+ {
+ msg.skip(2); // Length word
+ msg.skip(20); // Unused
+
+ delete_all(mCharacters);
+ mCharacters.clear();
+
+ // Derive number of characters from message length
+ const int count = (msg.getLength() - 24) / 106;
+
+ for (int i = 0; i < count; ++i)
+ {
+ Net::Character *character = new Net::Character;
+ readPlayerData(msg, character);
+ mCharacters.push_back(character);
+ logger->log("CharServer: Player: %s (%d)",
+ character->dummy->getName().c_str(), character->slot);
+ }
+
+ Client::setState(STATE_CHAR_SELECT);
+ }
+ break;
+
+ case SMSG_CHAR_LOGIN_ERROR:
+ switch (msg.readInt8())
+ {
+ case 0:
+ errorMessage = _("Access denied. Most likely, there are "
+ "too many players on this server.");
+ break;
+ case 1:
+ errorMessage = _("Cannot use this ID.");
+ break;
+ default:
+ errorMessage = _("Unknown char-server failure.");
+ break;
+ }
+ Client::setState(STATE_ERROR);
+ break;
+
+ case SMSG_CHAR_CREATE_SUCCEEDED:
+ {
+ Net::Character *character = new Net::Character;
+ readPlayerData(msg, character);
+ mCharacters.push_back(character);
+
+ updateCharSelectDialog();
+
+ // Close the character create dialog
+ if (mCharCreateDialog)
+ {
+ mCharCreateDialog->scheduleDelete();
+ mCharCreateDialog = 0;
+ }
+ }
+ break;
+
+ case SMSG_CHAR_CREATE_FAILED:
+ new OkDialog(_("Error"), _("Failed to create character. Most "
+ "likely the name is already taken."));
+ if (mCharCreateDialog)
+ mCharCreateDialog->unlock();
+ break;
+
+ case SMSG_CHAR_DELETE_SUCCEEDED:
+ delete mSelectedCharacter;
+ mCharacters.remove(mSelectedCharacter);
+ mSelectedCharacter = 0;
+ updateCharSelectDialog();
+ unlockCharSelectDialog();
+ new OkDialog(_("Info"), _("Character deleted."));
+ break;
+
+ case SMSG_CHAR_DELETE_FAILED:
+ unlockCharSelectDialog();
+ new OkDialog(_("Error"), _("Failed to delete character."));
+ break;
+
+ case SMSG_CHAR_MAP_INFO:
+ {
+// msg.skip(4); // CharID, must be the same as player_node->charID
+ PlayerInfo::setCharId(msg.readInt32());
+ GameHandler *gh = static_cast<GameHandler*>(Net::getGameHandler());
+ gh->setMap(msg.readString(16));
+ mapServer.hostname = ipToString(msg.readInt32());
+ mapServer.port = msg.readInt16();
+
+ // Prevent the selected local player from being deleted
+ player_node = mSelectedCharacter->dummy;
+ PlayerInfo::setBackend(mSelectedCharacter->data);
+
+ mSelectedCharacter->dummy = 0;
+
+ delete_all(mCharacters);
+ mCharacters.clear();
+ updateCharSelectDialog();
+
+ if (mNetwork)
+ mNetwork->disconnect();
+ Client::setState(STATE_CONNECT_GAME);
+ }
+ break;
+
+ case SMSG_CHANGE_MAP_SERVER:
+ {
+ GameHandler *gh = static_cast<GameHandler*>(Net::getGameHandler());
+ if (!gh || !mNetwork)
+ return;
+ gh->setMap(msg.readString(16));
+ int x = msg.readInt16();
+ int y = msg.readInt16();
+ mapServer.hostname = ipToString(msg.readInt32());
+ mapServer.port = msg.readInt16();
+
+ mNetwork->disconnect();
+ Client::setState(STATE_CHANGE_MAP);
+ if (player_node)
+ {
+ player_node->setTileCoords(x, y);
+ player_node->setMap(0);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void CharServerHandler::readPlayerData(Net::MessageIn &msg,
+ Net::Character *character)
+{
+ const Token &token =
+ static_cast<LoginHandler*>(Net::getLoginHandler())->getToken();
+
+ LocalPlayer *tempPlayer = new LocalPlayer(msg.readInt32(), 0);
+ tempPlayer->setGender(token.sex);
+
+ character->data.mAttributes[EXP] = msg.readInt32();
+ character->data.mAttributes[MONEY] = msg.readInt32();
+ character->data.mStats[JOB].exp = msg.readInt32();
+
+ int temp = msg.readInt32();
+ character->data.mStats[JOB].base = temp;
+ character->data.mStats[JOB].mod = temp;
+
+ tempPlayer->setSprite(SPRITE_SHOE, msg.readInt16());
+ tempPlayer->setSprite(SPRITE_GLOVES, msg.readInt16());
+ tempPlayer->setSprite(SPRITE_CAPE, msg.readInt16());
+ tempPlayer->setSprite(SPRITE_MISC1, msg.readInt16());
+
+ msg.readInt32(); // option
+ msg.readInt32(); // karma
+ msg.readInt32(); // manner
+ msg.skip(2); // unknown
+
+ character->data.mAttributes[HP] = msg.readInt16();
+ character->data.mAttributes[MAX_HP] = msg.readInt16();
+ character->data.mAttributes[MP] = msg.readInt16();
+ character->data.mAttributes[MAX_MP] = msg.readInt16();
+
+ msg.readInt16(); // speed
+ tempPlayer->setSubtype(msg.readInt16()); // class (used for race)
+ int hairStyle = msg.readInt16();
+ Uint16 weapon = msg.readInt16();
+ tempPlayer->setSprite(SPRITE_WEAPON, weapon, "", true);
+
+ character->data.mAttributes[LEVEL] = msg.readInt16();
+
+ msg.readInt16(); // skill point
+ tempPlayer->setSprite(SPRITE_BOTTOMCLOTHES, msg.readInt16());
+ //to avoid show error (error.xml) need remove this sprite
+ if (!config.getBoolValue("hideShield"))
+ tempPlayer->setSprite(SPRITE_SHIELD, msg.readInt16());
+ else
+ msg.readInt16();
+
+ tempPlayer->setSprite(SPRITE_HAT, msg.readInt16()); // head option top
+ tempPlayer->setSprite(SPRITE_TOPCLOTHES, msg.readInt16());
+ tempPlayer->setSprite(SPRITE_HAIR, hairStyle * -1,
+ ColorDB::get(msg.readInt16()));
+ tempPlayer->setSprite(SPRITE_MISC2, msg.readInt16());
+ tempPlayer->setName(msg.readString(24));
+
+ character->dummy = tempPlayer;
+
+ for (int i = 0; i < 6; i++)
+ character->data.mStats[i + STR].base = msg.readInt8();
+
+ character->slot = msg.readInt8(); // character slot
+ msg.readInt8(); // unknown
+}
+
+void CharServerHandler::setCharSelectDialog(CharSelectDialog *window)
+{
+ mCharSelectDialog = window;
+ updateCharSelectDialog();
+}
+
+void CharServerHandler::setCharCreateDialog(CharCreateDialog *window)
+{
+ mCharCreateDialog = window;
+
+ if (!mCharCreateDialog)
+ return;
+
+ std::vector<std::string> attributes;
+ attributes.push_back(_("Strength:"));
+ attributes.push_back(_("Agility:"));
+ attributes.push_back(_("Vitality:"));
+ attributes.push_back(_("Intelligence:"));
+ attributes.push_back(_("Dexterity:"));
+ attributes.push_back(_("Luck:"));
+
+ const Token &token =
+ static_cast<LoginHandler*>(Net::getLoginHandler())->getToken();
+
+ mCharCreateDialog->setAttributes(attributes, 30, 1, 9);
+ mCharCreateDialog->setFixedGender(true, token.sex);
+}
+
+void CharServerHandler::requestCharacters()
+{
+ connect();
+}
+
+void CharServerHandler::chooseCharacter(Net::Character *character)
+{
+ mSelectedCharacter = character;
+ mCharSelectDialog = 0;
+
+ MessageOut outMsg(CMSG_CHAR_SELECT);
+ outMsg.writeInt8(static_cast<unsigned char>(mSelectedCharacter->slot));
+}
+
+void CharServerHandler::newCharacter(const std::string &name, int slot,
+ bool gender _UNUSED_, int hairstyle,
+ int hairColor,
+ const std::vector<int> &stats)
+{
+ MessageOut outMsg(CMSG_CHAR_CREATE);
+ outMsg.writeString(name, 24);
+ for (int i = 0; i < 6; i++)
+ outMsg.writeInt8(static_cast<unsigned char>(stats[i]));
+
+ outMsg.writeInt8(static_cast<unsigned char>(slot));
+ outMsg.writeInt16(static_cast<short>(hairColor));
+ outMsg.writeInt16(static_cast<short>(hairstyle));
+}
+
+void CharServerHandler::deleteCharacter(Net::Character *character)
+{
+ if (!character)
+ return;
+
+ mSelectedCharacter = character;
+
+ MessageOut outMsg(CMSG_CHAR_DELETE);
+ outMsg.writeInt32(mSelectedCharacter->dummy->getId());
+ outMsg.writeString("a@a.com", 40);
+}
+
+void CharServerHandler::switchCharacter()
+{
+ // This is really a map-server packet
+ MessageOut outMsg(CMSG_PLAYER_RESTART);
+ outMsg.writeInt8(1);
+}
+
+unsigned int CharServerHandler::baseSprite() const
+{
+ return SPRITE_BASE;
+}
+
+unsigned int CharServerHandler::hairSprite() const
+{
+ return SPRITE_HAIR;
+}
+
+unsigned int CharServerHandler::maxSprite() const
+{
+ return SPRITE_VECTOREND;
+}
+
+void CharServerHandler::connect()
+{
+ const Token &token =
+ static_cast<LoginHandler*>(Net::getLoginHandler())->getToken();
+
+ if (!mNetwork)
+ return;
+
+ mNetwork->disconnect();
+ mNetwork->connect(charServer);
+ MessageOut outMsg(CMSG_CHAR_SERVER_CONNECT);
+ outMsg.writeInt32(token.account_ID);
+ outMsg.writeInt32(token.session_ID1);
+ outMsg.writeInt32(token.session_ID2);
+ // [Fate] The next word is unused by the old char server, so we squeeze in
+ // mana client version information
+ outMsg.writeInt16(CLIENT_PROTOCOL_VERSION);
+ outMsg.writeInt8((token.sex == GENDER_MALE) ? 1 : 0);
+
+ // We get 4 useless bytes before the real answer comes in (what are these?)
+ mNetwork->skip(4);
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/charserverhandler.h b/src/net/tmwa/charserverhandler.h
new file mode 100644
index 000000000..d6ce0a2dc
--- /dev/null
+++ b/src/net/tmwa/charserverhandler.h
@@ -0,0 +1,87 @@
+/*
+ * 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_TA_CHARSERVERHANDLER_H
+#define NET_TA_CHARSERVERHANDLER_H
+
+#include "net/charhandler.h"
+#include "net/serverinfo.h"
+
+#include "net/tmwa/messagehandler.h"
+#include "net/tmwa/token.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class LoginData;
+
+namespace TmwAthena
+{
+
+/**
+ * Deals with incoming messages from the character server.
+ */
+class CharServerHandler : public MessageHandler, public Net::CharHandler
+{
+ public:
+ CharServerHandler();
+
+ virtual 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 connect();
+
+ private:
+ void readPlayerData(Net::MessageIn &msg, Net::Character *character);
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_CHARSERVERHANDLER_H
diff --git a/src/net/tmwa/chathandler.cpp b/src/net/tmwa/chathandler.cpp
new file mode 100644
index 000000000..4dcfb2b34
--- /dev/null
+++ b/src/net/tmwa/chathandler.cpp
@@ -0,0 +1,552 @@
+/*
+ * 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/tmwa/chathandler.h"
+
+#include "actorspritemanager.h"
+#include "being.h"
+#include "configuration.h"
+#include "event.h"
+#include "game.h"
+#include "localplayer.h"
+#include "playerrelations.h"
+#include "log.h"
+
+#include "gui/chat.h"
+#include "gui/shopwindow.h"
+
+#include "gui/widgets/chattab.h"
+
+#include "net/messagein.h"
+#include "net/messageout.h"
+
+#include "net/tmwa/protocol.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include <string>
+
+extern Net::ChatHandler *chatHandler;
+
+namespace TmwAthena
+{
+
+ChatHandler::ChatHandler()
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_BEING_CHAT,
+ SMSG_PLAYER_CHAT,
+ SMSG_WHISPER,
+ SMSG_WHISPER_RESPONSE,
+ SMSG_GM_CHAT,
+ SMSG_MVP, // MVP
+ 0
+ };
+ handledMessages = _messages;
+ chatHandler = this;
+}
+
+void ChatHandler::handleMessage(Net::MessageIn &msg)
+{
+ if (!localChatTab)
+ return;
+
+ Being *being;
+ std::string chatMsg;
+ std::string nick;
+ int chatMsgLength;
+
+ switch (msg.getId())
+ {
+ case SMSG_WHISPER_RESPONSE:
+ {
+ if (mSentWhispers.empty())
+ {
+ nick = "user";
+ }
+ else
+ {
+ nick = mSentWhispers.front();
+ mSentWhispers.pop();
+ }
+
+ int type = msg.readInt8();
+ switch (type)
+ {
+ case 0x00:
+ // Success (don't need to report)
+ break;
+ case 0x01:
+ if (chatWindow)
+ {
+ chatWindow->whisper(nick,
+ strprintf(_("Whisper could not be "
+ "sent, %s is offline."), nick.c_str()), BY_SERVER);
+ }
+ break;
+ case 0x02:
+ if (chatWindow)
+ {
+ chatWindow->whisper(nick,
+ strprintf(_("Whisper could not "
+ "be sent, ignored by %s."), nick.c_str()),
+ BY_SERVER);
+ }
+ break;
+ default:
+ if (logger)
+ {
+ logger->log("QQQ SMSG_WHISPER_RESPONSE:"
+ + toString(type));
+ }
+ }
+ break;
+ }
+
+ // Received whisper
+ case SMSG_WHISPER:
+ {
+ chatMsgLength = msg.readInt16() - 28;
+ nick = msg.readString(24);
+
+ if (chatMsgLength <= 0)
+ break;
+
+ chatMsg = msg.readString(chatMsgLength);
+
+ if (nick != "Server")
+ {
+ if (player_relations.hasPermission(
+ nick, PlayerRelation::WHISPER))
+ {
+ bool tradeBot = config.getBoolValue("tradebot");
+ bool showMsg = !config.getBoolValue("hideShopMessages");
+ if (player_relations.hasPermission(
+ nick, PlayerRelation::TRADE))
+ {
+ if (shopWindow)
+ { //commands to shop from player
+ if (chatMsg.find("!selllist ") == 0)
+ {
+ if (tradeBot)
+ {
+ if (showMsg && chatWindow)
+ chatWindow->whisper(nick, chatMsg);
+ shopWindow->giveList(nick,
+ ShopWindow::SELL);
+ }
+ }
+ else if (chatMsg.find("!buylist ") == 0)
+ {
+ if (tradeBot)
+ {
+ if (showMsg && chatWindow)
+ chatWindow->whisper(nick, chatMsg);
+ shopWindow->giveList(nick,
+ ShopWindow::BUY);
+ }
+ }
+ else if (chatMsg.find("!buyitem ") == 0)
+ {
+ if (showMsg && chatWindow)
+ chatWindow->whisper(nick, chatMsg);
+ if (tradeBot)
+ {
+ shopWindow->processRequest(nick, chatMsg,
+ ShopWindow::BUY);
+ }
+ }
+ else if (chatMsg.find("!sellitem ") == 0)
+ {
+ if (showMsg && chatWindow)
+ chatWindow->whisper(nick, chatMsg);
+ if (tradeBot)
+ {
+ shopWindow->processRequest(nick, chatMsg,
+ ShopWindow::SELL);
+ }
+ }
+ else if (chatMsg.length() > 3
+ && chatMsg.find("\302\202") == 0)
+ {
+ chatMsg = chatMsg.erase(0, 2);
+ if (showMsg && chatWindow)
+ chatWindow->whisper(nick, chatMsg);
+ if (chatMsg.find("B1") == 0
+ || chatMsg.find("S1") == 0)
+ {
+ shopWindow->showList(nick, chatMsg);
+ }
+ }
+ else if (chatWindow)
+ {
+ chatWindow->whisper(nick, chatMsg);
+ }
+ }
+ else if (chatWindow)
+ {
+ chatWindow->whisper(nick, chatMsg);
+ }
+ }
+ else
+ {
+ if (chatWindow && (showMsg
+ || (chatMsg.find("!selllist")
+ != 0 && chatMsg.find("!buylist") != 0)))
+ {
+ chatWindow->whisper(nick, chatMsg);
+ }
+ }
+ }
+ }
+ else if (localChatTab)
+ {
+ localChatTab->chatLog(chatMsg, BY_SERVER);
+ }
+
+ break;
+ }
+
+ // Received speech from being
+ case SMSG_BEING_CHAT:
+ {
+ if (!actorSpriteManager)
+ return;
+
+ chatMsgLength = msg.readInt16() - 8;
+ being = actorSpriteManager->findBeing(msg.readInt32());
+
+ if (!being || chatMsgLength <= 0)
+ break;
+
+ std::string str2;
+ chatMsg = msg.readRawString(chatMsgLength);
+
+ if (being->getType() == Being::PLAYER)
+ being->setTalkTime();
+
+ std::string::size_type pos = chatMsg.find(" : ", 0);
+ std::string sender_name = ((pos == std::string::npos)
+ ? "" : chatMsg.substr(0, pos));
+
+ if (sender_name != being->getName()
+ && being->getType() == Being::PLAYER)
+ {
+ if (!being->getName().empty())
+ sender_name = being->getName();
+ }
+ else
+ {
+ chatMsg.erase(0, pos + 3);
+ }
+
+ trim(chatMsg);
+
+ // We use getIgnorePlayer instead of ignoringPlayer here
+ // because ignorePlayer' side effects are triggered
+ // right below for Being::IGNORE_SPEECH_FLOAT.
+ if (player_relations.checkPermissionSilently(sender_name,
+ PlayerRelation::SPEECH_LOG) && chatWindow)
+ {
+ chatWindow->resortChatLog(removeColors(sender_name) + " : "
+ + chatMsg, BY_OTHER);
+ }
+
+ if (player_relations.hasPermission(sender_name,
+ PlayerRelation::SPEECH_FLOAT))
+ {
+ being->setSpeech(chatMsg, SPEECH_TIME);
+ }
+ break;
+ }
+
+ case SMSG_PLAYER_CHAT:
+ case SMSG_GM_CHAT:
+ {
+ chatMsgLength = msg.readInt16() - 4;
+
+ if (chatMsgLength <= 0)
+ break;
+
+ std::string str2;
+ chatMsg = msg.readRawString(chatMsgLength);
+ std::string::size_type pos = chatMsg.find(" : ", 0);
+
+ if (msg.getId() == SMSG_PLAYER_CHAT)
+ {
+ if (chatWindow)
+ chatWindow->resortChatLog(chatMsg, BY_PLAYER);
+// if (localChatTab)
+// localChatTab->chatLog(chatMsg, BY_PLAYER);
+
+ const std::string senseStr = "You sense the following: ";
+ if (actorSpriteManager && !chatMsg.find(senseStr))
+ {
+ actorSpriteManager->parseLevels(
+ chatMsg.substr(senseStr.size()));
+ }
+
+ if (pos != std::string::npos)
+ chatMsg.erase(0, pos + 3);
+
+ trim(chatMsg);
+
+ if (player_node)
+ player_node->setSpeech(chatMsg, SPEECH_TIME);
+ }
+ else if (localChatTab)
+ {
+ localChatTab->chatLog(chatMsg, BY_GM);
+ }
+ break;
+ }
+
+ case SMSG_MVP:
+ {
+ // Display MVP player
+ int id = msg.readInt32(); // id
+ if (localChatTab && actorSpriteManager)
+ {
+ being = actorSpriteManager->findBeing(id);
+ if (!being)
+ {
+ localChatTab->chatLog(_("MVP player."), BY_SERVER);
+ }
+ else
+ {
+ localChatTab->chatLog(_("MVP player: ")
+ + being->getName(), BY_SERVER);
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void ChatHandler::talk(const std::string &text)
+{
+ if (!player_node)
+ return;
+
+ std::string mes = player_node->getName() + " : " + text;
+// std::string mes = player_node->getName() + "zzzz : " + text;
+
+ MessageOut outMsg(CMSG_CHAT_MESSAGE);
+ // Added + 1 in order to let eAthena parse admin commands correctly
+ outMsg.writeInt16(static_cast<short>(mes.length() + 4 + 1));
+ outMsg.writeString(mes, static_cast<int>(mes.length() + 1));
+}
+
+void ChatHandler::talkRaw(const std::string &mes)
+{
+ MessageOut outMsg(CMSG_CHAT_MESSAGE);
+ // Added + 1 in order to let eAthena parse admin commands correctly
+ outMsg.writeInt16(static_cast<short>(mes.length() + 4 + 1));
+ outMsg.writeString(mes, static_cast<int>(mes.length() + 1));
+}
+
+void ChatHandler::me(const std::string &text)
+{
+ std::string action = strprintf("*%s*", text.c_str());
+
+ talk(action);
+}
+
+void ChatHandler::privateMessage(const std::string &recipient,
+ const std::string &text)
+{
+ MessageOut outMsg(CMSG_CHAT_WHISPER);
+ outMsg.writeInt16(static_cast<short>(text.length() + 28));
+ outMsg.writeString(recipient, 24);
+ outMsg.writeString(text, static_cast<int>(text.length()));
+ mSentWhispers.push(recipient);
+}
+
+void ChatHandler::channelList()
+{
+ SERVER_NOTICE(_("Channels are not supported!"))
+}
+
+void ChatHandler::enterChannel(const std::string &channel _UNUSED_,
+ const std::string &password _UNUSED_)
+{
+ SERVER_NOTICE(_("Channels are not supported!"))
+}
+
+void ChatHandler::quitChannel(int channelId _UNUSED_)
+{
+ SERVER_NOTICE(_("Channels are not supported!"))
+}
+
+void ChatHandler::sendToChannel(int channelId _UNUSED_,
+ const std::string &text _UNUSED_)
+{
+ SERVER_NOTICE(_("Channels are not supported!"))
+}
+
+void ChatHandler::userList(const std::string &channel _UNUSED_)
+{
+ SERVER_NOTICE(_("Channels are not supported!"))
+}
+
+void ChatHandler::setChannelTopic(int channelId _UNUSED_,
+ const std::string &text _UNUSED_)
+{
+ SERVER_NOTICE(_("Channels are not supported!"))
+}
+
+void ChatHandler::setUserMode(int channelId _UNUSED_,
+ const std::string &name _UNUSED_,
+ int mode _UNUSED_)
+{
+ SERVER_NOTICE(_("Channels are not supported!"))
+}
+
+void ChatHandler::kickUser(int channelId _UNUSED_,
+ const std::string &name _UNUSED_)
+{
+ SERVER_NOTICE(_("Channels are not supported!"))
+}
+
+void ChatHandler::who()
+{
+ MessageOut outMsg(CMSG_WHO_REQUEST);
+}
+
+void ChatHandler::sendRaw(const std::string &args)
+{
+ std::string line = args;
+ std::string str;
+ MessageOut *outMsg = 0;
+
+ if (line == "")
+ return;
+
+ std::string::size_type pos = line.find(" ");
+ if (pos != std::string::npos)
+ {
+ str = line.substr(0, pos);
+ outMsg = new MessageOut(static_cast<short>(atoi(str.c_str())));
+ line = line.substr(pos + 1);
+ pos = line.find(" ");
+ }
+ else
+ {
+ outMsg = new MessageOut(static_cast<short>(atoi(line.c_str())));
+ delete outMsg;
+ return;
+ }
+
+ while (pos != std::string::npos)
+ {
+ str = line.substr(0, pos);
+ processRaw(*outMsg, str);
+ line = line.substr(pos + 1);
+ pos = line.find(" ");
+ }
+ if (outMsg)
+ {
+ if (line != "")
+ processRaw(*outMsg, line);
+ delete outMsg;
+ }
+}
+
+void ChatHandler::processRaw(MessageOut &outMsg, std::string &line)
+{
+ std::string::size_type pos = line.find(":");
+ if (pos == std::string::npos)
+ {
+ int i = atoi(line.c_str());
+ if (line.length() <= 3)
+ outMsg.writeInt8(static_cast<char>(i));
+ else if (line.length() <= 5)
+ outMsg.writeInt16(static_cast<short>(i));
+ else
+ outMsg.writeInt32(i);
+ }
+ else
+ {
+ std::string header = line.substr(0, pos);
+ std::string data = line.substr(pos + 1);
+ if (header.length() != 1)
+ return;
+
+ int i = 0;
+
+ switch (header[0])
+ {
+ case '1':
+ case '2':
+ case '4':
+ i = atoi(data.c_str());
+ break;
+ default:
+ break;
+ }
+
+ switch (header[0])
+ {
+ case '1':
+ outMsg.writeInt8(static_cast<char>(i));
+ break;
+ case '2':
+ outMsg.writeInt16(static_cast<short>(i));
+ break;
+ case '4':
+ outMsg.writeInt32(i);
+ break;
+ case 'c':
+ {
+ pos = line.find(",");
+ if (pos != std::string::npos)
+ {
+ unsigned short x = static_cast<unsigned short>(
+ atoi(data.substr(0, pos).c_str()));
+ data = data.substr(pos + 1);
+ pos = line.find(",");
+ if (pos == std::string::npos)
+ break;
+
+ unsigned short y = static_cast<unsigned short>(
+ atoi(data.substr(0, pos).c_str()));
+ int dir = atoi(data.substr(pos + 1).c_str());
+ outMsg.writeCoordinates(x, y,
+ static_cast<unsigned char>(dir));
+ }
+ break;
+ }
+ case 't':
+ outMsg.writeString(data, static_cast<int>(data.length()));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+} // namespace TmwAthena
+
diff --git a/src/net/tmwa/chathandler.h b/src/net/tmwa/chathandler.h
new file mode 100644
index 000000000..1432da3a2
--- /dev/null
+++ b/src/net/tmwa/chathandler.h
@@ -0,0 +1,87 @@
+/*
+ * 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_TA_CHATHANDLER_H
+#define NET_TA_CHATHANDLER_H
+
+#include "net/chathandler.h"
+#include "net/net.h"
+
+#include "net/tmwa/messagehandler.h"
+
+#include <queue>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+namespace TmwAthena
+{
+
+class ChatHandler : public MessageHandler, public Net::ChatHandler
+{
+ public:
+ ChatHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ 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);
+
+ void processRaw(MessageOut &outMsg, std::string &line);
+
+ private:
+ typedef std::queue<std::string> WhisperQueue;
+ WhisperQueue mSentWhispers;
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_CHATHANDLER_H
diff --git a/src/net/tmwa/gamehandler.cpp b/src/net/tmwa/gamehandler.cpp
new file mode 100644
index 000000000..5f949ce6e
--- /dev/null
+++ b/src/net/tmwa/gamehandler.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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/tmwa/gamehandler.h"
+
+#include "client.h"
+#include "event.h"
+#include "game.h"
+#include "localplayer.h"
+#include "log.h"
+
+#include "gui/okdialog.h"
+
+#include "net/messagein.h"
+#include "net/messageout.h"
+
+#include "net/tmwa/loginhandler.h"
+#include "net/tmwa/network.h"
+#include "net/tmwa/protocol.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+extern Net::GameHandler *gameHandler;
+
+namespace TmwAthena
+{
+
+extern ServerInfo mapServer;
+
+GameHandler::GameHandler()
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_MAP_LOGIN_SUCCESS,
+ SMSG_SERVER_PING,
+ SMSG_WHO_ANSWER,
+ SMSG_CHAR_SWITCH_RESPONSE,
+ SMSG_MAP_QUIT_RESPONSE,
+ 0
+ };
+ handledMessages = _messages;
+ gameHandler = this;
+
+ listen(CHANNEL_GAME);
+}
+
+void GameHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_MAP_LOGIN_SUCCESS:
+ {
+ unsigned char direction;
+ Uint16 x, y;
+ msg.readInt32(); // server tick
+ msg.readCoordinates(x, y, direction);
+ msg.skip(2); // unknown
+ logger->log("Protocol: Player start position: (%d, %d),"
+ " Direction: %d", x, y, direction);
+ // Switch now or we'll have problems
+ Client::setState(STATE_GAME);
+ if (player_node)
+ player_node->setTileCoords(x, y);
+ break;
+ }
+
+ case SMSG_SERVER_PING:
+ // We ignore this for now
+ // int tick = msg.readInt32()
+ break;
+
+ case SMSG_WHO_ANSWER:
+ SERVER_NOTICE(strprintf(_("Online users: %d"), msg.readInt32()))
+ break;
+
+ case SMSG_CHAR_SWITCH_RESPONSE:
+ if (msg.readInt8())
+ Client::setState(STATE_SWITCH_CHARACTER);
+ break;
+
+ case SMSG_MAP_QUIT_RESPONSE:
+ if (msg.readInt8())
+ new OkDialog(_("Game"), _("Request to quit denied!"), NULL);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void GameHandler::event(Channels channel, const Mana::Event &event)
+{
+ if (channel == CHANNEL_GAME)
+ {
+ if (event.getName() == EVENT_ENGINESINITALIZED)
+ Game::instance()->changeMap(mMap);
+ else if (event.getName() == EVENT_MAPLOADED)
+ MessageOut outMsg(CMSG_MAP_LOADED);
+ }
+}
+
+void GameHandler::connect()
+{
+ if (!mNetwork)
+ return;
+
+ mNetwork->connect(mapServer);
+
+ const Token &token =
+ static_cast<LoginHandler*>(Net::getLoginHandler())->getToken();
+
+
+ if (Client::getState() == STATE_CONNECT_GAME)
+ {
+ mCharID = player_node->getId();
+ // Change the player's ID to the account ID to match what eAthena uses
+ player_node->setId(token.account_ID);
+ }
+
+ // Send login infos
+ MessageOut outMsg(CMSG_MAP_SERVER_CONNECT);
+ outMsg.writeInt32(token.account_ID);
+ outMsg.writeInt32(mCharID);
+ outMsg.writeInt32(token.session_ID1);
+ outMsg.writeInt32(token.session_ID2);
+ outMsg.writeInt8((token.sex == GENDER_MALE) ? 1 : 0);
+
+/*
+ if (player_node)
+ {
+ // Change the player's ID to the account ID to match what eAthena uses
+ player_node->setId(token.account_ID);
+ }
+*/
+ // We get 4 useless bytes before the real answer comes in (what are these?)
+ mNetwork->skip(4);
+}
+
+bool GameHandler::isConnected()
+{
+ if (!mNetwork)
+ return false;
+ return mNetwork->isConnected();
+}
+
+void GameHandler::disconnect()
+{
+ if (mNetwork)
+ mNetwork->disconnect();
+}
+
+void GameHandler::who()
+{
+}
+
+void GameHandler::quit()
+{
+ MessageOut outMsg(CMSG_CLIENT_QUIT);
+}
+
+void GameHandler::ping(int tick)
+{
+ MessageOut msg(CMSG_CLIENT_PING);
+ msg.writeInt32(tick);
+}
+
+void GameHandler::setMap(const std::string map)
+{
+ mMap = map.substr(0, map.rfind("."));
+}
+
+void GameHandler::disconnect2()
+{
+ MessageOut outMsg(CMSG_CLIENT_DISCONNECT);
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/gamehandler.h b/src/net/tmwa/gamehandler.h
new file mode 100644
index 000000000..d59320bfa
--- /dev/null
+++ b/src/net/tmwa/gamehandler.h
@@ -0,0 +1,85 @@
+/*
+ * 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_TA_MAPHANDLER_H
+#define NET_TA_MAPHANDLER_H
+
+#include "listener.h"
+
+#include "net/gamehandler.h"
+#include "net/net.h"
+#include "net/serverinfo.h"
+
+#include "net/tmwa/messagehandler.h"
+#include "net/tmwa/token.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+namespace TmwAthena
+{
+
+class GameHandler : public MessageHandler, public Net::GameHandler,
+ public Mana::Listener
+{
+ public:
+ GameHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void event(Channels channel, const Mana::Event &event);
+
+ void connect();
+
+ bool isConnected();
+
+ void disconnect();
+
+ void who();
+
+ void quit();
+
+ void ping(int tick);
+
+ bool removeDeadBeings() const
+ { return true; }
+
+ void clear();
+
+ void setMap(const std::string map);
+
+ /** The tmwAthena protocol is making use of the MP status bar. */
+ bool canUseMagicBar() const
+ { return true; }
+
+ void disconnect2();
+
+ private:
+ std::string mMap;
+ int mCharID; /// < Saved for map-server switching
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_MAPHANDLER_H
diff --git a/src/net/tmwa/generalhandler.cpp b/src/net/tmwa/generalhandler.cpp
new file mode 100644
index 000000000..83d8ceb8d
--- /dev/null
+++ b/src/net/tmwa/generalhandler.cpp
@@ -0,0 +1,285 @@
+/*
+ * 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/tmwa/generalhandler.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "log.h"
+
+#include "gui/charselectdialog.h"
+#include "gui/inventorywindow.h"
+#include "gui/register.h"
+#include "gui/skilldialog.h"
+#include "gui/socialwindow.h"
+#include "gui/statuswindow.h"
+
+#include "net/messagein.h"
+#include "net/messageout.h"
+#include "net/serverinfo.h"
+
+#include "net/tmwa/adminhandler.h"
+#include "net/tmwa/beinghandler.h"
+#include "net/tmwa/buysellhandler.h"
+#include "net/tmwa/chathandler.h"
+#include "net/tmwa/charserverhandler.h"
+#include "net/tmwa/gamehandler.h"
+#include "net/tmwa/guildhandler.h"
+#include "net/tmwa/inventoryhandler.h"
+#include "net/tmwa/itemhandler.h"
+#include "net/tmwa/loginhandler.h"
+#include "net/tmwa/network.h"
+#include "net/tmwa/npchandler.h"
+#include "net/tmwa/partyhandler.h"
+#include "net/tmwa/playerhandler.h"
+#include "net/tmwa/protocol.h"
+#include "net/tmwa/tradehandler.h"
+#include "net/tmwa/specialhandler.h"
+
+#include "net/tmwa/gui/guildtab.h"
+#include "net/tmwa/gui/partytab.h"
+
+#include "resources/itemdb.h"
+
+#include "utils/gettext.h"
+
+#include <assert.h>
+#include <list>
+
+extern Net::GeneralHandler *generalHandler;
+
+namespace TmwAthena
+{
+
+ServerInfo charServer;
+ServerInfo mapServer;
+
+extern Guild *taGuild;
+extern Party *taParty;
+
+GeneralHandler::GeneralHandler():
+ mAdminHandler(new AdminHandler),
+ mBeingHandler(new BeingHandler(config.getBoolValue("EnableSync"))),
+ mBuySellHandler(new BuySellHandler),
+ mCharHandler(new CharServerHandler),
+ mChatHandler(new ChatHandler),
+ mGameHandler(new GameHandler),
+ mGuildHandler(new GuildHandler),
+ mInventoryHandler(new InventoryHandler),
+ mItemHandler(new ItemHandler),
+ mLoginHandler(new LoginHandler),
+ mNpcHandler(new NpcHandler),
+ mPartyHandler(new PartyHandler),
+ mPlayerHandler(new PlayerHandler),
+ mSpecialHandler(new SpecialHandler),
+ mTradeHandler(new TradeHandler)
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_CONNECTION_PROBLEM,
+ 0
+ };
+ handledMessages = _messages;
+ generalHandler = this;
+
+ std::list<ItemDB::Stat> stats;
+ stats.push_back(ItemDB::Stat("str", _("Strength %+d")));
+ stats.push_back(ItemDB::Stat("agi", _("Agility %+d")));
+ stats.push_back(ItemDB::Stat("vit", _("Vitality %+d")));
+ stats.push_back(ItemDB::Stat("int", _("Intelligence %+d")));
+ stats.push_back(ItemDB::Stat("dex", _("Dexterity %+d")));
+ stats.push_back(ItemDB::Stat("luck", _("Luck %+d")));
+
+ ItemDB::setStatsList(stats);
+
+ listen(CHANNEL_GAME);
+}
+
+GeneralHandler::~GeneralHandler()
+{
+ delete mNetwork;
+ mNetwork = 0;
+}
+
+void GeneralHandler::handleMessage(Net::MessageIn &msg)
+{
+ int code;
+
+ switch (msg.getId())
+ {
+ case SMSG_CONNECTION_PROBLEM:
+ code = msg.readInt8();
+ logger->log("Connection problem: %i", code);
+
+ switch (code)
+ {
+ case 0:
+ errorMessage = _("Authentication failed.");
+ break;
+ case 1:
+ errorMessage = _("No servers available.");
+ break;
+ case 2:
+ if (Client::getState() == STATE_GAME)
+ errorMessage = _("Someone else is trying to use this "
+ "account.");
+ else
+ errorMessage = _("This account is already logged in.");
+ break;
+ case 3:
+ errorMessage = _("Speed hack detected.");
+ break;
+ case 8:
+ errorMessage = _("Duplicated login.");
+ break;
+ default:
+ errorMessage = _("Unknown connection error.");
+ break;
+ }
+ Client::setState(STATE_ERROR);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void GeneralHandler::load()
+{
+ (new Network)->registerHandler(this);
+
+ if (!mNetwork)
+ return;
+
+ mNetwork->registerHandler(mAdminHandler.get());
+ mNetwork->registerHandler(mBeingHandler.get());
+ mNetwork->registerHandler(mBuySellHandler.get());
+ mNetwork->registerHandler(mChatHandler.get());
+ mNetwork->registerHandler(mCharHandler.get());
+ mNetwork->registerHandler(mGameHandler.get());
+ mNetwork->registerHandler(mGuildHandler.get());
+ mNetwork->registerHandler(mInventoryHandler.get());
+ mNetwork->registerHandler(mItemHandler.get());
+ mNetwork->registerHandler(mLoginHandler.get());
+ mNetwork->registerHandler(mNpcHandler.get());
+ mNetwork->registerHandler(mPlayerHandler.get());
+ mNetwork->registerHandler(mSpecialHandler.get());
+ mNetwork->registerHandler(mTradeHandler.get());
+ mNetwork->registerHandler(mPartyHandler.get());
+}
+
+void GeneralHandler::reload()
+{
+ if (mNetwork)
+ mNetwork->disconnect();
+
+ static_cast<LoginHandler*>(mLoginHandler.get())->clearWorlds();
+ static_cast<CharServerHandler*>(
+ mCharHandler.get())->setCharCreateDialog(0);
+ static_cast<CharServerHandler*>(
+ mCharHandler.get())->setCharSelectDialog(0);
+}
+
+void GeneralHandler::unload()
+{
+ if (mNetwork)
+ mNetwork->clearHandlers();
+}
+
+void GeneralHandler::flushNetwork()
+{
+ if (!mNetwork)
+ return;
+
+ mNetwork->flush();
+ mNetwork->dispatchMessages();
+
+ if (mNetwork->getState() == Network::NET_ERROR)
+ {
+ if (!mNetwork->getError().empty())
+ errorMessage = mNetwork->getError();
+ else
+ errorMessage = _("Got disconnected from server!");
+
+ Client::setState(STATE_ERROR);
+ }
+}
+
+void GeneralHandler::clearHandlers()
+{
+ if (mNetwork)
+ mNetwork->clearHandlers();
+}
+
+void GeneralHandler::event(Channels channel,
+ const Mana::Event &event)
+{
+ if (channel == CHANNEL_GAME)
+ {
+ if (event.getName() == EVENT_GUIWINDOWSLOADED)
+ {
+ if (inventoryWindow)
+ inventoryWindow->setSplitAllowed(false);
+ if (skillDialog)
+ skillDialog->loadSkills("ea-skills.xml");
+
+ if (!statusWindow)
+ return;
+
+ statusWindow->addAttribute(STR, _("Strength"), true, "");
+ statusWindow->addAttribute(AGI, _("Agility"), true, "");
+ statusWindow->addAttribute(VIT, _("Vitality"), true, "");
+ statusWindow->addAttribute(INT, _("Intelligence"), true, "");
+ statusWindow->addAttribute(DEX, _("Dexterity"), true, "");
+ statusWindow->addAttribute(LUK, _("Luck"), true, "");
+
+ statusWindow->addAttribute(ATK, _("Attack"), false, "");
+ statusWindow->addAttribute(DEF, _("Defense"), false, "");
+ statusWindow->addAttribute(MATK, _("M.Attack"), false, "");
+ statusWindow->addAttribute(MDEF, _("M.Defense"), false, "");
+ statusWindow->addAttribute(HIT, _("% Accuracy"), false, "");
+ statusWindow->addAttribute(FLEE, _("% Evade"), false, "");
+ statusWindow->addAttribute(CRIT, _("% Critical"), false, "");
+ statusWindow->addAttribute(ATTACK_SPEED, _("Attack Delay"),
+ false, "");
+ statusWindow->addAttribute(WALK_SPEED, _("Walk Delay"),
+ false, "");
+ statusWindow->addAttribute(ATTACK_RANGE, _("Attack Range"),
+ false, "");
+ }
+ else if (event.getName() == EVENT_GUIWINDOWSUNLOADING)
+ {
+ if (socialWindow)
+ {
+ socialWindow->removeTab(taGuild);
+ socialWindow->removeTab(taParty);
+ }
+
+ delete guildTab;
+ guildTab = 0;
+
+ delete partyTab;
+ partyTab = 0;
+ }
+ }
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/generalhandler.h b/src/net/tmwa/generalhandler.h
new file mode 100644
index 000000000..eded556d0
--- /dev/null
+++ b/src/net/tmwa/generalhandler.h
@@ -0,0 +1,83 @@
+/*
+ * 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_TMWA_GENERALHANDLER_H
+#define NET_TMWA_GENERALHANDLER_H
+
+#include "listener.h"
+
+#include "net/generalhandler.h"
+#include "net/net.h"
+
+#include "net/tmwa/messagehandler.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+namespace TmwAthena
+{
+
+class GeneralHandler : public MessageHandler, public Net::GeneralHandler,
+ public Mana::Listener
+{
+ public:
+ GeneralHandler();
+
+ ~GeneralHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void load();
+
+ void reload();
+
+ void unload();
+
+ void flushNetwork();
+
+ void clearHandlers();
+
+ void event(Channels channel, const Mana::Event &event);
+
+ protected:
+ MessageHandlerPtr mAdminHandler;
+ MessageHandlerPtr mBeingHandler;
+ MessageHandlerPtr mBuySellHandler;
+ MessageHandlerPtr mCharHandler;
+ MessageHandlerPtr mChatHandler;
+ MessageHandlerPtr mGameHandler;
+ MessageHandlerPtr mGuildHandler;
+ MessageHandlerPtr mInventoryHandler;
+ MessageHandlerPtr mItemHandler;
+ MessageHandlerPtr mLoginHandler;
+ MessageHandlerPtr mNpcHandler;
+ MessageHandlerPtr mPartyHandler;
+ MessageHandlerPtr mPlayerHandler;
+ MessageHandlerPtr mSpecialHandler;
+ MessageHandlerPtr mTradeHandler;
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_GENERALHANDLER_H
diff --git a/src/net/tmwa/gui/guildtab.cpp b/src/net/tmwa/gui/guildtab.cpp
new file mode 100644
index 000000000..4b080277c
--- /dev/null
+++ b/src/net/tmwa/gui/guildtab.cpp
@@ -0,0 +1,150 @@
+/*
+ * 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/tmwa/gui/guildtab.h"
+
+#include "chatlog.h"
+#include "commandhandler.h"
+#include "guild.h"
+#include "localplayer.h"
+
+#include "gui/theme.h"
+
+#include "net/net.h"
+#include "net/guildhandler.h"
+
+#include "resources/iteminfo.h"
+#include "resources/itemdb.h"
+
+#include "utils/dtor.h"
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+namespace TmwAthena
+{
+
+extern Guild *taGuild;
+
+GuildTab::GuildTab() :
+ ChatTab(_("Guild"))
+{
+ setTabColor(&Theme::getThemeColor(Theme::GUILD_CHAT_TAB));
+}
+
+GuildTab::~GuildTab()
+{
+}
+
+void GuildTab::handleInput(const std::string &msg)
+{
+ if (!taGuild)
+ return;
+
+ if (chatWindow)
+ {
+ Net::getGuildHandler()->chat(taGuild->getId(),
+ chatWindow->doReplace(msg));
+ }
+ else
+ {
+ Net::getGuildHandler()->chat(taGuild->getId(), msg);
+ }
+}
+
+void GuildTab::showHelp()
+{
+ chatLog(_("/help > Display this help."));
+ chatLog(_("/invite > Invite a player to your guild"));
+ chatLog(_("/leave > Leave the guild you are in"));
+ chatLog(_("/kick > Kick some one from the guild you are in"));
+}
+
+bool GuildTab::handleCommand(const std::string &type, const std::string &args)
+{
+ if (type == "help")
+ {
+ if (args == "invite")
+ {
+ chatLog(_("Command: /invite <nick>"));
+ chatLog(_("This command invites <nick> to the guild you're in."));
+ chatLog(_("If the <nick> has spaces in it, enclose it in "
+ "double quotes (\")."));
+ }
+ else if (args == "leave")
+ {
+ chatLog(_("Command: /leave"));
+ chatLog(_("This command causes the player to leave the guild."));
+ }
+ else
+ return false;
+ }
+/*
+ else if (type == "create" || type == "new")
+ {
+ if (args.empty())
+ chatLog(_("Guild name is missing."), BY_SERVER);
+ else
+ Net::getGuildHandler()->create(args);
+ }
+*/
+ else if (type == "invite" && taGuild)
+ {
+ Net::getGuildHandler()->invite(taGuild->getId(), args);
+ }
+ else if (type == "leave" && taGuild)
+ {
+ Net::getGuildHandler()->leave(taGuild->getId());
+ }
+ else if (type == "kick" && taGuild)
+ {
+ Net::getGuildHandler()->kick(taGuild->getMember(args));
+ }
+ else if (type == "notice" && taGuild)
+ {
+ std::string str1 = args.substr(0, 60);
+ std::string str2 = "";
+ if (args.size() > 60)
+ str2 = args.substr(60);
+ Net::getGuildHandler()->changeNotice(taGuild->getId(), str1, str2);
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void GuildTab::getAutoCompleteList(std::vector<std::string> &names) const
+{
+ if (taGuild)
+ taGuild->getNames(names);
+ names.push_back("/notice ");
+}
+
+void GuildTab::saveToLogFile(std::string &msg)
+{
+ if (chatLogger)
+ chatLogger->log("#Guild", msg);
+}
+
+} // namespace TmwAthena
+
diff --git a/src/net/tmwa/gui/guildtab.h b/src/net/tmwa/gui/guildtab.h
new file mode 100644
index 000000000..cce9eb596
--- /dev/null
+++ b/src/net/tmwa/gui/guildtab.h
@@ -0,0 +1,57 @@
+/*
+ * 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 TA_GUILDTAB_H
+#define TA_GUILDTAB_H
+
+#include "gui/widgets/chattab.h"
+
+namespace TmwAthena
+{
+
+/**
+ * A tab for a guild chat channel.
+ */
+class GuildTab : public ChatTab
+{
+ public:
+ GuildTab();
+ ~GuildTab();
+
+ void showHelp();
+
+ bool handleCommand(const std::string &type, const std::string &args);
+
+ void saveToLogFile(std::string &msg);
+
+ int getType() const { return ChatTab::TAB_GUILD; }
+
+ protected:
+ void handleInput(const std::string &msg);
+
+ void getAutoCompleteList(std::vector<std::string> &names) const;
+};
+
+extern GuildTab *guildTab;
+
+} // namespace TmwAthena
+
+#endif // TA_GUILDTAB_H
diff --git a/src/net/tmwa/gui/partytab.cpp b/src/net/tmwa/gui/partytab.cpp
new file mode 100644
index 000000000..9cfb4670f
--- /dev/null
+++ b/src/net/tmwa/gui/partytab.cpp
@@ -0,0 +1,242 @@
+/*
+ * 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/tmwa/gui/partytab.h"
+
+#include "chatlog.h"
+#include "commandhandler.h"
+#include "localplayer.h"
+#include "party.h"
+
+#include "gui/theme.h"
+
+#include "net/net.h"
+#include "net/partyhandler.h"
+
+#include "resources/iteminfo.h"
+#include "resources/itemdb.h"
+
+#include "utils/dtor.h"
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include "net/chathandler.h"
+
+namespace TmwAthena
+{
+
+PartyTab::PartyTab() :
+ ChatTab(_("Party"))
+{
+ setTabColor(&Theme::getThemeColor(Theme::PARTY_CHAT_TAB));
+}
+
+PartyTab::~PartyTab()
+{
+}
+
+void PartyTab::handleInput(const std::string &msg)
+{
+ if (chatWindow)
+ Net::getPartyHandler()->chat(chatWindow->doReplace(msg));
+ else
+ Net::getPartyHandler()->chat(msg);
+}
+
+void PartyTab::showHelp()
+{
+ chatLog(_("/help > Display this help."));
+ chatLog(_("/invite > Invite a player to your party"));
+ chatLog(_("/leave > Leave the party you are in"));
+ chatLog(_("/kick > Kick some one from the party you are in"));
+ chatLog(_("/item > Show/change party item sharing options"));
+ chatLog(_("/exp > Show/change party experience sharing options"));
+}
+
+bool PartyTab::handleCommand(const std::string &type, const std::string &args)
+{
+ if (type == "help")
+ {
+ if (args == "invite")
+ {
+ chatLog(_("Command: /invite <nick>"));
+ chatLog(_("This command invites <nick> to party with you."));
+ chatLog(_("If the <nick> has spaces in it, enclose it in "
+ "double quotes (\")."));
+ }
+ else if (args == "leave")
+ {
+ chatLog(_("Command: /leave"));
+ chatLog(_("This command causes the player to leave the party."));
+ }
+ else if (args == "item")
+ {
+ chatLog(_("Command: /item <policy>"));
+ chatLog(
+ _("This command changes the party's item sharing policy."));
+ chatLog(_("<policy> can be one of \"1\", \"yes\", \"true\" to "
+ "enable item sharing, or \"0\", \"no\", \"false\" to "
+ "disable item sharing."));
+ chatLog(_("Command: /item"));
+ chatLog(_("This command displays the party's"
+ " current item sharing policy."));
+ }
+ else if (args == "exp")
+ {
+ chatLog(_("Command: /exp <policy>"));
+ chatLog(_("This command changes the party's "
+ "experience sharing policy."));
+ chatLog(_("<policy> can be one of \"1\", \"yes\", \"true\" to "
+ "enable experience sharing, or \"0\","
+ " \"no\", \"false\" to disable experience sharing."));
+ chatLog(_("Command: /exp"));
+ chatLog(_("This command displays the party's current "
+ "experience sharing policy."));
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if (type == "create" || type == "new")
+ {
+ if (args.empty())
+ chatLog(_("Party name is missing."), BY_SERVER);
+ else
+ Net::getPartyHandler()->create(args);
+ }
+ else if (type == "invite")
+ {
+ Net::getPartyHandler()->invite(args);
+ }
+ else if (type == "leave")
+ {
+ Net::getPartyHandler()->leave();
+ }
+ else if (type == "kick")
+ {
+ Net::getPartyHandler()->kick(args);
+ }
+ else if (type == "item")
+ {
+ if (args.empty())
+ {
+ switch (Net::getPartyHandler()->getShareItems())
+ {
+ case PARTY_SHARE:
+ chatLog(_("Item sharing enabled."), BY_SERVER);
+ return true;
+ case PARTY_SHARE_NO:
+ chatLog(_("Item sharing disabled."), BY_SERVER);
+ return true;
+ case PARTY_SHARE_NOT_POSSIBLE:
+ chatLog(_("Item sharing not possible."), BY_SERVER);
+ return true;
+ case PARTY_SHARE_UNKNOWN:
+ chatLog(_("Item sharing unknown."), BY_SERVER);
+ return true;
+ default:
+ break;
+ }
+ }
+
+ char opt = CommandHandler::parseBoolean(args);
+
+ switch (opt)
+ {
+ case 1:
+ Net::getPartyHandler()->setShareItems(PARTY_SHARE);
+ break;
+ case 0:
+ Net::getPartyHandler()->setShareItems(PARTY_SHARE_NO);
+ break;
+ case -1:
+ chatLog(strprintf(BOOLEAN_OPTIONS, "item"));
+ default:
+ break;
+ }
+ }
+ else if (type == "exp")
+ {
+ if (args.empty())
+ {
+ switch (Net::getPartyHandler()->getShareExperience())
+ {
+ case PARTY_SHARE:
+ chatLog(_("Experience sharing enabled."), BY_SERVER);
+ return true;
+ case PARTY_SHARE_NO:
+ chatLog(_("Experience sharing disabled."), BY_SERVER);
+ return true;
+ case PARTY_SHARE_NOT_POSSIBLE:
+ chatLog(_("Experience sharing not possible."), BY_SERVER);
+ return true;
+ case PARTY_SHARE_UNKNOWN:
+ chatLog(_("Experience sharing unknown."), BY_SERVER);
+ return true;
+ default:
+ break;
+ }
+ }
+
+ char opt = CommandHandler::parseBoolean(args);
+
+ switch (opt)
+ {
+ case 1:
+ Net::getPartyHandler()->setShareExperience(PARTY_SHARE);
+ break;
+ case 0:
+ Net::getPartyHandler()->setShareExperience(PARTY_SHARE_NO);
+ break;
+ case -1:
+ chatLog(strprintf(BOOLEAN_OPTIONS, "exp"));
+ default:
+ break;
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void PartyTab::getAutoCompleteList(std::vector<std::string> &names) const
+{
+ if (!player_node)
+ return;
+
+ Party *p = player_node->getParty();
+
+ if (p)
+ p->getNames(names);
+}
+
+void PartyTab::saveToLogFile(std::string &msg)
+{
+ if (chatLogger)
+ chatLogger->log("#Party", msg);
+}
+
+} // namespace TmwAthena
+
diff --git a/src/net/tmwa/gui/partytab.h b/src/net/tmwa/gui/partytab.h
new file mode 100644
index 000000000..d5f4436db
--- /dev/null
+++ b/src/net/tmwa/gui/partytab.h
@@ -0,0 +1,57 @@
+/*
+ * 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 TA_PARTYTAB_H
+#define TA_PARTYTAB_H
+
+#include "gui/widgets/chattab.h"
+
+namespace TmwAthena
+{
+
+/**
+ * A tab for a party chat channel.
+ */
+class PartyTab : public ChatTab
+{
+ public:
+ PartyTab();
+ ~PartyTab();
+
+ void showHelp();
+
+ bool handleCommand(const std::string &type, const std::string &args);
+
+ int getType() const { return ChatTab::TAB_PARTY; }
+
+ void saveToLogFile(std::string &msg);
+
+ protected:
+ void handleInput(const std::string &msg);
+
+ virtual void getAutoCompleteList(std::vector<std::string>&) const;
+};
+
+extern PartyTab *partyTab;
+
+} // namespace TmwAthena
+
+#endif // TA_PARTYTAB_H
diff --git a/src/net/tmwa/guildhandler.cpp b/src/net/tmwa/guildhandler.cpp
new file mode 100644
index 000000000..88cd88009
--- /dev/null
+++ b/src/net/tmwa/guildhandler.cpp
@@ -0,0 +1,780 @@
+/*
+ * The Mana Client
+ * 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/tmwa/guildhandler.h"
+
+#include "actorspritemanager.h"
+#include "guild.h"
+#include "event.h"
+#include "localplayer.h"
+#include "log.h"
+#include "playerinfo.h"
+
+#include "gui/socialwindow.h"
+
+#include "net/tmwa/messagein.h"
+#include "net/tmwa/protocol.h"
+
+#include "net/tmwa/gui/guildtab.h"
+
+#include "utils/gettext.h"
+
+extern Net::GuildHandler *guildHandler;
+
+namespace TmwAthena
+{
+
+GuildTab *guildTab = 0;
+Guild *taGuild;
+bool showBasicInfo(false);
+
+GuildHandler::GuildHandler()
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_GUILD_CREATE_RESPONSE,
+ SMSG_GUILD_POSITION_INFO,
+ SMSG_GUILD_MEMBER_LOGIN,
+ SMSG_GUILD_MASTER_OR_MEMBER,
+ SMSG_GUILD_BASIC_INFO,
+ SMSG_GUILD_ALIANCE_INFO,
+ SMSG_GUILD_MEMBER_LIST,
+ SMSG_GUILD_POS_NAME_LIST,
+ SMSG_GUILD_POS_INFO_LIST,
+ SMSG_GUILD_POSITION_CHANGED,
+ SMSG_GUILD_MEMBER_POS_CHANGE,
+ SMSG_GUILD_EMBLEM,
+ SMSG_GUILD_SKILL_INFO,
+ SMSG_GUILD_NOTICE,
+ SMSG_GUILD_INVITE,
+ SMSG_GUILD_INVITE_ACK,
+ SMSG_GUILD_LEAVE,
+ SMSG_GUILD_EXPULSION,
+ SMSG_GUILD_EXPULSION_LIST,
+ SMSG_GUILD_MESSAGE,
+ SMSG_GUILD_SKILL_UP,
+ SMSG_GUILD_REQ_ALLIANCE,
+ SMSG_GUILD_REQ_ALLIANCE_ACK,
+ SMSG_GUILD_DEL_ALLIANCE,
+ SMSG_GUILD_OPPOSITION_ACK,
+ SMSG_GUILD_BROKEN,
+ 0
+ };
+ handledMessages = _messages;
+
+ guildHandler = this;
+}
+
+GuildHandler::~GuildHandler()
+{
+ delete guildTab;
+ guildTab = 0;
+}
+
+void GuildHandler::handleMessage(Net::MessageIn &msg)
+{
+ DEBUGLOG("guild message");
+
+ switch (msg.getId())
+ {
+ case SMSG_GUILD_CREATE_RESPONSE:
+ {
+ int flag = msg.readInt8();
+
+ if (flag == 0)
+ {
+ // Success
+ SERVER_NOTICE(_("Guild created."))
+ }
+ else if (flag == 1)
+ {
+ // Already in a guild
+ SERVER_NOTICE(_("You already in guild."))
+ }
+ else if (flag == 2)
+ {
+ // Unable to make (likely name already in use)
+ SERVER_NOTICE(_("You already in guild."))
+ }
+ else if (flag == 3)
+ {
+ // Emperium check failed
+ SERVER_NOTICE(_("Emperium check failed."))
+ }
+ else
+ {
+ // Unknown response
+ SERVER_NOTICE(_("Unknown server response."))
+ }
+ }
+ break;
+
+ case SMSG_GUILD_POSITION_INFO:
+ {
+ int guildId = msg.readInt32();
+ int emblem = msg.readInt32();
+ int posMode = msg.readInt32();
+ msg.readInt32(); // Unused
+ msg.readInt8(); // Unused
+ std::string guildName = msg.readString(24);
+
+ Guild *g = Guild::getGuild(guildId);
+ if (!g)
+ break;
+
+ g->setName(guildName);
+ g->setEmblemId(emblem);
+ if (!taGuild)
+ taGuild = g;
+ if (!guildTab && chatWindow)
+ {
+ guildTab = new GuildTab();
+ if (player_node)
+ player_node->addGuild(taGuild);
+ memberList(guildId);
+ }
+
+ if (player_node)
+ {
+ player_node->setGuild(g);
+ player_node->setGuildName(g->getName());
+ }
+
+ logger->log("Guild position info: %d %d %d %s\n", guildId,
+ emblem, posMode, guildName.c_str());
+ }
+ break;
+
+ case SMSG_GUILD_MEMBER_LOGIN:
+ {
+ int accountId = msg.readInt32(); // Account ID
+ int charId = msg.readInt32(); // Char ID
+ int online = msg.readInt32(); // Flag
+ if (taGuild)
+ {
+ GuildMember *m = taGuild->getMember(accountId, charId);
+ if (m)
+ m->setOnline(online);
+ }
+ break;
+ }
+
+ case SMSG_GUILD_MASTER_OR_MEMBER:
+ msg.readInt32(); // Type (0x57 for member, 0xd7 for master)
+ break;
+
+ case SMSG_GUILD_BASIC_INFO:
+ {
+ int guildId = msg.readInt32(); // Guild ID
+ int level = msg.readInt32(); // Guild level
+ int members = msg.readInt32(); // 'Connect member'
+ int maxMembers = msg.readInt32(); // 'Max member'
+ int avgLevel = msg.readInt32(); // Average level
+ int exp = msg.readInt32(); // Exp
+ int nextExp = msg.readInt32(); // Next exp
+ msg.skip(16); // unused
+ std::string name = msg.readString(24); // Name
+ std::string master = msg.readString(24); // Master's name
+ std::string castle = msg.readString(20); // Castles
+ // (ie: "Six Castles" or "None Taken")
+
+ if (guildTab && showBasicInfo)
+ {
+ showBasicInfo = false;
+ guildTab->chatLog(strprintf(
+ _("Guild name: %s"), name.c_str()), BY_SERVER);
+ guildTab->chatLog(strprintf(
+ _("Guild master: %s"), master.c_str()), BY_SERVER);
+ guildTab->chatLog(strprintf(
+ _("Guild level: %d"), level), BY_SERVER);
+ guildTab->chatLog(strprintf(
+ _("Online members: %d"), members), BY_SERVER);
+ guildTab->chatLog(strprintf(
+ _("Max members: %d"), maxMembers), BY_SERVER);
+ guildTab->chatLog(strprintf(
+ _("Average level: %d"), avgLevel), BY_SERVER);
+ guildTab->chatLog(strprintf(
+ _("Guild exp: %d"), exp), BY_SERVER);
+ guildTab->chatLog(strprintf(
+ _("Guild next exp: %d"), nextExp), BY_SERVER);
+ guildTab->chatLog(strprintf(
+ _("Guild castle: %s"), castle.c_str()), BY_SERVER);
+ }
+
+ Guild *g = Guild::getGuild(guildId);
+ if (!g)
+ break;
+ g->setName(name);
+ }
+ break;
+
+ case SMSG_GUILD_ALIANCE_INFO:
+ {
+ int length = msg.readInt16();
+ int count = (length - 4) / 32;
+
+ for (int i = 0; i < count; i++)
+ {
+ msg.readInt32(); // 'Opposition'
+ msg.readInt32(); // Other guild ID
+ msg.readString(24); // Other guild name
+ }
+ }
+ break;
+
+ case SMSG_GUILD_MEMBER_LIST:
+ {
+ int length = msg.readInt16();
+ int count = (length - 4) / 104;
+ if (!taGuild)
+ {
+ logger->log1("!taGuild");
+ break;
+ }
+
+ taGuild->clearMembers();
+
+ for (int i = 0; i < count; i++)
+ {
+ int id = msg.readInt32(); // Account ID
+ int charId = msg.readInt32(); // Char ID
+ msg.readInt16(); // Hair
+ msg.readInt16(); // Hair color
+ int gender = msg.readInt16(); // Gender
+ int race = msg.readInt16(); // Class
+ int level = msg.readInt16(); // Level
+ int exp = msg.readInt32(); // Exp
+ int online = msg.readInt32(); // Online
+ int pos = msg.readInt32(); // Position
+ msg.skip(50); // unused
+ std::string name = msg.readString(24); // Name
+
+ GuildMember *m = taGuild->addMember(id, charId, name);
+ if (m)
+ {
+ m->setOnline(online);
+ m->setID(id);
+ m->setCharId(charId);
+ if (!gender)
+ m->setGender(GENDER_FEMALE);
+ else if (gender == 1)
+ m->setGender(GENDER_MALE);
+ else
+ m->setGender(GENDER_UNSPECIFIED);
+
+ m->setLevel(level);
+ m->setExp(exp);
+ m->setPos(pos);
+ m->setRace(race);
+// m->setDisplayBold(!pos);
+ if (actorSpriteManager)
+ {
+ Being *being = actorSpriteManager->findBeingByName(
+ name, Being::PLAYER);
+ if (being)
+ {
+ being->setGuildName(taGuild->getName());
+ if (being->getLevel() != level)
+ {
+ being->setLevel(level);
+ being->updateName();
+ }
+ }
+ }
+ }
+ }
+ taGuild->sort();
+ if (actorSpriteManager)
+ {
+ actorSpriteManager->updatePlayerGuild();
+ actorSpriteManager->updatePlayerColors();
+ }
+ }
+ break;
+
+ case SMSG_GUILD_POS_NAME_LIST:
+ {
+ if (!taGuild)
+ {
+ logger->log1("!taGuild");
+ break;
+ }
+
+ int length = msg.readInt16();
+ int count = (length - 4) / 28;
+
+ for (int i = 0; i < count; i++)
+ {
+ int id = msg.readInt32(); // ID
+ std::string name = msg.readString(24); // Position name
+ taGuild->addPos(id, name);
+ }
+ }
+ break;
+
+ case SMSG_GUILD_POS_INFO_LIST:
+ {
+ int length = msg.readInt16();
+ int count = (length - 4) / 16;
+
+ for (int i = 0; i < count; i++)
+ {
+ msg.readInt32(); // ID
+ msg.readInt32(); // Mode
+ msg.readInt32(); // Same ID
+ msg.readInt32(); // Exp mode
+ }
+ }
+ break;
+
+ case SMSG_GUILD_POSITION_CHANGED:
+ msg.readInt16(); // Always 44
+ msg.readInt32(); // ID
+ msg.readInt32(); // Mode
+ msg.readInt32(); // Same ID
+ msg.readInt32(); // Exp mode
+ msg.readString(24); // Name
+ break;
+
+ case SMSG_GUILD_MEMBER_POS_CHANGE:
+ {
+ msg.readInt16(); // Always 16
+ int accountId = msg.readInt32(); // Account ID
+ int charId = msg.readInt32(); // Char ID
+ int pos = msg.readInt32(); // Position
+ if (taGuild)
+ {
+ GuildMember *m = taGuild->getMember(accountId, charId);
+ if (m)
+ m->setPos(pos);
+ }
+ break;
+ }
+
+ case SMSG_GUILD_EMBLEM:
+ {
+ int length = msg.readInt16();
+
+ msg.readInt32(); // Guild ID
+ msg.readInt32(); // Emblem ID
+ msg.skip(length - 12); // Emblem data (unknown format)
+ }
+ break;
+
+ case SMSG_GUILD_SKILL_INFO:
+ {
+ int length = msg.readInt16();
+ int count = (length - 6) / 37;
+
+ msg.readInt16(); // 'Skill point'
+
+ for (int i = 0; i < count; i++)
+ {
+ msg.readInt16(); // ID
+ msg.readInt16(); // 'Info' (unknown atm)
+ msg.readInt16(); // unused
+ msg.readInt16(); // Level
+ msg.readInt16(); // SP
+ msg.readInt16(); // 'Range'
+ msg.skip(24); // unused
+ msg.readInt8(); // Can be increased
+ }
+ }
+ break;
+
+ case SMSG_GUILD_NOTICE:
+ {
+ std::string msg1 = msg.readString(60); // Mes1
+ std::string msg2 = msg.readString(120); // Mes2
+ if (guildTab)
+ {
+ guildTab->chatLog(msg1, BY_SERVER);
+ guildTab->chatLog(msg2, BY_SERVER);
+ }
+ break;
+ }
+
+ case SMSG_GUILD_INVITE:
+ {
+ int guildId = msg.readInt32();
+ std::string guildName = msg.readString(24);
+
+ if (socialWindow)
+ socialWindow->showGuildInvite(guildName, guildId, "");
+ break;
+ }
+
+ case SMSG_GUILD_INVITE_ACK:
+ {
+ int flag = msg.readInt8();
+ if (!guildTab)
+ break;
+
+ switch (flag)
+ {
+ case 0:
+ guildTab->chatLog(_("Could not inivte user to guild."),
+ BY_SERVER);
+ break;
+
+ case 1:
+ guildTab->chatLog(_("User rejected guild invite."),
+ BY_SERVER);
+ break;
+
+ case 2:
+ guildTab->chatLog(_("User is now part of your guild."),
+ BY_SERVER);
+ break;
+
+ case 3:
+ guildTab->chatLog(_("Your guild is full."),
+ BY_SERVER);
+ break;
+
+ default:
+ guildTab->chatLog(_("Unknown guild invite response."),
+ BY_SERVER);
+ break;
+ }
+ }
+ break;
+
+ case SMSG_GUILD_LEAVE:
+ {
+ std::string nick = msg.readString(24); // Name
+ std::string message = msg.readString(40); // Message
+
+ if (taGuild)
+ taGuild->removeMember(nick);
+
+ if (player_node && nick == player_node->getName())
+ {
+ if (taGuild)
+ {
+ taGuild->removeFromMembers();
+ taGuild->clearMembers();
+ }
+ SERVER_NOTICE(_("You have left the guild."))
+ delete guildTab;
+ guildTab = 0;
+
+ if (socialWindow && taGuild)
+ socialWindow->removeTab(taGuild);
+ if (actorSpriteManager)
+ actorSpriteManager->updatePlayerColors();
+ }
+ else
+ {
+ if (guildTab)
+ {
+ guildTab->chatLog(strprintf(
+ _("%s has left your guild."),
+ nick.c_str()), BY_SERVER);
+ }
+ if (actorSpriteManager)
+ {
+ Being *b = actorSpriteManager->findBeingByName(
+ nick, Being::PLAYER);
+
+ if (b)
+ b->clearGuilds();
+ if (taGuild)
+ taGuild->removeMember(nick);
+ }
+ }
+ break;
+ }
+
+ case SMSG_GUILD_EXPULSION:
+ {
+ std::string nick = msg.readString(24); // Name (of expulsed?)
+ std::string message = msg.readString(40); // Message
+ msg.skip(24); // unused ("dummy")
+ if (taGuild)
+ taGuild->removeMember(nick);
+
+ if (player_node && nick == player_node->getName())
+ {
+ if (taGuild)
+ {
+ taGuild->removeFromMembers();
+ taGuild->clearMembers();
+ }
+ SERVER_NOTICE(_("You was kicked from guild."));
+ delete guildTab;
+ guildTab = 0;
+
+ if (socialWindow && taGuild)
+ socialWindow->removeTab(taGuild);
+ if (actorSpriteManager)
+ actorSpriteManager->updatePlayerColors();
+ }
+ else
+ {
+ if (guildTab)
+ {
+ guildTab->chatLog(strprintf(
+ _("%s has kicked from your guild."),
+ nick.c_str()), BY_SERVER);
+ }
+
+ if (actorSpriteManager)
+ {
+ Being *b = actorSpriteManager->findBeingByName(
+ nick, Being::PLAYER);
+
+ if (b)
+ b->clearGuilds();
+ if (taGuild)
+ taGuild->removeMember(nick);
+ }
+ }
+ break;
+ }
+
+ case SMSG_GUILD_EXPULSION_LIST:
+ {
+ int length = msg.readInt16();
+ int count = (length - 4) / 88;
+
+ for (int i = 0; i < count; i++)
+ {
+ msg.readString(24); // Name (of expulsed?)
+ msg.readString(24); // 'Acc' (name of expulser?)
+ msg.readString(24); // Message
+ }
+ }
+ break;
+
+ case SMSG_GUILD_MESSAGE:
+ {
+ int msgLength = msg.readInt16() - 4;
+
+ if (msgLength <= 0)
+ return;
+ if (guildTab)
+ {
+ std::string chatMsg = msg.readString(msgLength);
+
+ std::string::size_type pos = chatMsg.find(" : ", 0);
+ if (pos != std::string::npos)
+ {
+ std::string sender_name = ((pos == std::string::npos)
+ ? "" : chatMsg.substr(0, pos));
+
+ chatMsg.erase(0, pos + 3);
+
+ trim(chatMsg);
+ guildTab->chatLog(sender_name, chatMsg);
+ }
+ else
+ {
+ guildTab->chatLog(chatMsg);
+ }
+ }
+ }
+ break;
+
+ case SMSG_GUILD_SKILL_UP:
+ msg.readInt16(); // Skill ID
+ msg.readInt16(); // Level
+ msg.readInt16(); // SP
+ msg.readInt16(); // 'Range'
+ msg.readInt8(); // unused? (always 1)
+ break;
+
+ case SMSG_GUILD_REQ_ALLIANCE:
+ msg.readInt32(); // Account ID
+ msg.readString(24); // Name
+ break;
+
+ case SMSG_GUILD_REQ_ALLIANCE_ACK:
+ msg.readInt32(); // Flag
+ break;
+
+ case SMSG_GUILD_DEL_ALLIANCE:
+ msg.readInt32(); // Guild ID
+ msg.readInt32(); // Flag
+ break;
+
+ case SMSG_GUILD_OPPOSITION_ACK:
+ msg.readInt8(); // Flag
+ break;
+
+ case SMSG_GUILD_BROKEN:
+ msg.readInt32(); // Flag
+ break;
+
+ default:
+ break;
+ }
+}
+
+void GuildHandler::create(const std::string &name)
+{
+ MessageOut msg(CMSG_GUILD_CREATE);
+ msg.writeInt32(0); // Unused
+ msg.writeString(name, 24);
+}
+
+void GuildHandler::invite(int guildId _UNUSED_,
+ const std::string &name _UNUSED_)
+{
+ if (!actorSpriteManager)
+ return;
+
+ Being* being = actorSpriteManager->findBeingByName(name, Being::PLAYER);
+ if (being)
+ {
+ MessageOut msg(CMSG_GUILD_INVITE);
+ msg.writeInt32(being->getId());
+ msg.writeInt32(0); // Unused
+ msg.writeInt32(0); // Unused
+ }
+}
+
+void GuildHandler::invite(int guildId _UNUSED_, Being *being)
+{
+ if (!being)
+ return;
+
+ MessageOut msg(CMSG_GUILD_INVITE);
+ msg.writeInt32(being->getId());
+ msg.writeInt32(0); // Unused
+ msg.writeInt32(0); // Unused
+}
+
+void GuildHandler::inviteResponse(int guildId, bool response)
+{
+ MessageOut msg(CMSG_GUILD_INVITE_REPLY);
+ msg.writeInt32(guildId);
+ msg.writeInt8(response);
+ msg.writeInt8(0); // Unused
+ msg.writeInt16(0); // Unused
+}
+
+void GuildHandler::leave(int guildId)
+{
+ if (!player_node)
+ return;
+
+ MessageOut msg(CMSG_GUILD_LEAVE);
+ msg.writeInt32(guildId);
+ msg.writeInt32(player_node->getId()); // Account ID
+ msg.writeInt32(PlayerInfo::getCharId()); // Char ID
+ msg.writeString("", 40); // Message
+}
+
+void GuildHandler::kick(GuildMember *member, std::string reason)
+{
+ if (!member || !member->getGuild())
+ return;
+
+ MessageOut msg(CMSG_GUILD_EXPULSION);
+ msg.writeInt32(member->getGuild()->getId());
+ msg.writeInt32(member->getID()); // Account ID
+ msg.writeInt32(member->getCharId()); // Char ID
+ msg.writeString(reason, 40); // Message
+}
+
+void GuildHandler::chat(int guildId _UNUSED_, const std::string &text)
+{
+ if (!player_node)
+ return;
+
+ std::string str = player_node->getName() + " : " + text;
+ MessageOut msg(CMSG_GUILD_MESSAGE);
+ msg.writeInt16(str.size() + 4);
+ msg.writeString(str, str.length());
+}
+
+void GuildHandler::memberList(int guildId _UNUSED_)
+{
+ // TODO four types of info requests:
+ // 0 = basic info + alliance info
+ // 1 = position name list + member list
+ // 2 = position name list + position info list
+ // 3 = skill info
+ // 4 = expulsion list
+
+ MessageOut msg(CMSG_GUILD_REQUEST_INFO);
+ msg.writeInt32(1); // Request member list
+}
+
+void GuildHandler::info(int guildId _UNUSED_)
+{
+ // TODO four types of info requests:
+ // 0 = basic info + alliance info
+ // 1 = position name list + member list
+ // 2 = position name list + position info list
+ // 3 = skill info
+ // 4 = expulsion list
+
+ showBasicInfo = true;
+ MessageOut msg(CMSG_GUILD_REQUEST_INFO);
+ msg.writeInt32(0); // Request basic info
+}
+
+void GuildHandler::changeMemberPostion(GuildMember *member, int level)
+{
+ if (!member || !member->getGuild())
+ return;
+
+ MessageOut msg(CMSG_GUILD_CHANGE_MEMBER_POS);
+ msg.writeInt16(16); // size less then 16 <= 4 + 12
+ msg.writeInt32(member->getID()); // Account ID
+ msg.writeInt32(member->getCharId()); // Char ID
+ msg.writeInt32(level); // pos
+}
+
+void GuildHandler::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, std::string msg1,
+ std::string msg2)
+{
+ MessageOut msg(CMSG_GUILD_CHANGE_NOTICE);
+ msg.writeInt32(guildId);
+ msg.writeString(msg1, 60); // msg1
+ msg.writeString(msg2, 120); // msg2
+}
+
+bool GuildHandler::isSupported()
+{
+ return true;
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/guildhandler.h b/src/net/tmwa/guildhandler.h
new file mode 100644
index 000000000..80b03bd01
--- /dev/null
+++ b/src/net/tmwa/guildhandler.h
@@ -0,0 +1,84 @@
+/*
+ * The Mana Client
+ * 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_TA_GUILDHANDLER_H
+#define NET_TA_GUILDHANDLER_H
+
+#include "net/guildhandler.h"
+
+#include "net/tmwa/messagehandler.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+namespace TmwAthena
+{
+
+class GuildHandler : public Net::GuildHandler, public MessageHandler
+{
+ public:
+ GuildHandler();
+
+ ~GuildHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void create(const std::string &name);
+
+ void invite(int guildId, const std::string &name);
+
+ void invite(int guildId, Being *being);
+
+ void inviteResponse(int guildId, bool response);
+
+ void leave(int guildId);
+
+ void kick(GuildMember *member, std::string reason = "");
+
+ void chat(int guildId, const std::string &text);
+
+ void memberList(int guildId);
+
+ void info(int guildId _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);
+
+ bool isSupported();
+
+ private:
+ // TmwAthena (and eAthena) only supports one guild per player
+ Guild *mGuild;
+};
+
+}
+
+#endif // NET_TA_GUILDHANDLER_H
diff --git a/src/net/tmwa/inventoryhandler.cpp b/src/net/tmwa/inventoryhandler.cpp
new file mode 100644
index 000000000..b4b38e623
--- /dev/null
+++ b/src/net/tmwa/inventoryhandler.cpp
@@ -0,0 +1,609 @@
+/*
+ * 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/tmwa/inventoryhandler.h"
+
+#include "configuration.h"
+#include "equipment.h"
+#include "event.h"
+#include "inventory.h"
+#include "item.h"
+#include "itemshortcut.h"
+#include "localplayer.h"
+#include "log.h"
+
+#include "gui/widgets/chattab.h"
+
+#include "net/messagein.h"
+#include "net/messageout.h"
+
+#include "net/tmwa/protocol.h"
+
+#include "resources/iteminfo.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include <SDL_types.h>
+
+extern Net::InventoryHandler *inventoryHandler;
+
+const Equipment::Slot EQUIP_POINTS[Equipment::EQUIP_VECTOREND] =
+{
+ Equipment::EQUIP_LEGS_SLOT,
+ Equipment::EQUIP_FIGHT1_SLOT,
+ Equipment::EQUIP_GLOVES_SLOT,
+ Equipment::EQUIP_RING2_SLOT,
+ Equipment::EQUIP_RING1_SLOT,
+ Equipment::EQUIP_FIGHT2_SLOT,
+ Equipment::EQUIP_FEET_SLOT,
+ Equipment::EQUIP_NECK_SLOT,
+ Equipment::EQUIP_HEAD_SLOT,
+ Equipment::EQUIP_TORSO_SLOT,
+ Equipment::EQUIP_EVOL_RING1_SLOT,
+ Equipment::EQUIP_EVOL_RING2_SLOT,
+ Equipment::EQUIP_PROJECTILE_SLOT,
+};
+
+const Equipment::Slot EQUIP_CONVERT[] =
+{
+ Equipment::EQUIP_PROJECTILE_SLOT, // 0
+ Equipment::EQUIP_FEET_SLOT, // SPRITE_SHOE
+ Equipment::EQUIP_LEGS_SLOT, // SPRITE_BOTTOMCLOTHES
+ Equipment::EQUIP_TORSO_SLOT, // SPRITE_TOPCLOTHES
+ Equipment::EQUIP_PROJECTILE_SLOT, // 0
+ Equipment::EQUIP_PROJECTILE_SLOT, // 0
+ Equipment::EQUIP_PROJECTILE_SLOT, // 0
+ Equipment::EQUIP_HEAD_SLOT, // SPRITE_HAT
+ Equipment::EQUIP_PROJECTILE_SLOT, // 0
+ Equipment::EQUIP_GLOVES_SLOT, // SPRITE_GLOVES
+ Equipment::EQUIP_FIGHT1_SLOT, // SPRITE_WEAPON
+ Equipment::EQUIP_FIGHT2_SLOT, // SPRITE_SHIELD
+ Equipment::EQUIP_PROJECTILE_SLOT, // 0
+ Equipment::EQUIP_PROJECTILE_SLOT, // 0
+ Equipment::EQUIP_PROJECTILE_SLOT, // 0
+};
+
+namespace TmwAthena
+{
+
+int getSlot(int eAthenaSlot);
+
+int getSlot(int eAthenaSlot)
+{
+ if (eAthenaSlot == 0)
+ return Equipment::EQUIP_VECTOREND;
+
+ if (eAthenaSlot & 0x8000)
+ return Equipment::EQUIP_PROJECTILE_SLOT;
+
+ int mask = 1;
+ int position = 0;
+ while (!(eAthenaSlot & mask))
+ {
+ mask <<= 1;
+ position++;
+ }
+ return EQUIP_POINTS[position];
+}
+
+enum
+{
+ debugInventory = 1
+};
+
+InventoryHandler::InventoryHandler()
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_PLAYER_INVENTORY,
+ SMSG_PLAYER_INVENTORY_ADD,
+ SMSG_PLAYER_INVENTORY_REMOVE,
+ SMSG_PLAYER_INVENTORY_USE,
+ SMSG_ITEM_USE_RESPONSE,
+ SMSG_PLAYER_STORAGE_ITEMS,
+ SMSG_PLAYER_STORAGE_EQUIP,
+ SMSG_PLAYER_STORAGE_STATUS,
+ SMSG_PLAYER_STORAGE_ADD,
+ SMSG_PLAYER_STORAGE_REMOVE,
+ SMSG_PLAYER_STORAGE_CLOSE,
+ SMSG_PLAYER_EQUIPMENT,
+ SMSG_PLAYER_EQUIP,
+ SMSG_PLAYER_UNEQUIP,
+ SMSG_PLAYER_ARROW_EQUIP,
+ SMSG_PLAYER_ATTACK_RANGE,
+ 0
+ };
+ handledMessages = _messages;
+ inventoryHandler = this;
+
+ mStorage = 0;
+ mStorageWindow = 0;
+}
+
+InventoryHandler::~InventoryHandler()
+{
+ if (mStorageWindow)
+ {
+ mStorageWindow->close();
+ mStorageWindow = 0;
+ }
+
+ delete mStorage;
+ mStorage = 0;
+}
+
+void InventoryHandler::handleMessage(Net::MessageIn &msg)
+{
+ int number, flag;
+ int index, amount, itemId, equipType, arrow, refine;
+ int identified, cards[4], itemType;
+ Inventory *inventory = 0;
+ if (player_node)
+ inventory = PlayerInfo::getInventory();
+
+ switch (msg.getId())
+ {
+ case SMSG_PLAYER_INVENTORY:
+ case SMSG_PLAYER_STORAGE_ITEMS:
+ if (msg.getId() == SMSG_PLAYER_INVENTORY)
+ {
+ // Clear inventory - this will be a complete refresh
+ mEquips.clear();
+ PlayerInfo::getEquipment()->setBackend(&mEquips);
+
+ if (inventory)
+ inventory->clear();
+ }
+ else
+ {
+ mInventoryItems.clear();
+ }
+
+ msg.readInt16(); // length
+ number = (msg.getLength() - 4) / 18;
+
+ for (int loop = 0; loop < number; loop++)
+ {
+ index = msg.readInt16();
+ itemId = msg.readInt16();
+ itemType = msg.readInt8();
+ identified = msg.readInt8();
+ amount = msg.readInt16();
+ arrow = msg.readInt16();
+ for (int i = 0; i < 4; i++)
+ cards[i] = msg.readInt16();
+
+ index -= (msg.getId() == SMSG_PLAYER_INVENTORY) ?
+ INVENTORY_OFFSET : STORAGE_OFFSET;
+
+ if (debugInventory)
+ {
+ logger->log("Index: %d, ID: %d, Type: %d, Identified: %d, "
+ "Qty: %d, Cards: %d, %d, %d, %d",
+ index, itemId, itemType, identified, amount,
+ cards[0], cards[1], cards[2], cards[3]);
+ }
+
+ if (msg.getId() == SMSG_PLAYER_INVENTORY)
+ {
+ // Trick because arrows are not considered equipment
+ bool isEquipment = arrow & 0x8000;
+
+ if (inventory)
+ {
+ inventory->setItem(index, itemId, amount,
+ 0, isEquipment);
+ }
+ }
+ else
+ {
+ mInventoryItems.push_back(InventoryItem(index, itemId,
+ amount, 0, false));
+ }
+ }
+ break;
+
+ case SMSG_PLAYER_STORAGE_EQUIP:
+ msg.readInt16(); // length
+ number = (msg.getLength() - 4) / 20;
+
+ for (int loop = 0; loop < number; loop++)
+ {
+ index = msg.readInt16() - STORAGE_OFFSET;
+ itemId = msg.readInt16();
+ itemType = msg.readInt8();
+ identified = msg.readInt8();
+ amount = 1;
+ msg.readInt16(); // Equip Point?
+ msg.readInt16(); // Another Equip Point?
+ msg.readInt8(); // Attribute (broken)
+ refine = msg.readInt8();
+ for (int i = 0; i < 4; i++)
+ cards[i] = msg.readInt16();
+
+ if (debugInventory)
+ {
+ logger->log("Index: %d, ID: %d, Type: %d, Identified: %d, "
+ "Qty: %d, Cards: %d, %d, %d, %d, Refine: %d",
+ index, itemId, itemType, identified, amount,
+ cards[0], cards[1], cards[2], cards[3],
+ refine);
+ }
+
+ mInventoryItems.push_back(InventoryItem(index, itemId, amount,
+ refine, false));
+ }
+ break;
+
+ case SMSG_PLAYER_INVENTORY_ADD:
+ index = msg.readInt16() - INVENTORY_OFFSET;
+ amount = msg.readInt16();
+ itemId = msg.readInt16();
+ identified = msg.readInt8();
+ msg.readInt8(); // attribute
+ refine = msg.readInt8();
+ for (int i = 0; i < 4; i++)
+ cards[i] = msg.readInt16();
+ equipType = msg.readInt16();
+ itemType = msg.readInt8();
+
+ {
+ const ItemInfo &itemInfo = ItemDB::get(itemId);
+
+ if (msg.readInt8() > 0)
+ {
+ if (player_node)
+ player_node->pickedUp(itemInfo, 0);
+ }
+ else
+ {
+ if (player_node)
+ player_node->pickedUp(itemInfo, amount);
+
+ if (inventory)
+ {
+ Item *item = inventory->getItem(index);
+
+ if (item && item->getId() == itemId)
+ amount += inventory->getItem(index)->getQuantity();
+
+ inventory->setItem(index, itemId, amount, refine,
+ equipType != 0);
+ }
+ }
+ } break;
+
+ case SMSG_PLAYER_INVENTORY_REMOVE:
+ index = msg.readInt16() - INVENTORY_OFFSET;
+ amount = msg.readInt16();
+ if (inventory)
+ {
+ if (Item *item = inventory->getItem(index))
+ {
+ item->increaseQuantity(-amount);
+ if (item->getQuantity() == 0)
+ inventory->removeItemAt(index);
+ }
+ }
+ break;
+
+ case SMSG_PLAYER_INVENTORY_USE:
+ index = msg.readInt16() - INVENTORY_OFFSET;
+ msg.readInt16(); // item id
+ msg.readInt32(); // id
+ amount = msg.readInt16();
+ msg.readInt8(); // type
+
+ if (inventory)
+ {
+ if (Item *item = inventory->getItem(index))
+ {
+ if (amount)
+ item->setQuantity(amount);
+ else
+ inventory->removeItemAt(index);
+ }
+ }
+ break;
+
+ case SMSG_ITEM_USE_RESPONSE:
+ index = msg.readInt16() - INVENTORY_OFFSET;
+ amount = msg.readInt16();
+
+ if (msg.readInt8() == 0)
+ {
+ SERVER_NOTICE(_("Failed to use item."))
+ }
+ else
+ {
+ if (inventory)
+ {
+ if (Item *item = inventory->getItem(index))
+ {
+ if (amount)
+ item->setQuantity(amount);
+ else
+ inventory->removeItemAt(index);
+ }
+ }
+ }
+ break;
+
+ case SMSG_PLAYER_STORAGE_STATUS:
+ /*
+ * This is the closest we get to an "Open Storage" packet from the
+ * server. It always comes after the two SMSG_PLAYER_STORAGE_...
+ * packets that update storage contents.
+ */
+ {
+ msg.readInt16(); // Used count
+ int size = msg.readInt16(); // Max size
+
+ if (!mStorage)
+ mStorage = new Inventory(Inventory::STORAGE, size);
+
+ InventoryItems::iterator it = mInventoryItems.begin();
+ InventoryItems::iterator it_end = mInventoryItems.end();
+ for (; it != it_end; it++)
+ mStorage->setItem((*it).slot, (*it).id, (*it).quantity,
+ (*it).equip);
+ mInventoryItems.clear();
+
+ if (!mStorageWindow)
+ mStorageWindow = new InventoryWindow(mStorage);
+ }
+ break;
+
+ case SMSG_PLAYER_STORAGE_ADD:
+ // Move an item into storage
+ index = msg.readInt16() - STORAGE_OFFSET;
+ amount = msg.readInt32();
+ itemId = msg.readInt16();
+ identified = msg.readInt8();
+ msg.readInt8(); // attribute
+ refine = msg.readInt8();
+ for (int i = 0; i < 4; i++)
+ cards[i] = msg.readInt16();
+
+ if (Item *item = mStorage->getItem(index))
+ {
+ item->setId(itemId);
+ item->increaseQuantity(amount);
+ }
+ else
+ {
+ if (mStorage)
+ mStorage->setItem(index, itemId, amount, false);
+ }
+ break;
+
+ case SMSG_PLAYER_STORAGE_REMOVE:
+ // Move an item out of storage
+ index = msg.readInt16() - STORAGE_OFFSET;
+ amount = msg.readInt16();
+ if (mStorage)
+ {
+ if (Item *item = mStorage->getItem(index))
+ {
+ item->increaseQuantity(-amount);
+ if (item->getQuantity() == 0)
+ mStorage->removeItemAt(index);
+ }
+ }
+ break;
+
+ case SMSG_PLAYER_STORAGE_CLOSE:
+ // Storage access has been closed
+
+ // Storage window deletes itself
+ mStorageWindow = 0;
+
+ if (mStorage)
+ mStorage->clear();
+
+ delete mStorage;
+ mStorage = 0;
+ break;
+
+ case SMSG_PLAYER_EQUIPMENT:
+ msg.readInt16(); // length
+ number = (msg.getLength() - 4) / 20;
+
+ for (int loop = 0; loop < number; loop++)
+ {
+ index = msg.readInt16() - INVENTORY_OFFSET;
+ itemId = msg.readInt16();
+ msg.readInt8(); // type
+ msg.readInt8(); // identify flag
+ msg.readInt16(); // equip type
+ equipType = msg.readInt16();
+ msg.readInt8(); // attribute
+ refine = msg.readInt8();
+ msg.skip(8); // card
+
+ if (inventory)
+ inventory->setItem(index, itemId, 1, refine, true);
+
+ if (equipType)
+ mEquips.setEquipment(getSlot(equipType), index);
+ }
+ break;
+
+ case SMSG_PLAYER_EQUIP:
+ index = msg.readInt16() - INVENTORY_OFFSET;
+ equipType = msg.readInt16();
+ flag = msg.readInt8();
+
+ if (!flag)
+ SERVER_NOTICE(_("Unable to equip."))
+ else
+ mEquips.setEquipment(getSlot(equipType), index);
+ break;
+
+ case SMSG_PLAYER_UNEQUIP:
+ index = msg.readInt16() - INVENTORY_OFFSET;
+ equipType = msg.readInt16();
+ flag = msg.readInt8();
+
+ if (flag)
+ mEquips.setEquipment(getSlot(equipType), -1);
+ break;
+
+ case SMSG_PLAYER_ATTACK_RANGE:
+ {
+ int range = msg.readInt16();
+ if (player_node)
+ player_node->setAttackRange(range);
+ PlayerInfo::setStatBase(ATTACK_RANGE, range);
+ PlayerInfo::setStatMod(ATTACK_RANGE, 0);
+ break;
+ }
+
+ case SMSG_PLAYER_ARROW_EQUIP:
+ index = msg.readInt16();
+
+ if (index <= 1)
+ break;
+
+ index -= INVENTORY_OFFSET;
+
+ logger->log("Arrows equipped: %i", index);
+ mEquips.setEquipment(Equipment::EQUIP_PROJECTILE_SLOT, index);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void InventoryHandler::equipItem(const Item *item)
+{
+ if (!item)
+ return;
+
+ MessageOut outMsg(CMSG_PLAYER_EQUIP);
+ outMsg.writeInt16(static_cast<Sint16>(
+ item->getInvIndex() + INVENTORY_OFFSET));
+ outMsg.writeInt16(0);
+}
+
+void InventoryHandler::unequipItem(const Item *item)
+{
+ if (!item)
+ return;
+
+ MessageOut outMsg(CMSG_PLAYER_UNEQUIP);
+ outMsg.writeInt16(static_cast<Sint16>(
+ item->getInvIndex() + INVENTORY_OFFSET));
+}
+
+void InventoryHandler::useItem(const Item *item)
+{
+ if (!item)
+ return;
+
+ MessageOut outMsg(CMSG_PLAYER_INVENTORY_USE);
+ outMsg.writeInt16(static_cast<Sint16>(
+ item->getInvIndex() + INVENTORY_OFFSET));
+ outMsg.writeInt32(item->getId()); // unused
+}
+
+void InventoryHandler::dropItem(const Item *item, int amount)
+{
+ if (!item)
+ return;
+
+ // TODO: Fix wrong coordinates of drops, serverside? (what's wrong here?)
+ MessageOut outMsg(CMSG_PLAYER_INVENTORY_DROP);
+ outMsg.writeInt16(static_cast<Sint16>(
+ item->getInvIndex() + INVENTORY_OFFSET));
+ outMsg.writeInt16(static_cast<Sint16>(amount));
+}
+
+bool InventoryHandler::canSplit(const Item *item _UNUSED_)
+{
+ return false;
+}
+
+void InventoryHandler::splitItem(const Item *item _UNUSED_,
+ int amount _UNUSED_)
+{
+ // Not implemented for eAthena (possible?)
+}
+
+void InventoryHandler::moveItem(int oldIndex _UNUSED_, int newIndex _UNUSED_)
+{
+ // Not implemented for eAthena (possible?)
+}
+
+void InventoryHandler::openStorage(int type _UNUSED_)
+{
+ // Doesn't apply to eAthena, since opening happens through NPCs?
+}
+
+void InventoryHandler::closeStorage(int type _UNUSED_)
+{
+ MessageOut outMsg(CMSG_CLOSE_STORAGE);
+}
+
+void InventoryHandler::moveItem(int source, int slot, int amount,
+ int destination)
+{
+ if (source == Inventory::INVENTORY && destination == Inventory::STORAGE)
+ {
+ MessageOut outMsg(CMSG_MOVE_TO_STORAGE);
+ outMsg.writeInt16(slot + INVENTORY_OFFSET);
+ outMsg.writeInt32(amount);
+ }
+ else if (source == Inventory::STORAGE
+ && destination == Inventory::INVENTORY)
+ {
+ MessageOut outMsg(CSMG_MOVE_FROM_STORAGE);
+ outMsg.writeInt16(slot + STORAGE_OFFSET);
+ outMsg.writeInt32(amount);
+ }
+}
+
+size_t InventoryHandler::getSize(int type) const
+{
+ switch (type)
+ {
+ case Inventory::INVENTORY:
+ return 100;
+ case Inventory::STORAGE:
+ return 0; // Comes from server after items
+ case Inventory::TRADE:
+ return 12;
+ case GUILD_STORAGE:
+ return 0; // Comes from server after items
+ default:
+ return 0;
+ }
+}
+int InventoryHandler::convertFromServerSlot(int serverSlot)
+{
+ if (serverSlot < 0 || serverSlot > 13)
+ return 0;
+
+ return EQUIP_CONVERT[serverSlot];
+}
+} // namespace TmwAthena
diff --git a/src/net/tmwa/inventoryhandler.h b/src/net/tmwa/inventoryhandler.h
new file mode 100644
index 000000000..1a10b6ba0
--- /dev/null
+++ b/src/net/tmwa/inventoryhandler.h
@@ -0,0 +1,177 @@
+/*
+ * 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_TA_INVENTORYHANDLER_H
+#define NET_TA_INVENTORYHANDLER_H
+
+#include "equipment.h"
+#include "inventory.h"
+#include "playerinfo.h"
+
+#include "gui/inventorywindow.h"
+
+#include "net/inventoryhandler.h"
+#include "net/net.h"
+
+#include "net/tmwa/messagehandler.h"
+
+#include <list>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+namespace TmwAthena
+{
+
+class EquipBackend : public Equipment::Backend
+{
+ public:
+ EquipBackend()
+ {
+ memset(mEquipment, -1, sizeof(mEquipment));
+ }
+
+ Item *getEquipment(int index) const
+ {
+ int invyIndex = mEquipment[index];
+ if (invyIndex == -1)
+ {
+ return NULL;
+ }
+ return PlayerInfo::getInventory()->getItem(invyIndex);
+ }
+
+ void clear()
+ {
+ for (int i = 0; i < EQUIPMENT_SIZE; i++)
+ {
+ if (mEquipment[i] != -1)
+ {
+ Item* item = PlayerInfo::getInventory()->getItem(i);
+ if (item)
+ {
+ item->setEquipped(false);
+ }
+ }
+
+ mEquipment[i] = -1;
+ }
+ }
+
+ void setEquipment(int index, int inventoryIndex)
+ {
+ // Unequip existing item
+ Item* item = PlayerInfo::getInventory()
+ ->getItem(mEquipment[index]);
+
+ if (item)
+ item->setEquipped(false);
+
+ mEquipment[index] = inventoryIndex;
+
+ item = PlayerInfo::getInventory()->getItem(inventoryIndex);
+ if (item)
+ item->setEquipped(true);
+ }
+
+ private:
+ int mEquipment[EQUIPMENT_SIZE];
+};
+
+/**
+ * Used to cache storage data until we get size data for it.
+ */
+class InventoryItem
+{
+ public:
+ int slot;
+ int id;
+ int quantity;
+ int refine;
+ bool equip;
+
+ InventoryItem(int slot, int id, int quantity, int refine, bool equip)
+ {
+ this->slot = slot;
+ this->id = id;
+ this->quantity = quantity;
+ this->refine = refine;
+ this->equip = equip;
+ }
+};
+
+typedef std::list<InventoryItem> InventoryItems;
+
+class InventoryHandler : public MessageHandler, public Net::InventoryHandler
+{
+ public:
+ enum
+ {
+ GUILD_STORAGE = Inventory::TYPE_END,
+ CART
+ };
+
+ InventoryHandler();
+
+ ~InventoryHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void equipItem(const Item *item);
+
+ void unequipItem(const Item *item);
+
+ void useItem(const Item *item);
+
+ void dropItem(const Item *item, int amount);
+
+ 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 serverSlot);
+
+ private:
+ EquipBackend mEquips;
+ InventoryItems mInventoryItems;
+ Inventory *mStorage;
+ InventoryWindow *mStorageWindow;
+};
+
+} // namespace TmwAthena
+
+int getSlot(int eAthenaSlot);
+
+#endif // NET_TA_INVENTORYHANDLER_H
diff --git a/src/net/tmwa/itemhandler.cpp b/src/net/tmwa/itemhandler.cpp
new file mode 100644
index 000000000..7ae124646
--- /dev/null
+++ b/src/net/tmwa/itemhandler.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/tmwa/itemhandler.h"
+
+#include "actorspritemanager.h"
+
+#include "net/messagein.h"
+
+#include "net/tmwa/protocol.h"
+
+namespace TmwAthena
+{
+
+ItemHandler::ItemHandler()
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_ITEM_VISIBLE,
+ SMSG_ITEM_DROPPED,
+ SMSG_ITEM_REMOVE,
+ 0
+ };
+ handledMessages = _messages;
+}
+
+void ItemHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_ITEM_VISIBLE:
+ case SMSG_ITEM_DROPPED:
+ {
+ int id = msg.readInt32();
+ int itemId = msg.readInt16();
+ msg.readInt8(); // identify flag
+ int x = msg.readInt16();
+ int y = msg.readInt16();
+// msg.skip(4); // amount,subX,subY / subX,subY,amount
+ int amount1 = msg.readInt16();
+ int amount2 = msg.readInt16();
+
+ if (actorSpriteManager)
+ {
+ if (msg.getId() == SMSG_ITEM_VISIBLE)
+ {
+ actorSpriteManager->createItem(id, itemId,
+ x, y, amount1);
+ }
+ else
+ {
+ actorSpriteManager->createItem(id, itemId,
+ x, y, amount2);
+ }
+ }
+ }
+ break;
+
+ case SMSG_ITEM_REMOVE:
+ if (actorSpriteManager)
+ {
+ if (FloorItem *item = actorSpriteManager->findItem(
+ msg.readInt32()))
+ {
+ actorSpriteManager->destroy(item);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/itemhandler.h b/src/net/tmwa/itemhandler.h
new file mode 100644
index 000000000..0f740fe58
--- /dev/null
+++ b/src/net/tmwa/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_TA_ITEMHANDLER_H
+#define NET_TA_ITEMHANDLER_H
+
+#include "net/tmwa/messagehandler.h"
+
+namespace TmwAthena
+{
+
+class ItemHandler : public MessageHandler
+{
+ public:
+ ItemHandler();
+
+ virtual void handleMessage(Net::MessageIn &msg);
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_ITEMHANDLER_H
diff --git a/src/net/tmwa/loginhandler.cpp b/src/net/tmwa/loginhandler.cpp
new file mode 100644
index 000000000..73fec1322
--- /dev/null
+++ b/src/net/tmwa/loginhandler.cpp
@@ -0,0 +1,342 @@
+/*
+ * 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/tmwa/loginhandler.h"
+
+#include "client.h"
+#include "log.h"
+#include "configuration.h"
+
+#include "net/logindata.h"
+#include "net/messagein.h"
+#include "net/messageout.h"
+
+#include "net/tmwa/network.h"
+#include "net/tmwa/protocol.h"
+
+#include "utils/dtor.h"
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+extern Net::LoginHandler *loginHandler;
+
+namespace TmwAthena
+{
+
+extern ServerInfo charServer;
+
+LoginHandler::LoginHandler():
+ mVersionResponse(false),
+ mRegistrationEnabled(true)
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_UPDATE_HOST,
+ SMSG_LOGIN_DATA,
+ SMSG_LOGIN_ERROR,
+ SMSG_CHAR_PASSWORD_RESPONSE,
+ SMSG_SERVER_VERSION_RESPONSE,
+ 0
+ };
+ handledMessages = _messages;
+ loginHandler = this;
+}
+
+LoginHandler::~LoginHandler()
+{
+ delete_all(mWorlds);
+}
+
+void LoginHandler::handleMessage(Net::MessageIn &msg)
+{
+ int code, worldCount;
+
+ switch (msg.getId())
+ {
+ case SMSG_CHAR_PASSWORD_RESPONSE:
+ {
+ // 0: acc not found, 1: success, 2: password mismatch, 3: pass too short
+ int errMsg = msg.readInt8();
+ // Successful pass change
+ if (errMsg == 1)
+ {
+ Client::setState(STATE_CHANGEPASSWORD_SUCCESS);
+ }
+ // pass change failed
+ else
+ {
+ switch (errMsg)
+ {
+ case 0:
+ errorMessage =
+ _("Account was not found. Please re-login.");
+ break;
+ case 2:
+ errorMessage = _("Old password incorrect.");
+ break;
+ case 3:
+ errorMessage = _("New password too short.");
+ break;
+ default:
+ errorMessage = _("Unknown error.");
+ break;
+ }
+ Client::setState(STATE_ACCOUNTCHANGE_ERROR);
+ }
+ }
+ break;
+
+ case SMSG_UPDATE_HOST:
+ int len;
+
+ len = msg.readInt16() - 4;
+ mUpdateHost = msg.readString(len);
+ loginData.updateHost = mUpdateHost;
+
+ logger->log("Received update host \"%s\" from login server.",
+ mUpdateHost.c_str());
+ break;
+
+ case SMSG_LOGIN_DATA:
+ // Skip the length word
+ msg.skip(2);
+
+ clearWorlds();
+
+ worldCount = (msg.getLength() - 47) / 32;
+
+ mToken.session_ID1 = msg.readInt32();
+ mToken.account_ID = msg.readInt32();
+ mToken.session_ID2 = msg.readInt32();
+ msg.skip(30); // unknown
+ mToken.sex = msg.readInt8() ? GENDER_MALE : GENDER_FEMALE;
+
+ for (int i = 0; i < worldCount; i++)
+ {
+ WorldInfo *world = new WorldInfo;
+
+ world->address = msg.readInt32();
+ world->port = msg.readInt16();
+ world->name = msg.readString(20);
+ world->online_users = msg.readInt32();
+ config.setValue("updatehost", mUpdateHost);
+ world->updateHost = mUpdateHost;
+ msg.skip(2); // unknown
+
+ logger->log("Network: Server: %s (%s:%d)",
+ world->name.c_str(),
+ ipToString(world->address),
+ world->port);
+
+ mWorlds.push_back(world);
+ }
+ Client::setState(STATE_WORLD_SELECT);
+ break;
+
+ case SMSG_LOGIN_ERROR:
+ code = msg.readInt8();
+ logger->log("Login::error code: %i", code);
+
+ switch (code)
+ {
+ case 0:
+ errorMessage = _("Unregistered ID.");
+ break;
+ case 1:
+ errorMessage = _("Wrong password.");
+ break;
+ case 2:
+ errorMessage = _("Account expired.");
+ break;
+ case 3:
+ errorMessage = _("Rejected from server.");
+ break;
+ case 4:
+ errorMessage = _("You have been permanently banned from "
+ "the game. Please contact the GM team.");
+ break;
+ case 6:
+ errorMessage = strprintf(_("You have been temporarily "
+ "banned from the game until "
+ "%s.\nPlease contact the GM "
+ "team via the forums."),
+ msg.readString(20).c_str());
+ break;
+ case 9:
+ errorMessage = _("This user name is already taken.");
+ break;
+ default:
+ errorMessage = _("Unknown error.");
+ break;
+ }
+ Client::setState(STATE_ERROR);
+ break;
+
+ case SMSG_SERVER_VERSION_RESPONSE:
+ {
+ // TODO: verify these!
+
+ msg.readInt8(); // -1
+ msg.readInt8(); // T
+ msg.readInt8(); // M
+ msg.readInt8(); // W
+
+ unsigned int options = msg.readInt32();
+
+ mRegistrationEnabled = options;
+// mRegistrationEnabled = (options & 1);
+
+ // Leave this last
+ mVersionResponse = true;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void LoginHandler::connect()
+{
+ if (!mNetwork)
+ return;
+
+ mNetwork->connect(mServer);
+ MessageOut outMsg(CMSG_SERVER_VERSION_REQUEST);
+}
+
+bool LoginHandler::isConnected()
+{
+ if (!mNetwork)
+ return false;
+
+ return mVersionResponse && mNetwork->isConnected();
+}
+
+void LoginHandler::disconnect()
+{
+ if (mNetwork && mNetwork->getServer() == mServer)
+ mNetwork->disconnect();
+}
+
+bool LoginHandler::isRegistrationEnabled()
+{
+ return mRegistrationEnabled;
+}
+
+void LoginHandler::getRegistrationDetails()
+{
+ // Not supported, so move on
+ Client::setState(STATE_REGISTER);
+}
+
+void LoginHandler::loginAccount(LoginData *loginData)
+{
+
+ if (loginData)
+ {
+ // Since we're attempting to use the tAthena protocol,
+ // let's reset the character slots to the good value,
+ // in case we just logged out a Manaserv server
+ // with a different config.
+ loginData->resetCharacterSlots();
+
+ sendLoginRegister(loginData->username, loginData->password);
+ }
+}
+
+void LoginHandler::logout()
+{
+ // TODO
+}
+
+void LoginHandler::changeEmail(const std::string &email _UNUSED_)
+{
+ // TODO
+}
+
+void LoginHandler::changePassword(const std::string &username _UNUSED_,
+ const std::string &oldPassword,
+ const std::string &newPassword)
+{
+ MessageOut outMsg(CMSG_CHAR_PASSWORD_CHANGE);
+ outMsg.writeString(oldPassword, 24);
+ outMsg.writeString(newPassword, 24);
+}
+
+void LoginHandler::chooseServer(unsigned int server)
+{
+ if (server >= mWorlds.size() || !mWorlds[server])
+ return;
+
+ charServer.clear();
+ charServer.hostname = ipToString(mWorlds[server]->address);
+ charServer.port = mWorlds[server]->port;
+
+ Client::setState(STATE_UPDATE);
+}
+
+void LoginHandler::registerAccount(LoginData *loginData)
+{
+ if (!loginData)
+ return;
+
+ std::string username = loginData->username;
+ username.append((loginData->gender == GENDER_FEMALE) ? "_F" : "_M");
+
+ sendLoginRegister(username, loginData->password);
+}
+
+void LoginHandler::unregisterAccount(const std::string &username _UNUSED_,
+ const std::string &password _UNUSED_)
+{
+ // TODO
+}
+
+void LoginHandler::sendLoginRegister(const std::string &username,
+ const std::string &password)
+{
+ MessageOut outMsg(0x0064);
+ outMsg.writeInt32(0); // client version
+ outMsg.writeString(username, 24);
+ outMsg.writeString(password, 24);
+
+ /*
+ * eAthena calls the last byte "client version 2", but it isn't used at
+ * at all. We're retasking it, as a bit mask:
+ * 0 - can handle the 0x63 "update host" packet
+ * 1 - defaults to the first char-server (instead of the last)
+ */
+ outMsg.writeInt8(0x03);
+}
+
+Worlds LoginHandler::getWorlds() const
+{
+ return mWorlds;
+}
+
+void LoginHandler::clearWorlds()
+{
+ delete_all(mWorlds);
+ mWorlds.clear();
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/loginhandler.h b/src/net/tmwa/loginhandler.h
new file mode 100644
index 000000000..977b4ce0a
--- /dev/null
+++ b/src/net/tmwa/loginhandler.h
@@ -0,0 +1,105 @@
+/*
+ * 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_TMWA_LOGINHANDLER_H
+#define NET_TMWA_LOGINHANDLER_H
+
+#include "net/loginhandler.h"
+
+#include "net/tmwa/messagehandler.h"
+#include "net/tmwa/token.h"
+
+#include <string>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+struct LoginData;
+
+namespace TmwAthena
+{
+
+class LoginHandler : public MessageHandler, public Net::LoginHandler
+{
+ public:
+ LoginHandler();
+
+ ~LoginHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ void connect();
+
+ bool isConnected();
+
+ void disconnect();
+
+ int supportedOptionalActions() const
+ { return SetGenderOnRegister; }
+
+ bool isRegistrationEnabled();
+
+ void getRegistrationDetails();
+
+ unsigned int getMaxPasswordLength() const
+ { return 25; }
+
+ 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 clearWorlds();
+
+ const Token &getToken() const
+ { return mToken; }
+
+ private:
+ void sendLoginRegister(const std::string &username,
+ const std::string &password);
+
+ bool mVersionResponse;
+ bool mRegistrationEnabled;
+ std::string mUpdateHost;
+ Worlds mWorlds;
+ Token mToken;
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_LOGINHANDLER_H
diff --git a/src/net/tmwa/messagehandler.cpp b/src/net/tmwa/messagehandler.cpp
new file mode 100644
index 000000000..85a0caeae
--- /dev/null
+++ b/src/net/tmwa/messagehandler.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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/tmwa/messagehandler.h"
+
+#include "net/tmwa/network.h"
+
+#include <cassert>
+
+namespace TmwAthena
+{
+
+MessageHandler::MessageHandler()
+ : mNetwork(NULL)
+{
+}
+
+MessageHandler::~MessageHandler()
+{
+ if (mNetwork)
+ mNetwork->unregisterHandler(this);
+}
+
+void MessageHandler::setNetwork(Network *network)
+{
+ assert(!(network && mNetwork));
+ mNetwork = network;
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/messagehandler.h b/src/net/tmwa/messagehandler.h
new file mode 100644
index 000000000..d4b0c8094
--- /dev/null
+++ b/src/net/tmwa/messagehandler.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_TA_MESSAGEHANDLER_H
+#define NET_TA_MESSAGEHANDLER_H
+
+#include "net/messagehandler.h"
+#include "net/messagein.h"
+
+#include "net/tmwa/messageout.h"
+
+#include <SDL_types.h>
+
+#include <memory>
+
+namespace TmwAthena
+{
+
+class Network;
+
+/**
+ * \ingroup Network
+ */
+class MessageHandler : public Net::MessageHandler
+{
+ public:
+ MessageHandler();
+
+ ~MessageHandler();
+
+ void setNetwork(Network *network);
+
+ protected:
+ Network *mNetwork;
+};
+
+typedef const std::auto_ptr<MessageHandler> MessageHandlerPtr;
+
+}
+
+#endif // NET_TA_MESSAGEHANDLER_H
diff --git a/src/net/tmwa/messagein.cpp b/src/net/tmwa/messagein.cpp
new file mode 100644
index 000000000..23f5e09c1
--- /dev/null
+++ b/src/net/tmwa/messagein.cpp
@@ -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/>.
+ */
+
+#include "net/tmwa/messagein.h"
+
+#include "net/packetcounters.h"
+
+#include "log.h"
+
+#include "utils/stringutils.h"
+
+#include <SDL.h>
+#include <SDL_endian.h>
+
+#define MAKEWORD(low, high) \
+ ((unsigned short)(((unsigned char)(low)) | \
+ ((unsigned short)((unsigned char)(high))) << 8))
+
+namespace TmwAthena
+{
+
+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)
+ {
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ Sint16 swap;
+ memcpy(&swap, mData + mPos, sizeof(Sint16));
+ value = SDL_Swap16(swap);
+#else
+ memcpy(&value, mData + mPos, sizeof(Sint16));
+#endif
+ }
+ mPos += 2;
+ PacketCounters::incInBytes(2);
+ DEBUGLOG("readInt16: " + toString((int)value));
+ return value;
+}
+
+int MessageIn::readInt32()
+{
+ Sint32 value = -1;
+ if (mPos + 4 <= mLength)
+ {
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ Sint32 swap;
+ memcpy(&swap, mData + mPos, sizeof(Sint32));
+ value = SDL_Swap32(swap);
+#else
+ memcpy(&value, mData + mPos, sizeof(Sint32));
+#endif
+ }
+ mPos += 4;
+ PacketCounters::incInBytes(4);
+ DEBUGLOG(strprintf("readInt32: %u", value));
+ return value;
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/messagein.h b/src/net/tmwa/messagein.h
new file mode 100644
index 000000000..b3dedc7af
--- /dev/null
+++ b/src/net/tmwa/messagein.h
@@ -0,0 +1,52 @@
+/*
+ * 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_TA_MESSAGEIN_H
+#define NET_TA_MESSAGEIN_H
+
+#include "net/messagein.h"
+
+#include <SDL_types.h>
+#include <string>
+
+namespace TmwAthena
+{
+
+/**
+ * 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_TA_MESSAGEIN_H
diff --git a/src/net/tmwa/messageout.cpp b/src/net/tmwa/messageout.cpp
new file mode 100644
index 000000000..42d4f582c
--- /dev/null
+++ b/src/net/tmwa/messageout.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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/tmwa/messageout.h"
+
+#include "net/packetcounters.h"
+
+#include "net/tmwa/network.h"
+
+#include "log.h"
+
+#include "utils/stringutils.h"
+
+#include <SDL.h>
+#include <SDL_endian.h>
+
+#include <cstring>
+#include <string>
+
+namespace TmwAthena
+{
+
+MessageOut::MessageOut(short id):
+ Net::MessageOut(id)
+{
+ mNetwork = TmwAthena::Network::instance();
+ mData = mNetwork->mOutBuffer + mNetwork->mOutSize;
+ writeInt16(id);
+}
+
+void MessageOut::expand(size_t bytes)
+{
+ mNetwork->mOutSize += static_cast<unsigned>(bytes);
+ PacketCounters::incOutBytes(static_cast<int>(bytes));
+}
+
+void MessageOut::writeInt16(Sint16 value)
+{
+ DEBUGLOG("writeInt16: " + toString((int)value));
+ expand(2);
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ Sint16 swap = SDL_Swap16(value);
+ memcpy(mData + mPos, &swap, sizeof(Sint16));
+#else
+ memcpy(mData + mPos, &value, sizeof(Sint16));
+#endif
+ mPos += 2;
+ PacketCounters::incOutBytes(2);
+}
+
+void MessageOut::writeInt32(Sint32 value)
+{
+ DEBUGLOG("writeInt32: " + toString(value));
+ expand(4);
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ Sint32 swap = SDL_Swap32(value);
+ memcpy(mData + mPos, &swap, sizeof(Sint32));
+#else
+ memcpy(mData + mPos, &value, sizeof(Sint32));
+#endif
+ mPos += 4;
+ PacketCounters::incOutBytes(4);
+}
+
+#define LOBYTE(w) ((unsigned char)(w))
+#define HIBYTE(w) ((unsigned char)(((unsigned short)(w)) >> 8))
+
+void MessageOut::writeCoordinates(unsigned short x, unsigned short y,
+ unsigned char direction)
+{
+ DEBUGLOG(strprintf("writeCoordinates: %u,%u %u", x, y, direction));
+ char *data = mData + mPos;
+ mNetwork->mOutSize += 3;
+ mPos += 3;
+
+ short temp;
+ temp = x;
+ temp <<= 6;
+ data[0] = 0;
+ data[1] = 1;
+ data[2] = 2;
+ data[0] = HIBYTE(temp);
+ data[1] = (unsigned char) temp;
+ temp = y;
+ temp <<= 4;
+ data[1] |= HIBYTE(temp);
+ data[2] = LOBYTE(temp);
+
+ // Translate direction to eAthena format
+ switch (direction)
+ {
+ case 1:
+ direction = 0;
+ break;
+ case 3:
+ direction = 1;
+ break;
+ case 2:
+ direction = 2;
+ break;
+ case 6:
+ direction = 3;
+ break;
+ case 4:
+ direction = 4;
+ break;
+ case 12:
+ direction = 5;
+ break;
+ case 8:
+ direction = 6;
+ break;
+ case 9:
+ direction = 7;
+ break;
+ default:
+ // OOPSIE! Impossible or unknown
+ direction = (unsigned char) -1;
+ }
+ data[2] |= direction;
+ PacketCounters::incOutBytes(3);
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/messageout.h b/src/net/tmwa/messageout.h
new file mode 100644
index 000000000..ae0fc9a49
--- /dev/null
+++ b/src/net/tmwa/messageout.h
@@ -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/>.
+ */
+
+#ifndef NET_TA_MESSAGEOUT_H
+#define NET_TA_MESSAGEOUT_H
+
+#include "net/messageout.h"
+
+#include <iosfwd>
+#include <SDL_types.h>
+
+namespace TmwAthena
+{
+
+class Network;
+
+/**
+ * Used for building an outgoing message.
+ *
+ * \ingroup Network
+ */
+class MessageOut : public Net::MessageOut
+{
+ public:
+ /**
+ * Constructor.
+ */
+ MessageOut(short id);
+
+ void writeInt16(Sint16 value); /**< Writes a short. */
+ void writeInt32(Sint32 value); /**< Writes a long. */
+
+ /**
+ * Encodes coordinates and direction in 3 bytes.
+ */
+ void writeCoordinates(unsigned short x, unsigned short y,
+ unsigned char direction);
+
+ private:
+ void expand(size_t size);
+
+ Network *mNetwork;
+};
+
+}
+
+#endif // NET_TA_MESSAGEOUT_H
diff --git a/src/net/tmwa/network.cpp b/src/net/tmwa/network.cpp
new file mode 100644
index 000000000..7aa9ad7d2
--- /dev/null
+++ b/src/net/tmwa/network.cpp
@@ -0,0 +1,483 @@
+/*
+ * 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/tmwa/network.h"
+
+#include "log.h"
+
+#include "net/messagehandler.h"
+#include "net/messagein.h"
+
+#include "net/tmwa/protocol.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include <assert.h>
+#include <sstream>
+
+/** Warning: buffers and other variables are shared,
+ so there can be only one connection active at a time */
+
+short packet_lengths[] =
+{
+ 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// #0x0040
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 50, 3, -1, 55, 17, 3, 37, 46, -1, 23, -1, 3, 108, 3, 2,
+ 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6,
+// #0x0080
+ 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 23, -1, -1, -1, 0,
+ 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6,
+ 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6,
+ 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3,
+// #0x00C0
+ 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 6, 2, 27,
+ 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1,
+ 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2,
+ 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10,
+// #0x0100
+ 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1,
+ 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16,
+ 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1,
+ 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26,
+// #0x0140
+ 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6,
+ 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42,
+ -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14, 186, 182,
+ 14, 30, 10, 3, -1, 6, 106, -1, 4, 5, 4, -1, 6, 7, -1, -1,
+// #0x0180
+ 6, 3, 106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6,
+ 90, 86, 24, 6, 30, 102, 9, 4, 8, 4, 14, 10, 4, 6, 2, 6,
+ 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4,
+ 11, 7, -1, 67, 12, 18, 114, 6, 3, 6, 26, 26, 26, 26, 2, 3,
+// #0x01C0
+ 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 0, 9, 9, 29, 6, 28,
+ 8, 14, 10, 35, 6, 8, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6,
+ 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1,
+ -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10,
+// #0x2000
+ 26, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 19, 10, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+const unsigned int BUFFER_SIZE = 655360;
+
+namespace TmwAthena
+{
+
+int networkThread(void *data)
+{
+ Network *network = static_cast<Network*>(data);
+
+ if (!network || !network->realConnect())
+ return -1;
+
+ network->receive();
+
+ return 0;
+}
+
+Network *Network::mInstance = 0;
+
+Network::Network():
+ mSocket(0),
+ mInBuffer(new char[BUFFER_SIZE]),
+ mOutBuffer(new char[BUFFER_SIZE]),
+ mInSize(0), mOutSize(0),
+ mToSkip(0),
+ mState(IDLE),
+ mWorkerThread(0)
+{
+ SDLNet_Init();
+
+ mMutex = SDL_CreateMutex();
+ mInstance = this;
+}
+
+Network::~Network()
+{
+ clearHandlers();
+
+ if (mState != IDLE && mState != NET_ERROR)
+ disconnect();
+
+ SDL_DestroyMutex(mMutex);
+ mInstance = 0;
+
+ delete[] mInBuffer;
+ delete[] mOutBuffer;
+
+ SDLNet_Quit();
+}
+
+bool Network::connect(ServerInfo server)
+{
+ if (mState != IDLE && mState != NET_ERROR)
+ {
+ logger->log1("Tried to connect an already connected socket!");
+ assert(false);
+ return false;
+ }
+
+ if (server.hostname.empty())
+ {
+ setError(_("Empty address given to Network::connect()!"));
+ return false;
+ }
+
+ logger->log("Network::Connecting to %s:%i", server.hostname.c_str(),
+ server.port);
+
+ mServer.hostname = server.hostname;
+ mServer.port = server.port;
+
+ // Reset to sane values
+ mOutSize = 0;
+ mInSize = 0;
+ mToSkip = 0;
+
+ mState = CONNECTING;
+ mWorkerThread = SDL_CreateThread(networkThread, this);
+ if (!mWorkerThread)
+ {
+ setError("Unable to create network worker thread");
+ return false;
+ }
+
+ return true;
+}
+
+void Network::disconnect()
+{
+ mState = IDLE;
+
+ if (mWorkerThread && SDL_GetThreadID(mWorkerThread))
+ {
+ SDL_WaitThread(mWorkerThread, NULL);
+ mWorkerThread = NULL;
+ }
+
+ if (mSocket)
+ {
+ // need call SDLNet_TCP_DelSocket?
+ SDLNet_TCP_Close(mSocket);
+ mSocket = 0;
+ }
+}
+
+void Network::registerHandler(MessageHandler *handler)
+{
+ if (!handler)
+ return;
+
+ for (const Uint16 *i = handler->handledMessages; *i; ++i)
+ mMessageHandlers[*i] = handler;
+
+ handler->setNetwork(this);
+}
+
+void Network::unregisterHandler(MessageHandler *handler)
+{
+ if (!handler)
+ return;
+
+ for (const Uint16 *i = handler->handledMessages; *i; ++i)
+ mMessageHandlers.erase(*i);
+
+ handler->setNetwork(0);
+}
+
+void Network::clearHandlers()
+{
+ MessageHandlerIterator i;
+ for (i = mMessageHandlers.begin(); i != mMessageHandlers.end(); ++i)
+ {
+ if (i->second)
+ i->second->setNetwork(0);
+ }
+ mMessageHandlers.clear();
+}
+
+void Network::dispatchMessages()
+{
+ while (messageReady())
+ {
+ MessageIn msg = getNextMessage();
+
+ MessageHandlerIterator iter = mMessageHandlers.find(msg.getId());
+
+ if (msg.getLength() == 0)
+ logger->error("Zero length packet received. Exiting.");
+
+ if (iter != mMessageHandlers.end())
+ {
+ if (iter->second)
+ iter->second->handleMessage(msg);
+ }
+ else
+ {
+ logger->log("Unhandled packet: %x", msg.getId());
+ }
+
+ skip(msg.getLength());
+ }
+}
+
+void Network::flush()
+{
+ if (!mOutSize || mState != CONNECTED)
+ return;
+
+ int ret;
+
+ SDL_mutexP(mMutex);
+ ret = SDLNet_TCP_Send(mSocket, mOutBuffer, mOutSize);
+ if (ret < (int)mOutSize)
+ {
+ setError("Error in SDLNet_TCP_Send(): " +
+ std::string(SDLNet_GetError()));
+ }
+ mOutSize = 0;
+ SDL_mutexV(mMutex);
+}
+
+void Network::skip(int len)
+{
+ SDL_mutexP(mMutex);
+ mToSkip += len;
+ if (!mInSize)
+ {
+ SDL_mutexV(mMutex);
+ return;
+ }
+
+ if (mInSize >= mToSkip)
+ {
+ mInSize -= mToSkip;
+ memmove(mInBuffer, mInBuffer + mToSkip, mInSize);
+ mToSkip = 0;
+ }
+ else
+ {
+ mToSkip -= mInSize;
+ mInSize = 0;
+ }
+ SDL_mutexV(mMutex);
+}
+
+bool Network::messageReady()
+{
+ int len = -1, msgId;
+
+ SDL_mutexP(mMutex);
+ if (mInSize >= 2)
+ {
+ msgId = readWord(0);
+ if (msgId == SMSG_SERVER_VERSION_RESPONSE)
+ len = 10;
+ else
+ len = packet_lengths[msgId];
+
+ if (len == -1 && mInSize > 4)
+ len = readWord(2);
+
+ }
+
+ bool ret = (mInSize >= static_cast<unsigned int>(len));
+ SDL_mutexV(mMutex);
+
+ return ret;
+}
+
+MessageIn Network::getNextMessage()
+{
+ while (!messageReady())
+ {
+ if (mState == NET_ERROR)
+ break;
+ }
+
+ SDL_mutexP(mMutex);
+ int msgId = readWord(0);
+ int len;
+ if (msgId == SMSG_SERVER_VERSION_RESPONSE)
+ len = 10;
+ else
+ len = packet_lengths[msgId];
+
+ if (len == -1)
+ len = readWord(2);
+
+#ifdef DEBUG
+ logger->log("Received packet 0x%x of length %d\n", msgId, len);
+#endif
+
+ MessageIn msg(mInBuffer, len);
+ SDL_mutexV(mMutex);
+
+ return msg;
+}
+
+bool Network::realConnect()
+{
+ IPaddress ipAddress;
+
+ if (SDLNet_ResolveHost(&ipAddress, mServer.hostname.c_str(),
+ mServer.port) == -1)
+ {
+ std::string errorMessage = _("Unable to resolve host \"") +
+ mServer.hostname + "\"";
+ setError(errorMessage);
+ logger->log("SDLNet_ResolveHost: %s", errorMessage.c_str());
+ return false;
+ }
+
+ mState = CONNECTING;
+
+ mSocket = SDLNet_TCP_Open(&ipAddress);
+ if (!mSocket)
+ {
+ logger->log("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError());
+ setError(SDLNet_GetError());
+ return false;
+ }
+
+ logger->log("Network::Started session with %s:%i",
+ ipToString(ipAddress.host), ipAddress.port);
+
+ mState = CONNECTED;
+
+ return true;
+}
+
+void Network::receive()
+{
+ SDLNet_SocketSet set;
+
+ if (!(set = SDLNet_AllocSocketSet(1)))
+ {
+ setError("Error in SDLNet_AllocSocketSet(): " +
+ std::string(SDLNet_GetError()));
+ return;
+ }
+
+ if (SDLNet_TCP_AddSocket(set, mSocket) == -1)
+ {
+ setError("Error in SDLNet_AddSocket(): " +
+ std::string(SDLNet_GetError()));
+ }
+
+ while (mState == CONNECTED)
+ {
+ // TODO Try to get this to block all the time while still being able
+ // to escape the loop
+ int numReady = SDLNet_CheckSockets(set, ((Uint32)500));
+ int ret;
+ switch (numReady)
+ {
+ case -1:
+ logger->log1("Error: SDLNet_CheckSockets");
+ // FALLTHROUGH
+ case 0:
+ break;
+
+ case 1:
+ // Receive data from the socket
+ SDL_mutexP(mMutex);
+ ret = SDLNet_TCP_Recv(mSocket, mInBuffer + mInSize,
+ BUFFER_SIZE - mInSize);
+
+ if (!ret)
+ {
+ // We got disconnected
+ mState = IDLE;
+ logger->log1("Disconnected.");
+ }
+ else if (ret < 0)
+ {
+ setError(_("Connection to server terminated. ") +
+ std::string(SDLNet_GetError()));
+ }
+ else
+ {
+ mInSize += ret;
+ if (mToSkip)
+ {
+ if (mInSize >= mToSkip)
+ {
+ mInSize -= mToSkip;
+ memmove(mInBuffer, mInBuffer + mToSkip, mInSize);
+ mToSkip = 0;
+ }
+ else
+ {
+ mToSkip -= mInSize;
+ mInSize = 0;
+ }
+ }
+ }
+ SDL_mutexV(mMutex);
+ break;
+
+ default:
+ // more than one socket is ready..
+ // this should not happen since we only listen once socket.
+ std::stringstream errorStream;
+ errorStream << "Error in SDLNet_TCP_Recv(), " << numReady
+ << " sockets are ready: " << SDLNet_GetError();
+ setError(errorStream.str());
+ break;
+ }
+ }
+
+ if (SDLNet_TCP_DelSocket(set, mSocket) == -1)
+ logger->log("Error in SDLNet_DelSocket(): %s", SDLNet_GetError());
+
+ SDLNet_FreeSocketSet(set);
+}
+
+Network *Network::instance()
+{
+ return mInstance;
+}
+
+void Network::setError(const std::string &error)
+{
+ logger->log("Network error: %s", error.c_str());
+ mError = error;
+ mState = NET_ERROR;
+}
+
+Uint16 Network::readWord(int pos)
+{
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ return SDL_Swap16((*(Uint16*)(mInBuffer + (pos))));
+#else
+ return (*(Uint16*)(mInBuffer + (pos)));
+#endif
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/network.h b/src/net/tmwa/network.h
new file mode 100644
index 000000000..bdafd6a09
--- /dev/null
+++ b/src/net/tmwa/network.h
@@ -0,0 +1,136 @@
+/*
+ * 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_TA_NETWORK_H
+#define NET_TA_NETWORK_H
+
+#include "net/serverinfo.h"
+
+#include "net/tmwa/messagehandler.h"
+#include "net/tmwa/messagein.h"
+#include "net/tmwa/messageout.h"
+
+#include <SDL_net.h>
+#include <SDL_thread.h>
+
+#include <map>
+#include <string>
+
+/**
+ * Protocol version, reported to the eAthena char and mapserver who can adjust
+ * the protocol accordingly.
+ */
+#define CLIENT_PROTOCOL_VERSION 1
+
+namespace TmwAthena
+{
+
+class Network
+{
+ public:
+ Network();
+
+ ~Network();
+
+ bool connect(ServerInfo server);
+
+ void disconnect();
+
+ ServerInfo getServer() const
+ { return mServer; }
+
+ void registerHandler(MessageHandler *handler);
+
+ void unregisterHandler(MessageHandler *handler);
+
+ void clearHandlers();
+
+ int getState() const
+ { return mState; }
+
+ const std::string &getError() const
+ { return mError; }
+
+ bool isConnected() const
+ { return mState == CONNECTED; }
+
+ int getInSize() const
+ { return mInSize; }
+
+ void skip(int len);
+
+ bool messageReady();
+
+ MessageIn getNextMessage();
+
+ void dispatchMessages();
+
+ void flush();
+
+ // ERROR replaced by NET_ERROR because already defined in Windows
+ enum
+ {
+ IDLE = 0,
+ CONNECTED,
+ CONNECTING,
+ DATA,
+ NET_ERROR
+ };
+
+ protected:
+ friend int networkThread(void *data);
+ friend class MessageOut;
+
+ static Network *instance();
+
+ void setError(const std::string &error);
+
+ Uint16 readWord(int pos);
+
+ bool realConnect();
+
+ void receive();
+
+ TCPsocket mSocket;
+
+ ServerInfo mServer;
+
+ char *mInBuffer, *mOutBuffer;
+ unsigned int mInSize, mOutSize;
+
+ unsigned int mToSkip;
+
+ int mState;
+ std::string mError;
+
+ SDL_Thread *mWorkerThread;
+ SDL_mutex *mMutex;
+
+ typedef std::map<Uint16, MessageHandler*> MessageHandlers;
+ typedef MessageHandlers::iterator MessageHandlerIterator;
+ MessageHandlers mMessageHandlers;
+
+ static Network *mInstance;
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_NETWORK_H
diff --git a/src/net/tmwa/npchandler.cpp b/src/net/tmwa/npchandler.cpp
new file mode 100644
index 000000000..84fe1789c
--- /dev/null
+++ b/src/net/tmwa/npchandler.cpp
@@ -0,0 +1,249 @@
+/*
+ * 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/tmwa/npchandler.h"
+
+#include "actorspritemanager.h"
+#include "localplayer.h"
+
+#include "gui/npcdialog.h"
+
+#include "net/messagein.h"
+#include "net/messageout.h"
+#include "net/net.h"
+#include "net/npchandler.h"
+
+#include "net/tmwa/protocol.h"
+
+#include <SDL_types.h>
+
+extern Net::NpcHandler *npcHandler;
+
+namespace TmwAthena
+{
+
+NpcHandler::NpcHandler()
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_NPC_CHOICE,
+ SMSG_NPC_MESSAGE,
+ SMSG_NPC_NEXT,
+ SMSG_NPC_CLOSE,
+ SMSG_NPC_INT_INPUT,
+ SMSG_NPC_STR_INPUT,
+ 0
+ };
+ handledMessages = _messages;
+ npcHandler = this;
+}
+
+void NpcHandler::handleMessage(Net::MessageIn &msg)
+{
+ if (msg.getId() == SMSG_NPC_CHOICE || msg.getId() == SMSG_NPC_MESSAGE)
+ msg.readInt16(); // length
+
+ int npcId = msg.readInt32();
+ NpcDialogs::iterator diag = mNpcDialogs.find(npcId);
+ NpcDialog *dialog = 0;
+
+ if (diag == mNpcDialogs.end())
+ {
+ // Empty dialogs don't help
+ if (msg.getId() == SMSG_NPC_CLOSE)
+ {
+ closeDialog(npcId);
+ return;
+ }
+ else if (msg.getId() == SMSG_NPC_NEXT)
+ {
+ nextDialog(npcId);
+ return;
+ }
+ else
+ {
+ dialog = new NpcDialog(npcId);
+ Wrapper wrap;
+ wrap.dialog = dialog;
+ mNpcDialogs[npcId] = wrap;
+ }
+ }
+ else
+ {
+ dialog = diag->second.dialog;
+ }
+
+ switch (msg.getId())
+ {
+ case SMSG_NPC_CHOICE:
+ if (dialog)
+ {
+ dialog->choiceRequest();
+ dialog->parseListItems(msg.readString(msg.getLength() - 8));
+ }
+ else
+ {
+ msg.readString(msg.getLength() - 8);
+ }
+ break;
+
+ case SMSG_NPC_MESSAGE:
+ if (dialog)
+ dialog->addText(msg.readString(msg.getLength() - 8));
+ else
+ msg.readString(msg.getLength() - 8);
+ break;
+
+ case SMSG_NPC_CLOSE:
+ // Show the close button
+ if (dialog)
+ dialog->showCloseButton();
+ break;
+
+ case SMSG_NPC_NEXT:
+ // Show the next button
+ if (dialog)
+ dialog->showNextButton();
+ break;
+
+ case SMSG_NPC_INT_INPUT:
+ // Request for an integer
+ if (dialog)
+ dialog->integerRequest(0);
+ break;
+
+ case SMSG_NPC_STR_INPUT:
+ // Request for a string
+ if (dialog)
+ dialog->textRequest("");
+ break;
+
+ default:
+ break;
+ }
+
+ if (player_node && player_node->getCurrentAction() != Being::SIT)
+ player_node->setAction(Being::STAND);
+}
+
+void NpcHandler::talk(int npcId)
+{
+ MessageOut outMsg(CMSG_NPC_TALK);
+ outMsg.writeInt32(npcId);
+ outMsg.writeInt8(0); // Unused
+}
+
+void NpcHandler::nextDialog(int npcId)
+{
+ MessageOut outMsg(CMSG_NPC_NEXT_REQUEST);
+ outMsg.writeInt32(npcId);
+}
+
+void NpcHandler::closeDialog(int npcId)
+{
+ MessageOut outMsg(CMSG_NPC_CLOSE);
+ outMsg.writeInt32(npcId);
+
+ NpcDialogs::iterator it = mNpcDialogs.find(npcId);
+ if (it != mNpcDialogs.end())
+ {
+ if ((*it).second.dialog)
+ (*it).second.dialog->close();
+ mNpcDialogs.erase(it);
+ }
+}
+
+void NpcHandler::listInput(int npcId, unsigned char value)
+{
+ MessageOut outMsg(CMSG_NPC_LIST_CHOICE);
+ outMsg.writeInt32(npcId);
+ outMsg.writeInt8(static_cast<unsigned char>(value));
+}
+
+void NpcHandler::integerInput(int npcId, int value)
+{
+ MessageOut outMsg(CMSG_NPC_INT_RESPONSE);
+ outMsg.writeInt32(npcId);
+ outMsg.writeInt32(value);
+}
+
+void NpcHandler::stringInput(int npcId, const std::string &value)
+{
+ MessageOut outMsg(CMSG_NPC_STR_RESPONSE);
+ outMsg.writeInt16(value.length() + 9);
+ outMsg.writeInt32(npcId);
+ outMsg.writeString(value, value.length());
+ outMsg.writeInt8(0); // Prevent problems with string reading
+}
+
+void NpcHandler::sendLetter(int npcId _UNUSED_,
+ const std::string &recipient _UNUSED_,
+ const std::string &text _UNUSED_)
+{
+ // TODO
+}
+
+void NpcHandler::startShopping(int beingId _UNUSED_)
+{
+ // TODO
+}
+
+void NpcHandler::buy(int beingId)
+{
+ MessageOut outMsg(CMSG_NPC_BUY_SELL_REQUEST);
+ outMsg.writeInt32(beingId);
+ outMsg.writeInt8(0); // Buy
+}
+
+void NpcHandler::sell(int beingId)
+{
+ MessageOut outMsg(CMSG_NPC_BUY_SELL_REQUEST);
+ outMsg.writeInt32(beingId);
+ outMsg.writeInt8(1); // Sell
+}
+
+void NpcHandler::buyItem(int beingId _UNUSED_, int itemId, int amount)
+{
+ MessageOut outMsg(CMSG_NPC_BUY_REQUEST);
+ outMsg.writeInt16(8); // One item (length of packet)
+ outMsg.writeInt16(amount);
+ outMsg.writeInt16(itemId);
+}
+
+void NpcHandler::sellItem(int beingId _UNUSED_, int itemId, int amount)
+{
+ MessageOut outMsg(CMSG_NPC_SELL_REQUEST);
+ outMsg.writeInt16(8); // One item (length of packet)
+ outMsg.writeInt16(itemId + INVENTORY_OFFSET);
+ outMsg.writeInt16(amount);
+}
+
+void NpcHandler::endShopping(int beingId _UNUSED_)
+{
+ // TODO
+}
+
+void NpcHandler::clearDialogs()
+{
+ mNpcDialogs.clear();
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/npchandler.h b/src/net/tmwa/npchandler.h
new file mode 100644
index 000000000..7931a3d8b
--- /dev/null
+++ b/src/net/tmwa/npchandler.h
@@ -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/>.
+ */
+
+#ifndef NET_TA_NPCHANDLER_H
+#define NET_TA_NPCHANDLER_H
+
+#include "net/net.h"
+#include "net/npchandler.h"
+
+#include "net/tmwa/messagehandler.h"
+
+#include <map>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class NpcDialog;
+
+namespace TmwAthena
+{
+
+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 TmwAthena
+
+#endif // NET_TA_NPCHANDLER_H
diff --git a/src/net/tmwa/partyhandler.cpp b/src/net/tmwa/partyhandler.cpp
new file mode 100644
index 000000000..eecbf0c0e
--- /dev/null
+++ b/src/net/tmwa/partyhandler.cpp
@@ -0,0 +1,547 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net>
+ *
+ * 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/tmwa/partyhandler.h"
+
+#include "actorspritemanager.h"
+#include "event.h"
+#include "localplayer.h"
+#include "log.h"
+
+#include "gui/socialwindow.h"
+
+#include "net/messagein.h"
+#include "net/messageout.h"
+
+#include "net/tmwa/protocol.h"
+
+#include "net/tmwa/gui/partytab.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#define PARTY_ID 1
+
+extern Net::PartyHandler *partyHandler;
+
+namespace TmwAthena
+{
+
+PartyTab *partyTab = 0;
+Party *taParty;
+
+PartyHandler::PartyHandler():
+ mShareExp(PARTY_SHARE_UNKNOWN), mShareItems(PARTY_SHARE_UNKNOWN)
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_PARTY_CREATE,
+ SMSG_PARTY_INFO,
+ SMSG_PARTY_INVITE_RESPONSE,
+ SMSG_PARTY_INVITED,
+ SMSG_PARTY_SETTINGS,
+ SMSG_PARTY_MOVE,
+ SMSG_PARTY_LEAVE,
+ SMSG_PARTY_UPDATE_HP,
+ SMSG_PARTY_UPDATE_COORDS,
+ SMSG_PARTY_MESSAGE,
+ 0
+ };
+ handledMessages = _messages;
+ partyHandler = this;
+ taParty = Party::getParty(1);
+}
+
+PartyHandler::~PartyHandler()
+{
+ delete partyTab;
+ partyTab = 0;
+}
+
+void PartyHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_PARTY_CREATE:
+ if (msg.readInt8())
+ SERVER_NOTICE(_("Could not create party."))
+ else
+ SERVER_NOTICE(_("Party successfully created."))
+ break;
+ case SMSG_PARTY_INFO:
+ {
+ if (!taParty)
+ {
+ logger->log1("error: party empty in SMSG_PARTY_INFO");
+ taParty = Party::getParty(1);
+ }
+ if (!player_node)
+ logger->log1("error: player_node==0 in SMSG_PARTY_INFO");
+
+ if (taParty)
+ taParty->clearMembers();
+
+ int length = msg.readInt16();
+ if (taParty)
+ taParty->setName(msg.readString(24));
+
+ int count = (length - 28) / 46;
+ if (player_node && taParty)
+ {
+ player_node->setParty(taParty);
+ player_node->setPartyName(taParty->getName());
+ }
+
+ for (int i = 0; i < count; i++)
+ {
+ int id = msg.readInt32();
+ std::string nick = msg.readString(24);
+ std::string map = msg.readString(16);
+ bool leader = msg.readInt8() == 0;
+ bool online = msg.readInt8() == 0;
+
+ if (taParty)
+ {
+ PartyMember *member = taParty->addMember(id, nick);
+ if (member)
+ {
+ member->setLeader(leader);
+ member->setOnline(online);
+ member->setMap(map);
+ }
+ }
+ }
+
+ if (taParty)
+ taParty->sort();
+
+ if (player_node && taParty)
+ {
+ player_node->setParty(taParty);
+ player_node->setPartyName(taParty->getName());
+ }
+ }
+ break;
+ case SMSG_PARTY_INVITE_RESPONSE:
+ {
+ if (!partyTab)
+ break;
+
+ std::string nick = msg.readString(24);
+
+ switch (msg.readInt8())
+ {
+ case 0:
+ partyTab->chatLog(strprintf(
+ _("%s is already a member of a party."),
+ nick.c_str()), BY_SERVER);
+ break;
+ case 1:
+ partyTab->chatLog(strprintf(
+ _("%s refused your invitation."),
+ nick.c_str()), BY_SERVER);
+ break;
+ case 2:
+ partyTab->chatLog(strprintf(
+ _("%s is now a member of your party."),
+ nick.c_str()), BY_SERVER);
+ break;
+ case 3:
+ partyTab->chatLog(strprintf(
+ _("%s cant joid your party because party is "
+ "full."), nick.c_str()), BY_SERVER);
+ break;
+ default:
+ partyTab->chatLog(strprintf(
+ _("QQQ Unknown invite response for %s."),
+ nick.c_str()), BY_SERVER);
+ break;
+ }
+ break;
+ }
+ case SMSG_PARTY_INVITED:
+ {
+ int id = msg.readInt32();
+ std::string partyName = msg.readString(24);
+ std::string nick = "";
+ Being *being;
+
+ if (actorSpriteManager)
+ {
+ if ((being = actorSpriteManager->findBeing(id)))
+ {
+ if (being && being->getType() == Being::PLAYER)
+ nick = being->getName();
+ }
+ }
+
+ if (socialWindow)
+ socialWindow->showPartyInvite(partyName, nick);
+ break;
+ }
+ case SMSG_PARTY_SETTINGS:
+ {
+ if (!partyTab)
+ {
+ if (!chatWindow)
+ break;
+
+ partyTab = new PartyTab();
+ }
+
+ // These seem to indicate the sharing mode for exp and items
+ short exp = msg.readInt16();
+ short item = msg.readInt16();
+
+ if (!partyTab)
+ break;
+
+ switch (exp)
+ {
+ case PARTY_SHARE:
+ if (mShareExp == PARTY_SHARE)
+ break;
+ mShareExp = PARTY_SHARE;
+ if (partyTab)
+ {
+ partyTab->chatLog(
+ _("Experience sharing enabled."), BY_SERVER);
+ }
+ break;
+ case PARTY_SHARE_NO:
+ if (mShareExp == PARTY_SHARE_NO)
+ break;
+ mShareExp = PARTY_SHARE_NO;
+ if (partyTab)
+ {
+ partyTab->chatLog(
+ _("Experience sharing disabled."), BY_SERVER);
+ }
+ break;
+ case PARTY_SHARE_NOT_POSSIBLE:
+ if (mShareExp == PARTY_SHARE_NOT_POSSIBLE)
+ break;
+ mShareExp = PARTY_SHARE_NOT_POSSIBLE;
+ if (partyTab)
+ {
+ partyTab->chatLog(
+ _("Experience sharing not possible."),
+ BY_SERVER);
+ }
+ break;
+ default:
+ logger->log("QQQ Unknown party exp option: %d\n", exp);
+ break;
+ }
+
+ switch (item)
+ {
+ case PARTY_SHARE:
+ if (mShareItems == PARTY_SHARE)
+ break;
+ mShareItems = PARTY_SHARE;
+ if (partyTab)
+ {
+ partyTab->chatLog(
+ _("Item sharing enabled."), BY_SERVER);
+ }
+ break;
+ case PARTY_SHARE_NO:
+ if (mShareItems == PARTY_SHARE_NO)
+ break;
+ mShareItems = PARTY_SHARE_NO;
+ if (partyTab)
+ {
+ partyTab->chatLog(
+ _("Item sharing disabled."), BY_SERVER);
+ }
+ break;
+ case PARTY_SHARE_NOT_POSSIBLE:
+ if (mShareItems == PARTY_SHARE_NOT_POSSIBLE)
+ break;
+ mShareItems = PARTY_SHARE_NOT_POSSIBLE;
+ if (partyTab)
+ {
+ partyTab->chatLog(
+ _("Item sharing not possible."), BY_SERVER);
+ }
+ break;
+ default:
+ logger->log("QQQ Unknown party item option: %d\n",
+ exp);
+ break;
+ }
+ break;
+ }
+ case SMSG_PARTY_MOVE:
+ {
+ int id = msg.readInt32(); // id
+ PartyMember *m = 0;
+ if (taParty)
+ m = taParty->getMember(id);
+ if (m)
+ {
+ msg.skip(4);
+ m->setX(msg.readInt16()); // x
+ m->setY(msg.readInt16()); // y
+ m->setOnline(msg.readInt8()); // online (if 0)
+ msg.readString(24); // party
+ msg.readString(24); // nick
+ m->setMap(msg.readString(16)); // map
+ }
+ else
+ {
+ msg.skip(4);
+ msg.readInt16(); // x
+ msg.readInt16(); // y
+ msg.readInt8(); // online (if 0)
+ msg.readString(24); // party
+ msg.readString(24); // nick
+ msg.readString(16); // map
+ }
+ }
+ break;
+ case SMSG_PARTY_LEAVE:
+ {
+ int id = msg.readInt32();
+ std::string nick = msg.readString(24);
+ msg.readInt8(); // fail
+ if (player_node && id == player_node->getId())
+ {
+ if (taParty)
+ {
+ taParty->removeFromMembers();
+ taParty->clearMembers();
+ }
+ SERVER_NOTICE(_("You have left the party."))
+ delete partyTab;
+ partyTab = 0;
+
+ if (socialWindow && taParty)
+ socialWindow->removeTab(taParty);
+ }
+ else
+ {
+ if (partyTab)
+ {
+ partyTab->chatLog(strprintf(
+ _("%s has left your party."),
+ nick.c_str()), BY_SERVER);
+ }
+ if (actorSpriteManager)
+ {
+ Being *b = actorSpriteManager->findBeing(id);
+ if (b && b->getType() == Being::PLAYER)
+ b->setParty(0);
+ }
+ if (taParty)
+ taParty->removeMember(id);
+ }
+ break;
+ }
+ case SMSG_PARTY_UPDATE_HP:
+ {
+ int id = msg.readInt32();
+ int hp = msg.readInt16();
+ int maxhp = msg.readInt16();
+ PartyMember *m = 0;
+ if (taParty)
+ m = taParty->getMember(id);
+ if (m)
+ {
+ m->setHp(hp);
+ m->setMaxHp(maxhp);
+ }
+
+ // The server only sends this when the member is in range, so
+ // lets make sure they get the party hilight.
+ if (actorSpriteManager && taParty)
+ {
+ if (Being *b = actorSpriteManager->findBeing(id))
+ b->setParty(taParty);
+ }
+ }
+ break;
+ case SMSG_PARTY_UPDATE_COORDS:
+ {
+ int id = msg.readInt32(); // id
+ PartyMember *m = 0;
+ if (taParty)
+ m = taParty->getMember(id);
+ if (m)
+ {
+ m->setX(msg.readInt16()); // x
+ m->setY(msg.readInt16()); // y
+ }
+ else
+ {
+ msg.readInt16(); // x
+ msg.readInt16(); // y
+ }
+ }
+ break;
+ case SMSG_PARTY_MESSAGE:
+ {
+ int msgLength = msg.readInt16() - 8;
+ if (msgLength <= 0)
+ return;
+
+ int id = msg.readInt32();
+ std::string chatMsg = msg.readString(msgLength);
+
+ if (taParty)
+ {
+ PartyMember *member = taParty->getMember(id);
+ if (partyTab)
+ {
+ if (member)
+ {
+ partyTab->chatLog(member->getName(), chatMsg);
+ }
+ else
+ {
+ partyTab->chatLog(strprintf(
+ _("An unknown member tried to say: %s"),
+ chatMsg.c_str()), BY_SERVER);
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void PartyHandler::create(const std::string &name)
+{
+ MessageOut outMsg(CMSG_PARTY_CREATE);
+ outMsg.writeString(name.substr(0, 23), 24);
+}
+
+void PartyHandler::join(int partyId _UNUSED_)
+{
+ // TODO?
+}
+
+void PartyHandler::invite(Being *being)
+{
+ if (being)
+ {
+ MessageOut outMsg(CMSG_PARTY_INVITE);
+ outMsg.writeInt32(being->getId());
+ }
+}
+
+void PartyHandler::invite(const std::string &name)
+{
+ if (!actorSpriteManager)
+ return;
+
+ Being* being = actorSpriteManager->findBeingByName(name, Being::PLAYER);
+ if (being)
+ {
+ MessageOut outMsg(CMSG_PARTY_INVITE);
+ outMsg.writeInt32(being->getId());
+ }
+}
+
+void PartyHandler::inviteResponse(const std::string &inviter _UNUSED_,
+ bool accept)
+{
+ if (player_node)
+ {
+ MessageOut outMsg(CMSG_PARTY_INVITED);
+ outMsg.writeInt32(player_node->getId());
+ outMsg.writeInt32(accept ? 1 : 0);
+ }
+}
+
+void PartyHandler::leave()
+{
+ MessageOut outMsg(CMSG_PARTY_LEAVE);
+}
+
+void PartyHandler::kick(Being *being)
+{
+ if (being)
+ {
+ MessageOut outMsg(CMSG_PARTY_KICK);
+ outMsg.writeInt32(being->getId());
+ outMsg.writeString("", 24); //Unused
+ }
+}
+
+void PartyHandler::kick(const std::string &name)
+{
+ if (!taParty)
+ return;
+
+ PartyMember *m = taParty->getMember(name);
+ if (!m)
+ {
+ if (partyTab)
+ {
+ partyTab->chatLog(strprintf(_("%s is not in your party!"),
+ name.c_str()), BY_SERVER);
+ }
+ return;
+ }
+
+ MessageOut outMsg(CMSG_PARTY_KICK);
+ outMsg.writeInt32(m->getID());
+ outMsg.writeString(name, 24); //Unused
+}
+
+void PartyHandler::chat(const std::string &text)
+{
+ MessageOut outMsg(CMSG_PARTY_MESSAGE);
+ outMsg.writeInt16(text.length() + 4);
+ outMsg.writeString(text, text.length());
+}
+
+void PartyHandler::requestPartyMembers()
+{
+ // Our eAthena doesn't have this message
+ // Not needed anyways
+}
+
+void PartyHandler::setShareExperience(PartyShare share)
+{
+ if (share == PARTY_SHARE_NOT_POSSIBLE)
+ return;
+
+ MessageOut outMsg(CMSG_PARTY_SETTINGS);
+ outMsg.writeInt16(share);
+ outMsg.writeInt16(mShareItems);
+}
+
+void PartyHandler::setShareItems(PartyShare share)
+{
+ if (share == PARTY_SHARE_NOT_POSSIBLE)
+ return;
+
+ MessageOut outMsg(CMSG_PARTY_SETTINGS);
+ outMsg.writeInt16(mShareExp);
+ outMsg.writeInt16(share);
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/partyhandler.h b/src/net/tmwa/partyhandler.h
new file mode 100644
index 000000000..8a199ded3
--- /dev/null
+++ b/src/net/tmwa/partyhandler.h
@@ -0,0 +1,85 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net>
+ *
+ * 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_TA_PARTYHANDLER_H
+#define NET_TA_PARTYHANDLER_H
+
+#include "net/net.h"
+#include "net/partyhandler.h"
+
+#include "net/tmwa/messagehandler.h"
+
+#include "party.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+namespace TmwAthena
+{
+
+class PartyHandler : public MessageHandler, public Net::PartyHandler
+{
+ public:
+ PartyHandler();
+
+ ~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 mShareExp; }
+
+ void setShareExperience(PartyShare share);
+
+ PartyShare getShareItems()
+ { return mShareItems; }
+
+ void setShareItems(PartyShare share);
+
+ private:
+ PartyShare mShareExp, mShareItems;
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_PARTYHANDLER_H
diff --git a/src/net/tmwa/playerhandler.cpp b/src/net/tmwa/playerhandler.cpp
new file mode 100644
index 000000000..1d0d3e67d
--- /dev/null
+++ b/src/net/tmwa/playerhandler.cpp
@@ -0,0 +1,752 @@
+/*
+ * 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/tmwa/playerhandler.h"
+
+#include "event.h"
+#include "game.h"
+#include "localplayer.h"
+#include "log.h"
+#include "party.h"
+#include "playerinfo.h"
+#include "units.h"
+
+#include "gui/buy.h"
+#include "gui/buysell.h"
+#include "gui/gui.h"
+#include "gui/npcdialog.h"
+#include "gui/okdialog.h"
+#include "gui/sell.h"
+#include "gui/statuswindow.h"
+#include "gui/viewport.h"
+
+#include "net/messagein.h"
+#include "net/messageout.h"
+
+#include "net/tmwa/protocol.h"
+#include "net/tmwa/npchandler.h"
+
+#include "utils/stringutils.h"
+#include "utils/gettext.h"
+
+extern OkDialog *weightNotice;
+extern OkDialog *deathNotice;
+
+// Max. distance we are willing to scroll after a teleport;
+// everything beyond will reset the port hard.
+static const int MAP_TELEPORT_SCROLL_DISTANCE = 8;
+
+// TODO Move somewhere else
+namespace
+{
+ /**
+ * Listener used for handling the overweigth message.
+ */
+ struct WeightListener : public gcn::ActionListener
+ {
+ void action(const gcn::ActionEvent &event _UNUSED_)
+ {
+ weightNotice = NULL;
+ }
+ } weightListener;
+
+ /**
+ * Listener used for handling death message.
+ */
+ struct DeathListener : public gcn::ActionListener
+ {
+ void action(const gcn::ActionEvent &event _UNUSED_)
+ {
+ Net::getPlayerHandler()->respawn();
+ deathNotice = NULL;
+
+ BuyDialog::closeAll();
+ BuySellDialog::closeAll();
+ NpcDialog::closeAll();
+ SellDialog::closeAll();
+
+ if (viewport)
+ viewport->closePopupMenu();
+
+ TmwAthena::NpcHandler *handler =
+ static_cast<TmwAthena::NpcHandler*>(Net::getNpcHandler());
+ if (handler)
+ handler->clearDialogs();
+ if (player_node)
+ player_node->respawn();
+ }
+ } deathListener;
+
+} // anonymous namespace
+
+static const char *randomDeathMessage()
+{
+ static char const *const deadMsg[] =
+ {
+ N_("You are dead."),
+ N_("We regret to inform you that your character was killed in "
+ "battle."),
+ N_("You are not that alive anymore."),
+ N_("The cold hands of the grim reaper are grabbing for your soul."),
+ N_("Game Over!"),
+ N_("Insert coin to continue."),
+ N_("No, kids. Your character did not really die. It... "
+ "err... went to a better place."),
+ N_("Your plan of breaking your enemies weapon by "
+ "bashing it with your throat failed."),
+ N_("I guess this did not run too well."),
+ // NetHack reference:
+ N_("Do you want your possessions identified?"),
+ // Secret of Mana reference:
+ N_("Sadly, no trace of you was ever found..."),
+ // Final Fantasy VI reference:
+ N_("Annihilated."),
+ // Earthbound reference:
+ N_("Looks like you got your head handed to you."),
+ // Leisure Suit Larry 1 reference:
+ N_("You screwed up again, dump your body down the tubes "
+ "and get you another one."),
+ // Monty Python references (Dead Parrot sketch mostly):
+ N_("You're not dead yet. You're just resting."),
+ N_("You are no more."),
+ N_("You have ceased to be."),
+ N_("You've expired and gone to meet your maker."),
+ N_("You're a stiff."),
+ N_("Bereft of life, you rest in peace."),
+ N_("If you weren't so animated, you'd be pushing up the daisies."),
+ N_("Your metabolic processes are now history."),
+ N_("You're off the twig."),
+ N_("You've kicked the bucket."),
+ N_("You've shuffled off your mortal coil, run down the "
+ "curtain and joined the bleedin' choir invisibile."),
+ N_("You are an ex-player."),
+ N_("You're pining for the fjords.")
+ };
+
+ const int random = rand() % (sizeof(deadMsg) / sizeof(deadMsg[0]));
+ return gettext(deadMsg[random]);
+}
+
+extern Net::PlayerHandler *playerHandler;
+
+namespace TmwAthena
+{
+
+PlayerHandler::PlayerHandler()
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_WALK_RESPONSE,
+ SMSG_PLAYER_WARP,
+ SMSG_PLAYER_STAT_UPDATE_1,
+ SMSG_PLAYER_STAT_UPDATE_2,
+ SMSG_PLAYER_STAT_UPDATE_3,
+ SMSG_PLAYER_STAT_UPDATE_4,
+ SMSG_PLAYER_STAT_UPDATE_5,
+ SMSG_PLAYER_STAT_UPDATE_6,
+ SMSG_PLAYER_ARROW_MESSAGE,
+ 0
+ };
+ handledMessages = _messages;
+ playerHandler = this;
+}
+
+void PlayerHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_WALK_RESPONSE:
+ /*
+ * This client assumes that all walk messages succeed,
+ * and that the server will send a correction notice
+ * otherwise.
+ */
+ Uint16 srcX, srcY, dstX, dstY;
+ msg.readInt32(); //tick
+ msg.readCoordinatePair(srcX, srcY, dstX, dstY);
+ if (player_node)
+ player_node->setRealPos(dstX, dstY);
+// if (debugChatTab)
+// debugChatTab->chatLog("move resp: " + toString((int)srcX) + "," + toString((int)srcY) + " "
+// + toString((int)dstX) + "," + toString((int)dstY));
+
+ break;
+
+ case SMSG_PLAYER_WARP:
+ {
+ std::string mapPath = msg.readString(16);
+ int x = msg.readInt16();
+ int y = msg.readInt16();
+
+ logger->log("Warping to %s (%d, %d)", mapPath.c_str(), x, y);
+
+ if (!player_node)
+ logger->log1("SMSG_PLAYER_WARP player_node null");
+
+ /*
+ * We must clear the local player's target *before* the call
+ * to changeMap, as it deletes all beings.
+ */
+ if (player_node)
+ player_node->stopAttack();
+
+ Game *game = Game::instance();
+
+ const std::string &currentMapName = game->getCurrentMapName();
+ bool sameMap = (currentMapName == mapPath);
+
+ // Switch the actual map, deleting the previous one if necessary
+ mapPath = mapPath.substr(0, mapPath.rfind("."));
+ game->changeMap(mapPath);
+
+ float scrollOffsetX = 0.0f;
+ float scrollOffsetY = 0.0f;
+
+ if (player_node)
+ {
+ /* Scroll if neccessary */
+ if (!sameMap
+ || (abs(x - player_node->getTileX())
+ > MAP_TELEPORT_SCROLL_DISTANCE)
+ || (abs(y - player_node->getTileY())
+ > MAP_TELEPORT_SCROLL_DISTANCE))
+ {
+ Map *map = game->getCurrentMap();
+ if (map)
+ {
+ scrollOffsetX = (x - player_node->getTileX())
+ * map->getTileWidth();
+ scrollOffsetY = (y - player_node->getTileY())
+ * map->getTileHeight();
+ }
+ }
+
+ player_node->setAction(Being::STAND);
+ player_node->setTileCoords(x, y);
+ player_node->naviageClean();
+// player_node->updateNavigateList();
+ }
+
+ logger->log("Adjust scrolling by %d:%d", (int) scrollOffsetX,
+ (int) scrollOffsetY);
+
+ if (viewport)
+ viewport->scrollBy(scrollOffsetX, scrollOffsetY);
+ }
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_1:
+ {
+ int type = msg.readInt16();
+ int value = msg.readInt32();
+ if (!player_node)
+ return;
+
+ switch (type)
+ {
+ case 0x0000:
+ player_node->setWalkSpeed(Vector(value, value, 0));
+ PlayerInfo::setStatBase(WALK_SPEED, value);
+ PlayerInfo::setStatMod(WALK_SPEED, 0);
+ break;
+ case 0x0004: break; // manner
+ case 0x0005:
+ PlayerInfo::setAttribute(HP, value);
+ if (player_node->isInParty() && Party::getParty(1))
+ {
+ PartyMember *m = Party::getParty(1)
+ ->getMember(player_node->getId());
+ if (m)
+ {
+ m->setHp(value);
+ m->setMaxHp(PlayerInfo::getAttribute(MAX_HP));
+ }
+ }
+ break;
+ case 0x0006:
+ PlayerInfo::setAttribute(MAX_HP, value);
+
+ if (player_node->isInParty() && Party::getParty(1))
+ {
+ PartyMember *m = Party::getParty(1)->getMember(
+ player_node->getId());
+ if (m)
+ {
+ m->setHp(PlayerInfo::getAttribute(HP));
+ m->setMaxHp(value);
+ }
+ }
+ break;
+ case 0x0007:
+ PlayerInfo::setAttribute(MP, value);
+ break;
+ case 0x0008:
+ PlayerInfo::setAttribute(MAX_MP, value);
+ break;
+ case 0x0009:
+ PlayerInfo::setAttribute(CHAR_POINTS, value);
+ break;
+ case 0x000b:
+ PlayerInfo::setAttribute(LEVEL, value);
+ if (player_node)
+ {
+ player_node->setLevel(value);
+ player_node->updateName();
+ }
+ break;
+ case 0x000c:
+ PlayerInfo::setAttribute(SKILL_POINTS, value);
+ break;
+ case 0x0018:
+ if (!weightNotice)
+ {
+ const int max
+ = PlayerInfo::getAttribute(MAX_WEIGHT) / 2;
+ const int total
+ = PlayerInfo::getAttribute(TOTAL_WEIGHT);
+ if (value >= max && total < max)
+ {
+ weightNotice = new OkDialog(_("Message"),
+ _("You are carrying more than "
+ "half your weight. You are "
+ "unable to regain health."));
+ weightNotice->addActionListener(
+ &weightListener);
+ }
+ else if (value < max && total >= max)
+ {
+ weightNotice = new OkDialog(_("Message"),
+ _("You are carrying less than "
+ "half your weight. You are "
+ "can regain health."));
+ weightNotice->addActionListener(
+ &weightListener);
+ }
+ }
+ PlayerInfo::setAttribute(TOTAL_WEIGHT, value);
+ break;
+ case 0x0019:
+ PlayerInfo::setAttribute(MAX_WEIGHT, value);
+ break;
+
+ case 0x0029:
+ PlayerInfo::setStatBase(ATK, value);
+ break;
+ case 0x002a:
+ PlayerInfo::setStatMod(ATK, value);
+ break;
+
+ case 0x002b:
+ PlayerInfo::setStatBase(MATK, value);
+ break;
+ case 0x002c:
+ PlayerInfo::setStatMod(MATK, value);
+ break;
+
+ case 0x002d:
+ PlayerInfo::setStatBase(DEF, value);
+ break;
+ case 0x002e:
+ PlayerInfo::setStatMod(DEF, value);
+ break;
+
+ case 0x002f:
+ PlayerInfo::setStatBase(MDEF, value);
+ break;
+ case 0x0030:
+ PlayerInfo::setStatMod(MDEF, value);
+ break;
+
+ case 0x0031:
+ PlayerInfo::setStatBase(HIT, value);
+ break;
+
+ case 0x0032:
+ PlayerInfo::setStatBase(FLEE, value);
+ break;
+ case 0x0033:
+ PlayerInfo::setStatMod(FLEE, value);
+ break;
+
+ case 0x0034:
+ PlayerInfo::setStatBase(CRIT, value);
+ break;
+
+ case 0x0035:
+ player_node->setAttackSpeed(value);
+ PlayerInfo::setStatBase(ATTACK_SPEED, value);
+ PlayerInfo::setStatMod(ATTACK_SPEED, 0);
+ break;
+
+ case 0x0037:
+ PlayerInfo::setStatBase(JOB, value);
+ break;
+
+ case 500:
+ player_node->setGMLevel(value);
+ break;
+
+ default:
+ logger->log("QQQQ PLAYER_STAT_UPDATE_1 "
+ + toString(type) + "," + toString(value));
+ break;
+ }
+
+ if (PlayerInfo::getAttribute(HP) == 0 && !deathNotice)
+ {
+ deathNotice = new OkDialog(_("Message"),
+ randomDeathMessage(),
+ false);
+ deathNotice->addActionListener(&deathListener);
+ player_node->setAction(Being::DEAD);
+ }
+ }
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_2:
+ {
+ int type = msg.readInt16();
+ switch (type)
+ {
+ case 0x0001:
+ PlayerInfo::setAttribute(EXP, msg.readInt32());
+ break;
+ case 0x0002:
+ PlayerInfo::setStatExperience(JOB, msg.readInt32(),
+ PlayerInfo::getStatExperience(JOB).second);
+ break;
+ case 0x0014:
+ {
+ int oldMoney = PlayerInfo::getAttribute(MONEY);
+ int newMoney = msg.readInt32();
+ if (newMoney > oldMoney)
+ {
+ SERVER_NOTICE(strprintf(_("You picked up %s."),
+ Units::formatCurrency(newMoney -
+ oldMoney).c_str()))
+ }
+ else if (newMoney < oldMoney)
+ {
+ SERVER_NOTICE(strprintf(_("You spent %s."),
+ Units::formatCurrency(oldMoney -
+ newMoney).c_str()))
+ }
+
+ PlayerInfo::setAttribute(MONEY, newMoney);
+ break;
+ }
+ case 0x0016:
+ PlayerInfo::setAttribute(EXP_NEEDED, msg.readInt32());
+ break;
+ case 0x0017:
+ PlayerInfo::setStatExperience(JOB,
+ PlayerInfo::getStatExperience(JOB).first,
+ msg.readInt32());
+ break;
+ default:
+ logger->log("QQQQ PLAYER_STAT_UPDATE_2 " + toString(type));
+ break;
+ }
+ break;
+ }
+ case SMSG_PLAYER_STAT_UPDATE_3: // Update a base attribute
+ {
+ int type = msg.readInt32();
+ int base = msg.readInt32();
+ int bonus = msg.readInt32();
+
+ PlayerInfo::setStatBase(type, base, false);
+ PlayerInfo::setStatMod(type, bonus);
+ }
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_4: // Attribute increase ack
+ {
+ int type = msg.readInt16();
+ int ok = msg.readInt8();
+ int value = msg.readInt8();
+
+ if (ok != 1)
+ {
+ int oldValue = PlayerInfo::getStatBase(type);
+ int points = PlayerInfo::getAttribute(CHAR_POINTS);
+ points += oldValue - value;
+ PlayerInfo::setAttribute(CHAR_POINTS, points);
+ SERVER_NOTICE(_("Cannot raise skill!"))
+ }
+
+ PlayerInfo::setStatBase(type, value);
+ }
+ break;
+
+ // Updates stats and status points
+ case SMSG_PLAYER_STAT_UPDATE_5:
+ PlayerInfo::setAttribute(CHAR_POINTS, msg.readInt16());
+ {
+ int val = msg.readInt8();
+ PlayerInfo::setStatBase(STR, val);
+ if (statusWindow)
+ statusWindow->setPointsNeeded(STR, msg.readInt8());
+ else
+ msg.readInt8();
+
+ val = msg.readInt8();
+ PlayerInfo::setStatBase(AGI, val);
+ if (statusWindow)
+ statusWindow->setPointsNeeded(AGI, msg.readInt8());
+ else
+ msg.readInt8();
+
+ val = msg.readInt8();
+ PlayerInfo::setStatBase(VIT, val);
+ if (statusWindow)
+ statusWindow->setPointsNeeded(VIT, msg.readInt8());
+ else
+ msg.readInt8();
+
+ val = msg.readInt8();
+ PlayerInfo::setStatBase(INT, val);
+ if (statusWindow)
+ statusWindow->setPointsNeeded(INT, msg.readInt8());
+ else
+ msg.readInt8();
+
+ val = msg.readInt8();
+ PlayerInfo::setStatBase(DEX, val);
+ if (statusWindow)
+ statusWindow->setPointsNeeded(DEX, msg.readInt8());
+ else
+ msg.readInt8();
+
+ val = msg.readInt8();
+ PlayerInfo::setStatBase(LUK, val);
+ if (statusWindow)
+ statusWindow->setPointsNeeded(LUK, msg.readInt8());
+ else
+ msg.readInt8();
+
+ PlayerInfo::setStatBase(ATK, msg.readInt16(), false);
+ PlayerInfo::setStatMod(ATK, msg.readInt16());
+
+ val = msg.readInt16();
+ PlayerInfo::setStatBase(MATK, val, false);
+
+ val = msg.readInt16();
+ PlayerInfo::setStatMod(MATK, val);
+
+ PlayerInfo::setStatBase(DEF, msg.readInt16(), false);
+ PlayerInfo::setStatMod(DEF, msg.readInt16());
+
+ PlayerInfo::setStatBase(MDEF, msg.readInt16(), false);
+ PlayerInfo::setStatMod(MDEF, msg.readInt16());
+
+ PlayerInfo::setStatBase(HIT, msg.readInt16());
+
+ PlayerInfo::setStatBase(FLEE, msg.readInt16(), false);
+ PlayerInfo::setStatMod(FLEE, msg.readInt16());
+
+ PlayerInfo::setStatBase(CRIT, msg.readInt16());
+ }
+
+ msg.readInt16(); // manner
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_6:
+ {
+ int type = msg.readInt16();
+ if (statusWindow)
+ {
+ switch (type)
+ {
+ case 0x0020:
+ statusWindow->setPointsNeeded(STR, msg.readInt8());
+ break;
+ case 0x0021:
+ statusWindow->setPointsNeeded(AGI, msg.readInt8());
+ break;
+ case 0x0022:
+ statusWindow->setPointsNeeded(VIT, msg.readInt8());
+ break;
+ case 0x0023:
+ statusWindow->setPointsNeeded(INT, msg.readInt8());
+ break;
+ case 0x0024:
+ statusWindow->setPointsNeeded(DEX, msg.readInt8());
+ break;
+ case 0x0025:
+ statusWindow->setPointsNeeded(LUK, msg.readInt8());
+ break;
+ default:
+ logger->log("QQQQ PLAYER_STAT_UPDATE_6 "
+ + toString(type));
+ break;
+ }
+ }
+ break;
+ }
+ case SMSG_PLAYER_ARROW_MESSAGE:
+ {
+ int type = msg.readInt16();
+
+ switch (type)
+ {
+ case 0:
+ {
+ SERVER_NOTICE(_("Equip arrows first."))
+ }
+ break;
+ case 3:
+ // arrows equiped
+ break;
+ default:
+ logger->log("QQQQ 0x013b: Unhandled message %i", type);
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void PlayerHandler::attack(int id, bool keep)
+{
+ MessageOut outMsg(CMSG_PLAYER_ATTACK);
+ outMsg.writeInt32(id);
+ if (keep)
+ outMsg.writeInt8(7);
+ else
+ outMsg.writeInt8(0);
+}
+
+void PlayerHandler::stopAttack()
+{
+ MessageOut outMsg(CMSG_PLAYER_STOP_ATTACK);
+}
+
+void PlayerHandler::emote(int emoteId)
+{
+ MessageOut outMsg(CMSG_PLAYER_EMOTE);
+ outMsg.writeInt8(emoteId);
+}
+
+void PlayerHandler::increaseAttribute(int attr)
+{
+ if (attr >= STR && attr <= LUK)
+ {
+ MessageOut outMsg(CMSG_STAT_UPDATE_REQUEST);
+ outMsg.writeInt16(attr);
+ outMsg.writeInt8(1);
+ }
+}
+
+void PlayerHandler::decreaseAttribute(int attr _UNUSED_)
+{
+ // Supported by eA?
+}
+
+void PlayerHandler::increaseSkill(int skillId)
+{
+ if (PlayerInfo::getAttribute(SKILL_POINTS) <= 0)
+ return;
+
+ MessageOut outMsg(CMSG_SKILL_LEVELUP_REQUEST);
+ outMsg.writeInt16(skillId);
+}
+
+void PlayerHandler::pickUp(FloorItem *floorItem)
+{
+ if (!floorItem)
+ return;
+
+ MessageOut outMsg(CMSG_ITEM_PICKUP);
+ outMsg.writeInt32(floorItem->getId());
+}
+
+void PlayerHandler::setDirection(char direction)
+{
+ MessageOut outMsg(CMSG_PLAYER_CHANGE_DIR);
+ outMsg.writeInt16(0);
+ outMsg.writeInt8(direction);
+}
+
+void PlayerHandler::setDestination(int x, int y, int direction)
+{
+ MessageOut outMsg(CMSG_PLAYER_CHANGE_DEST);
+ outMsg.writeCoordinates(x, y, direction);
+}
+
+void PlayerHandler::changeAction(Being::Action action)
+{
+ char type;
+ switch (action)
+ {
+ case Being::SIT: type = 2; break;
+ case Being::STAND: type = 3; break;
+ default: return;
+ }
+
+ MessageOut outMsg(CMSG_PLAYER_CHANGE_ACT);
+ outMsg.writeInt32(0);
+ outMsg.writeInt8(type);
+}
+
+void PlayerHandler::respawn()
+{
+ MessageOut outMsg(CMSG_PLAYER_RESTART);
+ outMsg.writeInt8(0);
+}
+
+void PlayerHandler::ignorePlayer(const std::string &player _UNUSED_,
+ bool ignore _UNUSED_)
+{
+ // TODO
+}
+
+void PlayerHandler::ignoreAll(bool ignore _UNUSED_)
+{
+ // TODO
+}
+
+bool PlayerHandler::canUseMagic()
+{
+ return PlayerInfo::getStatEffective(MATK) > 0;
+}
+
+bool PlayerHandler::canCorrectAttributes()
+{
+ return false;
+}
+
+int PlayerHandler::getJobLocation()
+{
+ return JOB;
+}
+
+Vector PlayerHandler::getDefaultWalkSpeed()
+{
+ // Return an normalized speed for any side
+ // as the offset is calculated elsewhere.
+ return Vector(150, 150, 0);
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/playerhandler.h b/src/net/tmwa/playerhandler.h
new file mode 100644
index 000000000..6e1c6dc78
--- /dev/null
+++ b/src/net/tmwa/playerhandler.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_TA_PLAYERHANDLER_H
+#define NET_TA_PLAYERHANDLER_H
+
+#include "net/net.h"
+#include "net/playerhandler.h"
+
+#include "net/tmwa/messagehandler.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+namespace TmwAthena
+{
+
+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();
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_PLAYERHANDLER_H
diff --git a/src/net/tmwa/protocol.h b/src/net/tmwa/protocol.h
new file mode 100644
index 000000000..6001fae3b
--- /dev/null
+++ b/src/net/tmwa/protocol.h
@@ -0,0 +1,327 @@
+/*
+ * 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 TA_PROTOCOL_H
+#define TA_PROTOCOL_H
+
+enum
+{
+ JOB = 0xa,
+
+ STR = 0xd,
+ AGI,
+ VIT,
+ INT,
+ DEX,
+ LUK,
+
+ ATK,
+ DEF,
+ MATK,
+ MDEF,
+ HIT,
+ FLEE,
+ CRIT
+
+// KARMA,
+// MANNER
+};
+
+enum
+{
+ SPRITE_BASE = 0,
+ SPRITE_SHOE,
+ SPRITE_BOTTOMCLOTHES,
+ SPRITE_TOPCLOTHES,
+ SPRITE_MISC1,
+ SPRITE_MISC2,
+ SPRITE_HAIR,
+ SPRITE_HAT,
+ SPRITE_CAPE,
+ SPRITE_GLOVES,
+ SPRITE_WEAPON,
+ SPRITE_SHIELD,
+ SPRITE_VECTOREND
+};
+
+static const int INVENTORY_OFFSET = 2;
+static const int STORAGE_OFFSET = 1;
+
+/*********************************
+ * Packets from server to client *
+ *********************************/
+#define SMSG_SERVER_VERSION_RESPONSE 0x7531
+
+#define SMSG_SERVER_PING 0x007f /**< Contains server tick */
+#define SMSG_CONNECTION_PROBLEM 0x0081
+
+#define SMSG_UPDATE_HOST 0x0063 /**< Custom update host packet */
+#define SMSG_LOGIN_DATA 0x0069
+#define SMSG_LOGIN_ERROR 0x006a
+
+#define SMSG_CHAR_LOGIN 0x006b
+#define SMSG_CHAR_LOGIN_ERROR 0x006c
+#define SMSG_CHAR_CREATE_SUCCEEDED 0x006d
+#define SMSG_CHAR_CREATE_FAILED 0x006e
+#define SMSG_CHAR_DELETE_SUCCEEDED 0x006f
+#define SMSG_CHAR_DELETE_FAILED 0x0070
+#define SMSG_CHAR_MAP_INFO 0x0071
+#define SMSG_CHAR_PASSWORD_RESPONSE 0x0062 /**< Custom packet reply to password change request */
+
+#define SMSG_CHAR_SWITCH_RESPONSE 0x00b3
+#define SMSG_CHANGE_MAP_SERVER 0x0092
+
+#define SMSG_MAP_LOGIN_SUCCESS 0x0073 /**< Contains starting location */
+#define SMSG_MAP_QUIT_RESPONSE 0x018b
+#define SMSG_PLAYER_UPDATE_1 0x01d8
+#define SMSG_PLAYER_UPDATE_2 0x01d9
+#define SMSG_PLAYER_MOVE 0x01da /**< A nearby player moves */
+#define SMSG_PLAYER_STOP 0x0088 /**< Stop walking, set position */
+#define SMSG_PLAYER_MOVE_TO_ATTACK 0x0139 /**< Move to within attack range */
+#define SMSG_PLAYER_STAT_UPDATE_1 0x00b0
+#define SMSG_PLAYER_STAT_UPDATE_2 0x00b1
+#define SMSG_PLAYER_STAT_UPDATE_3 0x0141
+#define SMSG_PLAYER_STAT_UPDATE_4 0x00bc
+#define SMSG_PLAYER_STAT_UPDATE_5 0x00bd
+#define SMSG_PLAYER_STAT_UPDATE_6 0x00be
+#define SMSG_WHO_ANSWER 0x00c2
+#define SMSG_PLAYER_WARP 0x0091 /**< Warp player to map/location */
+#define SMSG_PLAYER_INVENTORY 0x01ee
+#define SMSG_PLAYER_INVENTORY_ADD 0x00a0
+#define SMSG_PLAYER_INVENTORY_REMOVE 0x00af
+#define SMSG_PLAYER_INVENTORY_USE 0x01c8
+#define SMSG_PLAYER_EQUIPMENT 0x00a4
+#define SMSG_PLAYER_EQUIP 0x00aa
+#define SMSG_PLAYER_UNEQUIP 0x00ac
+#define SMSG_PLAYER_ATTACK_RANGE 0x013a
+#define SMSG_PLAYER_ARROW_EQUIP 0x013c
+#define SMSG_PLAYER_ARROW_MESSAGE 0x013b
+#define SMSG_PLAYER_SKILLS 0x010f
+#define SMSG_PLAYER_SKILL_UP 0x010e
+#define SMSG_SKILL_FAILED 0x0110
+#define SMSG_SKILL_DAMAGE 0x01de
+#define SMSG_ITEM_USE_RESPONSE 0x00a8
+#define SMSG_ITEM_VISIBLE 0x009d /**< An item is on the floor */
+#define SMSG_ITEM_DROPPED 0x009e /**< An item is dropped */
+#define SMSG_ITEM_REMOVE 0x00a1 /**< An item disappers */
+#define SMSG_BEING_VISIBLE 0x0078
+#define SMSG_BEING_MOVE 0x007b /**< A nearby monster moves */
+#define SMSG_BEING_SPAWN 0x007c /**< A being spawns nearby */
+#define SMSG_BEING_MOVE2 0x0086 /**< New eAthena being moves */
+#define SMSG_BEING_REMOVE 0x0080
+#define SMSG_BEING_CHANGE_LOOKS 0x00c3
+#define SMSG_BEING_CHANGE_LOOKS2 0x01d7 /**< Same as 0x00c3, but 16 bit ID */
+#define SMSG_BEING_SELFEFFECT 0x019b
+#define SMSG_BEING_EMOTION 0x00c0
+#define SMSG_BEING_ACTION 0x008a /**< Attack, sit, stand up, ... */
+#define SMSG_BEING_CHAT 0x008d /**< A being talks */
+#define SMSG_BEING_NAME_RESPONSE 0x0095 /**< Has to be requested */
+#define SMSG_BEING_CHANGE_DIRECTION 0x009c
+#define SMSG_BEING_RESURRECT 0x0148
+
+#define SMSG_PLAYER_STATUS_CHANGE 0x0119
+#define SMSG_PLAYER_GUILD_PARTY_INFO 0x0195
+#define SMSG_BEING_STATUS_CHANGE 0x0196
+
+#define SMSG_NPC_MESSAGE 0x00b4
+#define SMSG_NPC_NEXT 0x00b5
+#define SMSG_NPC_CLOSE 0x00b6
+#define SMSG_NPC_CHOICE 0x00b7 /**< Display a choice */
+#define SMSG_NPC_BUY_SELL_CHOICE 0x00c4
+#define SMSG_NPC_BUY 0x00c6
+#define SMSG_NPC_SELL 0x00c7
+#define SMSG_NPC_BUY_RESPONSE 0x00ca
+#define SMSG_NPC_SELL_RESPONSE 0x00cb
+#define SMSG_NPC_INT_INPUT 0x0142 /**< Integer input */
+#define SMSG_NPC_STR_INPUT 0x01d4 /**< String input */
+#define SMSG_PLAYER_CHAT 0x008e /**< Player talks */
+#define SMSG_WHISPER 0x0097 /**< Whisper Recieved */
+#define SMSG_WHISPER_RESPONSE 0x0098
+#define SMSG_GM_CHAT 0x009a /**< GM announce */
+#define SMSG_WALK_RESPONSE 0x0087
+
+#define SMSG_TRADE_REQUEST 0x00e5 /**< Receiving a request to trade */
+#define SMSG_TRADE_RESPONSE 0x00e7
+#define SMSG_TRADE_ITEM_ADD 0x00e9
+#define SMSG_TRADE_ITEM_ADD_RESPONSE 0x01b1 /**< Not standard eAthena! */
+#define SMSG_TRADE_OK 0x00ec
+#define SMSG_TRADE_CANCEL 0x00ee
+#define SMSG_TRADE_COMPLETE 0x00f0
+
+#define SMSG_PARTY_CREATE 0x00fa
+#define SMSG_PARTY_INFO 0x00fb
+#define SMSG_PARTY_INVITE_RESPONSE 0x00fd
+#define SMSG_PARTY_INVITED 0x00fe
+#define SMSG_PARTY_SETTINGS 0x0101
+#define SMSG_PARTY_MOVE 0x0104
+#define SMSG_PARTY_LEAVE 0x0105
+#define SMSG_PARTY_UPDATE_HP 0x0106
+#define SMSG_PARTY_UPDATE_COORDS 0x0107
+#define SMSG_PARTY_MESSAGE 0x0109
+
+#define SMSG_PLAYER_STORAGE_ITEMS 0x01f0 /**< Item list for storage */
+#define SMSG_PLAYER_STORAGE_EQUIP 0x00a6 /**< Equipment list for storage */
+#define SMSG_PLAYER_STORAGE_STATUS 0x00f2 /**< Slots used and total slots */
+#define SMSG_PLAYER_STORAGE_ADD 0x00f4 /**< Add item/equip to storage */
+#define SMSG_PLAYER_STORAGE_REMOVE 0x00f6 /**< Remove item/equip from storage */
+#define SMSG_PLAYER_STORAGE_CLOSE 0x00f8 /**< Storage access closed */
+
+#define SMSG_ADMIN_KICK_ACK 0x00cd
+
+#define SMSG_GUILD_CREATE_RESPONSE 0x0167
+#define SMSG_GUILD_POSITION_INFO 0x016c
+#define SMSG_GUILD_MEMBER_LOGIN 0x016d
+#define SMSG_GUILD_MASTER_OR_MEMBER 0x014e
+#define SMSG_GUILD_BASIC_INFO 0x01b6
+#define SMSG_GUILD_ALIANCE_INFO 0x014c
+#define SMSG_GUILD_MEMBER_LIST 0x0154
+#define SMSG_GUILD_POS_NAME_LIST 0x0166
+#define SMSG_GUILD_POS_INFO_LIST 0x0160
+#define SMSG_GUILD_POSITION_CHANGED 0x0174
+#define SMSG_GUILD_MEMBER_POS_CHANGE 0x0156
+#define SMSG_GUILD_EMBLEM 0x0152
+#define SMSG_GUILD_SKILL_INFO 0x0162
+#define SMSG_GUILD_NOTICE 0x016f
+#define SMSG_GUILD_INVITE 0x016a
+#define SMSG_GUILD_INVITE_ACK 0x0169
+#define SMSG_GUILD_LEAVE 0x015a
+#define SMSG_GUILD_EXPULSION 0x015c
+#define SMSG_GUILD_EXPULSION_LIST 0x0163
+#define SMSG_GUILD_MESSAGE 0x017f
+#define SMSG_GUILD_SKILL_UP 0x010e
+#define SMSG_GUILD_REQ_ALLIANCE 0x0171
+#define SMSG_GUILD_REQ_ALLIANCE_ACK 0x0173
+#define SMSG_GUILD_DEL_ALLIANCE 0x0184
+#define SMSG_GUILD_OPPOSITION_ACK 0x0181
+#define SMSG_GUILD_BROKEN 0x015e
+
+#define SMSG_MVP 0x010c
+
+/**********************************
+ * Packets from client to server *
+ **********************************/
+#define CMSG_SERVER_VERSION_REQUEST 0x7530
+
+#define CMSG_CHAR_PASSWORD_CHANGE 0x0061 /**< Custom change password packet */
+#define CMSG_CHAR_SERVER_CONNECT 0x0065
+#define CMSG_CHAR_SELECT 0x0066
+#define CMSG_CHAR_CREATE 0x0067
+#define CMSG_CHAR_DELETE 0x0068
+
+#define CMSG_MAP_SERVER_CONNECT 0x0072
+#define CMSG_CLIENT_PING 0x007e /**< Send to server with tick */
+#define CMSG_MAP_LOADED 0x007d
+#define CMSG_CLIENT_QUIT 0x018A
+
+#define CMSG_CHAT_MESSAGE 0x008c
+#define CMSG_CHAT_WHISPER 0x0096
+#define CMSG_CHAT_ANNOUNCE 0x0099
+#define CMSG_CHAT_WHO 0x00c1
+
+#define CMSG_SKILL_LEVELUP_REQUEST 0x0112
+#define CMSG_STAT_UPDATE_REQUEST 0x00bb
+#define CMSG_SKILL_USE_BEING 0x0113
+#define CMSG_SKILL_USE_POSITION 0x0116
+// Variant of 0x116 with 80 char string at end (unsure of use)
+#define CMSG_SKILL_USE_POSITION_MORE 0x0190
+#define CMSG_SKILL_USE_MAP 0x011b
+
+#define CMSG_PLAYER_INVENTORY_USE 0x00a7
+#define CMSG_PLAYER_INVENTORY_DROP 0x00a2
+#define CMSG_PLAYER_EQUIP 0x00a9
+#define CMSG_PLAYER_UNEQUIP 0x00ab
+
+#define CMSG_ITEM_PICKUP 0x009f
+#define CMSG_PLAYER_CHANGE_DIR 0x009b
+#define CMSG_PLAYER_CHANGE_DEST 0x0085
+#define CMSG_PLAYER_CHANGE_ACT 0x0089
+#define CMSG_PLAYER_RESTART 0x00b2
+#define CMSG_PLAYER_EMOTE 0x00bf
+#define CMSG_PLAYER_ATTACK 0x0089
+#define CMSG_PLAYER_STOP_ATTACK 0x0118
+#define CMSG_WHO_REQUEST 0x00c1
+
+#define CMSG_NPC_TALK 0x0090
+#define CMSG_NPC_NEXT_REQUEST 0x00b9
+#define CMSG_NPC_CLOSE 0x0146
+#define CMSG_NPC_LIST_CHOICE 0x00b8
+#define CMSG_NPC_INT_RESPONSE 0x0143
+#define CMSG_NPC_STR_RESPONSE 0x01d5
+#define CMSG_NPC_BUY_SELL_REQUEST 0x00c5
+#define CMSG_NPC_BUY_REQUEST 0x00c8
+#define CMSG_NPC_SELL_REQUEST 0x00c9
+
+#define CMSG_TRADE_REQUEST 0x00e4
+#define CMSG_TRADE_RESPONSE 0x00e6
+#define CMSG_TRADE_ITEM_ADD_REQUEST 0x00e8
+#define CMSG_TRADE_CANCEL_REQUEST 0x00ed
+#define CMSG_TRADE_ADD_COMPLETE 0x00eb
+#define CMSG_TRADE_OK 0x00ef
+
+#define CMSG_PARTY_CREATE 0x00f9
+#define CMSG_PARTY_INVITE 0x00fc
+#define CMSG_PARTY_INVITED 0x00ff
+#define CMSG_PARTY_LEAVE 0x0100
+#define CMSG_PARTY_SETTINGS 0x0102
+#define CMSG_PARTY_KICK 0x0103
+#define CMSG_PARTY_MESSAGE 0x0108
+
+#define CMSG_MOVE_TO_STORAGE 0x00f3 /** Move item to storage */
+#define CSMG_MOVE_FROM_STORAGE 0x00f5 /** Remove item from storage */
+#define CMSG_CLOSE_STORAGE 0x00f7 /** Request storage close */
+
+#define CMSG_ADMIN_ANNOUNCE 0x0099
+#define CMSG_ADMIN_LOCAL_ANNOUNCE 0x019C
+#define CMSG_ADMIN_HIDE 0x019D
+#define CMSG_ADMIN_KICK 0x00CC
+#define CMSG_ADMIN_MUTE 0x0149
+
+#define CMSG_GUILD_CHECK_MASTER 0x014d
+#define CMSG_GUILD_REQUEST_INFO 0x014f
+#define CMSG_GUILD_REQUEST_EMBLEM 0x0151
+#define CMSG_GUILD_CHANGE_EMBLEM 0x0153
+#define CMSG_GUILD_CHANGE_MEMBER_POS 0x0155
+#define CMSG_GUILD_LEAVE 0x0159
+#define CMSG_GUILD_EXPULSION 0x015b
+#define CMSG_GUILD_BREAK 0x015d
+#define CMSG_GUILD_CHANGE_POS_INFO 0x0161
+#define CMSG_GUILD_CREATE 0x0165
+#define CMSG_GUILD_INVITE 0x0168
+#define CMSG_GUILD_INVITE_REPLY 0x016b
+#define CMSG_GUILD_CHANGE_NOTICE 0x016e
+#define CMSG_GUILD_ALLIANCE_REQUEST 0x0170
+#define CMSG_GUILD_ALLIANCE_REPLY 0x0172
+#define CMSG_GUILD_MESSAGE 0x017e
+#define CMSG_GUILD_OPPOSITION 0x0180
+#define CMSG_GUILD_ALLIANCE_DELETE 0x0183
+
+#define CMSG_SOLVE_CHAR_NAME 0x0193
+#define SMSG_SOLVE_CHAR_NAME 0x0194
+#define CMSG_CLIENT_DISCONNECT 0x7532
+#define SMSG_SKILL_CASTING 0x013e
+#define SMSG_SKILL_CAST_CANCEL 0x01b9
+#define SMSG_SKILL_NO_DAMAGE 0x011a
+
+#define SMSG_BEING_IP_RESPONSE 0x020c
+#define SMSG_PVP_MAP_MODE 0x0199
+#define SMSG_PVP_SET 0x019a
+
+#endif
diff --git a/src/net/tmwa/specialhandler.cpp b/src/net/tmwa/specialhandler.cpp
new file mode 100644
index 000000000..d502cc85f
--- /dev/null
+++ b/src/net/tmwa/specialhandler.cpp
@@ -0,0 +1,273 @@
+/*
+ * 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/tmwa/specialhandler.h"
+
+#include "log.h"
+#include "playerinfo.h"
+
+#include "gui/skilldialog.h"
+
+#include "gui/widgets/chattab.h"
+
+#include "net/messagein.h"
+#include "net/messageout.h"
+
+#include "net/tmwa/protocol.h"
+
+#include "utils/gettext.h"
+
+/** job dependend identifiers (?) */
+#define SKILL_BASIC 0x0001
+#define SKILL_WARP 0x001b
+#define SKILL_STEAL 0x0032
+#define SKILL_ENVENOM 0x0034
+
+/** basic skills identifiers */
+#define BSKILL_TRADE 0x0000
+#define BSKILL_EMOTE 0x0001
+#define BSKILL_SIT 0x0002
+#define BSKILL_CREATECHAT 0x0003
+#define BSKILL_JOINPARTY 0x0004
+#define BSKILL_SHOUT 0x0005
+#define BSKILL_PK 0x0006 // ??
+#define BSKILL_SETALLIGN 0x0007 // ??
+
+/** reasons why action failed */
+#define RFAIL_SKILLDEP 0x00
+#define RFAIL_INSUFSP 0x01
+#define RFAIL_INSUFHP 0x02
+#define RFAIL_NOMEMO 0x03
+#define RFAIL_SKILLDELAY 0x04
+#define RFAIL_ZENY 0x05
+#define RFAIL_WEAPON 0x06
+#define RFAIL_REDGEM 0x07
+#define RFAIL_BLUEGEM 0x08
+#define RFAIL_OVERWEIGHT 0x09
+#define RFAIL_GENERIC 0x0a
+
+/** should always be zero if failed */
+#define SKILL_FAILED 0x00
+
+extern Net::SpecialHandler *specialHandler;
+
+namespace TmwAthena
+{
+
+SpecialHandler::SpecialHandler()
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_PLAYER_SKILLS,
+ SMSG_SKILL_FAILED,
+ SMSG_PLAYER_SKILL_UP,
+ 0
+ };
+ handledMessages = _messages;
+ specialHandler = this;
+}
+
+void SpecialHandler::handleMessage(Net::MessageIn &msg)
+{
+ int skillCount;
+ int skillId;
+
+ switch (msg.getId())
+ {
+ case SMSG_PLAYER_SKILLS:
+ {
+ msg.readInt16(); // length
+ skillCount = (msg.getLength() - 4) / 37;
+
+ for (int k = 0; k < skillCount; k++)
+ {
+ skillId = msg.readInt16();
+ msg.readInt16(); // target type
+ msg.skip(2); // unused
+ int level = msg.readInt16();
+ msg.readInt16(); // sp
+ msg.readInt16(); // range
+ msg.skip(24); // unused
+ int up = msg.readInt8();
+
+ PlayerInfo::setStatBase(skillId, level);
+ if (skillDialog)
+ skillDialog->setModifiable(skillId, up);
+ }
+ break;
+ }
+ case SMSG_PLAYER_SKILL_UP:
+ {
+ skillId = msg.readInt16();
+ int level = msg.readInt16();
+ msg.readInt16(); // sp
+ msg.readInt16(); // range
+ int up = msg.readInt8();
+
+ PlayerInfo::setStatBase(skillId, level);
+ if (skillDialog)
+ skillDialog->setModifiable(skillId, up);
+ }
+ break;
+
+ case SMSG_SKILL_FAILED:
+ {
+ // Action failed (ex. sit because you have not reached the
+ // right level)
+ skillId = msg.readInt16();
+ short bskill = msg.readInt16();
+ msg.readInt16(); // btype
+ char success = msg.readInt8();
+ char reason = msg.readInt8();
+ if (success != SKILL_FAILED && bskill == BSKILL_EMOTE)
+ logger->log("Action: %d/%d", bskill, success);
+
+ std::string msg;
+ if (success == SKILL_FAILED && skillId == SKILL_BASIC)
+ {
+ switch (bskill)
+ {
+ case BSKILL_TRADE:
+ msg = _("Trade failed!");
+ break;
+ case BSKILL_EMOTE:
+ msg = _("Emote failed!");
+ break;
+ case BSKILL_SIT:
+ msg = _("Sit failed!");
+ break;
+ case BSKILL_CREATECHAT:
+ msg = _("Chat creating failed!");
+ break;
+ case BSKILL_JOINPARTY:
+ msg = _("Could not join party!");
+ break;
+ case BSKILL_SHOUT:
+ msg = _("Cannot shout!");
+ break;
+ default:
+ logger->log("QQQ SMSG_SKILL_FAILED: bskill "
+ + toString(bskill));
+ break;
+ }
+
+ msg += " ";
+
+ switch (reason)
+ {
+ case RFAIL_SKILLDEP:
+ msg += _("You have not yet reached a high enough "
+ "lvl!");
+ break;
+ case RFAIL_INSUFHP:
+ msg += _("Insufficient HP!");
+ break;
+ case RFAIL_INSUFSP:
+ msg += _("Insufficient SP!");
+ break;
+ case RFAIL_NOMEMO:
+ msg += _("You have no memos!");
+ break;
+ case RFAIL_SKILLDELAY:
+ msg += _("You cannot do that right now!");
+ break;
+ case RFAIL_ZENY:
+ msg += _("Seems you need more money... ;-)");
+ break;
+ case RFAIL_WEAPON:
+ msg += _("You cannot use this skill with that "
+ "kind of weapon!");
+ break;
+ case RFAIL_REDGEM:
+ msg += _("You need another red gem!");
+ break;
+ case RFAIL_BLUEGEM:
+ msg += _("You need another blue gem!");
+ break;
+ case RFAIL_OVERWEIGHT:
+ msg += _("You're carrying to much to do this!");
+ break;
+ default:
+ msg += _("Huh? What's that?");
+ logger->log("QQQ SMSG_SKILL_FAILED: reason "
+ + toString(reason));
+ break;
+ }
+ }
+ else
+ {
+ switch (skillId)
+ {
+ case SKILL_WARP :
+ msg = _("Warp failed...");
+ break;
+ case SKILL_STEAL :
+ msg = _("Could not steal anything...");
+ break;
+ case SKILL_ENVENOM :
+ msg = _("Poison had no effect...");
+ break;
+ default:
+ logger->log("QQQ SMSG_SKILL_FAILED: skillId "
+ + toString(skillId));
+ break;
+ }
+ }
+
+ if (localChatTab)
+ localChatTab->chatLog(msg);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void SpecialHandler::use(int id _UNUSED_)
+{
+ // TODO
+}
+
+void SpecialHandler::use(int id, int level, int beingId)
+{
+ MessageOut outMsg(CMSG_SKILL_USE_BEING);
+ outMsg.writeInt16(level);
+ outMsg.writeInt16(id);
+ outMsg.writeInt16(beingId);
+}
+
+void SpecialHandler::use(int id, int level, int x, int y)
+{
+ MessageOut outMsg(CMSG_SKILL_USE_POSITION);
+ outMsg.writeInt16(level);
+ outMsg.writeInt16(id);
+ outMsg.writeInt16(x);
+ outMsg.writeInt16(y);
+}
+
+void SpecialHandler::use(int id, const std::string &map)
+{
+ MessageOut outMsg(CMSG_SKILL_USE_MAP);
+ outMsg.writeInt16(id);
+ outMsg.writeString(map, 16);
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/specialhandler.h b/src/net/tmwa/specialhandler.h
new file mode 100644
index 000000000..e4e65c607
--- /dev/null
+++ b/src/net/tmwa/specialhandler.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_TA_SKILLHANDLER_H
+#define NET_TA_SKILLHANDLER_H
+
+#include "net/net.h"
+#include "net/specialhandler.h"
+
+#include "net/tmwa/messagehandler.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+namespace TmwAthena
+{
+
+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 TmwAthena
+
+#endif // NET_TA_SKILLHANDLER_H
diff --git a/src/net/tmwa/token.h b/src/net/tmwa/token.h
new file mode 100644
index 000000000..3e781cd89
--- /dev/null
+++ b/src/net/tmwa/token.h
@@ -0,0 +1,43 @@
+/*
+ * 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 "being.h"
+
+#ifndef NET_TA_TOKEN_H
+#define NET_TA_TOKEN_H
+
+struct Token
+{
+ int account_ID;
+ int session_ID1;
+ int session_ID2;
+ Gender sex;
+
+ void clear()
+ {
+ account_ID = 0;
+ session_ID1 = 0;
+ session_ID2 = 0;
+ sex = GENDER_UNSPECIFIED;
+ }
+};
+
+#endif // NET_TA_TOKEN_H
diff --git a/src/net/tmwa/tradehandler.cpp b/src/net/tmwa/tradehandler.cpp
new file mode 100644
index 000000000..6c48d5e6c
--- /dev/null
+++ b/src/net/tmwa/tradehandler.cpp
@@ -0,0 +1,347 @@
+/*
+ * 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/tmwa/tradehandler.h"
+
+#include "event.h"
+#include "inventory.h"
+#include "item.h"
+#include "localplayer.h"
+#include "log.h"
+#include "playerinfo.h"
+#include "playerrelations.h"
+
+#include "gui/confirmdialog.h"
+#include "gui/trade.h"
+
+#include "net/inventoryhandler.h"
+#include "net/messagein.h"
+#include "net/messageout.h"
+
+#include "net/tmwa/protocol.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+extern std::string tradePartnerName;
+ConfirmDialog *confirmDlg;
+
+/**
+ * Listener for request trade dialogs
+ */
+namespace
+{
+ struct RequestTradeListener : public gcn::ActionListener
+ {
+ void action(const gcn::ActionEvent &event)
+ {
+ confirmDlg = 0;
+ if (event.getId() == "ignore")
+ player_relations.ignoreTrade(tradePartnerName);
+ Net::getTradeHandler()->respond(event.getId() == "yes");
+ }
+ } listener;
+}
+
+extern Net::TradeHandler *tradeHandler;
+
+namespace TmwAthena
+{
+
+TradeHandler::TradeHandler()
+{
+ static const Uint16 _messages[] =
+ {
+ SMSG_TRADE_REQUEST,
+ SMSG_TRADE_RESPONSE,
+ SMSG_TRADE_ITEM_ADD,
+ SMSG_TRADE_ITEM_ADD_RESPONSE,
+ SMSG_TRADE_OK,
+ SMSG_TRADE_CANCEL,
+ SMSG_TRADE_COMPLETE,
+ 0
+ };
+ handledMessages = _messages;
+ tradeHandler = this;
+ confirmDlg = 0;
+}
+
+
+void TradeHandler::handleMessage(Net::MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_TRADE_REQUEST:
+ {
+ // If a trade window or request window is already open, send a
+ // trade cancel to any other trade request.
+ //
+ // Note that it would be nice if the server would prevent this
+ // situation, and that the requesting player would get a
+ // special message about the player being occupied.
+ std::string tradePartnerNameTemp = msg.readString(24);
+
+ if (player_relations.hasPermission(tradePartnerName,
+ PlayerRelation::TRADE))
+ {
+ if (PlayerInfo::isTrading() || confirmDlg)
+ {
+ Net::getTradeHandler()->respond(false);
+ break;
+ }
+
+ tradePartnerName = tradePartnerNameTemp;
+ PlayerInfo::setTrading(true);
+ if (tradeWindow)
+ {
+ if (tradePartnerName.empty()
+ || tradeWindow->getAutoTradeNick()
+ != tradePartnerName)
+ {
+ tradeWindow->clear();
+ confirmDlg = new ConfirmDialog(
+ _("Request for Trade"),
+ strprintf(_("%s wants to trade with you, do"
+ " you accept?"), tradePartnerName.c_str()),
+ true);
+ confirmDlg->addActionListener(&listener);
+ }
+ else
+ {
+ Net::getTradeHandler()->respond(true);
+ }
+ }
+ }
+ else
+ {
+ Net::getTradeHandler()->respond(false);
+ break;
+ }
+ }
+ break;
+
+ case SMSG_TRADE_RESPONSE:
+ switch (msg.readInt8())
+ {
+ case 0: // Too far away
+ SERVER_NOTICE(_("Trading isn't possible. Trade "
+ "partner is too far away."))
+ break;
+ case 1: // Character doesn't exist
+ SERVER_NOTICE(_("Trading isn't possible. Character "
+ "doesn't exist."))
+ break;
+ case 2: // Invite request check failed...
+ SERVER_NOTICE(_("Trade cancelled due to an unknown "
+ "reason."))
+ break;
+ case 3: // Trade accepted
+ if (tradeWindow)
+ {
+ tradeWindow->reset();
+ tradeWindow->setCaption(strprintf(
+ _("Trade: You and %s"),
+ tradePartnerName.c_str()));
+ tradeWindow->initTrade(tradePartnerName);
+ tradeWindow->setVisible(true);
+ }
+ break;
+ case 4: // Trade cancelled
+ if (player_relations.hasPermission(tradePartnerName,
+ PlayerRelation::SPEECH_LOG))
+ {
+ SERVER_NOTICE(strprintf(_("Trade with %s cancelled."),
+ tradePartnerName.c_str()))
+ }
+ // otherwise ignore silently
+
+ if (tradeWindow)
+ {
+ tradeWindow->setVisible(false);
+// tradeWindow->clear();
+ }
+ PlayerInfo::setTrading(false);
+ break;
+ default: // Shouldn't happen as well, but to be sure
+ SERVER_NOTICE(_("Unhandled trade cancel packet."))
+ if (tradeWindow)
+ tradeWindow->clear();
+ break;
+ }
+ break;
+
+ case SMSG_TRADE_ITEM_ADD:
+ {
+ int amount = msg.readInt32();
+ int type = msg.readInt16();
+ msg.readInt8(); // identified flag
+ msg.readInt8(); // attribute
+ msg.readInt8(); // refine
+ msg.skip(8); // card (4 shorts)
+
+ // TODO: handle also identified, etc
+ if (tradeWindow)
+ {
+ if (type == 0)
+ tradeWindow->setMoney(amount);
+ else
+ tradeWindow->addItem(type, false, amount, false);
+ }
+ }
+ break;
+
+ case SMSG_TRADE_ITEM_ADD_RESPONSE:
+ // Trade: New Item add response (was 0x00ea, now 01b1)
+ {
+ const int index = msg.readInt16() - INVENTORY_OFFSET;
+ Item *item = PlayerInfo::getInventory()->getItem(index);
+ if (!item)
+ {
+ if (tradeWindow)
+ tradeWindow->receivedOk(true);
+ return;
+ }
+ int quantity = msg.readInt16();
+
+ int res = msg.readInt8();
+ switch (res)
+ {
+ case 0:
+ // Successfully added item
+ if (item->isEquipment() && item->isEquipped())
+ Net::getInventoryHandler()->unequipItem(item);
+
+ if (tradeWindow)
+ {
+ tradeWindow->addItem(item->getId(), true, quantity,
+ item->isEquipment());
+ }
+ item->increaseQuantity(-quantity);
+ break;
+ case 1:
+ // Add item failed - player overweighted
+ SERVER_NOTICE(_("Failed adding item. Trade "
+ "partner is over weighted."))
+ break;
+ case 2:
+ // Add item failed - player has no free slot
+ SERVER_NOTICE(_("Failed adding item. Trade "
+ "partner has no free slot."))
+ break;
+ default:
+ SERVER_NOTICE(_("Failed adding item for "
+ "unknown reason."))
+ logger->log("QQQ SMSG_TRADE_ITEM_ADD_RESPONSE: "
+ + toString(res));
+ break;
+ }
+ }
+ break;
+
+ case SMSG_TRADE_OK:
+ // 0 means ok from myself, 1 means ok from other;
+ if (tradeWindow)
+ tradeWindow->receivedOk(msg.readInt8() == 0);
+ else
+ msg.readInt8();
+ break;
+
+ case SMSG_TRADE_CANCEL:
+ SERVER_NOTICE(_("Trade canceled."))
+ if (tradeWindow)
+ {
+ tradeWindow->setVisible(false);
+ tradeWindow->reset();
+ }
+ PlayerInfo::setTrading(false);
+ break;
+
+ case SMSG_TRADE_COMPLETE:
+ SERVER_NOTICE(_("Trade completed."))
+ if (tradeWindow)
+ {
+ tradeWindow->setVisible(false);
+ tradeWindow->reset();
+ }
+ PlayerInfo::setTrading(false);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void TradeHandler::request(Being *being)
+{
+ if (!being)
+ return;
+
+ MessageOut outMsg(CMSG_TRADE_REQUEST);
+ outMsg.writeInt32(being->getId());
+}
+
+void TradeHandler::respond(bool accept)
+{
+ if (!accept)
+ PlayerInfo::setTrading(false);
+
+ MessageOut outMsg(CMSG_TRADE_RESPONSE);
+ outMsg.writeInt8(accept ? 3 : 4);
+}
+
+void TradeHandler::addItem(Item *item, int amount)
+{
+ if (!item)
+ return;
+
+ MessageOut outMsg(CMSG_TRADE_ITEM_ADD_REQUEST);
+ outMsg.writeInt16(item->getInvIndex() + INVENTORY_OFFSET);
+ outMsg.writeInt32(amount);
+}
+
+void TradeHandler::removeItem(int slotNum _UNUSED_, int amount _UNUSED_)
+{
+ // TODO
+}
+
+void TradeHandler::setMoney(int amount)
+{
+ MessageOut outMsg(CMSG_TRADE_ITEM_ADD_REQUEST);
+ outMsg.writeInt16(0);
+ outMsg.writeInt32(amount);
+}
+
+void TradeHandler::confirm()
+{
+ MessageOut outMsg(CMSG_TRADE_ADD_COMPLETE);
+}
+
+void TradeHandler::finish()
+{
+ MessageOut outMsg(CMSG_TRADE_OK);
+}
+
+void TradeHandler::cancel()
+{
+ MessageOut outMsg(CMSG_TRADE_CANCEL_REQUEST);
+}
+
+} // namespace TmwAthena
diff --git a/src/net/tmwa/tradehandler.h b/src/net/tmwa/tradehandler.h
new file mode 100644
index 000000000..39364e52d
--- /dev/null
+++ b/src/net/tmwa/tradehandler.h
@@ -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/>.
+ */
+
+#ifndef NET_TA_TRADEHANDLER_H
+#define NET_TA_TRADEHANDLER_H
+
+#include "net/net.h"
+#include "net/tradehandler.h"
+
+#include "net/tmwa/messagehandler.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+namespace TmwAthena
+{
+
+class TradeHandler : public MessageHandler, public Net::TradeHandler
+{
+ public:
+ TradeHandler();
+
+ void handleMessage(Net::MessageIn &msg);
+
+ 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();
+};
+
+} // namespace TmwAthena
+
+#endif // NET_TA_TRADEHANDLER_H
diff --git a/src/net/tradehandler.h b/src/net/tradehandler.h
new file mode 100644
index 000000000..d433cacbf
--- /dev/null
+++ b/src/net/tradehandler.h
@@ -0,0 +1,67 @@
+/*
+ * 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 TRADEHANDLER_H
+#define TRADEHANDLER_H
+
+#include "being.h"
+
+#include <iosfwd>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+namespace Net
+{
+class TradeHandler
+{
+ public:
+ virtual void request(Being *being _UNUSED_)
+ { }
+// virtual ~TradeHandler() {}
+
+ virtual void respond(bool accept _UNUSED_)
+ { }
+
+ virtual void addItem(Item *item _UNUSED_, int amount _UNUSED_)
+ { }
+
+ virtual void removeItem(int slotNum _UNUSED_, int amount _UNUSED_)
+ { }
+
+ virtual void setMoney(int amount _UNUSED_)
+ { }
+
+ virtual void confirm()
+ { }
+
+ virtual void finish()
+ { }
+
+ virtual void cancel()
+ { }
+};
+}
+
+#endif // TRADEHANDLER_H
diff --git a/src/net/worldinfo.h b/src/net/worldinfo.h
new file mode 100644
index 000000000..a92a5735e
--- /dev/null
+++ b/src/net/worldinfo.h
@@ -0,0 +1,39 @@
+/*
+ * 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 WORLD_INFO_H
+#define WORLD_INFO_H
+
+#include <string>
+#include <vector>
+
+struct WorldInfo
+{
+ int address;
+ std::string name;
+ short port;
+ short online_users;
+ std::string updateHost;
+};
+
+typedef std::vector<WorldInfo*> Worlds;
+
+#endif // WORLD_INFO_H