summaryrefslogtreecommitdiff
path: root/src/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/net')
-rw-r--r--src/net/accountserver/account.cpp107
-rw-r--r--src/net/accountserver/account.h54
-rw-r--r--src/net/accountserver/accountserver.cpp81
-rw-r--r--src/net/accountserver/accountserver.h47
-rw-r--r--src/net/accountserver/internal.cpp32
-rw-r--r--src/net/accountserver/internal.h35
-rw-r--r--src/net/beinghandler.cpp753
-rw-r--r--src/net/beinghandler.h26
-rw-r--r--src/net/buysellhandler.cpp111
-rw-r--r--src/net/buysellhandler.h2
-rw-r--r--src/net/charserverhandler.cpp310
-rw-r--r--src/net/charserverhandler.h37
-rw-r--r--src/net/chathandler.cpp318
-rw-r--r--src/net/chathandler.h53
-rw-r--r--src/net/chatserver/chatserver.cpp146
-rw-r--r--src/net/chatserver/chatserver.h60
-rw-r--r--src/net/chatserver/guild.cpp95
-rw-r--r--src/net/chatserver/guild.h69
-rw-r--r--src/net/chatserver/internal.cpp32
-rw-r--r--src/net/chatserver/internal.h35
-rw-r--r--src/net/chatserver/party.cpp70
-rw-r--r--src/net/chatserver/party.h57
-rw-r--r--src/net/connection.cpp103
-rw-r--r--src/net/connection.h80
-rw-r--r--src/net/ea/beinghandler.cpp542
-rw-r--r--src/net/ea/beinghandler.h39
-rw-r--r--src/net/ea/buysellhandler.cpp133
-rw-r--r--src/net/ea/buysellhandler.h35
-rw-r--r--src/net/ea/charserverhandler.cpp235
-rw-r--r--src/net/ea/charserverhandler.h65
-rw-r--r--src/net/ea/chathandler.cpp175
-rw-r--r--src/net/ea/chathandler.h35
-rw-r--r--src/net/ea/equipmenthandler.cpp (renamed from src/net/equipmenthandler.cpp)58
-rw-r--r--src/net/ea/equipmenthandler.h (renamed from src/net/equipmenthandler.h)4
-rw-r--r--src/net/ea/inventoryhandler.cpp227
-rw-r--r--src/net/ea/inventoryhandler.h35
-rw-r--r--src/net/ea/loginhandler.cpp158
-rw-r--r--src/net/ea/loginhandler.h45
-rw-r--r--src/net/ea/maploginhandler.cpp (renamed from src/net/maploginhandler.cpp)26
-rw-r--r--src/net/ea/maploginhandler.h (renamed from src/net/maploginhandler.h)4
-rw-r--r--src/net/ea/network.cpp436
-rw-r--r--src/net/ea/network.h118
-rw-r--r--src/net/ea/npchandler.cpp111
-rw-r--r--src/net/ea/npchandler.h35
-rw-r--r--src/net/ea/partyhandler.cpp122
-rw-r--r--src/net/ea/partyhandler.h39
-rw-r--r--src/net/ea/playerhandler.cpp417
-rw-r--r--src/net/ea/playerhandler.h35
-rw-r--r--src/net/ea/protocol.cpp (renamed from src/net/protocol.cpp)0
-rw-r--r--src/net/ea/protocol.h162
-rw-r--r--src/net/ea/skillhandler.cpp (renamed from src/net/skillhandler.cpp)42
-rw-r--r--src/net/ea/skillhandler.h (renamed from src/net/skillhandler.h)4
-rw-r--r--src/net/ea/tradehandler.cpp220
-rw-r--r--src/net/ea/tradehandler.h37
-rw-r--r--src/net/effecthandler.cpp58
-rw-r--r--src/net/effecthandler.h38
-rw-r--r--src/net/gameserver/gameserver.cpp49
-rw-r--r--src/net/gameserver/gameserver.h39
-rw-r--r--src/net/gameserver/internal.cpp32
-rw-r--r--src/net/gameserver/internal.h35
-rw-r--r--src/net/gameserver/player.cpp202
-rw-r--r--src/net/gameserver/player.h71
-rw-r--r--src/net/guildhandler.cpp240
-rw-r--r--src/net/guildhandler.h40
-rw-r--r--src/net/internal.cpp27
-rw-r--r--src/net/internal.h30
-rw-r--r--src/net/inventoryhandler.cpp212
-rw-r--r--src/net/inventoryhandler.h2
-rw-r--r--src/net/itemhandler.cpp49
-rw-r--r--src/net/itemhandler.h2
-rw-r--r--src/net/loginhandler.cpp289
-rw-r--r--src/net/loginhandler.h28
-rw-r--r--src/net/logouthandler.cpp215
-rw-r--r--src/net/logouthandler.h62
-rw-r--r--src/net/messagehandler.cpp16
-rw-r--r--src/net/messagehandler.h9
-rw-r--r--src/net/messagein.cpp89
-rw-r--r--src/net/messagein.h37
-rw-r--r--src/net/messageout.cpp106
-rw-r--r--src/net/messageout.h43
-rw-r--r--src/net/network.cpp435
-rw-r--r--src/net/network.h143
-rw-r--r--src/net/npchandler.cpp103
-rw-r--r--src/net/npchandler.h2
-rw-r--r--src/net/partyhandler.cpp145
-rw-r--r--src/net/partyhandler.h26
-rw-r--r--src/net/playerhandler.cpp482
-rw-r--r--src/net/playerhandler.h5
-rw-r--r--src/net/protocol.h419
-rw-r--r--src/net/tradehandler.cpp217
-rw-r--r--src/net/tradehandler.h22
91 files changed, 7819 insertions, 2507 deletions
diff --git a/src/net/accountserver/account.cpp b/src/net/accountserver/account.cpp
new file mode 100644
index 00000000..f734c4eb
--- /dev/null
+++ b/src/net/accountserver/account.cpp
@@ -0,0 +1,107 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "account.h"
+
+#include <string>
+
+#include "internal.h"
+
+#include "../connection.h"
+#include "../messageout.h"
+#include "../protocol.h"
+
+#include "../../utils/sha256.h"
+
+void Net::AccountServer::Account::createCharacter(
+ const std::string &name, char hairStyle, char hairColor, char gender,
+ short strength, short agility, short vitality,
+ short intelligence, short dexterity, short willpower)
+{
+ MessageOut msg(PAMSG_CHAR_CREATE);
+
+ msg.writeString(name);
+ msg.writeInt8(hairStyle);
+ msg.writeInt8(hairColor);
+ msg.writeInt8(gender);
+ msg.writeInt16(strength);
+ msg.writeInt16(agility);
+ msg.writeInt16(vitality);
+ msg.writeInt16(intelligence);
+ msg.writeInt16(dexterity);
+ msg.writeInt16(willpower);
+
+ Net::AccountServer::connection->send(msg);
+}
+
+void Net::AccountServer::Account::deleteCharacter(char slot)
+{
+ MessageOut msg(PAMSG_CHAR_DELETE);
+
+ msg.writeInt8(slot);
+
+ Net::AccountServer::connection->send(msg);
+}
+
+void Net::AccountServer::Account::selectCharacter(char slot)
+{
+ MessageOut msg(PAMSG_CHAR_SELECT);
+
+ msg.writeInt8(slot);
+
+ Net::AccountServer::connection->send(msg);
+}
+
+void Net::AccountServer::Account::unregister(const std::string &username,
+ const std::string &password)
+{
+ MessageOut msg(PAMSG_UNREGISTER);
+
+ msg.writeString(username);
+ msg.writeString(sha256(username + password));
+
+ Net::AccountServer::connection->send(msg);
+}
+
+void Net::AccountServer::Account::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);
+
+ Net::AccountServer::connection->send(msg);
+}
+
+void Net::AccountServer::Account::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));
+
+ Net::AccountServer::connection->send(msg);
+}
diff --git a/src/net/accountserver/account.h b/src/net/accountserver/account.h
new file mode 100644
index 00000000..581bcb42
--- /dev/null
+++ b/src/net/accountserver/account.h
@@ -0,0 +1,54 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_NET_ACCOUNTSERVER_CHARACTER_H
+#define _TMW_NET_ACCOUNTSERVER_CHARACTER_H
+
+#include <iosfwd>
+
+namespace Net
+{
+ namespace AccountServer
+ {
+ namespace Account
+ {
+ void createCharacter(const std::string &name,
+ char hairStyle, char hairColor, char gender,
+ short strength, short agility, short vitality,
+ short intelligence, short dexterity, short willpower);
+
+ void deleteCharacter(char slot);
+
+ void selectCharacter(char slot);
+
+ void unregister(const std::string &username,
+ const std::string &password);
+
+ void changeEmail(const std::string &email);
+
+ void changePassword(const std::string &username,
+ const std::string &oldPassword,
+ const std::string &newPassword);
+ }
+ }
+}
+
+#endif
diff --git a/src/net/accountserver/accountserver.cpp b/src/net/accountserver/accountserver.cpp
new file mode 100644
index 00000000..b1ce590c
--- /dev/null
+++ b/src/net/accountserver/accountserver.cpp
@@ -0,0 +1,81 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "accountserver.h"
+
+#include <string>
+
+#include "internal.h"
+
+#include "../connection.h"
+#include "../messageout.h"
+#include "../protocol.h"
+
+#include "../../utils/sha256.h"
+
+void Net::AccountServer::login(Net::Connection *connection, int version,
+ const std::string &username, const std::string &password)
+{
+ Net::AccountServer::connection = connection;
+
+ MessageOut msg(PAMSG_LOGIN);
+
+ msg.writeInt32(version);
+ msg.writeString(username);
+ msg.writeString(sha256(username + password));
+
+ Net::AccountServer::connection->send(msg);
+}
+
+void Net::AccountServer::registerAccount(Net::Connection *connection,
+ int version, const std::string &username, const std::string &password,
+ const std::string &email)
+{
+ Net::AccountServer::connection = connection;
+
+ MessageOut msg(PAMSG_REGISTER);
+
+ msg.writeInt32(version); // client version
+ msg.writeString(username);
+ // When registering, the password and email hash is assumed by server.
+ // Hence, data can be validated safely server-side.
+ // This is the only time we send a clear password.
+ msg.writeString(password);
+ msg.writeString(email);
+
+ Net::AccountServer::connection->send(msg);
+}
+
+void Net::AccountServer::logout()
+{
+ MessageOut msg(PAMSG_LOGOUT);
+ Net::AccountServer::connection->send(msg);
+}
+
+void Net::AccountServer::reconnectAccount(Net::Connection *connection,
+ const std::string &passToken)
+{
+ Net::AccountServer::connection = connection;
+
+ MessageOut msg(PAMSG_RECONNECT);
+ msg.writeString(passToken, 32);
+ Net::AccountServer::connection->send(msg);
+}
diff --git a/src/net/accountserver/accountserver.h b/src/net/accountserver/accountserver.h
new file mode 100644
index 00000000..8e0573fc
--- /dev/null
+++ b/src/net/accountserver/accountserver.h
@@ -0,0 +1,47 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_NET_ACCOUNTSERVER_ACCOUNTSERVER_H
+#define _TMW_NET_ACCOUNTSERVER_ACCOUNTSERVER_H
+
+#include <iosfwd>
+
+namespace Net
+{
+ class Connection;
+
+ namespace AccountServer
+ {
+ void login(Net::Connection *connection, int version,
+ const std::string &username, const std::string &password);
+
+ void registerAccount(Net::Connection *connection, int version,
+ const std::string &username, const std::string &password,
+ const std::string &email);
+
+ void logout();
+
+ void reconnectAccount(Net::Connection *connection,
+ const std::string &passToken);
+ }
+}
+
+#endif
diff --git a/src/net/accountserver/internal.cpp b/src/net/accountserver/internal.cpp
new file mode 100644
index 00000000..a3be76a1
--- /dev/null
+++ b/src/net/accountserver/internal.cpp
@@ -0,0 +1,32 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "internal.h"
+
+namespace Net
+{
+ class Connection;
+
+ namespace AccountServer
+ {
+ Connection *connection = 0;
+ }
+}
diff --git a/src/net/accountserver/internal.h b/src/net/accountserver/internal.h
new file mode 100644
index 00000000..b3d64582
--- /dev/null
+++ b/src/net/accountserver/internal.h
@@ -0,0 +1,35 @@
+/*
+ * 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 _TMW_NET_ACCOUNTSERVER_INTERNAL_H
+#define _TMW_NET_ACCOUNTSERVER_INTERNAL_H
+
+namespace Net
+{
+ class Connection;
+
+ namespace AccountServer
+ {
+ extern Connection *connection;
+ }
+}
+
+#endif
diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp
index 3e746eb5..8f1fb8fd 100644
--- a/src/net/beinghandler.cpp
+++ b/src/net/beinghandler.cpp
@@ -1,542 +1,359 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <iostream>
+#include "beinghandler.h"
+
#include <SDL_types.h>
-#include "beinghandler.h"
#include "messagein.h"
#include "protocol.h"
#include "../being.h"
#include "../beingmanager.h"
-#include "../effectmanager.h"
#include "../game.h"
#include "../localplayer.h"
#include "../log.h"
+#include "../main.h"
#include "../npc.h"
-#include "../player_relations.h"
+#include "../particle.h"
+#include "../sound.h"
+
+#include "../gui/ok_dialog.h"
+
+#include "../utils/gettext.h"
+
+#include "gameserver/player.h"
const int EMOTION_TIME = 150; /**< Duration of emotion icon */
-BeingHandler::BeingHandler(bool enableSync):
- mSync(enableSync)
+BeingHandler::BeingHandler()
{
static const Uint16 _messages[] = {
- SMSG_BEING_VISIBLE,
- SMSG_BEING_MOVE,
- SMSG_BEING_MOVE2,
- SMSG_BEING_REMOVE,
- SMSG_BEING_ACTION,
- SMSG_BEING_SELFEFFECT,
- SMSG_BEING_EMOTION,
- SMSG_BEING_CHANGE_LOOKS,
- SMSG_BEING_CHANGE_LOOKS2,
- SMSG_BEING_NAME_RESPONSE,
- SMSG_PLAYER_UPDATE_1,
- SMSG_PLAYER_UPDATE_2,
- SMSG_PLAYER_MOVE,
- SMSG_PLAYER_STOP,
- SMSG_PLAYER_MOVE_TO_ATTACK,
- 0x0119,
- 0x0196,
+ 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;
}
-void BeingHandler::handleMessage(MessageIn *msg)
+void BeingHandler::handleMessage(MessageIn &msg)
{
- Uint32 id;
- Uint16 job, speed;
- Uint16 headTop, headMid, headBottom;
- Uint16 shoes, gloves;
- Uint16 weapon, shield;
- Uint16 gmstatus;
- Sint16 param1;
- int stunMode;
- Uint32 statusEffects;
- Sint8 type;
- Uint16 status;
- Being *srcBeing, *dstBeing;
- int hairStyle, hairColor, flag;
-
- switch (msg->getId())
+ 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 = beingManager->findBeing(id);
-
- 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;
- }
-
- dstBeing = beingManager->createBeing(id, job);
- }
- else if (msg->getId() == 0x0078)
- {
- dstBeing->clearPath();
- dstBeing->mFrame = 0;
- dstBeing->mWalkTime = tick_time;
- dstBeing->setAction(Being::STAND);
- }
-
-
- // Prevent division by 0 when calculating frame
- if (speed == 0) { speed = 150; }
-
- dstBeing->setWalkSpeed(speed);
- dstBeing->mJob = job;
- hairStyle = msg->readInt16();
- dstBeing->setSprite(Being::WEAPON_SPRITE, msg->readInt16());
- headBottom = msg->readInt16();
-
- if (msg->getId() == SMSG_BEING_MOVE)
- {
- msg->readInt32(); // server tick
- }
-
- dstBeing->setSprite(Being::SHIELD_SPRITE, msg->readInt16());
- headTop = msg->readInt16();
- headMid = msg->readInt16();
- hairColor = msg->readInt16();
- shoes = msg->readInt16(); // clothes color - "abused" as shoes
- gloves = msg->readInt16(); // head dir - "abused" as gloves
- msg->readInt16(); // guild
- msg->readInt16(); // unknown
- msg->readInt16(); // unknown
- 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(Being::BOTTOMCLOTHES_SPRITE, headBottom);
- dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, headMid);
- dstBeing->setSprite(Being::HAT_SPRITE, headTop);
- dstBeing->setSprite(Being::SHOE_SPRITE, shoes);
- dstBeing->setSprite(Being::GLOVES_SPRITE, gloves);
- dstBeing->setHairStyle(hairStyle, hairColor);
-
- if (msg->getId() == SMSG_BEING_MOVE)
- {
- Uint16 srcX, srcY, dstX, dstY;
- msg->readCoordinatePair(srcX, srcY, dstX, dstY);
- dstBeing->setAction(Being::STAND);
- dstBeing->mX = srcX;
- dstBeing->mY = srcY;
- dstBeing->setDestination(dstX, dstY);
- }
- else
- {
- Uint8 dir;
- msg->readCoordinates(dstBeing->mX, dstBeing->mY, dir);
- dstBeing->setDirection(dir);
- }
-
- msg->readInt8(); // unknown
- msg->readInt8(); // unknown
- msg->readInt8(); // unknown / sit
-
- dstBeing->setStunMode(stunMode);
- dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff);
- dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
+ case GPMSG_BEING_ENTER:
+ handleBeingEnterMessage(msg);
break;
-
- case SMSG_BEING_MOVE2:
- /*
- * A simplified movement packet, used by the
- * later versions of eAthena for both mobs and
- * players
- */
- dstBeing = beingManager->findBeing(msg->readInt32());
-
- Uint16 srcX, srcY, dstX, dstY;
- msg->readCoordinatePair(srcX, srcY, dstX, dstY);
- msg->readInt32(); // Server tick
-
- /*
- * 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) {
- dstBeing->setAction(Being::STAND);
- dstBeing->mX = srcX;
- dstBeing->mY = srcY;
- dstBeing->setDestination(dstX, dstY);
- }
-
+ case GPMSG_BEING_LEAVE:
+ handleBeingLeaveMessage(msg);
break;
-
- case SMSG_BEING_REMOVE:
- // A being should be removed or has died
- dstBeing = beingManager->findBeing(msg->readInt32());
-
- if (!dstBeing)
- break;
-
- // If this is player's current target, clear it.
- if (dstBeing == player_node->getTarget())
- player_node->stopAttack();
-
- if (dstBeing == current_npc)
- current_npc->handleDeath();
-
- if (msg->readInt8() == 1)
- dstBeing->setAction(Being::DEAD);
- else
- beingManager->destroyBeing(dstBeing);
-
+ case GPMSG_BEINGS_MOVE:
+ handleBeingsMoveMessage(msg);
break;
-
- case SMSG_BEING_ACTION:
- srcBeing = beingManager->findBeing(msg->readInt32());
- dstBeing = beingManager->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 0x0a: // Critical Damage
- if (dstBeing)
- dstBeing->showCrit();
- case 0x00: // Damage
- if (dstBeing)
- dstBeing->takeDamage(param1);
- if (srcBeing)
- srcBeing->handleAttack(dstBeing, param1);
- break;
-
- case 0x02: // Sit
- if (srcBeing)
- {
- srcBeing->mFrame = 0;
- srcBeing->setAction(Being::SIT);
- }
- break;
-
- case 0x03: // Stand up
- if (srcBeing)
- {
- srcBeing->mFrame = 0;
- srcBeing->setAction(Being::STAND);
- }
- 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;
+ }
+}
- case SMSG_BEING_SELFEFFECT: {
- id = (Uint32)msg->readInt32();
- if (!beingManager->findBeing(id))
- break;
-
- int effectType = msg->readInt32();
- Being* being = beingManager->findBeing(id);
+static void handleLooks(Player *being, 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] =
+ { Being::WEAPON_SPRITE, Being::HAT_SPRITE, Being::TOPCLOTHES_SPRITE,
+ Being::BOTTOMCLOTHES_SPRITE };
- effectManager->trigger(effectType, being);
+ int mask = msg.readInt8();
- break;
+ if (mask & (1 << 7))
+ {
+ // The equipment has to be cleared first.
+ for (int i = 0; i < nb_slots; ++i)
+ {
+ being->setSprite(slots[i], 0);
}
+ }
- case SMSG_BEING_EMOTION:
- if (!(dstBeing = beingManager->findBeing(msg->readInt32())))
- {
- break;
- }
-
- if (player_relations.hasPermission(dstBeing, PlayerRelation::EMOTE))
- dstBeing->setEmote(msg->readInt8(), EMOTION_TIME);
-
- break;
+ // 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);
+ }
+}
- case SMSG_BEING_CHANGE_LOOKS:
- case SMSG_BEING_CHANGE_LOOKS2:
+void BeingHandler::handleBeingEnterMessage(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_PLAYER:
{
- /*
- * 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 = beingManager->findBeing(msg->readInt32())))
+ std::string name = msg.readString();
+ if (player_node->getName() == name)
{
- break;
+ being = player_node;
+ being->setId(id);
}
-
- 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();
- }
-
- switch (type) {
- case 1: // eAthena LOOK_HAIR
- dstBeing->setHairStyle(id, -1);
- break;
- case 2: // Weapon ID in id, Shield ID in id2
- dstBeing->setSprite(Being::WEAPON_SPRITE, id);
- dstBeing->setSprite(Being::SHIELD_SPRITE, id2);
- break;
- case 3: // Change lower headgear for eAthena, pants for us
- dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, id);
- break;
- case 4: // Change upper headgear for eAthena, hat for us
- dstBeing->setSprite(Being::HAT_SPRITE, id);
- break;
- case 5: // Change middle headgear for eathena, armor for us
- dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, id);
- break;
- case 6: // eAthena LOOK_HAIR_COLOR
- dstBeing->setHairStyle(-1, id);
- break;
- case 8: // eAthena LOOK_SHIELD
- dstBeing->setSprite(Being::SHIELD_SPRITE, id);
- break;
- case 9: // eAthena LOOK_SHOES
- dstBeing->setSprite(Being::SHOE_SPRITE, id);
- break;
- case 10: // LOOK_GLOVES
- dstBeing->setSprite(Being::GLOVES_SPRITE, id);
- break;
- case 11: // LOOK_CAPE
- dstBeing->setSprite(Being::CAPE_SPRITE, id);
- break;
- case 12:
- dstBeing->setSprite(Being::MISC1_SPRITE, id);
- break;
- case 13:
- dstBeing->setSprite(Being::MISC2_SPRITE, id);
- break;
- default:
- logger->log("SMSG_BEING_CHANGE_LOOKS: unsupported type: "
- "%d, id: %d", type, id);
- break;
- }
- }
- break;
-
- case SMSG_BEING_NAME_RESPONSE:
- if ((dstBeing = beingManager->findBeing(msg->readInt32())))
+ else
{
- dstBeing->setName(msg->readString(24));
+ being = beingManager->createBeing(id, type, 0);
+ being->setName(name);
}
- break;
+ Player *p = static_cast< Player * >(being);
+ int hs = msg.readInt8(), hc = msg.readInt8();
+ p->setHairStyle(hs, hc);
+ p->setGender(msg.readInt8() == GENDER_MALE ?
+ GENDER_MALE : GENDER_FEMALE);
+ handleLooks(p, msg);
+ } break;
+
+ case OBJECT_MONSTER:
+ case OBJECT_NPC:
+ {
+ int subtype = msg.readInt16();
+ being = beingManager->createBeing(id, type, subtype);
+ std::string name = msg.readString();
+ if (name.length() > 0) being->setName(name);
+ } break;
+
+ default:
+ return;
+ }
- case SMSG_PLAYER_UPDATE_1:
- case SMSG_PLAYER_UPDATE_2:
- case SMSG_PLAYER_MOVE:
- // 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 = beingManager->findBeing(id);
-
- if (!dstBeing)
- {
- dstBeing = beingManager->createBeing(id, job);
- }
+ being->setPosition(px, py);
+ being->setDestination(px, py);
+ being->setAction(action);
+}
- dstBeing->setWalkSpeed(speed);
- dstBeing->mJob = job;
- hairStyle = msg->readInt16();
- weapon = msg->readInt16();
- shield = msg->readInt16();
- headBottom = msg->readInt16();
+void BeingHandler::handleBeingLeaveMessage(MessageIn &msg)
+{
+ Being *being = beingManager->findBeing(msg.readInt16());
+ if (!being) return;
- if (msg->getId() == SMSG_PLAYER_MOVE)
- {
- msg->readInt32(); // server tick
- }
+ beingManager->destroyBeing(being);
+}
- headTop = msg->readInt16();
- headMid = msg->readInt16();
- hairColor = msg->readInt16();
- msg->readInt16(); // clothes color - Aethyra-"abused" as shoes, we ignore it
- msg->readInt16(); // head dir - Aethyra-"abused" as gloves, we ignore it
- msg->readInt32(); // 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(Being::WEAPON_SPRITE, weapon);
- dstBeing->setSprite(Being::SHIELD_SPRITE, shield);
- dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, headBottom);
- dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, headMid);
- dstBeing->setSprite(Being::HAT_SPRITE, headTop);
- //dstBeing->setSprite(Being::CAPE_SPRITE, cape);
- //dstBeing->setSprite(Being::MISC1_SPRITE, misc1);
- //dstBeing->setSprite(Being::MISC2_SPRITE, misc2);
- dstBeing->setHairStyle(hairStyle, hairColor);
-
- if (msg->getId() == SMSG_PLAYER_MOVE)
- {
- Uint16 srcX, srcY, dstX, dstY;
- msg->readCoordinatePair(srcX, srcY, dstX, dstY);
- dstBeing->mX = srcX;
- dstBeing->mY = srcY;
- dstBeing->setDestination(dstX, dstY);
- }
- else
+void BeingHandler::handleBeingsMoveMessage(MessageIn &msg)
+{
+ while (msg.getUnreadLength())
+ {
+ int id = msg.readInt16();
+ int flags = msg.readInt8();
+ Being *being = beingManager->findBeing(id);
+ int sx = 0;
+ int sy = 0;
+ int dx = 0;
+ int dy = 0;
+ int speed = 0;
+
+ printf("handleBeingsMoveMessage for %p (%s | %s)\n",
+ (void*) being,
+ (flags & MOVING_POSITION) ? "pos" : "",
+ (flags & MOVING_DESTINATION) ? "dest" : "");
+
+ if (flags & MOVING_POSITION)
+ {
+ Uint16 sx2, sy2;
+ msg.readCoordinates(sx2, sy2);
+ sx = sx2 * 32 + 16;
+ sy = sy2 * 32 + 16;
+ speed = msg.readInt8();
+ }
+ if (flags & MOVING_DESTINATION)
+ {
+ dx = msg.readInt16();
+ dy = msg.readInt16();
+ if (!(flags & MOVING_POSITION))
{
- Uint8 dir;
- msg->readCoordinates(dstBeing->mX, dstBeing->mY, dir);
- dstBeing->setDirection(dir);
+ sx = dx;
+ sy = dy;
}
+ }
+ if (!being || !(flags & (MOVING_POSITION | MOVING_DESTINATION)))
+ {
+ continue;
+ }
+ if (speed)
+ {
+ /* The speed on the server is the cost of moving from one tile to
+ * the next. Beings get 1000 cost units per second. The speed is
+ * transferred as devided by 10, so that slower speeds fit in a
+ * byte. Here we convert the speed to pixels per second.
+ */
+ const float tilesPerSecond = 100.0f / speed;
+ being->setWalkSpeed((int) (tilesPerSecond * 32));
+ }
- gmstatus = msg->readInt16();
- if (gmstatus & 0x80)
- dstBeing->setGM();
+ // Ignore messages from the server for the local player
+ if (being == player_node)
+ continue;
- if (msg->getId() == SMSG_PLAYER_UPDATE_1)
- {
- switch (msg->readInt8())
- {
- case 1:
- if (dstBeing->getType() != Being::NPC)
- dstBeing->setAction(Being::DEAD);
- break;
-
- case 2:
- dstBeing->setAction(Being::SIT);
- break;
- }
- }
- else if (msg->getId() == SMSG_PLAYER_MOVE)
- {
- msg->readInt8(); // unknown
- }
+ // If being is a player, and he only moves a little, its ok to be a little out of sync
+ if (being->getType() == Being::PLAYER && abs(being->getPixelX() - dx) +
+ abs(being->getPixelY() - dy) < 2 * 32 &&
+ (dx != being->getDestination().x && dy != being->getDestination().y))
+ {
+ being->setDestination(being->getPixelX(),being->getPixelY());
+ continue;
+ }
+ if (abs(being->getPixelX() - sx) +
+ abs(being->getPixelY() - sy) > 10 * 32)
+ {
+ // Too large a desynchronization.
+ being->setPosition(sx, sy);
+ being->setDestination(dx, dy);
+ }
+ else if (!(flags & MOVING_POSITION))
+ {
+ being->setDestination(dx, dy);
+ }
+ else if (!(flags & MOVING_DESTINATION))
+ {
+ being->adjustCourse(sx, sy);
+ }
+ else
+ {
+ being->adjustCourse(sx, sy, dx, dy);
+ }
+ }
+}
- msg->readInt8(); // Lv
- msg->readInt8(); // unknown
+void BeingHandler::handleBeingAttackMessage(MessageIn &msg)
+{
+ Being *being = beingManager->findBeing(msg.readInt16());
+ int direction = msg.readInt8();
+ int attackType = msg.readInt8();
- dstBeing->mWalkTime = tick_time;
- dstBeing->mFrame = 0;
+ if (!being) return;
- dstBeing->setStunMode(stunMode);
- dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff);
- dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
- break;
+ 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;
+ }
- 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.
- */
+ being->setAction(Being::ATTACK, attackType);
+}
- id = msg->readInt32();
- if (mSync || id != player_node->getId()) {
- dstBeing = beingManager->findBeing(id);
- if (dstBeing) {
- dstBeing->mX = msg->readInt16();
- dstBeing->mY = msg->readInt16();
- if (dstBeing->mAction == Being::WALK) {
- dstBeing->mFrame = 0;
- dstBeing->setAction(Being::STAND);
- }
- }
- }
- break;
+void BeingHandler::handleBeingsDamageMessage(MessageIn &msg)
+{
+ while (msg.getUnreadLength())
+ {
+ Being *being = beingManager->findBeing(msg.readInt16());
+ int damage = msg.readInt16();
+ if (being)
+ {
+ being->takeDamage(damage);
+ }
+ }
+}
- 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...
- */
- break;
+void BeingHandler::handleBeingActionChangeMessage(MessageIn &msg)
+{
+ Being* being = beingManager->findBeing(msg.readInt16());
+ Being::Action action = (Being::Action) msg.readInt8();
+ if (!being) return;
- case 0x0119:
- // Change in players' flags
- id = msg->readInt32();
- dstBeing = beingManager->findBeing(id);
- stunMode = msg->readInt16();
- statusEffects = msg->readInt16();
- statusEffects |= ((Uint32) msg->readInt16()) << 16;
- msg->readInt8();
-
- if (dstBeing) {
- dstBeing->setStunMode(stunMode);
- dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff);
- dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
- }
- break;
+ 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(_(" Press OK to respawn"));
+ OkDialog *dlg = new OkDialog(_("You died"), message);
+ dlg->addActionListener(&(Net::GameServer::Player::respawnListener));
+ }
+}
- case 0x0196:
- // Status change
- status = msg->readInt16();
- id = msg->readInt32();
- flag = msg->readInt8(); // 0: stop, 1: start
+void BeingHandler::handleBeingLooksChangeMessage(MessageIn &msg)
+{
+ Being *being = beingManager->findBeing(msg.readInt16());
+ if (!being || being->getType() != Being::PLAYER) return;
+ Player * player = static_cast< Player * >(being);
+ handleLooks(player, msg);
+ if (msg.getUnreadLength())
+ {
+ int style = msg.readInt16();
+ int color = msg.readInt16();
+ player->setHairStyle(style, color);
+ player->setGender((Gender)msg.readInt16());
+ }
+}
- dstBeing = beingManager->findBeing(id);
- if (dstBeing)
- dstBeing->setStatusEffect(status, flag);
- break;
+void BeingHandler::handleBeingDirChangeMessage(MessageIn &msg)
+{
+ Being *being = beingManager->findBeing(msg.readInt16());
+ if (!being) return;
+ int data = msg.readInt8();
+ 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;
}
}
+
diff --git a/src/net/beinghandler.h b/src/net/beinghandler.h
index 54b82075..b02728b4 100644
--- a/src/net/beinghandler.h
+++ b/src/net/beinghandler.h
@@ -1,39 +1,45 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef NET_BEINGHANDLER_H
-#define NET_BEINGHANDLER_H
+#ifndef _TMW_NET_BEINGHANDLER_H
+#define _TMW_NET_BEINGHANDLER_H
#include "messagehandler.h"
class BeingHandler : public MessageHandler
{
public:
- BeingHandler(bool enableSync);
+ BeingHandler();
- void handleMessage(MessageIn *msg);
+ void handleMessage(MessageIn &msg);
private:
- // Should we honor server "Stop Walking" packets
- bool mSync;
+ void handleBeingAttackMessage(MessageIn &msg);
+ void handleBeingEnterMessage(MessageIn &msg);
+ void handleBeingLeaveMessage(MessageIn &msg);
+ void handleBeingsMoveMessage(MessageIn &msg);
+ void handleBeingsDamageMessage(MessageIn &msg);
+ void handleBeingActionChangeMessage(MessageIn &msg);
+ void handleBeingLooksChangeMessage(MessageIn &msg);
+ void handleBeingDirChangeMessage(MessageIn &msg);
};
#endif
diff --git a/src/net/buysellhandler.cpp b/src/net/buysellhandler.cpp
index a2442d70..a551f213 100644
--- a/src/net/buysellhandler.cpp
+++ b/src/net/buysellhandler.cpp
@@ -1,32 +1,32 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include "buysellhandler.h"
+
#include <SDL_types.h>
-#include "buysellhandler.h"
#include "messagein.h"
#include "protocol.h"
#include "../beingmanager.h"
-#include "../inventory.h"
#include "../item.h"
#include "../localplayer.h"
#include "../npc.h"
@@ -35,98 +35,57 @@
#include "../gui/chat.h"
#include "../gui/sell.h"
-#include "../utils/gettext.h"
-
extern BuyDialog *buyDialog;
-extern Window *buySellDialog;
extern SellDialog *sellDialog;
+extern Window *buySellDialog;
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,
+ GPMSG_NPC_BUY,
+ GPMSG_NPC_SELL,
0
};
handledMessages = _messages;
}
-void BuySellHandler::handleMessage(MessageIn *msg)
+void BuySellHandler::handleMessage(MessageIn &msg)
{
- int n_items;
- switch (msg->getId())
+ Being *being = beingManager->findBeing(msg.readInt16());
+ if (!being || being->getType() != Being::NPC)
{
- case SMSG_NPC_BUY_SELL_CHOICE:
- buyDialog->setVisible(false);
- buyDialog->reset();
- sellDialog->setVisible(false);
- sellDialog->reset();
- buySellDialog->setVisible(true);
- current_npc = dynamic_cast<NPC*>(beingManager->findBeing(msg->readInt32()));
- break;
+ return;
+ }
+
+ current_npc = static_cast< NPC * >(being);
- case SMSG_NPC_BUY:
- msg->readInt16(); // length
- n_items = (msg->getLength() - 4) / 11;
+ switch (msg.getId())
+ {
+ case GPMSG_NPC_BUY:
buyDialog->reset();
- buyDialog->setMoney(player_node->mGp);
+ buyDialog->setMoney(player_node->getMoney());
buyDialog->setVisible(true);
- for (int k = 0; k < n_items; k++)
+ while (msg.getUnreadLength())
{
- Sint32 value = msg->readInt32();
- msg->readInt32(); // DCvalue
- msg->readInt8(); // type
- Sint16 itemId = msg->readInt16();
- buyDialog->addItem(itemId, value);
+ int itemId = msg.readInt16();
+ int amount = msg.readInt16();
+ int value = msg.readInt16();
+ buyDialog->addItem(itemId, amount, value);
}
break;
- case SMSG_NPC_SELL:
- msg->readInt16(); // length
- n_items = (msg->getLength() - 4) / 10;
- if (n_items > 0) {
- sellDialog->setMoney(player_node->mGp);
- sellDialog->reset();
- sellDialog->setVisible(true);
-
- for (int k = 0; k < n_items; k++)
- {
- Sint16 index = msg->readInt16();
- Sint32 value = msg->readInt32();
- msg->readInt32(); // OCvalue
-
- Item *item = player_node->getInventory()->getItem(index);
- if (item && !(item->isEquipped())) {
- sellDialog->addItem(item, value);
- }
- }
- }
- else {
- chatWindow->chatLog(_("Nothing to sell"), BY_SERVER);
- if (current_npc) current_npc->handleDeath();
- }
- break;
-
- case SMSG_NPC_BUY_RESPONSE:
- if (msg->readInt8() == 0) {
- chatWindow->chatLog(_("Thanks for buying"), BY_SERVER);
- } else {
- // Reset player money since buy dialog already assumed purchase
- // would go fine
- buyDialog->setMoney(player_node->mGp);
- chatWindow->chatLog(_("Unable to buy"), BY_SERVER);
- }
- break;
+ case GPMSG_NPC_SELL:
+ sellDialog->setMoney(player_node->getMoney());
+ sellDialog->reset();
+ sellDialog->setVisible(true);
- case SMSG_NPC_SELL_RESPONSE:
- if (msg->readInt8() == 0) {
- chatWindow->chatLog(_("Thanks for selling"), BY_SERVER);
- } else {
- chatWindow->chatLog(_("Unable to sell"), BY_SERVER);
+ while (msg.getUnreadLength())
+ {
+ int itemId = msg.readInt16();
+ int amount = msg.readInt16();
+ int value = msg.readInt16();
+ sellDialog->addItem(itemId, amount, value);
}
break;
}
diff --git a/src/net/buysellhandler.h b/src/net/buysellhandler.h
index 0ede7b48..719b76d9 100644
--- a/src/net/buysellhandler.h
+++ b/src/net/buysellhandler.h
@@ -29,7 +29,7 @@ class BuySellHandler : public MessageHandler
public:
BuySellHandler();
- void handleMessage(MessageIn *msg);
+ void handleMessage(MessageIn &msg);
};
#endif
diff --git a/src/net/charserverhandler.cpp b/src/net/charserverhandler.cpp
index 8e743bf0..5905dba0 100644
--- a/src/net/charserverhandler.cpp
+++ b/src/net/charserverhandler.cpp
@@ -1,25 +1,27 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "charserverhandler.h"
+
+#include "connection.h"
#include "messagein.h"
#include "protocol.h"
@@ -29,107 +31,73 @@
#include "../logindata.h"
#include "../main.h"
-#include "../gui/char_select.h"
#include "../gui/ok_dialog.h"
+#include "../gui/char_select.h"
-#include "../utils/gettext.h"
-#include "../utils/stringutils.h"
+extern Net::Connection *gameServerConnection;
+extern Net::Connection *chatServerConnection;
CharServerHandler::CharServerHandler():
mCharCreateDialog(0)
{
static const Uint16 _messages[] = {
- SMSG_CONNECTION_PROBLEM,
- 0x006b,
- 0x006c,
- 0x006d,
- 0x006e,
- 0x006f,
- 0x0070,
- 0x0071,
+ APMSG_CHAR_CREATE_RESPONSE,
+ APMSG_CHAR_DELETE_RESPONSE,
+ APMSG_CHAR_INFO,
+ APMSG_CHAR_SELECT_RESPONSE,
0
};
handledMessages = _messages;
}
-void CharServerHandler::handleMessage(MessageIn *msg)
+void CharServerHandler::handleMessage(MessageIn &msg)
{
- int slot, flags, code;
+ int slot;
LocalPlayer *tempPlayer;
- logger->log("CharServerHandler: Packet ID: %x, Length: %d",
- msg->getId(), msg->getLength());
- switch (msg->getId())
+ 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 = _("Map server(s) offline");
- break;
- case 2:
- 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;
- }
- state = ERROR_STATE;
+ case APMSG_CHAR_CREATE_RESPONSE:
+ handleCharCreateResponse(msg);
break;
- case 0x006b:
- msg->skip(2); // Length word
- flags = msg->readInt32(); // Aethyra extensions flags
- logger->log("Server flags are: %x", flags);
- msg->skip(16); // Unused
-
- // Derive number of characters from message length
- n_character = (msg->getLength() - 24) / 106;
-
- for (int i = 0; i < n_character; i++)
+ case APMSG_CHAR_DELETE_RESPONSE:
+ {
+ int errMsg = msg.readInt8();
+ // Character deletion successful
+ if (errMsg == ERRMSG_OK)
{
- tempPlayer = readPlayerData(*msg, slot);
- mCharInfo->select(slot);
- mCharInfo->setEntry(tempPlayer);
- logger->log("CharServer: Player: %s (%d)",
- tempPlayer->getName().c_str(), slot);
+ delete mCharInfo->getEntry();
+ mCharInfo->setEntry(0);
+ mCharInfo->unlock();
+ new OkDialog("Info", "Player deleted");
}
-
- state = CHAR_SELECT_STATE;
- break;
-
- case 0x006c:
- switch (msg->readInt8()) {
- case 0:
- errorMessage = _("Access denied");
- break;
- case 1:
- errorMessage = _("Cannot use this ID");
- break;
- default:
- errorMessage = _("Unknown failure to select character");
- break;
+ // Character deletion failed
+ else
+ {
+ std::string message = "";
+ switch (errMsg)
+ {
+ case ERRMSG_NO_LOGIN:
+ message = "Not logged in";
+ break;
+ case ERRMSG_INVALID_ARGUMENT:
+ message = "Selection out of range";
+ break;
+ default:
+ message = "Unknown error";
+ }
+ mCharInfo->unlock();
+ new OkDialog("Error", message);
}
- mCharInfo->unlock();
+ }
break;
- case 0x006d:
- tempPlayer = readPlayerData(*msg, slot);
+ case APMSG_CHAR_INFO:
+ tempPlayer = readPlayerData(msg, slot);
mCharInfo->unlock();
mCharInfo->select(slot);
mCharInfo->setEntry(tempPlayer);
- n_character++;
// Close the character create dialog
if (mCharCreateDialog)
@@ -139,97 +107,121 @@ void CharServerHandler::handleMessage(MessageIn *msg)
}
break;
- case 0x006e:
- new OkDialog(_("Error"), _("Failed to create character. Most likely"
- " the name is already taken."));
-
- if (mCharCreateDialog)
- mCharCreateDialog->unlock();
+ case APMSG_CHAR_SELECT_RESPONSE:
+ handleCharSelectResponse(msg);
break;
+ }
+}
- case 0x006f:
- delete mCharInfo->getEntry();
- mCharInfo->setEntry(0);
- mCharInfo->unlock();
- n_character--;
- new OkDialog(_("Info"), _("Player deleted"));
- break;
+void CharServerHandler::handleCharCreateResponse(MessageIn &msg)
+{
+ int errMsg = msg.readInt8();
- case 0x0070:
- mCharInfo->unlock();
- new OkDialog(_("Error"), _("Failed to delete character."));
- break;
+ // Character creation failed
+ if (errMsg != ERRMSG_OK)
+ {
+ std::string message = "";
+ switch (errMsg)
+ {
+ case ERRMSG_NO_LOGIN:
+ message = "Not logged in";
+ break;
+ case CREATE_TOO_MUCH_CHARACTERS:
+ message = "No empty slot";
+ break;
+ case ERRMSG_INVALID_ARGUMENT:
+ message = "Invalid name";
+ break;
+ case CREATE_EXISTS_NAME:
+ message = "Character's name already exists";
+ break;
+ case CREATE_INVALID_HAIRSTYLE:
+ message = "Invalid hairstyle";
+ break;
+ case CREATE_INVALID_HAIRCOLOR:
+ message = "Invalid hair color";
+ break;
+ case CREATE_INVALID_GENDER:
+ message = "Invalid gender";
+ break;
+ case CREATE_RAW_STATS_TOO_HIGH:
+ message = "Character's stats are too high";
+ break;
+ case CREATE_RAW_STATS_TOO_LOW:
+ message = "Character's stats are too low";
+ break;
+ case CREATE_RAW_STATS_EQUAL_TO_ZERO:
+ message = "One stat is zero";
+ break;
+ default:
+ message = "Unknown error";
+ break;
+ }
+ new OkDialog("Error", message);
+ }
- case 0x0071:
- player_node = mCharInfo->getEntry();
- slot = mCharInfo->getPos();
- msg->skip(4); // CharID, must be the same as player_node->charID
- map_path = msg->readString(16);
- mLoginData->hostname = ipToString(msg->readInt32());
- mLoginData->port = msg->readInt16();
- mCharInfo->unlock();
- mCharInfo->select(0);
- // Clear unselected players infos
- do
+ if (mCharCreateDialog)
+ mCharCreateDialog->unlock();
+}
+
+void CharServerHandler::handleCharSelectResponse(MessageIn &msg)
+{
+ int errMsg = msg.readInt8();
+
+ if (errMsg == ERRMSG_OK)
+ {
+ token = msg.readString(32);
+ std::string gameServer = msg.readString();
+ unsigned short gameServerPort = msg.readInt16();
+ std::string chatServer = msg.readString();
+ unsigned short chatServerPort = msg.readInt16();
+
+ logger->log("Game server: %s:%d", gameServer.c_str(), gameServerPort);
+ logger->log("Chat server: %s:%d", chatServer.c_str(), chatServerPort);
+
+ gameServerConnection->connect(gameServer, gameServerPort);
+ chatServerConnection->connect(chatServer, chatServerPort);
+
+ // Keep the selected character and delete the others
+ player_node = mCharInfo->getEntry();
+ int slot = mCharInfo->getPos();
+ mCharInfo->unlock();
+ mCharInfo->select(0);
+
+ do {
+ LocalPlayer *tmp = mCharInfo->getEntry();
+ if (tmp != player_node)
{
- LocalPlayer *tmp = mCharInfo->getEntry();
- if (tmp != player_node)
- {
- delete tmp;
- mCharInfo->setEntry(0);
- }
- mCharInfo->next();
- } while (mCharInfo->getPos());
+ delete tmp;
+ mCharInfo->setEntry(0);
+ }
+ mCharInfo->next();
+ } while (mCharInfo->getPos());
+ mCharInfo->select(slot);
- mCharInfo->select(slot);
- state = CONNECTING_STATE;
- break;
+ mCharInfo->clear(); //player_node will be deleted by ~Game
+
+ state = STATE_CONNECT_GAME;
}
}
-LocalPlayer *CharServerHandler::readPlayerData(MessageIn &msg, int &slot)
+LocalPlayer* CharServerHandler::readPlayerData(MessageIn &msg, int &slot)
{
- LocalPlayer *tempPlayer = new LocalPlayer(mLoginData->account_ID, 0, NULL);
- tempPlayer->setGender(
- (mLoginData->sex == 0) ? GENDER_FEMALE : GENDER_MALE);
-
- tempPlayer->mCharId = msg.readInt32();
- tempPlayer->setXp(msg.readInt32());
- tempPlayer->mGp = msg.readInt32();
- tempPlayer->mJobXp = msg.readInt32();
- tempPlayer->mJobLevel = msg.readInt32();
- tempPlayer->setSprite(Being::SHOE_SPRITE, msg.readInt16());
- tempPlayer->setSprite(Being::GLOVES_SPRITE, msg.readInt16());
- tempPlayer->setSprite(Being::CAPE_SPRITE, msg.readInt16());
- tempPlayer->setSprite(Being::MISC1_SPRITE, msg.readInt16());
- msg.readInt32(); // option
- msg.readInt32(); // karma
- msg.readInt32(); // manner
- msg.skip(2); // unknown
- tempPlayer->mHp = msg.readInt16();
- tempPlayer->mMaxHp = msg.readInt16();
- tempPlayer->mMp = msg.readInt16();
- tempPlayer->mMaxMp = msg.readInt16();
- msg.readInt16(); // speed
- msg.readInt16(); // class
- int hairStyle = msg.readInt16();
- Uint16 weapon = msg.readInt16();
- tempPlayer->setSprite(Being::WEAPON_SPRITE, weapon);
- tempPlayer->mLevel = msg.readInt16();
- msg.readInt16(); // skill point
- tempPlayer->setSprite(Being::BOTTOMCLOTHES_SPRITE, msg.readInt16()); // head bottom
- tempPlayer->setSprite(Being::SHIELD_SPRITE, msg.readInt16());
- tempPlayer->setSprite(Being::HAT_SPRITE, msg.readInt16()); // head option top
- tempPlayer->setSprite(Being::TOPCLOTHES_SPRITE, msg.readInt16()); // head option mid
- int hairColor = msg.readInt16();
- tempPlayer->setHairStyle(hairStyle, hairColor);
- tempPlayer->setSprite(Being::MISC2_SPRITE, msg.readInt16());
- tempPlayer->setName(msg.readString(24));
- for (int i = 0; i < 6; i++) {
- tempPlayer->mAttr[i] = msg.readInt8();
- }
+ LocalPlayer *tempPlayer = new LocalPlayer;
slot = msg.readInt8(); // character slot
- msg.readInt8(); // unknown
+ tempPlayer->setName(msg.readString());
+ tempPlayer->setGender(msg.readInt8() == GENDER_MALE ? GENDER_MALE : GENDER_FEMALE);
+ int hs = msg.readInt8(), hc = msg.readInt8();
+ tempPlayer->setHairStyle(hs, hc);
+ tempPlayer->setLevel(msg.readInt16());
+ tempPlayer->setCharacterPoints(msg.readInt16());
+ tempPlayer->setCorrectionPoints(msg.readInt16());
+ tempPlayer->setMoney(msg.readInt32());
+
+ for (int i = 0; i < 7; i++)
+ {
+ tempPlayer->setAttributeBase(i, msg.readInt8());
+ }
return tempPlayer;
}
diff --git a/src/net/charserverhandler.h b/src/net/charserverhandler.h
index 37b378f2..08ba5102 100644
--- a/src/net/charserverhandler.h
+++ b/src/net/charserverhandler.h
@@ -1,26 +1,26 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef NET_CHARSERVERHANDLER_H
-#define NET_CHARSERVERHANDLER_H
+#ifndef _TMW_NET_CHARSERVERHANDLER_H
+#define _TMW_NET_CHARSERVERHANDLER_H
#include "messagehandler.h"
@@ -31,20 +31,21 @@ class LocalPlayer;
class LoginData;
/**
- * Deals with incoming messages from the character server.
+ * Deals with incoming messages related to character selection.
*/
class CharServerHandler : public MessageHandler
{
public:
CharServerHandler();
- void handleMessage(MessageIn *msg);
+ void
+ handleMessage(MessageIn &msg);
- void setCharInfo(LockedArray<LocalPlayer*> *charInfo)
- { mCharInfo = charInfo; }
-
- void setLoginData(LoginData *loginData)
- { mLoginData = loginData; }
+ void
+ setCharInfo(LockedArray<LocalPlayer*> *charInfo)
+ {
+ mCharInfo = charInfo;
+ }
/**
* Sets the character create dialog. The handler will clean up this
@@ -55,11 +56,17 @@ class CharServerHandler : public MessageHandler
{ mCharCreateDialog = window; }
protected:
- LoginData *mLoginData;
+ void
+ handleCharCreateResponse(MessageIn &msg);
+
+ void
+ handleCharSelectResponse(MessageIn &msg);
+
LockedArray<LocalPlayer*> *mCharInfo;
CharCreateDialog *mCharCreateDialog;
- LocalPlayer* readPlayerData(MessageIn &msg, int &slot);
+ LocalPlayer*
+ readPlayerData(MessageIn &msg, int &slot);
};
#endif
diff --git a/src/net/chathandler.cpp b/src/net/chathandler.cpp
index a3ccc4fb..90af899a 100644
--- a/src/net/chathandler.cpp
+++ b/src/net/chathandler.cpp
@@ -1,175 +1,285 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include "chathandler.h"
+
#include <SDL_types.h>
#include <string>
+#include <iostream>
-#include "chathandler.h"
#include "messagein.h"
#include "protocol.h"
#include "../being.h"
#include "../beingmanager.h"
#include "../game.h"
-#include "../player_relations.h"
+#include "../channel.h"
+#include "../channelmanager.h"
#include "../gui/chat.h"
-
-#include "../utils/gettext.h"
-#include "../utils/stringutils.h"
+#include "../gui/guildwindow.h"
extern Being *player_node;
-#define SERVER_NAME "Server"
-
ChatHandler::ChatHandler()
{
static const Uint16 _messages[] = {
- SMSG_BEING_CHAT,
- SMSG_PLAYER_CHAT,
- SMSG_WHISPER,
- SMSG_WHISPER_RESPONSE,
- SMSG_GM_CHAT,
- SMSG_WHO_ANSWER,
- 0x10c, // MVP
+ 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,
0
};
handledMessages = _messages;
}
-void ChatHandler::handleMessage(MessageIn *msg)
+void ChatHandler::handleMessage(MessageIn &msg)
{
- Being *being;
- std::string chatMsg;
- std::string nick;
- Sint16 chatMsgLength;
-
- switch (msg->getId())
+ switch (msg.getId())
{
- case SMSG_WHISPER_RESPONSE:
- switch (msg->readInt8())
- {
- case 0x00:
- // comment out since we'll local echo in chat.cpp instead, then only report failures
- //chatWindow->chatLog("Whisper sent", BY_SERVER);
- break;
- case 0x01:
- chatWindow->chatLog(_("Whisper could not be sent, user is offline"), BY_SERVER);
- break;
- case 0x02:
- chatWindow->chatLog(_("Whisper could not be sent, ignored by user"), BY_SERVER);
- break;
- }
+ case GPMSG_SAY:
+ handleGameChatMessage(msg);
break;
- // Received whisper
- case SMSG_WHISPER:
- chatMsgLength = msg->readInt16() - 28;
- nick = msg->readString(24);
+ case CPMSG_ENTER_CHANNEL_RESPONSE:
+ handleEnterChannelResponse(msg);
+ break;
- if (chatMsgLength <= 0)
- break;
+ case CPMSG_LIST_CHANNELS_RESPONSE:
+ handleListChannelsResponse(msg);
+ break;
- chatMsg = msg->readString(chatMsgLength);
- if (nick != SERVER_NAME)
- chatMsg = nick + " : " + chatMsg;
+ case CPMSG_PRIVMSG:
+ handlePrivateMessage(msg);
+ break;
- if (nick == SERVER_NAME)
- chatWindow->chatLog(chatMsg, BY_SERVER);
- else {
- if (player_relations.hasPermission(nick, PlayerRelation::WHISPER))
- chatWindow->chatLog(chatMsg, ACT_WHISPER);
- }
+ case CPMSG_ANNOUNCEMENT:
+ handleAnnouncement(msg);
+ break;
+
+ case CPMSG_PUBMSG:
+ handleChatMessage(msg);
+ break;
+ case CPMSG_QUIT_CHANNEL_RESPONSE:
+ handleQuitChannelResponse(msg);
break;
- // Received speech from being
- case SMSG_BEING_CHAT: {
- chatMsgLength = msg->readInt16() - 8;
- being = beingManager->findBeing(msg->readInt32());
+ case CPMSG_LIST_CHANNELUSERS_RESPONSE:
+ handleListChannelUsersResponse(msg);
+ break;
+
+ case CPMSG_CHANNEL_EVENT:
+ handleChannelEvent(msg);
+ }
+}
+
+void ChatHandler::handleGameChatMessage(MessageIn &msg)
+{
+ short id = msg.readInt16();
+ std::string chatMsg = msg.readString();
+
+ if (id == 0)
+ {
+ chatWindow->chatLog(chatMsg, BY_SERVER);
+ return;
+ }
+
+ Being *being = beingManager->findBeing(id);
+
+ if (being)
+ {
+ chatWindow->chatLog(being->getName() + " : " + chatMsg,
+ being == player_node ? BY_PLAYER : BY_OTHER, "General");
+ being->setSpeech(chatMsg, SPEECH_TIME);
+ }
+ else
+ {
+ chatWindow->chatLog("Unknown : " + chatMsg, BY_OTHER, "General");
+ }
+}
- if (!being || chatMsgLength <= 0)
+void ChatHandler::handleEnterChannelResponse(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);
+ chatWindow->createNewChannelTab(channelName);
+ chatWindow->chatLog("Topic: " + announcement, BY_CHANNEL, channelName);
+
+ std::string user;
+ std::string userModes;
+ chatWindow->chatLog("Players in this channel:", BY_CHANNEL, channelName);
+ while(msg.getUnreadLength())
+ {
+ user = msg.readString();
+ if (user == "")
+ return;
+ userModes = msg.readString();
+ if (userModes.find('o') != std::string::npos)
{
- break;
+ user = "@" + user;
}
+ chatWindow->chatLog(user, BY_CHANNEL, channelName);
+ }
+
+ }
+ else
+ {
+ chatWindow->chatLog("Error joining channel", BY_SERVER);
+ }
+}
- chatMsg = msg->readString(chatMsgLength);
+void ChatHandler::handleListChannelsResponse(MessageIn &msg)
+{
+ chatWindow->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();
+ chatWindow->chatLog(channelName, BY_SERVER);
+ }
+ chatWindow->chatLog("End of channel list", BY_SERVER);
+}
- std::string::size_type pos = chatMsg.find(" : ", 0);
- std::string sender_name = ((pos == std::string::npos)
- ? ""
- : chatMsg.substr(0, pos));
+void ChatHandler::handlePrivateMessage(MessageIn &msg)
+{
+ std::string userNick = msg.readString();
+ std::string chatMsg = msg.readString();
- // 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->chatLog(chatMsg, BY_OTHER);
+ if (!chatWindow->tabExists(userNick))
+ {
+ chatWindow->createNewChannelTab(userNick);
- chatMsg.erase(0, pos + 3);
- trim(chatMsg);
+ }
+ chatWindow->chatLog(userNick + ": " + chatMsg, BY_OTHER, userNick);
+}
+
+void ChatHandler::handleAnnouncement(MessageIn &msg)
+{
+ std::string chatMsg = msg.readString();
+ chatWindow->chatLog(chatMsg, BY_GM);
+}
- if (player_relations.hasPermission(sender_name, PlayerRelation::SPEECH_FLOAT))
- being->setSpeech(chatMsg, SPEECH_TIME);
+void ChatHandler::handleChatMessage(MessageIn &msg)
+{
+ short channelId = msg.readInt16();
+ std::string userNick = msg.readString();
+ std::string chatMsg = msg.readString();
+
+ chatWindow->sendToChannel(channelId, userNick, chatMsg);
+}
+
+void ChatHandler::handleQuitChannelResponse(MessageIn &msg)
+{
+ if(msg.readInt8() == ERRMSG_OK)
+ {
+ short channelId = msg.readInt16();
+ // remove the chat tab
+ chatWindow->removeChannel(channelId);
+ }
+}
+
+void ChatHandler::handleListChannelUsersResponse(MessageIn &msg)
+{
+ std::string channel = msg.readString();
+ std::string userNick;
+ std::string userModes;
+ chatWindow->chatLog("Players in this channel:", BY_CHANNEL, channel);
+ while(msg.getUnreadLength())
+ {
+ userNick = msg.readString();
+ if (userNick == "")
+ {
break;
}
+ userModes = msg.readString();
+ if (userModes.find('o') != std::string::npos)
+ {
+ userNick = "@" + userNick;
+ }
+ chatWindow->chatLog(userNick, BY_CHANNEL, channel);
+ }
+}
- case SMSG_PLAYER_CHAT:
- case SMSG_GM_CHAT: {
- chatMsgLength = msg->readInt16() - 4;
+void ChatHandler::handleChannelEvent(MessageIn &msg)
+{
+ short channelId = msg.readInt16();
+ char eventId = msg.readInt8();
+ std::string line = msg.readString();
+ Channel *channel = channelManager->findById(channelId);
- if (chatMsgLength <= 0)
- {
+ if(channel)
+ {
+ switch(eventId)
+ {
+ case CHAT_EVENT_NEW_PLAYER:
+ line += " entered the channel.";
break;
- }
-
- chatMsg = msg->readString(chatMsgLength);
- std::string::size_type pos = chatMsg.find(" : ", 0);
- if (msg->getId() == SMSG_PLAYER_CHAT)
- {
- chatWindow->chatLog(chatMsg, BY_PLAYER);
+ case CHAT_EVENT_LEAVING_PLAYER:
+ line += " left the channel.";
+ break;
- if (pos != std::string::npos)
- chatMsg.erase(0, pos + 3);
+ case CHAT_EVENT_TOPIC_CHANGE:
+ line = "Topic: " + line;
+ break;
- trim(chatMsg);
+ 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());
+ line = user1 + " has set mode " + mode + " on user " + user2;
+ } break;
- player_node->setSpeech(chatMsg, SPEECH_TIME);
- }
- else
+ case CHAT_EVENT_KICKED_PLAYER:
{
- chatWindow->chatLog(chatMsg, BY_GM);
- }
- break;
- }
+ int first = line.find(":");
+ std::string user1 = line.substr(0, first);
+ std::string user2 = line.substr(first+1, line.length());
+ line = user1 + " has kicked " + user2;
+ } break;
- case SMSG_WHO_ANSWER:
- chatWindow->chatLog("Online users: " + toString(msg->readInt32()),
- BY_SERVER);
- break;
+ default:
+ line = "Unknown channel event.";
+ }
- case 0x010c:
- // Display MVP player
- msg->readInt32(); // id
- chatWindow->chatLog("MVP player", BY_SERVER);
- break;
+ chatWindow->chatLog(line, BY_CHANNEL, channel->getName());
}
}
+
diff --git a/src/net/chathandler.h b/src/net/chathandler.h
index ff649205..a9e9bd99 100644
--- a/src/net/chathandler.h
+++ b/src/net/chathandler.h
@@ -28,8 +28,57 @@ class ChatHandler : public MessageHandler
{
public:
ChatHandler();
-
- void handleMessage(MessageIn *msg);
+
+ /**
+ * Handle the given message appropriately.
+ */
+ void handleMessage(MessageIn &msg);
+
+ private:
+ /**
+ * Handle chat messages sent from the game server.
+ */
+ void handleGameChatMessage(MessageIn &msg);
+
+ /**
+ * Handle channel entry responses.
+ */
+ void handleEnterChannelResponse(MessageIn &msg);
+
+ /**
+ * Handle list channels responses.
+ */
+ void handleListChannelsResponse(MessageIn &msg);
+
+ /**
+ * Handle private messages.
+ */
+ void handlePrivateMessage(MessageIn &msg);
+
+ /**
+ * Handle announcements.
+ */
+ void handleAnnouncement(MessageIn &msg);
+
+ /**
+ * Handle chat messages.
+ */
+ void handleChatMessage(MessageIn &msg);
+
+ /**
+ * Handle quit channel responses.
+ */
+ void handleQuitChannelResponse(MessageIn &msg);
+
+ /**
+ * Handle list channel users responses.
+ */
+ void handleListChannelUsersResponse(MessageIn &msg);
+
+ /**
+ * Handle channel events.
+ */
+ void handleChannelEvent(MessageIn &msg);
};
#endif
diff --git a/src/net/chatserver/chatserver.cpp b/src/net/chatserver/chatserver.cpp
new file mode 100644
index 00000000..94e36b94
--- /dev/null
+++ b/src/net/chatserver/chatserver.cpp
@@ -0,0 +1,146 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "chatserver.h"
+
+#include "internal.h"
+
+#include "../connection.h"
+#include "../messageout.h"
+#include "../protocol.h"
+
+using Net::ChatServer::connection;
+
+void Net::ChatServer::connect(Net::Connection *connection,
+ const std::string &token)
+{
+ Net::ChatServer::connection = connection;
+
+ MessageOut msg(PCMSG_CONNECT);
+
+ msg.writeString(token, 32);
+
+ connection->send(msg);
+}
+
+void Net::ChatServer::logout()
+{
+ MessageOut msg(PCMSG_DISCONNECT);
+
+ connection->send(msg);
+}
+
+void Net::ChatServer::chat(short channel, const std::string &text)
+{
+ MessageOut msg(PCMSG_CHAT);
+
+ msg.writeString(text);
+ msg.writeInt16(channel);
+
+ connection->send(msg);
+}
+
+void Net::ChatServer::announce(const std::string &text)
+{
+ MessageOut msg(PCMSG_ANNOUNCE);
+
+ msg.writeString(text);
+
+ connection->send(msg);
+}
+
+void Net::ChatServer::privMsg(const std::string &recipient,
+ const std::string &text)
+{
+ MessageOut msg(PCMSG_PRIVMSG);
+
+ msg.writeString(recipient);
+ msg.writeString(text);
+
+ connection->send(msg);
+}
+
+void Net::ChatServer::enterChannel(const std::string &channel, const std::string &password)
+{
+ MessageOut msg(PCMSG_ENTER_CHANNEL);
+
+ msg.writeString(channel);
+ msg.writeString(password);
+
+ connection->send(msg);
+}
+
+void Net::ChatServer::quitChannel(short channel)
+{
+ MessageOut msg(PCMSG_QUIT_CHANNEL);
+
+ msg.writeInt16(channel);
+
+ connection->send(msg);
+}
+
+void Net::ChatServer::getChannelList()
+{
+ MessageOut msg(PCMSG_LIST_CHANNELS);
+
+ connection->send(msg);
+}
+
+void Net::ChatServer::getUserList(const std::string &channel)
+{
+ MessageOut msg(PCMSG_LIST_CHANNELUSERS);
+
+ msg.writeString(channel);
+
+ connection->send(msg);
+}
+
+void Net::ChatServer::setChannelTopic(short channel, const std::string &topic)
+{
+ MessageOut msg(PCMSG_TOPIC_CHANGE);
+
+ msg.writeInt16(channel);
+ msg.writeString(topic);
+
+ connection->send(msg);
+}
+
+void Net::ChatServer::setUserMode(short channel, const std::string &user,
+ unsigned char mode)
+{
+ MessageOut msg(PCMSG_USER_MODE);
+
+ msg.writeInt16(channel);
+ msg.writeString(user);
+ msg.writeInt8(mode);
+
+ connection->send(msg);
+}
+
+void Net::ChatServer::kickUser(short channel, const std::string &user)
+{
+ MessageOut msg(PCMSG_KICK_USER);
+
+ msg.writeInt16(channel);
+ msg.writeString(user);
+
+ connection->send(msg);
+}
diff --git a/src/net/chatserver/chatserver.h b/src/net/chatserver/chatserver.h
new file mode 100644
index 00000000..1129239a
--- /dev/null
+++ b/src/net/chatserver/chatserver.h
@@ -0,0 +1,60 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_NET_CHATSERVER_CHATSERVER_H
+#define _TMW_NET_CHATSERVER_CHATSERVER_H
+
+#include <iosfwd>
+
+namespace Net
+{
+ class Connection;
+
+ namespace ChatServer
+ {
+ void connect(Net::Connection *connection, const std::string &token);
+
+ void logout();
+
+ void chat(short channel, const std::string &text);
+
+ void announce(const std::string &text);
+
+ void privMsg(const std::string &recipient, const std::string &text);
+
+ void enterChannel(const std::string &channel, const std::string &password);
+
+ void quitChannel(short channel);
+
+ void getChannelList();
+
+ void getUserList(const std::string &channel);
+
+ void setChannelTopic(short channel, const std::string &topic);
+
+ void setUserMode(short channel, const std::string &user, unsigned char mode);
+
+ void kickUser(short channel, const std::string &user);
+
+ }
+}
+
+#endif
diff --git a/src/net/chatserver/guild.cpp b/src/net/chatserver/guild.cpp
new file mode 100644
index 00000000..042ff013
--- /dev/null
+++ b/src/net/chatserver/guild.cpp
@@ -0,0 +1,95 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "guild.h"
+
+#include "internal.h"
+
+#include "../connection.h"
+#include "../messageout.h"
+#include "../protocol.h"
+
+#include "../../log.h"
+
+void Net::ChatServer::Guild::createGuild(const std::string &name)
+{
+ logger->log("Sending PCMSG_GUILD_CREATE");
+ MessageOut msg(PCMSG_GUILD_CREATE);
+
+ msg.writeString(name);
+
+ Net::ChatServer::connection->send(msg);
+}
+
+void Net::ChatServer::Guild::invitePlayer(const std::string &name, short guildId)
+{
+ logger->log("Sending PCMSG_GUILD_INVITE");
+ MessageOut msg(PCMSG_GUILD_INVITE);
+
+ msg.writeInt16(guildId);
+ msg.writeString(name);
+
+ Net::ChatServer::connection->send(msg);
+}
+
+void Net::ChatServer::Guild::acceptInvite(const std::string &name)
+{
+ logger->log("Sending PCMSG_GUILD_ACCEPT");
+ MessageOut msg(PCMSG_GUILD_ACCEPT);
+
+ msg.writeString(name);
+
+ Net::ChatServer::connection->send(msg);
+}
+
+void Net::ChatServer::Guild::getGuildMembers(short guildId)
+{
+ logger->log("Sending PCMSG_GUILD_GET_MEMBERS");
+ MessageOut msg(PCMSG_GUILD_GET_MEMBERS);
+
+ msg.writeInt16(guildId);
+
+ Net::ChatServer::connection->send(msg);
+}
+
+void Net::ChatServer::Guild::promoteMember(const std::string &name,
+ short guildId, short level)
+{
+ logger->log("Sending PCMSG_GUILD_PROMOTE_MEMBER");
+ MessageOut msg(PCMSG_GUILD_PROMOTE_MEMBER);
+
+ msg.writeInt16(guildId);
+ msg.writeString(name);
+ msg.writeInt8(level);
+
+ Net::ChatServer::connection->send(msg);
+}
+
+void Net::ChatServer::Guild::quitGuild(short guildId)
+{
+ logger->log("Sending PCMSG_GUILD_QUIT");
+ MessageOut msg(PCMSG_GUILD_QUIT);
+
+ msg.writeInt16(guildId);
+
+ Net::ChatServer::connection->send(msg);
+}
diff --git a/src/net/chatserver/guild.h b/src/net/chatserver/guild.h
new file mode 100644
index 00000000..6c35be9f
--- /dev/null
+++ b/src/net/chatserver/guild.h
@@ -0,0 +1,69 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _TMW_NET_CHATSERVER_GUILD_H
+#define _TMW_NET_CHATSERVER_GUILD_H
+
+#include <iosfwd>
+
+namespace Net
+{
+ namespace ChatServer
+ {
+ namespace Guild
+ {
+ /**
+ * Create guild.
+ */
+ void createGuild(const std::string &name);
+
+ /**
+ * Invite a player to your guild.
+ */
+ void invitePlayer(const std::string &name, short guildId);
+
+ /**
+ * Accept an invite another player has sent to join their guild.
+ */
+ void acceptInvite(const std::string &name);
+
+ /**
+ * Get a list of members in a guild.
+ */
+ void getGuildMembers(short guildId);
+
+ /**
+ * Promote guild member
+ */
+ void promoteMember(const std::string &name, short guildId,
+ short level);
+
+ /**
+ * Quit guild.
+ */
+ void quitGuild(short guildId);
+ }
+ }
+}
+
+#endif
+
diff --git a/src/net/chatserver/internal.cpp b/src/net/chatserver/internal.cpp
new file mode 100644
index 00000000..0822d93d
--- /dev/null
+++ b/src/net/chatserver/internal.cpp
@@ -0,0 +1,32 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "internal.h"
+
+namespace Net
+{
+ class Connection;
+
+ namespace ChatServer
+ {
+ Connection *connection = 0;
+ }
+}
diff --git a/src/net/chatserver/internal.h b/src/net/chatserver/internal.h
new file mode 100644
index 00000000..b0f137c5
--- /dev/null
+++ b/src/net/chatserver/internal.h
@@ -0,0 +1,35 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_NET_CHATSERVER_INTERNAL_H
+#define _TMW_NET_CHATSERVER_INTERNAL_H
+
+namespace Net
+{
+ class Connection;
+
+ namespace ChatServer
+ {
+ extern Connection *connection;
+ }
+}
+
+#endif
diff --git a/src/net/chatserver/party.cpp b/src/net/chatserver/party.cpp
new file mode 100644
index 00000000..56eb57d2
--- /dev/null
+++ b/src/net/chatserver/party.cpp
@@ -0,0 +1,70 @@
+/*
+ * The Mana World
+ * Copyright 2008 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "party.h"
+
+#include "internal.h"
+
+#include "../connection.h"
+#include "../messageout.h"
+#include "../protocol.h"
+
+#include "../../log.h"
+
+void Net::ChatServer::Party::invitePlayer(const std::string &name)
+{
+ logger->log("Sending PCMSG_PARTY_INVITE");
+ MessageOut msg(PCMSG_PARTY_INVITE);
+
+ msg.writeString(name);
+
+ Net::ChatServer::connection->send(msg);
+}
+
+void Net::ChatServer::Party::acceptInvite(const std::string &name)
+{
+ logger->log("Sending PCMSG_PARTY_ACCEPT_INVITE");
+ MessageOut msg(PCMSG_PARTY_ACCEPT_INVITE);
+
+ msg.writeString(name);
+
+ Net::ChatServer::connection->send(msg);
+}
+
+void Net::ChatServer::Party::getPartyMembers()
+{
+ logger->log("Sending PCMSG_PARTY_GET_MEMBERS");
+// MessageOut msg(PCMSG_GUILD_GET_MEMBERS);
+
+// msg.writeInt16(guildId);
+
+// Net::ChatServer::connection->send(msg);
+}
+
+void Net::ChatServer::Party::quitParty()
+{
+ logger->log("Sending PCMSG_PARTY_QUIT");
+ MessageOut msg(PCMSG_PARTY_QUIT);
+
+ Net::ChatServer::connection->send(msg);
+}
+
diff --git a/src/net/chatserver/party.h b/src/net/chatserver/party.h
new file mode 100644
index 00000000..c1febd66
--- /dev/null
+++ b/src/net/chatserver/party.h
@@ -0,0 +1,57 @@
+/*
+ * The Mana World
+ * Copyright 2008 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _TMW_NET_CHATSERVER_PARTY_H
+#define _TMW_NET_CHATSERVER_PARTY_H
+
+#include <iosfwd>
+
+namespace Net
+{
+ namespace ChatServer
+ {
+ namespace Party
+ {
+ /**
+ * Invite a player to the party.
+ */
+ void invitePlayer(const std::string &name);
+
+ /**
+ * Accept an invite another player has sent to join their party
+ */
+ void acceptInvite(const std::string &name);
+
+ /**
+ * Get a list of party members
+ */
+ void getPartyMembers();
+
+ /**
+ * Leave party
+ */
+ void quitParty();
+ }
+ }
+}
+
+#endif
diff --git a/src/net/connection.cpp b/src/net/connection.cpp
new file mode 100644
index 00000000..7d3c2328
--- /dev/null
+++ b/src/net/connection.cpp
@@ -0,0 +1,103 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "connection.h"
+
+#include <string>
+
+#include "internal.h"
+#include "messageout.h"
+
+#include "../log.h"
+
+Net::Connection::Connection(ENetHost *client):
+ mConnection(0), mClient(client)
+{
+ Net::connections++;
+}
+
+Net::Connection::~Connection()
+{
+ Net::connections--;
+}
+
+bool Net::Connection::connect(const std::string &address, short port)
+{
+ logger->log("Net::Connection::connect(%s, %i)", address.c_str(), port);
+
+ if (address.empty())
+ {
+ logger->log("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.
+ mConnection = enet_host_connect(mClient, &enetAddress, 1);
+
+ if (!mConnection)
+ {
+ logger->log("Unable to initiate connection to the server.");
+ mState = NET_ERROR;
+ return false;
+ }
+
+ return true;
+}
+
+void Net::Connection::disconnect()
+{
+ if (!mConnection)
+ return;
+
+ enet_peer_disconnect(mConnection, 0);
+ enet_host_flush(mClient);
+ enet_peer_reset(mConnection);
+
+ mConnection = 0;
+}
+
+bool Net::Connection::isConnected()
+{
+ return bool (mConnection) ?
+ (mConnection->state == ENET_PEER_STATE_CONNECTED) : false;
+}
+
+void Net::Connection::send(const MessageOut &msg)
+{
+ if (!isConnected())
+ {
+ logger->log("Warning: cannot send message to not connected server!");
+ return;
+ }
+
+ //logger->log("Sending message of size %d...", msg.getDataSize());
+
+ ENetPacket *packet = enet_packet_create(msg.getData(),
+ msg.getDataSize(),
+ ENET_PACKET_FLAG_RELIABLE);
+ enet_peer_send(mConnection, 0, packet);
+}
diff --git a/src/net/connection.h b/src/net/connection.h
new file mode 100644
index 00000000..4ab3d24d
--- /dev/null
+++ b/src/net/connection.h
@@ -0,0 +1,80 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_NET_CONNECTION_H
+#define _TMW_NET_CONNECTION_H
+
+#include <iosfwd>
+
+#include <enet/enet.h>
+
+class MessageOut;
+
+namespace Net
+{
+ /**
+ * \ingroup Network
+ */
+ class Connection
+ {
+ public:
+ enum State {
+ OK,
+ 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 MessageOut &msg);
+
+ private:
+ friend Connection *Net::getConnection();
+ Connection(ENetHost *client);
+
+ ENetPeer *mConnection;
+ ENetHost *mClient;
+ State mState;
+ };
+}
+
+#endif
diff --git a/src/net/ea/beinghandler.cpp b/src/net/ea/beinghandler.cpp
new file mode 100644
index 00000000..1edc6079
--- /dev/null
+++ b/src/net/ea/beinghandler.cpp
@@ -0,0 +1,542 @@
+/*
+ * 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
+ */
+
+#include <iostream>
+#include <SDL_types.h>
+
+#include "beinghandler.h"
+#include "../messagein.h"
+#include "protocol.h"
+
+#include "../../being.h"
+#include "../../beingmanager.h"
+#include "../../effectmanager.h"
+#include "../../game.h"
+#include "../../localplayer.h"
+#include "../../log.h"
+#include "../../npc.h"
+#include "../../player_relations.h"
+
+const int EMOTION_TIME = 150; /**< Duration of emotion icon */
+
+BeingHandler::BeingHandler(bool enableSync):
+ mSync(enableSync)
+{
+ static const Uint16 _messages[] = {
+ SMSG_BEING_VISIBLE,
+ SMSG_BEING_MOVE,
+ SMSG_BEING_MOVE2,
+ SMSG_BEING_REMOVE,
+ SMSG_BEING_ACTION,
+ SMSG_BEING_SELFEFFECT,
+ SMSG_BEING_EMOTION,
+ SMSG_BEING_CHANGE_LOOKS,
+ SMSG_BEING_CHANGE_LOOKS2,
+ SMSG_BEING_NAME_RESPONSE,
+ SMSG_PLAYER_UPDATE_1,
+ SMSG_PLAYER_UPDATE_2,
+ SMSG_PLAYER_MOVE,
+ SMSG_PLAYER_STOP,
+ SMSG_PLAYER_MOVE_TO_ATTACK,
+ 0x0119,
+ 0x0196,
+ 0
+ };
+ handledMessages = _messages;
+}
+
+void BeingHandler::handleMessage(MessageIn &msg)
+{
+ Uint32 id;
+ Uint16 job, speed;
+ Uint16 headTop, headMid, headBottom;
+ Uint16 shoes, gloves;
+ Uint16 weapon, shield;
+ Uint16 gmstatus;
+ Sint16 param1;
+ int stunMode;
+ Uint32 statusEffects;
+ Sint8 type;
+ Uint16 status;
+ Being *srcBeing, *dstBeing;
+ int hairStyle, hairColor, flag;
+
+ 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 = beingManager->findBeing(id);
+
+ 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;
+ }
+
+ dstBeing = beingManager->createBeing(id, job);
+ }
+ else if (msg.getId() == 0x0078)
+ {
+ dstBeing->clearPath();
+ dstBeing->mFrame = 0;
+ dstBeing->mWalkTime = tick_time;
+ dstBeing->setAction(Being::STAND);
+ }
+
+
+ // Prevent division by 0 when calculating frame
+ if (speed == 0) { speed = 150; }
+
+ dstBeing->setWalkSpeed(speed);
+ dstBeing->mJob = job;
+ hairStyle = msg.readInt16();
+ dstBeing->setSprite(Being::WEAPON_SPRITE, msg.readInt16());
+ headBottom = msg.readInt16();
+
+ if (msg.getId() == SMSG_BEING_MOVE)
+ {
+ msg.readInt32(); // server tick
+ }
+
+ dstBeing->setSprite(Being::SHIELD_SPRITE, msg.readInt16());
+ headTop = msg.readInt16();
+ headMid = msg.readInt16();
+ hairColor = msg.readInt16();
+ shoes = msg.readInt16(); // clothes color - "abused" as shoes
+ gloves = msg.readInt16(); // head dir - "abused" as gloves
+ msg.readInt16(); // guild
+ msg.readInt16(); // unknown
+ msg.readInt16(); // unknown
+ 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(Being::BOTTOMCLOTHES_SPRITE, headBottom);
+ dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, headMid);
+ dstBeing->setSprite(Being::HAT_SPRITE, headTop);
+ dstBeing->setSprite(Being::SHOE_SPRITE, shoes);
+ dstBeing->setSprite(Being::GLOVES_SPRITE, gloves);
+ dstBeing->setHairStyle(hairStyle, hairColor);
+
+ if (msg.getId() == SMSG_BEING_MOVE)
+ {
+ Uint16 srcX, srcY, dstX, dstY;
+ msg.readCoordinatePair(srcX, srcY, dstX, dstY);
+ dstBeing->setAction(Being::STAND);
+ dstBeing->mX = srcX;
+ dstBeing->mY = srcY;
+ dstBeing->setDestination(dstX, dstY);
+ }
+ else
+ {
+ Uint8 dir;
+ msg.readCoordinates(dstBeing->mX, dstBeing->mY, dir);
+ dstBeing->setDirection(dir);
+ }
+
+ msg.readInt8(); // unknown
+ msg.readInt8(); // unknown
+ msg.readInt8(); // unknown / sit
+
+ 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 = beingManager->findBeing(msg.readInt32());
+
+ Uint16 srcX, srcY, dstX, dstY;
+ msg.readCoordinatePair(srcX, srcY, dstX, dstY);
+ msg.readInt32(); // Server tick
+
+ /*
+ * 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) {
+ dstBeing->setAction(Being::STAND);
+ dstBeing->mX = srcX;
+ dstBeing->mY = srcY;
+ dstBeing->setDestination(dstX, dstY);
+ }
+
+ break;
+
+ case SMSG_BEING_REMOVE:
+ // A being should be removed or has died
+ dstBeing = beingManager->findBeing(msg.readInt32());
+
+ if (!dstBeing)
+ break;
+
+ // If this is player's current target, clear it.
+ if (dstBeing == player_node->getTarget())
+ player_node->stopAttack();
+
+ if (dstBeing == current_npc)
+ current_npc->handleDeath();
+
+ if (msg.readInt8() == 1)
+ dstBeing->setAction(Being::DEAD);
+ else
+ beingManager->destroyBeing(dstBeing);
+
+ break;
+
+ case SMSG_BEING_ACTION:
+ srcBeing = beingManager->findBeing(msg.readInt32());
+ dstBeing = beingManager->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 0x0a: // Critical Damage
+ if (dstBeing)
+ dstBeing->showCrit();
+ case 0x00: // Damage
+ if (dstBeing)
+ dstBeing->takeDamage(param1);
+ if (srcBeing)
+ srcBeing->handleAttack(dstBeing, param1);
+ break;
+
+ case 0x02: // Sit
+ if (srcBeing)
+ {
+ srcBeing->mFrame = 0;
+ srcBeing->setAction(Being::SIT);
+ }
+ break;
+
+ case 0x03: // Stand up
+ if (srcBeing)
+ {
+ srcBeing->mFrame = 0;
+ srcBeing->setAction(Being::STAND);
+ }
+ break;
+ }
+ break;
+
+ case SMSG_BEING_SELFEFFECT: {
+ id = (Uint32)msg.readInt32();
+ if (!beingManager->findBeing(id))
+ break;
+
+ int effectType = msg.readInt32();
+ Being* being = beingManager->findBeing(id);
+
+ effectManager->trigger(effectType, being);
+
+ break;
+ }
+
+ case SMSG_BEING_EMOTION:
+ if (!(dstBeing = beingManager->findBeing(msg.readInt32())))
+ {
+ break;
+ }
+
+ if (player_relations.hasPermission(dstBeing, PlayerRelation::EMOTE))
+ dstBeing->setEmote(msg.readInt8(), EMOTION_TIME);
+
+ 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 = beingManager->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();
+ }
+
+ switch (type) {
+ case 1: // eAthena LOOK_HAIR
+ dstBeing->setHairStyle(id, -1);
+ break;
+ case 2: // Weapon ID in id, Shield ID in id2
+ dstBeing->setSprite(Being::WEAPON_SPRITE, id);
+ dstBeing->setSprite(Being::SHIELD_SPRITE, id2);
+ break;
+ case 3: // Change lower headgear for eAthena, pants for us
+ dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, id);
+ break;
+ case 4: // Change upper headgear for eAthena, hat for us
+ dstBeing->setSprite(Being::HAT_SPRITE, id);
+ break;
+ case 5: // Change middle headgear for eathena, armor for us
+ dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, id);
+ break;
+ case 6: // eAthena LOOK_HAIR_COLOR
+ dstBeing->setHairStyle(-1, id);
+ break;
+ case 8: // eAthena LOOK_SHIELD
+ dstBeing->setSprite(Being::SHIELD_SPRITE, id);
+ break;
+ case 9: // eAthena LOOK_SHOES
+ dstBeing->setSprite(Being::SHOE_SPRITE, id);
+ break;
+ case 10: // LOOK_GLOVES
+ dstBeing->setSprite(Being::GLOVES_SPRITE, id);
+ break;
+ case 11: // LOOK_CAPE
+ dstBeing->setSprite(Being::CAPE_SPRITE, id);
+ break;
+ case 12:
+ dstBeing->setSprite(Being::MISC1_SPRITE, id);
+ break;
+ case 13:
+ dstBeing->setSprite(Being::MISC2_SPRITE, id);
+ break;
+ default:
+ logger->log("SMSG_BEING_CHANGE_LOOKS: unsupported type: "
+ "%d, id: %d", type, id);
+ break;
+ }
+ }
+ break;
+
+ case SMSG_BEING_NAME_RESPONSE:
+ if ((dstBeing = beingManager->findBeing(msg.readInt32())))
+ {
+ dstBeing->setName(msg.readString(24));
+ }
+ break;
+
+ case SMSG_PLAYER_UPDATE_1:
+ case SMSG_PLAYER_UPDATE_2:
+ case SMSG_PLAYER_MOVE:
+ // 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 = beingManager->findBeing(id);
+
+ if (!dstBeing)
+ {
+ dstBeing = beingManager->createBeing(id, job);
+ }
+
+ dstBeing->setWalkSpeed(speed);
+ dstBeing->mJob = 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();
+ msg.readInt16(); // clothes color - Aethyra-"abused" as shoes, we ignore it
+ msg.readInt16(); // head dir - Aethyra-"abused" as gloves, we ignore it
+ msg.readInt32(); // 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(Being::WEAPON_SPRITE, weapon);
+ dstBeing->setSprite(Being::SHIELD_SPRITE, shield);
+ dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, headBottom);
+ dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, headMid);
+ dstBeing->setSprite(Being::HAT_SPRITE, headTop);
+ //dstBeing->setSprite(Being::CAPE_SPRITE, cape);
+ //dstBeing->setSprite(Being::MISC1_SPRITE, misc1);
+ //dstBeing->setSprite(Being::MISC2_SPRITE, misc2);
+ dstBeing->setHairStyle(hairStyle, hairColor);
+
+ if (msg.getId() == SMSG_PLAYER_MOVE)
+ {
+ Uint16 srcX, srcY, dstX, dstY;
+ msg.readCoordinatePair(srcX, srcY, dstX, dstY);
+ dstBeing->mX = srcX;
+ dstBeing->mY = srcY;
+ dstBeing->setDestination(dstX, dstY);
+ }
+ else
+ {
+ Uint8 dir;
+ msg.readCoordinates(dstBeing->mX, dstBeing->mY, dir);
+ dstBeing->setDirection(dir);
+ }
+
+ gmstatus = msg.readInt16();
+ if (gmstatus & 0x80)
+ dstBeing->setGM();
+
+ if (msg.getId() == SMSG_PLAYER_UPDATE_1)
+ {
+ switch (msg.readInt8())
+ {
+ case 1:
+ if (dstBeing->getType() != Being::NPC)
+ dstBeing->setAction(Being::DEAD);
+ break;
+
+ case 2:
+ dstBeing->setAction(Being::SIT);
+ break;
+ }
+ }
+ else if (msg.getId() == SMSG_PLAYER_MOVE)
+ {
+ msg.readInt8(); // unknown
+ }
+
+ msg.readInt8(); // Lv
+ msg.readInt8(); // unknown
+
+ dstBeing->mWalkTime = tick_time;
+ dstBeing->mFrame = 0;
+
+ dstBeing->setStunMode(stunMode);
+ dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff);
+ dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
+ 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 = beingManager->findBeing(id);
+ if (dstBeing) {
+ dstBeing->mX = msg.readInt16();
+ dstBeing->mY = msg.readInt16();
+ if (dstBeing->mAction == Being::WALK) {
+ dstBeing->mFrame = 0;
+ 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...
+ */
+ break;
+
+ case 0x0119:
+ // Change in players' flags
+ id = msg.readInt32();
+ dstBeing = beingManager->findBeing(id);
+ stunMode = msg.readInt16();
+ statusEffects = msg.readInt16();
+ statusEffects |= ((Uint32) msg.readInt16()) << 16;
+ msg.readInt8();
+
+ if (dstBeing) {
+ dstBeing->setStunMode(stunMode);
+ dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff);
+ dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
+ }
+ break;
+
+ case 0x0196:
+ // Status change
+ status = msg.readInt16();
+ id = msg.readInt32();
+ flag = msg.readInt8(); // 0: stop, 1: start
+
+ dstBeing = beingManager->findBeing(id);
+ if (dstBeing)
+ dstBeing->setStatusEffect(status, flag);
+ break;
+ }
+}
diff --git a/src/net/ea/beinghandler.h b/src/net/ea/beinghandler.h
new file mode 100644
index 00000000..16a7c8d6
--- /dev/null
+++ b/src/net/ea/beinghandler.h
@@ -0,0 +1,39 @@
+/*
+ * 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 NET_BEINGHANDLER_H
+#define NET_BEINGHANDLER_H
+
+#include "../messagehandler.h"
+
+class BeingHandler : public MessageHandler
+{
+ public:
+ BeingHandler(bool enableSync);
+
+ void handleMessage(MessageIn &msg);
+
+ private:
+ // Should we honor server "Stop Walking" packets
+ bool mSync;
+};
+
+#endif
diff --git a/src/net/ea/buysellhandler.cpp b/src/net/ea/buysellhandler.cpp
new file mode 100644
index 00000000..480c71b8
--- /dev/null
+++ b/src/net/ea/buysellhandler.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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
+ */
+
+#include <SDL_types.h>
+
+#include "buysellhandler.h"
+#include "../messagein.h"
+#include "protocol.h"
+
+#include "../../beingmanager.h"
+#include "../../inventory.h"
+#include "../../item.h"
+#include "../../localplayer.h"
+#include "../../npc.h"
+
+#include "../../gui/buy.h"
+#include "../../gui/chat.h"
+#include "../../gui/sell.h"
+
+#include "../../utils/gettext.h"
+
+extern BuyDialog *buyDialog;
+extern Window *buySellDialog;
+extern SellDialog *sellDialog;
+
+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
+ };
+ handledMessages = _messages;
+}
+
+void BuySellHandler::handleMessage(MessageIn &msg)
+{
+ int n_items;
+ switch (msg.getId())
+ {
+ case SMSG_NPC_BUY_SELL_CHOICE:
+ buyDialog->setVisible(false);
+ buyDialog->reset();
+ sellDialog->setVisible(false);
+ sellDialog->reset();
+ buySellDialog->setVisible(true);
+ current_npc = dynamic_cast<NPC*>(beingManager->findBeing(msg.readInt32()));
+ break;
+
+ case SMSG_NPC_BUY:
+ msg.readInt16(); // length
+ n_items = (msg.getLength() - 4) / 11;
+ buyDialog->reset();
+ buyDialog->setMoney(player_node->getMoney());
+ buyDialog->setVisible(true);
+
+ for (int k = 0; k < n_items; k++)
+ {
+ Sint32 value = msg.readInt32();
+ msg.readInt32(); // DCvalue
+ msg.readInt8(); // type
+ Sint16 itemId = msg.readInt16();
+ buyDialog->addItem(itemId, 0, value);
+ }
+ break;
+
+ case SMSG_NPC_SELL:
+ msg.readInt16(); // length
+ n_items = (msg.getLength() - 4) / 10;
+ if (n_items > 0) {
+ sellDialog->setMoney(player_node->getMoney());
+ sellDialog->reset();
+ sellDialog->setVisible(true);
+
+ for (int k = 0; k < n_items; k++)
+ {
+ Sint16 index = msg.readInt16();
+ Sint32 value = msg.readInt32();
+ msg.readInt32(); // OCvalue
+
+ Item *item = player_node->getInventory()->getItem(index);
+ if (item && !(item->isEquipped())) {
+ sellDialog->addItem(item, value);
+ }
+ }
+ }
+ else {
+ chatWindow->chatLog(_("Nothing to sell"), BY_SERVER);
+ if (current_npc) current_npc->handleDeath();
+ }
+ break;
+
+ case SMSG_NPC_BUY_RESPONSE:
+ if (msg.readInt8() == 0) {
+ chatWindow->chatLog(_("Thanks for buying"), BY_SERVER);
+ } else {
+ // Reset player money since buy dialog already assumed purchase
+ // would go fine
+ buyDialog->setMoney(player_node->getMoney());
+ chatWindow->chatLog(_("Unable to buy"), BY_SERVER);
+ }
+ break;
+
+ case SMSG_NPC_SELL_RESPONSE:
+ if (msg.readInt8() == 0) {
+ chatWindow->chatLog(_("Thanks for selling"), BY_SERVER);
+ } else {
+ chatWindow->chatLog(_("Unable to sell"), BY_SERVER);
+ }
+ break;
+ }
+}
diff --git a/src/net/ea/buysellhandler.h b/src/net/ea/buysellhandler.h
new file mode 100644
index 00000000..5bf58d8e
--- /dev/null
+++ b/src/net/ea/buysellhandler.h
@@ -0,0 +1,35 @@
+/*
+ * 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 NET_BUYSELLHANDLER_H
+#define NET_BUYSELLHANDLER_H
+
+#include "../messagehandler.h"
+
+class BuySellHandler : public MessageHandler
+{
+ public:
+ BuySellHandler();
+
+ void handleMessage(MessageIn &msg);
+};
+
+#endif
diff --git a/src/net/ea/charserverhandler.cpp b/src/net/ea/charserverhandler.cpp
new file mode 100644
index 00000000..0fef3de7
--- /dev/null
+++ b/src/net/ea/charserverhandler.cpp
@@ -0,0 +1,235 @@
+/*
+ * 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
+ */
+
+#include "charserverhandler.h"
+#include "../messagein.h"
+#include "protocol.h"
+
+#include "../../game.h"
+#include "../../localplayer.h"
+#include "../../log.h"
+#include "../../logindata.h"
+#include "../../main.h"
+
+#include "../../gui/char_select.h"
+#include "../../gui/ok_dialog.h"
+
+#include "../../utils/gettext.h"
+#include "../../utils/stringutils.h"
+
+CharServerHandler::CharServerHandler():
+ mCharCreateDialog(0)
+{
+ static const Uint16 _messages[] = {
+ SMSG_CONNECTION_PROBLEM,
+ 0x006b,
+ 0x006c,
+ 0x006d,
+ 0x006e,
+ 0x006f,
+ 0x0070,
+ 0x0071,
+ 0
+ };
+ handledMessages = _messages;
+}
+
+void CharServerHandler::handleMessage(MessageIn &msg)
+{
+ int slot, flags, code;
+ LocalPlayer *tempPlayer;
+
+ logger->log("CharServerHandler: Packet ID: %x, Length: %d",
+ msg.getId(), msg.getLength());
+ 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 = _("Map server(s) offline");
+ break;
+ case 2:
+ 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;
+ }
+ state = STATE_ERROR;
+ break;
+
+ case 0x006b:
+ msg.skip(2); // Length word
+ flags = msg.readInt32(); // Aethyra extensions flags
+ logger->log("Server flags are: %x", flags);
+ msg.skip(16); // Unused
+
+ // Derive number of characters from message length
+ n_character = (msg.getLength() - 24) / 106;
+
+ for (int i = 0; i < n_character; i++)
+ {
+ tempPlayer = readPlayerData(msg, slot);
+ mCharInfo->select(slot);
+ mCharInfo->setEntry(tempPlayer);
+ logger->log("CharServer: Player: %s (%d)",
+ tempPlayer->getName().c_str(), slot);
+ }
+
+ state = STATE_CHAR_SELECT;
+ break;
+
+ case 0x006c:
+ switch (msg.readInt8()) {
+ case 0:
+ errorMessage = _("Access denied");
+ break;
+ case 1:
+ errorMessage = _("Cannot use this ID");
+ break;
+ default:
+ errorMessage = _("Unknown failure to select character");
+ break;
+ }
+ mCharInfo->unlock();
+ break;
+
+ case 0x006d:
+ tempPlayer = readPlayerData(msg, slot);
+ mCharInfo->unlock();
+ mCharInfo->select(slot);
+ mCharInfo->setEntry(tempPlayer);
+ n_character++;
+
+ // Close the character create dialog
+ if (mCharCreateDialog)
+ {
+ mCharCreateDialog->scheduleDelete();
+ mCharCreateDialog = 0;
+ }
+ break;
+
+ case 0x006e:
+ new OkDialog(_("Error"), _("Failed to create character. Most likely"
+ " the name is already taken."));
+
+ if (mCharCreateDialog)
+ mCharCreateDialog->unlock();
+ break;
+
+ case 0x006f:
+ delete mCharInfo->getEntry();
+ mCharInfo->setEntry(0);
+ mCharInfo->unlock();
+ n_character--;
+ new OkDialog(_("Info"), _("Player deleted"));
+ break;
+
+ case 0x0070:
+ mCharInfo->unlock();
+ new OkDialog(_("Error"), _("Failed to delete character."));
+ break;
+
+ case 0x0071:
+ player_node = mCharInfo->getEntry();
+ slot = mCharInfo->getPos();
+ msg.skip(4); // CharID, must be the same as player_node->charID
+ map_path = msg.readString(16);
+ mLoginData->hostname = ipToString(msg.readInt32());
+ mLoginData->port = msg.readInt16();
+ mCharInfo->unlock();
+ mCharInfo->select(0);
+ // Clear unselected players infos
+ do
+ {
+ LocalPlayer *tmp = mCharInfo->getEntry();
+ if (tmp != player_node)
+ {
+ delete tmp;
+ mCharInfo->setEntry(0);
+ }
+ mCharInfo->next();
+ } while (mCharInfo->getPos());
+
+ mCharInfo->select(slot);
+ state = STATE_CONNECTING;
+ break;
+ }
+}
+
+LocalPlayer *CharServerHandler::readPlayerData(MessageIn &msg, int &slot)
+{
+ LocalPlayer *tempPlayer = new LocalPlayer(mLoginData->account_ID, 0, NULL);
+ tempPlayer->setGender(
+ (mLoginData->sex == 0) ? GENDER_FEMALE : GENDER_MALE);
+
+ tempPlayer->mCharId = msg.readInt32();
+ tempPlayer->setXp(msg.readInt32());
+ tempPlayer->setMoney(msg.readInt32());
+ tempPlayer->mJobXp = msg.readInt32();
+ tempPlayer->mJobLevel = msg.readInt32();
+ tempPlayer->setSprite(Being::SHOE_SPRITE, msg.readInt16());
+ tempPlayer->setSprite(Being::GLOVES_SPRITE, msg.readInt16());
+ tempPlayer->setSprite(Being::CAPE_SPRITE, msg.readInt16());
+ tempPlayer->setSprite(Being::MISC1_SPRITE, msg.readInt16());
+ msg.readInt32(); // option
+ msg.readInt32(); // karma
+ msg.readInt32(); // manner
+ msg.skip(2); // unknown
+ tempPlayer->setHp(msg.readInt16());
+ tempPlayer->setMaxHp(msg.readInt16());
+ tempPlayer->mMp = msg.readInt16();
+ tempPlayer->mMaxMp = msg.readInt16();
+ msg.readInt16(); // speed
+ msg.readInt16(); // class
+ int hairStyle = msg.readInt16();
+ Uint16 weapon = msg.readInt16();
+ tempPlayer->setSprite(Being::WEAPON_SPRITE, weapon);
+ tempPlayer->setLevel(msg.readInt16());
+ msg.readInt16(); // skill point
+ tempPlayer->setSprite(Being::BOTTOMCLOTHES_SPRITE, msg.readInt16()); // head bottom
+ tempPlayer->setSprite(Being::SHIELD_SPRITE, msg.readInt16());
+ tempPlayer->setSprite(Being::HAT_SPRITE, msg.readInt16()); // head option top
+ tempPlayer->setSprite(Being::TOPCLOTHES_SPRITE, msg.readInt16()); // head option mid
+ int hairColor = msg.readInt16();
+ tempPlayer->setHairStyle(hairStyle, hairColor);
+ tempPlayer->setSprite(Being::MISC2_SPRITE, msg.readInt16());
+ tempPlayer->setName(msg.readString(24));
+ for (int i = 0; i < 6; i++) {
+ tempPlayer->mAttr[i] = msg.readInt8();
+ }
+ slot = msg.readInt8(); // character slot
+ msg.readInt8(); // unknown
+
+ return tempPlayer;
+}
diff --git a/src/net/ea/charserverhandler.h b/src/net/ea/charserverhandler.h
new file mode 100644
index 00000000..237f5e49
--- /dev/null
+++ b/src/net/ea/charserverhandler.h
@@ -0,0 +1,65 @@
+/*
+ * 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 NET_CHARSERVERHANDLER_H
+#define NET_CHARSERVERHANDLER_H
+
+#include "../messagehandler.h"
+
+#include "../../lockedarray.h"
+
+class CharCreateDialog;
+class LocalPlayer;
+class LoginData;
+
+/**
+ * Deals with incoming messages from the character server.
+ */
+class CharServerHandler : public MessageHandler
+{
+ public:
+ CharServerHandler();
+
+ void handleMessage(MessageIn &msg);
+
+ void setCharInfo(LockedArray<LocalPlayer*> *charInfo)
+ { mCharInfo = charInfo; }
+
+ void setLoginData(LoginData *loginData)
+ { mLoginData = loginData; }
+
+ /**
+ * 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)
+ { mCharCreateDialog = window; }
+
+ protected:
+ LoginData *mLoginData;
+ LockedArray<LocalPlayer*> *mCharInfo;
+ CharCreateDialog *mCharCreateDialog;
+
+ LocalPlayer* readPlayerData(MessageIn &msg, int &slot);
+};
+
+#endif
diff --git a/src/net/ea/chathandler.cpp b/src/net/ea/chathandler.cpp
new file mode 100644
index 00000000..0293f987
--- /dev/null
+++ b/src/net/ea/chathandler.cpp
@@ -0,0 +1,175 @@
+/*
+ * 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
+ */
+
+#include <SDL_types.h>
+#include <string>
+
+#include "chathandler.h"
+#include "../messagein.h"
+#include "protocol.h"
+
+#include "../../being.h"
+#include "../../beingmanager.h"
+#include "../../game.h"
+#include "../../player_relations.h"
+
+#include "../../gui/chat.h"
+
+#include "../../utils/gettext.h"
+#include "../../utils/stringutils.h"
+
+extern Being *player_node;
+
+#define SERVER_NAME "Server"
+
+ChatHandler::ChatHandler()
+{
+ static const Uint16 _messages[] = {
+ SMSG_BEING_CHAT,
+ SMSG_PLAYER_CHAT,
+ SMSG_WHISPER,
+ SMSG_WHISPER_RESPONSE,
+ SMSG_GM_CHAT,
+ SMSG_WHO_ANSWER,
+ 0x10c, // MVP
+ 0
+ };
+ handledMessages = _messages;
+}
+
+void ChatHandler::handleMessage(MessageIn &msg)
+{
+ Being *being;
+ std::string chatMsg;
+ std::string nick;
+ Sint16 chatMsgLength;
+
+ switch (msg.getId())
+ {
+ case SMSG_WHISPER_RESPONSE:
+ switch (msg.readInt8())
+ {
+ case 0x00:
+ // comment out since we'll local echo in chat.cpp instead, then only report failures
+ //chatWindow->chatLog("Whisper sent", BY_SERVER);
+ break;
+ case 0x01:
+ chatWindow->chatLog(_("Whisper could not be sent, user is offline"), BY_SERVER);
+ break;
+ case 0x02:
+ chatWindow->chatLog(_("Whisper could not be sent, ignored by user"), BY_SERVER);
+ break;
+ }
+ 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_NAME)
+ chatMsg = nick + " : " + chatMsg;
+
+ if (nick == SERVER_NAME)
+ chatWindow->chatLog(chatMsg, BY_SERVER);
+ else {
+ if (player_relations.hasPermission(nick, PlayerRelation::WHISPER))
+ chatWindow->chatLog(chatMsg, ACT_WHISPER);
+ }
+
+ break;
+
+ // Received speech from being
+ case SMSG_BEING_CHAT: {
+ chatMsgLength = msg.readInt16() - 8;
+ being = beingManager->findBeing(msg.readInt32());
+
+ if (!being || chatMsgLength <= 0)
+ {
+ break;
+ }
+
+ chatMsg = msg.readString(chatMsgLength);
+
+ std::string::size_type pos = chatMsg.find(" : ", 0);
+ std::string sender_name = ((pos == std::string::npos)
+ ? ""
+ : chatMsg.substr(0, pos));
+
+ // 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->chatLog(chatMsg, BY_OTHER);
+
+ chatMsg.erase(0, pos + 3);
+ trim(chatMsg);
+
+ 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;
+ }
+
+ chatMsg = msg.readString(chatMsgLength);
+ std::string::size_type pos = chatMsg.find(" : ", 0);
+
+ if (msg.getId() == SMSG_PLAYER_CHAT)
+ {
+ chatWindow->chatLog(chatMsg, BY_PLAYER);
+
+ if (pos != std::string::npos)
+ chatMsg.erase(0, pos + 3);
+
+ trim(chatMsg);
+
+ player_node->setSpeech(chatMsg, SPEECH_TIME);
+ }
+ else
+ {
+ chatWindow->chatLog(chatMsg, BY_GM);
+ }
+ break;
+ }
+
+ case SMSG_WHO_ANSWER:
+ chatWindow->chatLog("Online users: " + toString(msg.readInt32()),
+ BY_SERVER);
+ break;
+
+ case 0x010c:
+ // Display MVP player
+ msg.readInt32(); // id
+ chatWindow->chatLog("MVP player", BY_SERVER);
+ break;
+ }
+}
diff --git a/src/net/ea/chathandler.h b/src/net/ea/chathandler.h
new file mode 100644
index 00000000..8207b1d5
--- /dev/null
+++ b/src/net/ea/chathandler.h
@@ -0,0 +1,35 @@
+/*
+ * 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 NET_CHATHANDLER_H
+#define NET_CHATHANDLER_H
+
+#include "../messagehandler.h"
+
+class ChatHandler : public MessageHandler
+{
+ public:
+ ChatHandler();
+
+ void handleMessage(MessageIn &msg);
+};
+
+#endif
diff --git a/src/net/equipmenthandler.cpp b/src/net/ea/equipmenthandler.cpp
index 9a3c396a..19063daf 100644
--- a/src/net/equipmenthandler.cpp
+++ b/src/net/ea/equipmenthandler.cpp
@@ -20,18 +20,18 @@
*/
#include "equipmenthandler.h"
-#include "messagein.h"
+#include "../messagein.h"
#include "protocol.h"
-#include "../equipment.h"
-#include "../inventory.h"
-#include "../item.h"
-#include "../localplayer.h"
-#include "../log.h"
+#include "../../equipment.h"
+#include "../../inventory.h"
+#include "../../item.h"
+#include "../../localplayer.h"
+#include "../../log.h"
-#include "../gui/chat.h"
+#include "../../gui/chat.h"
-#include "../utils/gettext.h"
+#include "../../utils/gettext.h"
EquipmentHandler::EquipmentHandler()
{
@@ -46,7 +46,7 @@ EquipmentHandler::EquipmentHandler()
handledMessages = _messages;
}
-void EquipmentHandler::handleMessage(MessageIn *msg)
+void EquipmentHandler::handleMessage(MessageIn &msg)
{
Sint32 itemCount;
Sint16 index, equipPoint, itemId;
@@ -55,23 +55,23 @@ void EquipmentHandler::handleMessage(MessageIn *msg)
Item *item;
Inventory *inventory = player_node->getInventory();
- switch (msg->getId())
+ switch (msg.getId())
{
case SMSG_PLAYER_EQUIPMENT:
- msg->readInt16(); // length
- itemCount = (msg->getLength() - 4) / 20;
+ msg.readInt16(); // length
+ itemCount = (msg.getLength() - 4) / 20;
for (int loop = 0; loop < itemCount; loop++)
{
- index = msg->readInt16();
- itemId = msg->readInt16();
- msg->readInt8(); // type
- msg->readInt8(); // identify flag
- msg->readInt16(); // equip type
- equipPoint = msg->readInt16();
- msg->readInt8(); // attribute
- msg->readInt8(); // refine
- msg->skip(8); // card
+ index = msg.readInt16();
+ itemId = msg.readInt16();
+ msg.readInt8(); // type
+ msg.readInt8(); // identify flag
+ msg.readInt16(); // equip type
+ equipPoint = msg.readInt16();
+ msg.readInt8(); // attribute
+ msg.readInt8(); // refine
+ msg.skip(8); // card
inventory->setItem(index, itemId, 1, true);
@@ -91,9 +91,9 @@ void EquipmentHandler::handleMessage(MessageIn *msg)
break;
case SMSG_PLAYER_EQUIP:
- index = msg->readInt16();
- equipPoint = msg->readInt16();
- type = msg->readInt8();
+ index = msg.readInt16();
+ equipPoint = msg.readInt16();
+ type = msg.readInt8();
logger->log("Equipping: %i %i %i", index, equipPoint, type);
@@ -131,9 +131,9 @@ void EquipmentHandler::handleMessage(MessageIn *msg)
break;
case SMSG_PLAYER_UNEQUIP:
- index = msg->readInt16();
- equipPoint = msg->readInt16();
- type = msg->readInt8();
+ index = msg.readInt16();
+ equipPoint = msg.readInt16();
+ type = msg.readInt8();
if (!type) {
chatWindow->chatLog(_("Unable to unequip."), BY_SERVER);
@@ -169,11 +169,11 @@ void EquipmentHandler::handleMessage(MessageIn *msg)
break;
case SMSG_PLAYER_ATTACK_RANGE:
- player_node->setAttackRange(msg->readInt16());
+ player_node->setAttackRange(msg.readInt16());
break;
case SMSG_PLAYER_ARROW_EQUIP:
- index = msg->readInt16();
+ index = msg.readInt16();
if (index <= 1)
break;
diff --git a/src/net/equipmenthandler.h b/src/net/ea/equipmenthandler.h
index c66d7932..fe4a7ecc 100644
--- a/src/net/equipmenthandler.h
+++ b/src/net/ea/equipmenthandler.h
@@ -22,14 +22,14 @@
#ifndef NET_EQUIPMENTHANDLER_H
#define NET_EQUIPMENTHANDLER_H
-#include "messagehandler.h"
+#include "../messagehandler.h"
class EquipmentHandler : public MessageHandler
{
public:
EquipmentHandler();
- void handleMessage(MessageIn *msg);
+ void handleMessage(MessageIn &msg);
};
#endif
diff --git a/src/net/ea/inventoryhandler.cpp b/src/net/ea/inventoryhandler.cpp
new file mode 100644
index 00000000..71eee291
--- /dev/null
+++ b/src/net/ea/inventoryhandler.cpp
@@ -0,0 +1,227 @@
+/*
+ * 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
+ */
+
+#include <SDL_types.h>
+
+#include "inventoryhandler.h"
+#include "../messagein.h"
+#include "protocol.h"
+
+#include "../../inventory.h"
+#include "../../item.h"
+#include "../../itemshortcut.h"
+#include "../../localplayer.h"
+#include "../../log.h"
+
+#include "../../gui/chat.h"
+
+#include "../../resources/iteminfo.h"
+
+#include "../../utils/gettext.h"
+#include "../../utils/strprintf.h"
+#include "../../utils/stringutils.h"
+
+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,
+ 0
+ };
+ handledMessages = _messages;
+}
+
+void InventoryHandler::handleMessage(MessageIn &msg)
+{
+ Sint32 number;
+ Sint16 index, amount, itemId, equipType, arrow;
+ Sint16 identified, cards[4], itemType;
+ Inventory *inventory = player_node->getInventory();
+ Inventory *storage = player_node->getStorage();
+
+ switch (msg.getId())
+ {
+ case SMSG_PLAYER_INVENTORY:
+ case SMSG_PLAYER_STORAGE_ITEMS:
+ case SMSG_PLAYER_STORAGE_EQUIP:
+ switch (msg.getId()) {
+ case SMSG_PLAYER_INVENTORY:
+ // Clear inventory - this will be a complete refresh
+ inventory->clear();
+ break;
+ case SMSG_PLAYER_STORAGE_ITEMS:
+ /*
+ * This packet will always be followed by a
+ * SMSG_PLAYER_STORAGE_EQUIP packet. The two packets
+ * together comprise a complete refresh of storage, so
+ * clear storage here
+ */
+ storage->clear();
+ logger->log("Received SMSG_PLAYER_STORAGE_ITEMS");
+ break;
+ default:
+ logger->log("Received SMSG_PLAYER_STORAGE_EQUIP");
+ break;
+ }
+ 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();
+ if (msg.getId() == SMSG_PLAYER_STORAGE_EQUIP) {
+ amount = 1;
+ msg.readInt16(); // Equip Point?
+ } else {
+ amount = msg.readInt16();
+ }
+ arrow = msg.readInt16();
+ if (msg.getId() == SMSG_PLAYER_STORAGE_EQUIP) {
+ msg.readInt8(); // Attribute (broken)
+ msg.readInt8(); // Refine level
+ }
+ for (int i = 0; i < 4; i++)
+ cards[i] = msg.readInt16();
+
+ if (msg.getId() == SMSG_PLAYER_INVENTORY) {
+ inventory->setItem(index, itemId, amount, false);
+
+ // Trick because arrows are not considered equipment
+ if (arrow & 0x8000) {
+ if (Item *item = inventory->getItem(index))
+ item->setEquipment(true);
+ }
+ } else {
+ 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]);
+ storage->setItem(index, itemId, amount, false);
+ }
+ }
+ break;
+
+ case SMSG_PLAYER_INVENTORY_ADD:
+ index = msg.readInt16();
+ amount = msg.readInt16();
+ itemId = msg.readInt16();
+ identified = msg.readInt8();
+ msg.readInt8(); // attribute
+ msg.readInt8(); // refine
+ for (int i = 0; i < 4; i++)
+ cards[i] = msg.readInt16();
+ equipType = msg.readInt16();
+ itemType = msg.readInt8();
+
+ if (msg.readInt8() > 0) {
+ chatWindow->chatLog(_("Unable to pick up item"), BY_SERVER);
+ } else {
+ const ItemInfo &itemInfo = ItemDB::get(itemId);
+ const std::string amountStr =
+ (amount > 1) ? toString(amount) : "a";
+ chatWindow->chatLog(strprintf(_("You picked up %s %s"),
+ amountStr.c_str(), itemInfo.getName().c_str()), BY_SERVER);
+
+ if (Item *item = inventory->getItem(index)) {
+ item->setId(itemId);
+ item->increaseQuantity(amount);
+ } else {
+ inventory->setItem(index, itemId, amount, equipType != 0);
+ }
+ }
+ break;
+
+ case SMSG_PLAYER_INVENTORY_REMOVE:
+ index = msg.readInt16();
+ amount = msg.readInt16();
+ 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();
+ msg.readInt16(); // item id
+ msg.readInt32(); // id
+ amount = msg.readInt16();
+ msg.readInt8(); // type
+
+ if (Item *item = inventory->getItem(index))
+ item->setQuantity(amount);
+ break;
+
+ case SMSG_ITEM_USE_RESPONSE:
+ index = msg.readInt16();
+ amount = msg.readInt16();
+
+ if (msg.readInt8() == 0) {
+ chatWindow->chatLog(_("Failed to use item"), BY_SERVER);
+ } else {
+ if (Item *item = inventory->getItem(index))
+ item->setQuantity(amount);
+ }
+ break;
+
+ case SMSG_PLAYER_STORAGE_STATUS:
+ /*
+ * Basic slots used vs total slots info
+ * We don't really need this information, but 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.
+ */
+ logger->log("Received SMSG_PLAYER_STORAGE_STATUS");
+ player_node->setInStorage(true);
+ break;
+
+ case SMSG_PLAYER_STORAGE_ADD:
+ /*
+ * Move an item into storage
+ */
+ break;
+
+ case SMSG_PLAYER_STORAGE_REMOVE:
+ /*
+ * Move an item out of storage
+ */
+ break;
+
+ case SMSG_PLAYER_STORAGE_CLOSE:
+ /*
+ * Storage access has been closed
+ */
+ player_node->setInStorage(false);
+ logger->log("Received SMSG_PLAYER_STORAGE_CLOSE");
+ break;
+ }
+}
diff --git a/src/net/ea/inventoryhandler.h b/src/net/ea/inventoryhandler.h
new file mode 100644
index 00000000..b2e469fa
--- /dev/null
+++ b/src/net/ea/inventoryhandler.h
@@ -0,0 +1,35 @@
+/*
+ * 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 NET_INVENTORYHANDLER_H
+#define NET_INVENTORYHANDLER_H
+
+#include "../messagehandler.h"
+
+class InventoryHandler : public MessageHandler
+{
+ public:
+ InventoryHandler();
+
+ void handleMessage(MessageIn &msg);
+};
+
+#endif
diff --git a/src/net/ea/loginhandler.cpp b/src/net/ea/loginhandler.cpp
new file mode 100644
index 00000000..3f58f2c0
--- /dev/null
+++ b/src/net/ea/loginhandler.cpp
@@ -0,0 +1,158 @@
+/*
+ * 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
+ */
+
+#include "loginhandler.h"
+#include "../messagein.h"
+#include "protocol.h"
+
+#include "../../log.h"
+#include "../../logindata.h"
+#include "../../main.h"
+#include "../../serverinfo.h"
+
+#include "../../utils/gettext.h"
+#include "../../utils/strprintf.h"
+#include "../../utils/stringutils.h"
+
+extern SERVER_INFO **server_info;
+
+LoginHandler::LoginHandler()
+{
+ static const Uint16 _messages[] = {
+ SMSG_CONNECTION_PROBLEM,
+ SMSG_UPDATE_HOST,
+ 0x0069,
+ 0x006a,
+ 0
+ };
+ handledMessages = _messages;
+}
+
+void LoginHandler::handleMessage(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:
+ errorMessage = _("This account is already logged in");
+ break;
+ default:
+ errorMessage = _("Unknown connection error");
+ break;
+ }
+ state = STATE_ERROR;
+ break;
+
+ case SMSG_UPDATE_HOST:
+ int len;
+
+ len = msg.readInt16() - 4;
+ mUpdateHost = msg.readString(len);
+
+ logger->log("Received update host \"%s\" from login server",
+ mUpdateHost.c_str());
+ break;
+
+ case 0x0069:
+ // Skip the length word
+ msg.skip(2);
+
+ n_server = (msg.getLength() - 47) / 32;
+ server_info =
+ (SERVER_INFO**) malloc(sizeof(SERVER_INFO*) * n_server);
+
+ mLoginData->session_ID1 = msg.readInt32();
+ mLoginData->account_ID = msg.readInt32();
+ mLoginData->session_ID2 = msg.readInt32();
+ msg.skip(30); // unknown
+ mLoginData->sex = msg.readInt8();
+
+ for (int i = 0; i < n_server; i++)
+ {
+ server_info[i] = new SERVER_INFO;
+
+ server_info[i]->address = msg.readInt32();
+ server_info[i]->port = msg.readInt16();
+ server_info[i]->name = msg.readString(20);
+ server_info[i]->online_users = msg.readInt32();
+ server_info[i]->updateHost = mUpdateHost;
+ msg.skip(2); // unknown
+
+ logger->log("Network: Server: %s (%s:%d)",
+ server_info[i]->name.c_str(),
+ ipToString(server_info[i]->address),
+ server_info[i]->port);
+ }
+ state = STATE_CHAR_SERVER;
+ break;
+
+ case 0x006a:
+ 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.\n Please 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;
+ }
+ state = STATE_ERROR;
+ break;
+ }
+}
diff --git a/src/net/ea/loginhandler.h b/src/net/ea/loginhandler.h
new file mode 100644
index 00000000..c2ba5083
--- /dev/null
+++ b/src/net/ea/loginhandler.h
@@ -0,0 +1,45 @@
+/*
+ * 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 NET_LOGINHANDLER_H
+#define NET_LOGINHANDLER_H
+
+#include <string>
+
+#include "../messagehandler.h"
+
+struct LoginData;
+
+class LoginHandler : public MessageHandler
+{
+ public:
+ LoginHandler();
+
+ void handleMessage(MessageIn &msg);
+
+ void setLoginData(LoginData *loginData) { mLoginData = loginData; };
+
+ private:
+ LoginData *mLoginData;
+ std::string mUpdateHost;
+};
+
+#endif
diff --git a/src/net/maploginhandler.cpp b/src/net/ea/maploginhandler.cpp
index b5192bd7..6931024e 100644
--- a/src/net/maploginhandler.cpp
+++ b/src/net/ea/maploginhandler.cpp
@@ -20,14 +20,14 @@
*/
#include "maploginhandler.h"
-#include "messagein.h"
+#include "../messagein.h"
#include "protocol.h"
-#include "../localplayer.h"
-#include "../log.h"
-#include "../main.h"
+#include "../../localplayer.h"
+#include "../../log.h"
+#include "../../main.h"
-#include "../utils/gettext.h"
+#include "../../utils/gettext.h"
MapLoginHandler::MapLoginHandler()
{
@@ -39,15 +39,15 @@ MapLoginHandler::MapLoginHandler()
handledMessages = _messages;
}
-void MapLoginHandler::handleMessage(MessageIn *msg)
+void MapLoginHandler::handleMessage(MessageIn &msg)
{
int code;
unsigned char direction;
- switch (msg->getId())
+ switch (msg.getId())
{
case SMSG_CONNECTION_PROBLEM:
- code = msg->readInt8();
+ code = msg.readInt8();
logger->log("Connection problem: %i", code);
switch (code) {
@@ -61,16 +61,16 @@ void MapLoginHandler::handleMessage(MessageIn *msg)
errorMessage = _("Unknown connection error");
break;
}
- state = ERROR_STATE;
+ state = STATE_ERROR;
break;
case SMSG_LOGIN_SUCCESS:
- msg->readInt32(); // server tick
- msg->readCoordinates(player_node->mX, player_node->mY, direction);
- msg->skip(2); // unknown
+ msg.readInt32(); // server tick
+ msg.readCoordinates(player_node->mX, player_node->mY, direction);
+ msg.skip(2); // unknown
logger->log("Protocol: Player start position: (%d, %d), Direction: %d",
player_node->mX, player_node->mY, direction);
- state = GAME_STATE;
+ state = STATE_GAME;
break;
}
}
diff --git a/src/net/maploginhandler.h b/src/net/ea/maploginhandler.h
index c7fee70c..1ce5ee79 100644
--- a/src/net/maploginhandler.h
+++ b/src/net/ea/maploginhandler.h
@@ -22,14 +22,14 @@
#ifndef NET_MAPLOGINHANDLER_H
#define NET_MAPLOGINHANDLER_H
-#include "messagehandler.h"
+#include "../messagehandler.h"
class MapLoginHandler : public MessageHandler
{
public:
MapLoginHandler();
- void handleMessage(MessageIn *msg);
+ void handleMessage(MessageIn &msg);
};
#endif
diff --git a/src/net/ea/network.cpp b/src/net/ea/network.cpp
new file mode 100644
index 00000000..199e94da
--- /dev/null
+++ b/src/net/ea/network.cpp
@@ -0,0 +1,436 @@
+/*
+ * 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
+ */
+
+#include <sstream>
+
+#include "../messagehandler.h"
+#include "../messagein.h"
+#include "network.h"
+
+#include "../../log.h"
+#include "../../utils/stringutils.h"
+
+/** 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, 0, 0, -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,
+// #0x200
+ 26, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+const unsigned int BUFFER_SIZE = 65536;
+
+int networkThread(void *data)
+{
+ Network *network = static_cast<Network*>(data);
+
+ if (!network->realConnect())
+ return -1;
+
+ network->receive();
+
+ return 0;
+}
+
+Network::Network():
+ mSocket(0),
+ mAddress(), mPort(0),
+ mInBuffer(new char[BUFFER_SIZE]),
+ mOutBuffer(new char[BUFFER_SIZE]),
+ mInSize(0), mOutSize(0),
+ mToSkip(0),
+ mState(IDLE),
+ mWorkerThread(0)
+{
+ mMutex = SDL_CreateMutex();
+}
+
+Network::~Network()
+{
+ clearHandlers();
+
+ if (mState != IDLE && mState != NET_ERROR)
+ disconnect();
+
+ SDL_DestroyMutex(mMutex);
+
+ delete[] mInBuffer;
+ delete[] mOutBuffer;
+}
+
+bool Network::connect(const std::string &address, short port)
+{
+ if (mState != IDLE && mState != NET_ERROR)
+ {
+ logger->log("Tried to connect an already connected socket!");
+ return false;
+ }
+
+ if (address.empty())
+ {
+ setError("Empty address given to Network::connect()!");
+ return false;
+ }
+
+ logger->log("Network::Connecting to %s:%i", address.c_str(), port);
+
+ mAddress = address;
+ mPort = 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_WaitThread(mWorkerThread, NULL);
+ mWorkerThread = NULL;
+ }
+
+ if (mSocket)
+ {
+ SDLNet_TCP_Close(mSocket);
+ mSocket = 0;
+ }
+}
+
+void Network::registerHandler(MessageHandler *handler)
+{
+ for (const Uint16 *i = handler->handledMessages; *i; i++)
+ {
+ mMessageHandlers[*i] = handler;
+ }
+
+ handler->setNetwork(this);
+}
+
+void Network::unregisterHandler(MessageHandler *handler)
+{
+ 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++)
+ {
+ i->second->setNetwork(0);
+ }
+ mMessageHandlers.clear();
+}
+
+void Network::dispatchMessages()
+{
+ while (messageReady())
+ {
+ MessageIn msg = getNextMessage();
+
+ MessageHandlerIterator iter = mMessageHandlers.find(msg.getId());
+
+ if (iter != mMessageHandlers.end())
+ 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;
+
+ SDL_mutexP(mMutex);
+ if (mInSize >= 2)
+ {
+ len = packet_lengths[readWord(0)];
+
+ 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 = packet_lengths[msgId];
+
+ if (len == -1)
+ len = readWord(2);
+
+#ifdef DEBUG
+ logger->log("Received packet 0x%x of length %d", msgId, len);
+#endif
+
+ MessageIn msg(mInBuffer, len);
+ SDL_mutexV(mMutex);
+
+ return msg;
+}
+
+bool Network::realConnect()
+{
+ IPaddress ipAddress;
+
+ if (SDLNet_ResolveHost(&ipAddress, mAddress.c_str(), mPort) == -1)
+ {
+ std::string error = "Unable to resolve host \"" + mAddress + "\"";
+ setError(error);
+ logger->log("SDLNet_ResolveHost: %s", error.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->log("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->log("Disconnected.");
+ }
+ else if (ret < 0)
+ {
+ setError("Error in SDLNet_TCP_Recv(): " +
+ 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);
+}
+
+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
+}
diff --git a/src/net/ea/network.h b/src/net/ea/network.h
new file mode 100644
index 00000000..02fe7538
--- /dev/null
+++ b/src/net/ea/network.h
@@ -0,0 +1,118 @@
+/*
+ * 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 NETWORK_
+#define NETWORK_
+
+#include <map>
+#include <SDL_net.h>
+#include <SDL_thread.h>
+#include <string>
+
+/**
+ * Protocol version, reported to the eAthena char and mapserver who can adjust
+ * the protocol accordingly.
+ */
+#define CLIENT_PROTOCOL_VERSION 1
+
+class MessageHandler;
+class MessageIn;
+
+class Network;
+
+class Network
+{
+ public:
+ friend int networkThread(void *data);
+ friend class MessageOut;
+
+ Network();
+
+ ~Network();
+
+ bool connect(const std::string &address, short port);
+
+ void disconnect();
+
+ 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,
+ CONNECTED,
+ CONNECTING,
+ DATA,
+ NET_ERROR
+ };
+
+ protected:
+ void setError(const std::string& error);
+
+ Uint16 readWord(int pos);
+
+ bool realConnect();
+
+ void receive();
+
+ TCPsocket mSocket;
+
+ std::string mAddress;
+ short mPort;
+
+ 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;
+};
+
+#endif
diff --git a/src/net/ea/npchandler.cpp b/src/net/ea/npchandler.cpp
new file mode 100644
index 00000000..068a3be6
--- /dev/null
+++ b/src/net/ea/npchandler.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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
+ */
+
+#include "../messagein.h"
+#include "npchandler.h"
+#include "protocol.h"
+
+#include "../../beingmanager.h"
+#include "../../localplayer.h"
+#include "../../npc.h"
+
+#include "../../gui/npc_text.h"
+#include "../../gui/npcintegerdialog.h"
+#include "../../gui/npclistdialog.h"
+#include "../../gui/npcstringdialog.h"
+
+extern NpcIntegerDialog *npcIntegerDialog;
+extern NpcListDialog *npcListDialog;
+extern NpcTextDialog *npcTextDialog;
+extern NpcStringDialog *npcStringDialog;
+
+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;
+}
+
+void NPCHandler::handleMessage(MessageIn &msg)
+{
+ int id;
+
+ switch (msg.getId())
+ {
+ case SMSG_NPC_CHOICE:
+ msg.readInt16(); // length
+ id = msg.readInt32();
+ player_node->setAction(LocalPlayer::STAND);
+ current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id));
+ npcListDialog->parseItems(msg.readString(msg.getLength() - 8));
+ npcListDialog->setVisible(true);
+ break;
+
+ case SMSG_NPC_MESSAGE:
+ msg.readInt16(); // length
+ id = msg.readInt32();
+ player_node->setAction(LocalPlayer::STAND);
+ current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id));
+ npcTextDialog->addText(msg.readString(msg.getLength() - 8));
+ npcListDialog->setVisible(false);
+ npcTextDialog->setVisible(true);
+ break;
+
+ case SMSG_NPC_CLOSE:
+ id = msg.readInt32();
+ current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id));
+ npcTextDialog->showCloseButton();
+ break;
+
+ case SMSG_NPC_NEXT:
+ // Next button in NPC dialog, currently unused
+ id = msg.readInt32();
+ current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id));
+ npcTextDialog->showNextButton();
+ break;
+
+ case SMSG_NPC_INT_INPUT:
+ // Request for an integer
+ id = msg.readInt32();
+ current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id));
+ npcIntegerDialog->setRange(0, 2147483647);
+ npcIntegerDialog->setDefaultValue(0);
+ npcIntegerDialog->setVisible(true);
+ npcIntegerDialog->requestFocus();
+ break;
+
+ case SMSG_NPC_STR_INPUT:
+ // Request for a string
+ id = msg.readInt32();
+ current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id));
+ npcStringDialog->setValue("");
+ npcStringDialog->setVisible(true);
+ npcStringDialog->requestFocus();
+ break;
+ }
+}
diff --git a/src/net/ea/npchandler.h b/src/net/ea/npchandler.h
new file mode 100644
index 00000000..49df20c3
--- /dev/null
+++ b/src/net/ea/npchandler.h
@@ -0,0 +1,35 @@
+/*
+ * 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 NET_NPCHANDLER_H
+#define NET_NPCHANDLER_H
+
+#include "../messagehandler.h"
+
+class NPCHandler : public MessageHandler
+{
+ public:
+ NPCHandler();
+
+ void handleMessage(MessageIn &msg);
+};
+
+#endif
diff --git a/src/net/ea/partyhandler.cpp b/src/net/ea/partyhandler.cpp
new file mode 100644
index 00000000..d1d3b55e
--- /dev/null
+++ b/src/net/ea/partyhandler.cpp
@@ -0,0 +1,122 @@
+/*
+ * The Mana World
+ * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net>
+ *
+ * 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
+ */
+
+#include <guichan/actionlistener.hpp>
+
+#include "partyhandler.h"
+#include "protocol.h"
+#include "../messagein.h"
+
+#include "../../gui/chat.h"
+#include "../../gui/confirm_dialog.h"
+
+#include "../../beingmanager.h"
+#include "../../party.h"
+
+PartyHandler::PartyHandler(Party *party) : mParty(party)
+{
+ static const Uint16 _messages[] = {
+ SMSG_PARTY_CREATE,
+ SMSG_PARTY_INFO,
+ SMSG_PARTY_INVITE,
+ SMSG_PARTY_INVITED,
+ SMSG_PARTY_SETTINGS,
+ SMSG_PARTY_MEMBER_INFO,
+ SMSG_PARTY_LEAVE,
+ SMSG_PARTY_UPDATE_HP,
+ SMSG_PARTY_UPDATE_COORDS,
+ SMSG_PARTY_MESSAGE,
+ 0
+ };
+ handledMessages = _messages;
+}
+
+void PartyHandler::handleMessage(MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case SMSG_PARTY_CREATE:
+ mParty->createResponse(msg.readInt8());
+ break;
+ case SMSG_PARTY_INFO:
+ break;
+ case SMSG_PARTY_INVITE:
+ {
+ std::string nick = msg.readString(24);
+ int status = msg.readInt8();
+ mParty->inviteResponse(nick, status);
+ break;
+ }
+ case SMSG_PARTY_INVITED:
+ {
+ int id = msg.readInt32();
+ Being *being = beingManager->findBeing(id);
+ if (!being)
+ {
+ break;
+ }
+ std::string nick;
+ int gender = 0;
+ std::string partyName = "";
+ if (being->getType() != Being::PLAYER)
+ {
+ nick = "";
+ }
+ else
+ {
+ nick = being->getName();
+ gender = being->getGender();
+ partyName = msg.readString(24);
+ }
+ mParty->invitedAsk(nick, gender, partyName);
+ break;
+ }
+ case SMSG_PARTY_SETTINGS:
+ break;
+ case SMSG_PARTY_MEMBER_INFO:
+ break;
+ case SMSG_PARTY_LEAVE:
+ {
+ /*int id = */msg.readInt32();
+ std::string nick = msg.readString(24);
+ /*int fail = */msg.readInt8();
+ mParty->leftResponse(nick);
+ break;
+ }
+ case SMSG_PARTY_UPDATE_HP:
+ break;
+ case SMSG_PARTY_UPDATE_COORDS:
+ break;
+ case SMSG_PARTY_MESSAGE:
+ { // new block to enable local variables
+ int msgLength = msg.readInt16() - 8;
+ if (msgLength <= 0)
+ {
+ return;
+ }
+ int id = msg.readInt32();
+ Being *being = beingManager->findBeing(id);
+ std::string chatMsg = msg.readString(msgLength);
+ mParty->receiveChat(being, chatMsg);
+ }
+ break;
+ }
+}
diff --git a/src/net/ea/partyhandler.h b/src/net/ea/partyhandler.h
new file mode 100644
index 00000000..5c10eb21
--- /dev/null
+++ b/src/net/ea/partyhandler.h
@@ -0,0 +1,39 @@
+/*
+ * The Mana World
+ * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net>
+ *
+ * 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 PARTYHANDLER_H
+#define PARTYHANDLER_H
+
+#include "../messagehandler.h"
+
+class Party;
+
+class PartyHandler : public MessageHandler
+{
+ public:
+ PartyHandler(Party *party);
+
+ void handleMessage(MessageIn &msg);
+ private:
+ Party *mParty;
+};
+
+#endif
diff --git a/src/net/ea/playerhandler.cpp b/src/net/ea/playerhandler.cpp
new file mode 100644
index 00000000..9f0acbb3
--- /dev/null
+++ b/src/net/ea/playerhandler.cpp
@@ -0,0 +1,417 @@
+/*
+ * 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
+ */
+
+#include "../messagein.h"
+#include "playerhandler.h"
+#include "protocol.h"
+
+#include "../../engine.h"
+#include "../../localplayer.h"
+#include "../../log.h"
+#include "../../npc.h"
+#include "../../units.h"
+
+#include "../../gui/buy.h"
+#include "../../gui/chat.h"
+#include "../../gui/gui.h"
+#include "../../gui/npclistdialog.h"
+#include "../../gui/npc_text.h"
+#include "../../gui/ok_dialog.h"
+#include "../../gui/sell.h"
+#include "../../gui/skill.h"
+#include "../../gui/viewport.h"
+
+#include "../../utils/stringutils.h"
+#include "../../utils/gettext.h"
+
+// TODO Move somewhere else
+OkDialog *weightNotice = NULL;
+OkDialog *deathNotice = NULL;
+
+extern NpcListDialog *npcListDialog;
+extern NpcTextDialog *npcTextDialog;
+extern BuyDialog *buyDialog;
+extern SellDialog *sellDialog;
+extern Window *buySellDialog;
+
+// 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;
+
+/**
+ * Listener used for handling the overweigth message.
+ */
+// TODO Move somewhere else
+namespace {
+ struct WeightListener : public gcn::ActionListener
+ {
+ void action(const gcn::ActionEvent &event)
+ {
+ weightNotice = NULL;
+ }
+ } weightListener;
+}
+
+/**
+ * Listener used for handling death message.
+ */
+// TODO Move somewhere else
+namespace {
+ struct DeathListener : public gcn::ActionListener
+ {
+ void action(const gcn::ActionEvent &event)
+ {
+ player_node->revive();
+ deathNotice = NULL;
+ npcListDialog->setVisible(false);
+ npcTextDialog->setVisible(false);
+ buyDialog->setVisible(false);
+ sellDialog->setVisible(false);
+ buySellDialog->setVisible(false);
+ if (current_npc) current_npc->handleDeath();
+ }
+ } deathListener;
+}
+
+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;
+}
+
+void PlayerHandler::handleMessage(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.
+ */
+ break;
+
+ case SMSG_PLAYER_WARP:
+ {
+ std::string mapPath = msg.readString(16);
+ bool nearby;
+ Uint16 x = msg.readInt16();
+ Uint16 y = msg.readInt16();
+
+ logger->log("Warping to %s (%d, %d)", mapPath.c_str(), x, y);
+
+ /*
+ * We must clear the local player's target *before* the call
+ * to changeMap, as it deletes all beings.
+ */
+ player_node->stopAttack();
+
+ nearby = (engine->getCurrentMapName() == mapPath);
+
+ // Switch the actual map, deleting the previous one if necessary
+ engine->changeMap(mapPath);
+
+ if (current_npc) current_npc->handleDeath();
+
+ float scrollOffsetX = 0.0f;
+ float scrollOffsetY = 0.0f;
+
+ /* Scroll if neccessary */
+ if (!nearby
+ || (abs(x - player_node->mX) > MAP_TELEPORT_SCROLL_DISTANCE)
+ || (abs(y - player_node->mY) > MAP_TELEPORT_SCROLL_DISTANCE))
+ {
+ scrollOffsetX = (x - player_node->mX) * 32;
+ scrollOffsetY = (y - player_node->mY) * 32;
+ }
+
+ player_node->setAction(Being::STAND);
+ player_node->mFrame = 0;
+ player_node->mX = x;
+ player_node->mY = y;
+
+ logger->log("Adjust scrolling by %d:%d",
+ (int)scrollOffsetX,
+ (int)scrollOffsetY);
+
+ viewport->scrollBy(scrollOffsetX, scrollOffsetY);
+ }
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_1:
+ {
+ Sint16 type = msg.readInt16();
+ Uint32 value = msg.readInt32();
+
+ switch (type)
+ {
+ //case 0x0000:
+ // player_node->setWalkSpeed(msg.readInt32());
+ // break;
+ case 0x0005: player_node->setHp(value); break;
+ case 0x0006: player_node->setMaxHp(value); break;
+ case 0x0007: player_node->mMp = value; break;
+ case 0x0008: player_node->mMaxMp = value; break;
+ case 0x0009:
+ player_node->mStatsPointsToAttribute = value;
+ break;
+ case 0x000b: player_node->setLevel(value); break;
+ case 0x000c:
+ player_node->mSkillPoint = value;
+ skillDialog->update();
+ break;
+ case 0x0018:
+ if ((int) value >= player_node->getMaxWeight() / 2 &&
+ player_node->getTotalWeight() <
+ player_node->getMaxWeight() / 2)
+ {
+ weightNotice = new OkDialog(_("Message"),
+ _("You are carrying more than "
+ "half your weight. You are "
+ "unable to regain health."));
+ weightNotice->addActionListener(
+ &weightListener);
+ }
+ player_node->setTotalWeight(value);
+ break;
+ case 0x0019: player_node->setMaxWeight(value); break;
+ case 0x0029: player_node->ATK = value; break;
+ case 0x002b: player_node->MATK = value; break;
+ case 0x002d: player_node->DEF = value; break;
+ case 0x002e: player_node->DEF_BONUS = value; break;
+ case 0x002f: player_node->MDEF = value; break;
+ case 0x0031: player_node->HIT = value; break;
+ case 0x0032: player_node->FLEE = value; break;
+ case 0x0035: player_node->mAttackSpeed = value; break;
+ case 0x0037: player_node->mJobLevel = value; break;
+ }
+
+ if (player_node->getHp() == 0 && !deathNotice)
+ {
+ 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!"),
+ _("Insert coin to continue"),
+ _("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."),
+ // NetHack reference:
+ _("Do you want your possessions identified?"),
+ // Secret of Mana reference:
+ _("Sadly, no trace of you was ever found..."),
+ // Final Fantasy VI reference:
+ _("Annihilated."),
+ // Earthbound reference:
+ _("Looks like you got your head handed to you."),
+ // Leisure Suit Larry 1 reference:
+ _("You screwed up again, dump your body down the tubes "
+ "and get you another one."),
+ // Monty Python references (Dead Parrot sketch mostly):
+ _("You're not dead yet. You're just resting."),
+ _("You are no more."),
+ _("You have ceased to be."),
+ _("You've expired and gone to meet your maker."),
+ _("You're a stiff."),
+ _("Bereft of life, you rest in peace."),
+ _("If you weren't so animated, you'd be pushing up the "
+ "daisies."),
+ _("Your metabolic processes are now history."),
+ _("You're off the twig."),
+ _("You've kicked the bucket."),
+ _("You've shuffled off your mortal coil, run down the "
+ "curtain and joined the bleedin' choir invisibile."),
+ _("You are an ex-player."),
+ _("You're pining for the fjords.")
+ };
+ std::string message(deadMsg[rand()%27]);
+
+ deathNotice = new OkDialog(_("Message"), message);
+ deathNotice->addActionListener(&deathListener);
+ player_node->setAction(Being::DEAD);
+ }
+ }
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_2:
+ switch (msg.readInt16()) {
+ case 0x0001:
+ player_node->setXp(msg.readInt32());
+ break;
+ case 0x0002:
+ player_node->mJobXp = msg.readInt32();
+ break;
+ case 0x0014: {
+ int curGp = player_node->getMoney();
+ player_node->setMoney(msg.readInt32());
+ if (player_node->getMoney() > curGp)
+ chatWindow->chatLog(_("You picked up ") +
+ Units::formatCurrency(player_node->getMoney()
+ - curGp), BY_SERVER);
+ }
+ break;
+ case 0x0016:
+ player_node->mXpForNextLevel = msg.readInt32();
+ break;
+ case 0x0017:
+ player_node->mJobXpForNextLevel = msg.readInt32();
+ break;
+ }
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_3:
+ {
+ Sint32 type = msg.readInt32();
+ Sint32 base = msg.readInt32();
+ Sint32 bonus = msg.readInt32();
+ Sint32 total = base + bonus;
+
+ switch (type) {
+ case 0x000d: player_node->mAttr[LocalPlayer::STR] = total;
+ break;
+ case 0x000e: player_node->mAttr[LocalPlayer::AGI] = total;
+ break;
+ case 0x000f: player_node->mAttr[LocalPlayer::VIT] = total;
+ break;
+ case 0x0010: player_node->mAttr[LocalPlayer::INT] = total;
+ break;
+ case 0x0011: player_node->mAttr[LocalPlayer::DEX] = total;
+ break;
+ case 0x0012: player_node->mAttr[LocalPlayer::LUK] = total;
+ break;
+ }
+ }
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_4:
+ {
+ Sint16 type = msg.readInt16();
+ Sint8 fail = msg.readInt8();
+ Sint8 value = msg.readInt8();
+
+ if (fail != 1)
+ break;
+
+ switch (type) {
+ case 0x000d: player_node->mAttr[LocalPlayer::STR] = value;
+ break;
+ case 0x000e: player_node->mAttr[LocalPlayer::AGI] = value;
+ break;
+ case 0x000f: player_node->mAttr[LocalPlayer::VIT] = value;
+ break;
+ case 0x0010: player_node->mAttr[LocalPlayer::INT] = value;
+ break;
+ case 0x0011: player_node->mAttr[LocalPlayer::DEX] = value;
+ break;
+ case 0x0012: player_node->mAttr[LocalPlayer::LUK] = value;
+ break;
+ }
+ }
+ break;
+
+ // Updates stats and status points
+ case SMSG_PLAYER_STAT_UPDATE_5:
+ player_node->mStatsPointsToAttribute = msg.readInt16();
+ player_node->mAttr[LocalPlayer::STR] = msg.readInt8();
+ player_node->mAttrUp[LocalPlayer::STR] = msg.readInt8();
+ player_node->mAttr[LocalPlayer::AGI] = msg.readInt8();
+ player_node->mAttrUp[LocalPlayer::AGI] = msg.readInt8();
+ player_node->mAttr[LocalPlayer::VIT] = msg.readInt8();
+ player_node->mAttrUp[LocalPlayer::VIT] = msg.readInt8();
+ player_node->mAttr[LocalPlayer::INT] = msg.readInt8();
+ player_node->mAttrUp[LocalPlayer::INT] = msg.readInt8();
+ player_node->mAttr[LocalPlayer::DEX] = msg.readInt8();
+ player_node->mAttrUp[LocalPlayer::DEX] = msg.readInt8();
+ player_node->mAttr[LocalPlayer::LUK] = msg.readInt8();
+ player_node->mAttrUp[LocalPlayer::LUK] = msg.readInt8();
+ player_node->ATK = msg.readInt16(); // ATK
+ player_node->ATK_BONUS = msg.readInt16(); // ATK bonus
+ player_node->MATK = msg.readInt16(); // MATK max
+ player_node->MATK_BONUS = msg.readInt16(); // MATK min
+ player_node->DEF = msg.readInt16(); // DEF
+ player_node->DEF_BONUS = msg.readInt16(); // DEF bonus
+ player_node->MDEF = msg.readInt16(); // MDEF
+ player_node->MDEF_BONUS = msg.readInt16(); // MDEF bonus
+ player_node->HIT = msg.readInt16(); // HIT
+ player_node->FLEE = msg.readInt16(); // FLEE
+ player_node->FLEE_BONUS = msg.readInt16(); // FLEE bonus
+ msg.readInt16(); // critical
+ msg.readInt16(); // unknown
+ break;
+
+ case SMSG_PLAYER_STAT_UPDATE_6:
+ switch (msg.readInt16()) {
+ case 0x0020:
+ player_node->mAttrUp[LocalPlayer::STR] = msg.readInt8();
+ break;
+ case 0x0021:
+ player_node->mAttrUp[LocalPlayer::AGI] = msg.readInt8();
+ break;
+ case 0x0022:
+ player_node->mAttrUp[LocalPlayer::VIT] = msg.readInt8();
+ break;
+ case 0x0023:
+ player_node->mAttrUp[LocalPlayer::INT] = msg.readInt8();
+ break;
+ case 0x0024:
+ player_node->mAttrUp[LocalPlayer::DEX] = msg.readInt8();
+ break;
+ case 0x0025:
+ player_node->mAttrUp[LocalPlayer::LUK] = msg.readInt8();
+ break;
+ }
+ break;
+
+ case SMSG_PLAYER_ARROW_MESSAGE:
+ {
+ Sint16 type = msg.readInt16();
+
+ switch (type) {
+ case 0:
+ chatWindow->chatLog(_("Equip arrows first"),
+ BY_SERVER);
+ break;
+ default:
+ logger->log("0x013b: Unhandled message %i", type);
+ break;
+ }
+ }
+ break;
+ }
+}
diff --git a/src/net/ea/playerhandler.h b/src/net/ea/playerhandler.h
new file mode 100644
index 00000000..f3352289
--- /dev/null
+++ b/src/net/ea/playerhandler.h
@@ -0,0 +1,35 @@
+/*
+ * 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 NET_PLAYERHANDLER_H
+#define NET_PLAYERHANDLER_H
+
+#include "../messagehandler.h"
+
+class PlayerHandler : public MessageHandler
+{
+ public:
+ PlayerHandler();
+
+ void handleMessage(MessageIn &msg);
+};
+
+#endif
diff --git a/src/net/protocol.cpp b/src/net/ea/protocol.cpp
index 69d69901..69d69901 100644
--- a/src/net/protocol.cpp
+++ b/src/net/ea/protocol.cpp
diff --git a/src/net/ea/protocol.h b/src/net/ea/protocol.h
new file mode 100644
index 00000000..55c0d8b6
--- /dev/null
+++ b/src/net/ea/protocol.h
@@ -0,0 +1,162 @@
+/*
+ * 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 PROTOCOL_H
+#define PROTOCOL_H
+
+/*********************************
+ * Packets from server to client *
+ *********************************/
+#define SMSG_LOGIN_SUCCESS 0x0073 /**< Contains starting location */
+#define SMSG_SERVER_PING 0x007f /**< Contains server tick */
+#define SMSG_CONNECTION_PROBLEM 0x0081
+#define SMSG_UPDATE_HOST 0x0063 /**< Custom update host packet */
+#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_SKILL_FAILED 0x0110
+#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_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 0x00fd
+#define SMSG_PARTY_INVITED 0x00fe
+#define SMSG_PARTY_SETTINGS 0x0102
+#define SMSG_PARTY_MEMBER_INFO 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 */
+
+/**********************************
+ * Packets from client to server *
+ **********************************/
+#define CMSG_CLIENT_PING 0x007e /**< Send to server with tick */
+#define CMSG_TRADE_RESPONSE 0x00e6
+#define CMSG_ITEM_PICKUP 0x009f
+#define CMSG_MAP_LOADED 0x007d
+#define CMSG_NPC_BUY_REQUEST 0x00c8
+#define CMSG_NPC_BUY_SELL_REQUEST 0x00c5
+#define CMSG_CHAT_MESSAGE 0x008c
+#define CMSG_CHAT_WHISPER 0x0096
+#define CMSG_CHAT_ANNOUNCE 0x0099
+#define CMSG_CHAT_WHO 0x00c1
+#define CMSG_NPC_LIST_CHOICE 0x00b8
+#define CMSG_NPC_NEXT_REQUEST 0x00b9
+#define CMSG_NPC_SELL_REQUEST 0x00c9
+#define CMSG_NPC_INT_RESPONSE 0x0143
+#define CMSG_NPC_STR_RESPONSE 0x01d5
+#define CMSG_SKILL_LEVELUP_REQUEST 0x0112
+#define CMSG_STAT_UPDATE_REQUEST 0x00bb
+#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_NPC_TALK 0x0090
+#define CMSG_TRADE_REQUEST 0x00e4
+#define CMSG_PLAYER_INVENTORY_USE 0x00a7
+#define CMSG_PLAYER_INVENTORY_DROP 0x00a2
+#define CMSG_PLAYER_EQUIP 0x00a9
+#define CMSG_PLAYER_UNEQUIP 0x00ab
+
+#define CMSG_PARTY_CREATE 0x00f9
+#define CMSG_PARTY_INVITE 0x00fc
+#define CMSG_PARTY_INVITED 0x00ff
+#define CMSG_PARTY_LEAVE 0x0100 /** Undocumented */
+#define CMSG_PARTY_SETTINGS 0x0101
+#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 */
+
+/** Encodes coords and direction in 3 bytes data */
+void set_coordinates(char *data, unsigned short x, unsigned short y, unsigned char direction);
+
+#endif
diff --git a/src/net/skillhandler.cpp b/src/net/ea/skillhandler.cpp
index e2185524..6e766008 100644
--- a/src/net/skillhandler.cpp
+++ b/src/net/ea/skillhandler.cpp
@@ -19,14 +19,14 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "messagein.h"
+#include "../messagein.h"
#include "protocol.h"
#include "skillhandler.h"
-#include "../log.h"
+#include "../../log.h"
-#include "../gui/chat.h"
-#include "../gui/skill.h"
+#include "../../gui/chat.h"
+#include "../../gui/skill.h"
SkillHandler::SkillHandler()
{
@@ -38,27 +38,27 @@ SkillHandler::SkillHandler()
handledMessages = _messages;
}
-void SkillHandler::handleMessage(MessageIn *msg)
+void SkillHandler::handleMessage(MessageIn &msg)
{
int skillCount;
- switch (msg->getId())
+ switch (msg.getId())
{
case SMSG_PLAYER_SKILLS:
- msg->readInt16(); // length
- skillCount = (msg->getLength() - 4) / 37;
+ msg.readInt16(); // length
+ skillCount = (msg.getLength() - 4) / 37;
skillDialog->cleanList();
for (int k = 0; k < skillCount; k++)
{
- Sint16 skillId = msg->readInt16();
- msg->readInt16(); // target type
- msg->readInt16(); // unknown
- Sint16 level = msg->readInt16();
- Sint16 sp = msg->readInt16();
- msg->readInt16(); // range
- std::string skillName = msg->readString(24);
- Sint8 up = msg->readInt8();
+ Sint16 skillId = msg.readInt16();
+ msg.readInt16(); // target type
+ msg.readInt16(); // unknown
+ Sint16 level = msg.readInt16();
+ Sint16 sp = msg.readInt16();
+ msg.readInt16(); // range
+ std::string skillName = msg.readString(24);
+ Sint8 up = msg.readInt8();
if (level != 0 || up != 0)
{
@@ -77,11 +77,11 @@ void SkillHandler::handleMessage(MessageIn *msg)
// Action failed (ex. sit because you have not reached the
// right level)
CHATSKILL action;
- action.skill = msg->readInt16();
- action.bskill = msg->readInt16();
- action.unused = msg->readInt16(); // unknown
- action.success = msg->readInt8();
- action.reason = msg->readInt8();
+ action.skill = msg.readInt16();
+ action.bskill = msg.readInt16();
+ action.unused = msg.readInt16(); // unknown
+ action.success = msg.readInt8();
+ action.reason = msg.readInt8();
if (action.success != SKILL_FAILED &&
action.bskill == BSKILL_EMOTE)
{
diff --git a/src/net/skillhandler.h b/src/net/ea/skillhandler.h
index 2b55605d..57d68f47 100644
--- a/src/net/skillhandler.h
+++ b/src/net/ea/skillhandler.h
@@ -22,14 +22,14 @@
#ifndef NET_SKILLHANDLER_H
#define NET_SKILLHANDLER_H
-#include "messagehandler.h"
+#include "../messagehandler.h"
class SkillHandler : public MessageHandler
{
public:
SkillHandler();
- void handleMessage(MessageIn *msg);
+ void handleMessage(MessageIn &msg);
};
#endif
diff --git a/src/net/ea/tradehandler.cpp b/src/net/ea/tradehandler.cpp
new file mode 100644
index 00000000..6c953a11
--- /dev/null
+++ b/src/net/ea/tradehandler.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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
+ */
+
+#include "../messagein.h"
+#include "protocol.h"
+#include "tradehandler.h"
+
+#include "../../inventory.h"
+#include "../../item.h"
+#include "../../localplayer.h"
+#include "../../player_relations.h"
+
+#include "../../gui/chat.h"
+#include "../../gui/confirm_dialog.h"
+#include "../../gui/trade.h"
+
+#include "../../utils/gettext.h"
+
+std::string tradePartnerName;
+
+/**
+ * Listener for request trade dialogs
+ */
+namespace {
+ struct RequestTradeListener : public gcn::ActionListener
+ {
+ void action(const gcn::ActionEvent &event)
+ {
+ player_node->tradeReply(event.getId() == "yes");
+ };
+ } listener;
+}
+
+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;
+}
+
+
+void TradeHandler::handleMessage(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.
+ tradePartnerName = msg.readString(24);
+
+ if (player_relations.hasPermission(tradePartnerName, PlayerRelation::TRADE))
+ {
+ if (!player_node->tradeRequestOk())
+ {
+ player_node->tradeReply(false);
+ break;
+ }
+
+ player_node->setTrading(true);
+ ConfirmDialog *dlg;
+ dlg = new ConfirmDialog(_("Request for trade"),
+ tradePartnerName +
+ _(" wants to trade with you, do you accept?"));
+ dlg->addActionListener(&listener);
+ }
+ else
+ {
+ player_node->tradeReply(false);
+ break;
+ }
+ break;
+
+ case SMSG_TRADE_RESPONSE:
+ switch (msg.readInt8())
+ {
+ case 0: // Too far away
+ chatWindow->chatLog(_("Trading isn't possible. Trade partner is too far away."),
+ BY_SERVER);
+ break;
+ case 1: // Character doesn't exist
+ chatWindow->chatLog(_("Trading isn't possible. Character doesn't exist."),
+ BY_SERVER);
+ break;
+ case 2: // Invite request check failed...
+ chatWindow->chatLog(_("Trade cancelled due to an unknown reason."),
+ BY_SERVER);
+ break;
+ case 3: // Trade accepted
+ tradeWindow->reset();
+ tradeWindow->setCaption(
+ _("Trade: You and ") + tradePartnerName);
+ tradeWindow->setVisible(true);
+ break;
+ case 4: // Trade cancelled
+ if (player_relations.hasPermission(tradePartnerName,
+ PlayerRelation::SPEECH_LOG))
+ chatWindow->chatLog(_("Trade with ") + tradePartnerName +
+ _(" cancelled"), BY_SERVER);
+ // otherwise ignore silently
+
+ tradeWindow->setVisible(false);
+ player_node->setTrading(false);
+ break;
+ default: // Shouldn't happen as well, but to be sure
+ chatWindow->chatLog(_("Unhandled trade cancel packet"),
+ BY_SERVER);
+ break;
+ }
+ break;
+
+ case SMSG_TRADE_ITEM_ADD:
+ {
+ Sint32 amount = msg.readInt32();
+ Sint16 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 (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();
+ Item *item = player_node->getInventory()->getItem(index);
+ if (!item)
+ {
+ tradeWindow->receivedOk(true);
+ return;
+ }
+ Sint16 quantity = msg.readInt16();
+
+ switch (msg.readInt8())
+ {
+ case 0:
+ // Successfully added item
+ if (item->isEquipment() && item->isEquipped())
+ {
+ player_node->unequipItem(item);
+ }
+ tradeWindow->addItem(item->getId(), true, quantity,
+ item->isEquipment());
+ item->increaseQuantity(-quantity);
+ break;
+ case 1:
+ // Add item failed - player overweighted
+ chatWindow->chatLog(_("Failed adding item. Trade partner is over weighted."),
+ BY_SERVER);
+ break;
+ case 2:
+ // Add item failed - player has no free slot
+ chatWindow->chatLog(_("Failed adding item. Trade partner has no free slot."),
+ BY_SERVER);
+ break;
+ default:
+ chatWindow->chatLog(_("Failed adding item for unknown reason."),
+ BY_SERVER);
+ break;
+ }
+ }
+ break;
+
+ case SMSG_TRADE_OK:
+ // 0 means ok from myself, 1 means ok from other;
+ tradeWindow->receivedOk(msg.readInt8() == 0);
+ break;
+
+ case SMSG_TRADE_CANCEL:
+ chatWindow->chatLog(_("Trade canceled."), BY_SERVER);
+ tradeWindow->setVisible(false);
+ tradeWindow->reset();
+ player_node->setTrading(false);
+ break;
+
+ case SMSG_TRADE_COMPLETE:
+ chatWindow->chatLog(_("Trade completed."), BY_SERVER);
+ tradeWindow->setVisible(false);
+ tradeWindow->reset();
+ player_node->setTrading(false);
+ break;
+ }
+}
diff --git a/src/net/ea/tradehandler.h b/src/net/ea/tradehandler.h
new file mode 100644
index 00000000..04335069
--- /dev/null
+++ b/src/net/ea/tradehandler.h
@@ -0,0 +1,37 @@
+/*
+ * 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 NET_TRADEHANDLER_H
+#define NET_TRADEHANDLER_H
+
+#include "../messagehandler.h"
+
+class Network;
+
+class TradeHandler : public MessageHandler
+{
+ public:
+ TradeHandler();
+
+ void handleMessage(MessageIn &msg);
+};
+
+#endif
diff --git a/src/net/effecthandler.cpp b/src/net/effecthandler.cpp
new file mode 100644
index 00000000..8411b9e7
--- /dev/null
+++ b/src/net/effecthandler.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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
+ */
+
+#include "effecthandler.h"
+
+#include "messagein.h"
+#include "protocol.h"
+
+#include "../effectmanager.h"
+
+
+EffectHandler::EffectHandler()
+{
+ static const Uint16 _messages[] = {
+ GPMSG_CREATE_EFFECT,
+ 0
+ };
+ handledMessages = _messages;
+}
+
+void EffectHandler::handleMessage(MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case GPMSG_CREATE_EFFECT:
+ handleCreateEffects(msg);
+ break;
+ default:
+ break;
+ }
+}
+
+void EffectHandler::handleCreateEffects(MessageIn &msg)
+{
+ int id = msg.readInt16();
+ Uint16 x = msg.readInt16();
+ Uint16 y = msg.readInt16();
+ effectManager->trigger(id, x, y);
+}
+
diff --git a/src/net/effecthandler.h b/src/net/effecthandler.h
new file mode 100644
index 00000000..283c7c10
--- /dev/null
+++ b/src/net/effecthandler.h
@@ -0,0 +1,38 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_NET_EFFECTSHANDLER_H
+#define _TMW_NET_EFFECTSHANDLER_H
+
+#include "messagehandler.h"
+
+class EffectHandler : public MessageHandler
+{
+ public:
+ EffectHandler();
+
+ void handleMessage(MessageIn &msg);
+
+ private:
+ void handleCreateEffects(MessageIn &msg);
+};
+
+#endif
diff --git a/src/net/gameserver/gameserver.cpp b/src/net/gameserver/gameserver.cpp
new file mode 100644
index 00000000..1bdaef26
--- /dev/null
+++ b/src/net/gameserver/gameserver.cpp
@@ -0,0 +1,49 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "gameserver.h"
+
+#include "internal.h"
+
+#include "../connection.h"
+#include "../messageout.h"
+#include "../protocol.h"
+
+void Net::GameServer::connect(Net::Connection *connection,
+ const std::string &token)
+{
+ Net::GameServer::connection = connection;
+
+ MessageOut msg(PGMSG_CONNECT);
+
+ msg.writeString(token, 32);
+
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::logout(bool reconnectAccount)
+{
+ MessageOut msg(PGMSG_DISCONNECT);
+
+ msg.writeInt8((unsigned char) reconnectAccount);
+
+ Net::GameServer::connection->send(msg);
+}
diff --git a/src/net/gameserver/gameserver.h b/src/net/gameserver/gameserver.h
new file mode 100644
index 00000000..5ea2c718
--- /dev/null
+++ b/src/net/gameserver/gameserver.h
@@ -0,0 +1,39 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_NET_GAMESERVER_GAMESERVER_H
+#define _TMW_NET_GAMESERVER_GAMESERVER_H
+
+#include <iosfwd>
+
+namespace Net
+{
+ class Connection;
+
+ namespace GameServer
+ {
+ void connect(Net::Connection *connection, const std::string &token);
+
+ void logout(bool reconnectAccount);
+ }
+}
+
+#endif
diff --git a/src/net/gameserver/internal.cpp b/src/net/gameserver/internal.cpp
new file mode 100644
index 00000000..6b6ba081
--- /dev/null
+++ b/src/net/gameserver/internal.cpp
@@ -0,0 +1,32 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "internal.h"
+
+namespace Net
+{
+ class Connection;
+
+ namespace GameServer
+ {
+ Connection *connection = 0;
+ }
+}
diff --git a/src/net/gameserver/internal.h b/src/net/gameserver/internal.h
new file mode 100644
index 00000000..df9787fe
--- /dev/null
+++ b/src/net/gameserver/internal.h
@@ -0,0 +1,35 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_NET_GAMESERVER_INTERNAL_H
+#define _TMW_NET_GAMESERVER_INTERNAL_H
+
+namespace Net
+{
+ class Connection;
+
+ namespace GameServer
+ {
+ extern Connection *connection;
+ }
+}
+
+#endif
diff --git a/src/net/gameserver/player.cpp b/src/net/gameserver/player.cpp
new file mode 100644
index 00000000..95c13ec2
--- /dev/null
+++ b/src/net/gameserver/player.cpp
@@ -0,0 +1,202 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "player.h"
+
+#include "internal.h"
+
+#include "../connection.h"
+#include "../messageout.h"
+#include "../protocol.h"
+
+void RespawnRequestListener::action(const gcn::ActionEvent &event)
+{
+ Net::GameServer::Player::respawn();
+}
+
+void Net::GameServer::Player::say(const std::string &text)
+{
+ MessageOut msg(PGMSG_SAY);
+ msg.writeString(text);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::walk(int x, int y)
+{
+ MessageOut msg(PGMSG_WALK);
+ msg.writeInt16(x);
+ msg.writeInt16(y);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::pickUp(int x, int y)
+{
+ MessageOut msg(PGMSG_PICKUP);
+ msg.writeInt16(x);
+ msg.writeInt16(y);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::moveItem(int oldSlot, int newSlot, int amount)
+{
+ MessageOut msg(PGMSG_MOVE_ITEM);
+ msg.writeInt8(oldSlot);
+ msg.writeInt8(newSlot);
+ msg.writeInt8(amount);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::drop(int slot, int amount)
+{
+ MessageOut msg(PGMSG_DROP);
+ msg.writeInt8(slot);
+ msg.writeInt8(amount);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::equip(int slot)
+{
+ MessageOut msg(PGMSG_EQUIP);
+ msg.writeInt8(slot);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::unequip(int slot)
+{
+ MessageOut msg(PGMSG_UNEQUIP);
+ msg.writeInt8(slot);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::useItem(int slot)
+{
+ MessageOut msg(PGMSG_USE_ITEM);
+ msg.writeInt8(slot);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::attack(int direction)
+{
+ MessageOut msg(PGMSG_ATTACK);
+ msg.writeInt8(direction);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::useSpecial(int special)
+{
+ MessageOut msg(PGMSG_USE_SPECIAL);
+ msg.writeInt8(special);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::changeAction(Being::Action action)
+{
+ MessageOut msg(PGMSG_ACTION_CHANGE);
+ msg.writeInt8(action);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::talkToNPC(int id, bool restart)
+{
+ MessageOut msg(restart ? PGMSG_NPC_TALK : PGMSG_NPC_TALK_NEXT);
+ msg.writeInt16(id);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::selectFromNPC(int id, int choice)
+{
+ MessageOut msg(PGMSG_NPC_SELECT);
+ msg.writeInt16(id);
+ msg.writeInt8(choice);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::requestTrade(int id)
+{
+ MessageOut msg(PGMSG_TRADE_REQUEST);
+ msg.writeInt16(id);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::acceptTrade(bool accept)
+{
+ MessageOut msg(accept ? PGMSG_TRADE_ACCEPT : PGMSG_TRADE_CANCEL);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::tradeItem(int slot, int amount)
+{
+ MessageOut msg(PGMSG_TRADE_ADD_ITEM);
+ msg.writeInt8(slot);
+ msg.writeInt8(amount);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::tradeMoney(int amount)
+{
+ MessageOut msg(PGMSG_TRADE_SET_MONEY);
+ msg.writeInt32(amount);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::tradeWithNPC(int item, int amount)
+{
+ MessageOut msg(PGMSG_NPC_BUYSELL);
+ msg.writeInt16(item);
+ msg.writeInt16(amount);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::sendLetter(const std::string &player,
+ const std::string &text)
+{
+ MessageOut msg(PGMSG_NPC_POST_SEND);
+ msg.writeString(player);
+ msg.writeString(text);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::raiseAttribute(int attribute)
+{
+ MessageOut msg(PGMSG_RAISE_ATTRIBUTE);
+ msg.writeInt8(attribute);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::lowerAttribute(int attribute)
+{
+ MessageOut msg(PGMSG_LOWER_ATTRIBUTE);
+ msg.writeInt8(attribute);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::respawn()
+{
+ MessageOut msg(PGMSG_RESPAWN);
+ Net::GameServer::connection->send(msg);
+}
+
+void Net::GameServer::Player::changeDir(unsigned char dir)
+{
+ MessageOut msg(PGMSG_DIRECTION_CHANGE);
+ msg.writeInt8(dir);
+ Net::GameServer::connection->send(msg);
+}
diff --git a/src/net/gameserver/player.h b/src/net/gameserver/player.h
new file mode 100644
index 00000000..9e68ced9
--- /dev/null
+++ b/src/net/gameserver/player.h
@@ -0,0 +1,71 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_NET_GAMESERVER_PLAYER_H
+#define _TMW_NET_GAMESERVER_PLAYER_H
+
+#include "../../being.h"
+
+#include <guichan/actionlistener.hpp>
+
+#include <iosfwd>
+
+
+struct RespawnRequestListener : public gcn::ActionListener
+{
+ void action(const gcn::ActionEvent &event);
+};
+
+namespace Net
+{
+ namespace GameServer
+ {
+ namespace Player
+ {
+ void say(const std::string &text);
+ void walk(int x, int y);
+ void pickUp(int x, int y);
+ void moveItem(int oldSlot, int newSlot, int amount);
+ void drop(int slot, int amount);
+ void equip(int slot);
+ void unequip(int slot);
+ void useItem(int slot);
+ void attack(int direction);
+ void useSpecial(int special);
+ void changeAction(Being::Action action);
+ void talkToNPC(int id, bool restart);
+ void selectFromNPC(int id, int choice);
+ void requestTrade(int id);
+ void acceptTrade(bool accept);
+ void tradeItem(int slot, int amount);
+ void tradeMoney(int amount);
+ void tradeWithNPC(int item, int amount);
+ void sendLetter(const std::string &player, const std::string &text);
+ void raiseAttribute(int attribute);
+ void lowerAttribute(int attribute);
+ void respawn();
+ static RespawnRequestListener respawnListener;
+ void changeDir(unsigned char dir);
+ }
+ }
+}
+
+#endif
diff --git a/src/net/guildhandler.cpp b/src/net/guildhandler.cpp
new file mode 100644
index 00000000..cf886ab3
--- /dev/null
+++ b/src/net/guildhandler.cpp
@@ -0,0 +1,240 @@
+/*
+ * The Mana World
+ * Copyright 2008 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iostream>
+#include "guildhandler.h"
+
+#include "protocol.h"
+#include "messagein.h"
+
+#include "chatserver/chatserver.h"
+#include "chatserver/guild.h"
+
+#include "../gui/guildwindow.h"
+#include "../gui/chat.h"
+#include "../guild.h"
+#include "../log.h"
+#include "../localplayer.h"
+#include "../channel.h"
+#include "../channelmanager.h"
+
+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;
+
+}
+
+void GuildHandler::handleMessage(MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case CPMSG_GUILD_CREATE_RESPONSE:
+ {
+ logger->log("Received CPMSG_GUILD_CREATE_RESPONSE");
+ if(msg.readInt8() == ERRMSG_OK)
+ {
+ // TODO - Acknowledge guild was created
+ chatWindow->chatLog("Guild created.");
+ joinedGuild(msg);
+ }
+ else
+ {
+ chatWindow->chatLog("Error creating guild.");
+ }
+ } break;
+
+ case CPMSG_GUILD_INVITE_RESPONSE:
+ {
+ logger->log("Received CPMSG_GUILD_INVITE_RESPONSE");
+ if(msg.readInt8() == ERRMSG_OK)
+ {
+ // TODO - Acknowledge invite was sent
+ chatWindow->chatLog("Invite sent.");
+ }
+ } break;
+
+ case CPMSG_GUILD_ACCEPT_RESPONSE:
+ {
+ logger->log("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->log("Received CPMSG_GUILD_GET_MEMBERS_RESPONSE");
+ if(msg.readInt8() == ERRMSG_OK)
+ {
+ std::string guildMember;
+ bool online;
+ std::string guildName;
+ Guild *guild;
+
+ short guildId = msg.readInt16();
+ guild = player_node->getGuild(guildId);
+
+ if (!guild)
+ return;
+
+ guildName = guild->getName();
+
+ while(msg.getUnreadLength())
+ {
+ guildMember = msg.readString();
+ online = msg.readInt8();
+ if(guildMember != "")
+ {
+ guild->addMember(guildMember);
+ guildWindow->setOnline(guildName, guildMember, online);
+ }
+ }
+
+ guildWindow->updateTab();
+ }
+ } break;
+
+ case CPMSG_GUILD_UPDATE_LIST:
+ {
+ logger->log("Received CPMSG_GUILD_UPDATE_LIST");
+ short guildId = msg.readInt16();
+ std::string guildMember = msg.readString();
+ char eventId = msg.readInt8();
+
+ Guild *guild = player_node->getGuild(guildId);
+ if (guild)
+ {
+ switch(eventId)
+ {
+ case GUILD_EVENT_NEW_PLAYER:
+ guild->addMember(guildMember);
+ guildWindow->setOnline(guild->getName(), guildMember, true);
+ break;
+
+ case GUILD_EVENT_LEAVING_PLAYER:
+ guild->removeMember(guildMember);
+ break;
+
+ case GUILD_EVENT_ONLINE_PLAYER:
+ guildWindow->setOnline(guild->getName(), guildMember, true);
+ break;
+
+ case GUILD_EVENT_OFFLINE_PLAYER:
+ guildWindow->setOnline(guild->getName(), guildMember, false);
+ break;
+
+ default:
+ logger->log("Invalid guild event");
+ }
+ }
+ guildWindow->updateTab();
+
+
+ } break;
+
+ case CPMSG_GUILD_INVITED:
+ {
+ logger->log("Received CPMSG_GUILD_INVITED");
+ std::string inviterName = msg.readString();
+ std::string guildName = msg.readString();
+
+ // Open a dialog asking if the player accepts joining the guild.
+ guildWindow->openAcceptDialog(inviterName, guildName);
+ } break;
+
+ case CPMSG_GUILD_PROMOTE_MEMBER_RESPONSE:
+ {
+ logger->log("Received CPMSG_GUILD_PROMOTE_MEMBER_RESPONSE");
+
+ if (msg.readInt8() == ERRMSG_OK)
+ {
+ // promotion succeeded
+ chatWindow->chatLog("Member was promoted successfully");
+ }
+ else
+ {
+ // promotion failed
+ chatWindow->chatLog("Failed to promote member");
+ }
+ }
+
+ case CPMSG_GUILD_REJOIN:
+ {
+ logger->log("Received CPMSG_GUILD_REJOIN");
+
+ joinedGuild(msg);
+ } break;
+
+ case CPMSG_GUILD_QUIT_RESPONSE:
+ {
+ logger->log("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)
+ {
+ chatWindow->removeChannel(guild->getName());
+ guildWindow->removeTab(guildId);
+ player_node->removeGuild(guildId);
+ }
+ }
+ } break;
+ }
+}
+
+void GuildHandler::joinedGuild(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 and create new guild tab
+ Guild *guild = player_node->addGuild(guildId, permissions);
+ guild->setName(guildName);
+ guildWindow->newGuildTab(guildName);
+ guildWindow->requestMemberList(guildId);
+
+ // Automatically create the guild channel
+ // COMMENT: Should this go here??
+ Channel *channel = new Channel(channelId, guildName, announcement);
+ channelManager->addChannel(channel);
+ chatWindow->createNewChannelTab(guildName);
+ chatWindow->chatLog("Topic: " + announcement, BY_CHANNEL, guildName);
+}
diff --git a/src/net/guildhandler.h b/src/net/guildhandler.h
new file mode 100644
index 00000000..4eb2da0b
--- /dev/null
+++ b/src/net/guildhandler.h
@@ -0,0 +1,40 @@
+/*
+ * The Mana World
+ * Copyright 2008 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_NET_GUILDHANDLER_H
+#define _TMW_NET_GUILDHANDLER_H
+
+#include "messagehandler.h"
+
+#include <string>
+
+class GuildHandler : public MessageHandler
+{
+public:
+ GuildHandler();
+
+ void handleMessage(MessageIn &msg);
+
+protected:
+ void joinedGuild(MessageIn &msg);
+};
+
+#endif
diff --git a/src/net/internal.cpp b/src/net/internal.cpp
new file mode 100644
index 00000000..4cb88a4e
--- /dev/null
+++ b/src/net/internal.cpp
@@ -0,0 +1,27 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "internal.h"
+
+namespace Net
+{
+ int connections = 0;
+}
diff --git a/src/net/internal.h b/src/net/internal.h
new file mode 100644
index 00000000..1e411605
--- /dev/null
+++ b/src/net/internal.h
@@ -0,0 +1,30 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_NET_INTERNAL_H
+#define _TMW_NET_INTERNAL_H
+
+namespace Net
+{
+ extern int connections;
+}
+
+#endif
diff --git a/src/net/inventoryhandler.cpp b/src/net/inventoryhandler.cpp
index d5ac0c29..41032f13 100644
--- a/src/net/inventoryhandler.cpp
+++ b/src/net/inventoryhandler.cpp
@@ -1,227 +1,79 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include "inventoryhandler.h"
+
#include <SDL_types.h>
-#include "inventoryhandler.h"
#include "messagein.h"
#include "protocol.h"
+#include "../equipment.h"
#include "../inventory.h"
#include "../item.h"
#include "../itemshortcut.h"
#include "../localplayer.h"
-#include "../log.h"
#include "../gui/chat.h"
-
#include "../resources/iteminfo.h"
-#include "../utils/gettext.h"
-#include "../utils/strprintf.h"
-#include "../utils/stringutils.h"
-
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,
+ GPMSG_INVENTORY_FULL,
+ GPMSG_INVENTORY,
0
};
handledMessages = _messages;
}
-void InventoryHandler::handleMessage(MessageIn *msg)
+void InventoryHandler::handleMessage(MessageIn &msg)
{
- Sint32 number;
- Sint16 index, amount, itemId, equipType, arrow;
- Sint16 identified, cards[4], itemType;
- Inventory *inventory = player_node->getInventory();
- Inventory *storage = player_node->getStorage();
-
- switch (msg->getId())
+ switch (msg.getId())
{
- case SMSG_PLAYER_INVENTORY:
- case SMSG_PLAYER_STORAGE_ITEMS:
- case SMSG_PLAYER_STORAGE_EQUIP:
- switch (msg->getId()) {
- case SMSG_PLAYER_INVENTORY:
- // Clear inventory - this will be a complete refresh
- inventory->clear();
- break;
- case SMSG_PLAYER_STORAGE_ITEMS:
- /*
- * This packet will always be followed by a
- * SMSG_PLAYER_STORAGE_EQUIP packet. The two packets
- * together comprise a complete refresh of storage, so
- * clear storage here
- */
- storage->clear();
- logger->log("Received SMSG_PLAYER_STORAGE_ITEMS");
- break;
- default:
- logger->log("Received SMSG_PLAYER_STORAGE_EQUIP");
- break;
- }
- 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();
- if (msg->getId() == SMSG_PLAYER_STORAGE_EQUIP) {
- amount = 1;
- msg->readInt16(); // Equip Point?
- } else {
- amount = msg->readInt16();
- }
- arrow = msg->readInt16();
- if (msg->getId() == SMSG_PLAYER_STORAGE_EQUIP) {
- msg->readInt8(); // Attribute (broken)
- msg->readInt8(); // Refine level
+ case GPMSG_INVENTORY_FULL:
+ player_node->clearInventory();
+ // no break!
+
+ case GPMSG_INVENTORY:
+ while (msg.getUnreadLength())
+ {
+ int slot = msg.readInt8();
+ if (slot == 255)
+ {
+ player_node->setMoney(msg.readInt32());
+ continue;
}
- for (int i = 0; i < 4; i++)
- cards[i] = msg->readInt16();
- if (msg->getId() == SMSG_PLAYER_INVENTORY) {
- inventory->setItem(index, itemId, amount, false);
-
- // Trick because arrows are not considered equipment
- if (arrow & 0x8000) {
- if (Item *item = inventory->getItem(index))
- item->setEquipment(true);
- }
- } else {
- 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]);
- storage->setItem(index, itemId, amount, false);
+ int id = msg.readInt16();
+ if (slot < EQUIPMENT_SIZE)
+ {
+ player_node->mEquipment->setEquipment(slot, id);
}
- }
- break;
-
- case SMSG_PLAYER_INVENTORY_ADD:
- index = msg->readInt16();
- amount = msg->readInt16();
- itemId = msg->readInt16();
- identified = msg->readInt8();
- msg->readInt8(); // attribute
- msg->readInt8(); // refine
- for (int i = 0; i < 4; i++)
- cards[i] = msg->readInt16();
- equipType = msg->readInt16();
- itemType = msg->readInt8();
-
- if (msg->readInt8() > 0) {
- chatWindow->chatLog(_("Unable to pick up item"), BY_SERVER);
- } else {
- const ItemInfo &itemInfo = ItemDB::get(itemId);
- const std::string amountStr =
- (amount > 1) ? toString(amount) : "a";
- chatWindow->chatLog(strprintf(_("You picked up %s %s"),
- amountStr.c_str(), itemInfo.getName().c_str()), BY_SERVER);
-
- if (Item *item = inventory->getItem(index)) {
- item->setId(itemId);
- item->increaseQuantity(amount);
- } else {
- inventory->setItem(index, itemId, amount, equipType != 0);
+ else if (slot >= 32 && slot < 32 + INVENTORY_SIZE)
+ {
+ int amount = id ? msg.readInt8() : 0;
+ player_node->setInvItem(slot - 32, id, amount);
}
- }
- break;
-
- case SMSG_PLAYER_INVENTORY_REMOVE:
- index = msg->readInt16();
- amount = msg->readInt16();
- 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();
- msg->readInt16(); // item id
- msg->readInt32(); // id
- amount = msg->readInt16();
- msg->readInt8(); // type
-
- if (Item *item = inventory->getItem(index))
- item->setQuantity(amount);
- break;
-
- case SMSG_ITEM_USE_RESPONSE:
- index = msg->readInt16();
- amount = msg->readInt16();
-
- if (msg->readInt8() == 0) {
- chatWindow->chatLog(_("Failed to use item"), BY_SERVER);
- } else {
- if (Item *item = inventory->getItem(index))
- item->setQuantity(amount);
- }
- break;
-
- case SMSG_PLAYER_STORAGE_STATUS:
- /*
- * Basic slots used vs total slots info
- * We don't really need this information, but 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.
- */
- logger->log("Received SMSG_PLAYER_STORAGE_STATUS");
- player_node->setInStorage(true);
- break;
-
- case SMSG_PLAYER_STORAGE_ADD:
- /*
- * Move an item into storage
- */
- break;
-
- case SMSG_PLAYER_STORAGE_REMOVE:
- /*
- * Move an item out of storage
- */
- break;
-
- case SMSG_PLAYER_STORAGE_CLOSE:
- /*
- * Storage access has been closed
- */
- player_node->setInStorage(false);
- logger->log("Received SMSG_PLAYER_STORAGE_CLOSE");
+ };
break;
}
}
diff --git a/src/net/inventoryhandler.h b/src/net/inventoryhandler.h
index 336b2e98..9b457abe 100644
--- a/src/net/inventoryhandler.h
+++ b/src/net/inventoryhandler.h
@@ -29,7 +29,7 @@ class InventoryHandler : public MessageHandler
public:
InventoryHandler();
- void handleMessage(MessageIn *msg);
+ void handleMessage(MessageIn &msg);
};
#endif
diff --git a/src/net/itemhandler.cpp b/src/net/itemhandler.cpp
index 8c4af4e4..189c6eb9 100644
--- a/src/net/itemhandler.cpp
+++ b/src/net/itemhandler.cpp
@@ -29,39 +29,36 @@
ItemHandler::ItemHandler()
{
static const Uint16 _messages[] = {
- SMSG_ITEM_VISIBLE,
- SMSG_ITEM_DROPPED,
- SMSG_ITEM_REMOVE,
+ GPMSG_ITEMS,
+ GPMSG_ITEM_APPEAR,
0
};
handledMessages = _messages;
}
-void ItemHandler::handleMessage(MessageIn *msg)
+void ItemHandler::handleMessage(MessageIn &msg)
{
- Uint32 id;
- Uint16 x, y;
- Sint16 itemId;
-
- switch (msg->getId())
+ switch (msg.getId())
{
- case SMSG_ITEM_VISIBLE:
- case SMSG_ITEM_DROPPED:
- id = msg->readInt32();
- itemId = msg->readInt16();
- msg->readInt8(); // identify flag
- x = msg->readInt16();
- y = msg->readInt16();
- msg->skip(4); // amount,subX,subY / subX,subY,amount
-
- floorItemManager->create(id, itemId, x, y, engine->getCurrentMap());
- break;
+ 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
- case SMSG_ITEM_REMOVE:
- FloorItem *item;
- item = floorItemManager->findById(msg->readInt32());
- if (item)
- floorItemManager->destroy(item);
- break;
+ if (itemId)
+ {
+ floorItemManager->create(id, itemId, x / 32, y / 32, engine->getCurrentMap());
+ }
+ else if (FloorItem *item = floorItemManager->findById(id))
+ {
+ floorItemManager->destroy(item);
+ }
+ }
+ } break;
}
}
diff --git a/src/net/itemhandler.h b/src/net/itemhandler.h
index 0cb3b42a..12057bb1 100644
--- a/src/net/itemhandler.h
+++ b/src/net/itemhandler.h
@@ -29,7 +29,7 @@ class ItemHandler : public MessageHandler
public:
ItemHandler();
- void handleMessage(MessageIn *msg);
+ void handleMessage(MessageIn &msg);
};
#endif
diff --git a/src/net/loginhandler.cpp b/src/net/loginhandler.cpp
index 2695fc7b..37c3608a 100644
--- a/src/net/loginhandler.cpp
+++ b/src/net/loginhandler.cpp
@@ -1,158 +1,225 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "loginhandler.h"
+
#include "messagein.h"
#include "protocol.h"
-#include "../log.h"
#include "../logindata.h"
#include "../main.h"
-#include "../serverinfo.h"
-
-#include "../utils/gettext.h"
-#include "../utils/strprintf.h"
-#include "../utils/stringutils.h"
-
-extern SERVER_INFO **server_info;
LoginHandler::LoginHandler()
{
static const Uint16 _messages[] = {
- SMSG_CONNECTION_PROBLEM,
- SMSG_UPDATE_HOST,
- 0x0069,
- 0x006a,
+ APMSG_LOGIN_RESPONSE,
+ APMSG_REGISTER_RESPONSE,
+ APMSG_RECONNECT_RESPONSE,
+ APMSG_PASSWORD_CHANGE_RESPONSE,
+ APMSG_EMAIL_CHANGE_RESPONSE,
0
};
handledMessages = _messages;
}
-void LoginHandler::handleMessage(MessageIn *msg)
+void LoginHandler::setLoginData(LoginData *loginData)
{
- int code;
+ mLoginData = loginData;
+}
- switch (msg->getId())
+void LoginHandler::handleMessage(MessageIn &msg)
+{
+ 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:
- errorMessage = _("This account is already logged in");
- break;
- default:
- errorMessage = _("Unknown connection error");
- break;
+ 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)
+ {
+ state = STATE_CHAR_SELECT;
}
- state = ERROR_STATE;
+ // Login failed
+ else
+ {
+ switch (errMsg) {
+ case ERRMSG_INVALID_ARGUMENT:
+ errorMessage = "Wrong magic_token";
+ break;
+ case ERRMSG_FAILURE:
+ errorMessage = "Already logged in";
+ break;
+ case LOGIN_SERVER_FULL:
+ errorMessage = "Server is full";
+ break;
+ default:
+ errorMessage = "Unknown error";
+ break;
+ }
+ state = STATE_ERROR;
+ }
+ }
break;
- case SMSG_UPDATE_HOST:
- int len;
-
- len = msg->readInt16() - 4;
- mUpdateHost = msg->readString(len);
-
- logger->log("Received update host \"%s\" from login server",
- mUpdateHost.c_str());
- break;
-
- case 0x0069:
- // Skip the length word
- msg->skip(2);
-
- n_server = (msg->getLength() - 47) / 32;
- server_info =
- (SERVER_INFO**) malloc(sizeof(SERVER_INFO*) * n_server);
-
- mLoginData->session_ID1 = msg->readInt32();
- mLoginData->account_ID = msg->readInt32();
- mLoginData->session_ID2 = msg->readInt32();
- msg->skip(30); // unknown
- mLoginData->sex = msg->readInt8();
-
- for (int i = 0; i < n_server; i++)
+ case APMSG_PASSWORD_CHANGE_RESPONSE:
+ {
+ int errMsg = msg.readInt8();
+ // Successful pass change
+ if (errMsg == ERRMSG_OK)
{
- server_info[i] = new SERVER_INFO;
-
- server_info[i]->address = msg->readInt32();
- server_info[i]->port = msg->readInt16();
- server_info[i]->name = msg->readString(20);
- server_info[i]->online_users = msg->readInt32();
- server_info[i]->updateHost = mUpdateHost;
- msg->skip(2); // unknown
+ state = STATE_CHANGEPASSWORD;
+ }
+ // 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;
+ }
+ state = STATE_ACCOUNTCHANGE_ERROR;
+ }
+ }
+ break;
- logger->log("Network: Server: %s (%s:%d)",
- server_info[i]->name.c_str(),
- ipToString(server_info[i]->address),
- server_info[i]->port);
+ case APMSG_EMAIL_CHANGE_RESPONSE:
+ {
+ int errMsg = msg.readInt8();
+ // Successful pass change
+ if (errMsg == ERRMSG_OK)
+ {
+ state = STATE_CHANGEEMAIL;
}
- state = CHAR_SERVER_STATE;
+ // 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;
+ }
+ state = STATE_ACCOUNTCHANGE_ERROR;
+ }
+ }
break;
- case 0x006a:
- 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:
+void LoginHandler::handleLoginResponse(MessageIn &msg)
+{
+ const int errMsg = msg.readInt8();
- 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.\n Please 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;
- }
- state = ERROR_STATE;
- break;
+ if (errMsg == ERRMSG_OK)
+ {
+ readUpdateHost(msg);
+ state = STATE_CHAR_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_SERVER_FULL:
+ errorMessage = "Server is full";
+ break;
+ default:
+ errorMessage = "Unknown error";
+ break;
+ }
+ state = STATE_LOGIN_ERROR;
+ }
+}
+
+void LoginHandler::handleRegisterResponse(MessageIn &msg)
+{
+ const int errMsg = msg.readInt8();
+
+ if (errMsg == ERRMSG_OK)
+ {
+ readUpdateHost(msg);
+ state = STATE_CHAR_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;
+ default:
+ errorMessage = "Unknown error";
+ break;
+ }
+ state = STATE_LOGIN_ERROR;
+ }
+}
+
+void LoginHandler::readUpdateHost(MessageIn &msg)
+{
+ // Set the update host when included in the message
+ if (msg.getUnreadLength() > 0)
+ {
+ mLoginData->updateHost = msg.readString();
}
}
diff --git a/src/net/loginhandler.h b/src/net/loginhandler.h
index df86b634..015d6383 100644
--- a/src/net/loginhandler.h
+++ b/src/net/loginhandler.h
@@ -1,45 +1,47 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef NET_LOGINHANDLER_H
-#define NET_LOGINHANDLER_H
-
-#include <string>
+#ifndef _TMW_NET_LOGINHANDLER_H
+#define _TMW_NET_LOGINHANDLER_H
#include "messagehandler.h"
-struct LoginData;
+class LoginData;
class LoginHandler : public MessageHandler
{
public:
LoginHandler();
- void handleMessage(MessageIn *msg);
+ void setLoginData(LoginData *loginData);
- void setLoginData(LoginData *loginData) { mLoginData = loginData; };
+ void handleMessage(MessageIn &msg);
private:
+ void handleLoginResponse(MessageIn &msg);
+ void handleRegisterResponse(MessageIn &msg);
+
+ void readUpdateHost(MessageIn &msg);
+
LoginData *mLoginData;
- std::string mUpdateHost;
};
-#endif
+#endif // _TMW_NET_LOGINHANDLER_H
diff --git a/src/net/logouthandler.cpp b/src/net/logouthandler.cpp
new file mode 100644
index 00000000..6dea4c83
--- /dev/null
+++ b/src/net/logouthandler.cpp
@@ -0,0 +1,215 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "logouthandler.h"
+
+#include "messagein.h"
+#include "protocol.h"
+
+#include "../main.h"
+
+LogoutHandler::LogoutHandler():
+mPassToken(NULL), mScenario(LOGOUT_EXIT),
+mLoggedOutAccount(false), mLoggedOutGame(false), mLoggedOutChat(false)
+{
+ static const Uint16 _messages[] = {
+ APMSG_LOGOUT_RESPONSE,
+ APMSG_UNREGISTER_RESPONSE,
+ GPMSG_DISCONNECT_RESPONSE,
+ CPMSG_DISCONNECT_RESPONSE,
+ 0
+ };
+ handledMessages = _messages;
+}
+
+void LogoutHandler::handleMessage(MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case APMSG_LOGOUT_RESPONSE:
+ {
+ int errMsg = msg.readInt8();
+
+ // Successful logout
+ if (errMsg == ERRMSG_OK)
+ {
+ mLoggedOutAccount = true;
+
+ switch (mScenario)
+ {
+ case LOGOUT_SWITCH_ACCOUNTSERVER:
+ if (mLoggedOutGame && mLoggedOutChat)
+ state = STATE_SWITCH_ACCOUNTSERVER;
+ break;
+
+ case LOGOUT_EXIT:
+ default:
+ if (mLoggedOutGame && mLoggedOutChat)
+ state = STATE_FORCE_QUIT;
+ break;
+ }
+ }
+ // Logout failed
+ else
+ {
+ switch (errMsg) {
+ case ERRMSG_NO_LOGIN:
+ errorMessage = "Accountserver: Not logged in";
+ break;
+ default:
+ errorMessage = "Accountserver: Unknown error";
+ break;
+ }
+ state = STATE_ERROR;
+ }
+ }
+ break;
+ case APMSG_UNREGISTER_RESPONSE:
+ {
+ int errMsg = msg.readInt8();
+ // Successful unregistration
+ if (errMsg == ERRMSG_OK)
+ {
+ state = STATE_UNREGISTER;
+ }
+ // Unregistration failed
+ else
+ {
+ switch (errMsg) {
+ case ERRMSG_INVALID_ARGUMENT:
+ errorMessage =
+ "Accountserver: Wrong username or password";
+ break;
+ default:
+ errorMessage = "Accountserver: Unknown error";
+ break;
+ }
+ state = STATE_ACCOUNTCHANGE_ERROR;
+ }
+ }
+ break;
+ case GPMSG_DISCONNECT_RESPONSE:
+ {
+ int errMsg = msg.readInt8();
+ // Successful logout
+ if (errMsg == ERRMSG_OK)
+ {
+ mLoggedOutGame = true;
+
+ switch (mScenario)
+ {
+ case LOGOUT_SWITCH_CHARACTER:
+ if (mPassToken)
+ {
+ *mPassToken = msg.readString(32);
+ mPassToken = NULL;
+ }
+ if (mLoggedOutChat) state = STATE_RECONNECT_ACCOUNT;
+ break;
+
+ case LOGOUT_SWITCH_ACCOUNTSERVER:
+ if (mLoggedOutAccount && mLoggedOutChat)
+ state = STATE_SWITCH_ACCOUNTSERVER;
+ break;
+
+ case LOGOUT_EXIT:
+ default:
+ if (mLoggedOutAccount && mLoggedOutChat)
+ state = STATE_FORCE_QUIT;
+ break;
+ }
+ }
+ // Logout failed
+ else
+ {
+ switch (errMsg) {
+ case ERRMSG_NO_LOGIN:
+ errorMessage = "Gameserver: Not logged in";
+ break;
+ default:
+ errorMessage = "Gameserver: Unknown error";
+ break;
+ }
+ state = STATE_ERROR;
+ }
+ }
+ break;
+ case CPMSG_DISCONNECT_RESPONSE:
+ {
+ int errMsg = msg.readInt8();
+ // Successful logout
+ if (errMsg == ERRMSG_OK)
+ {
+ mLoggedOutChat = true;
+
+ switch (mScenario)
+ {
+ case LOGOUT_SWITCH_CHARACTER:
+ if (mLoggedOutGame) state = STATE_RECONNECT_ACCOUNT;
+ break;
+
+ case LOGOUT_SWITCH_ACCOUNTSERVER:
+ if (mLoggedOutAccount && mLoggedOutGame)
+ state = STATE_SWITCH_ACCOUNTSERVER;
+ break;
+
+ case LOGOUT_EXIT:
+ default:
+ if (mLoggedOutAccount && mLoggedOutGame)
+ {
+ state = STATE_FORCE_QUIT;
+ }
+ break;
+ }
+ }
+ else
+ {
+ switch (errMsg) {
+ case ERRMSG_NO_LOGIN:
+ errorMessage = "Chatserver: Not logged in";
+ break;
+ default:
+ errorMessage = "Chatserver: Unknown error";
+ break;
+ }
+ state = STATE_ERROR;
+ }
+ }
+ break;
+ }
+}
+
+void
+LogoutHandler::setScenario(unsigned short scenario, std::string* passToken)
+{
+ mScenario = scenario;
+ mPassToken = passToken;
+}
+
+void
+LogoutHandler::reset()
+{
+ mPassToken = NULL;
+ mScenario = LOGOUT_EXIT;
+ mLoggedOutAccount = false;
+ mLoggedOutGame = false;
+ mLoggedOutChat = false;
+}
diff --git a/src/net/logouthandler.h b/src/net/logouthandler.h
new file mode 100644
index 00000000..369eaa80
--- /dev/null
+++ b/src/net/logouthandler.h
@@ -0,0 +1,62 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_NET_LOGOUTHANDLER_H
+#define _TMW_NET_LOGOUTHANDLER_H
+
+#include <string>
+
+#include "messagehandler.h"
+
+/**
+ * The different scenarios for which LogoutHandler can be used
+ */
+enum {
+ LOGOUT_EXIT,
+ LOGOUT_SWITCH_ACCOUNTSERVER,
+ LOGOUT_SWITCH_CHARACTER
+};
+
+class LogoutHandler : public MessageHandler
+{
+ public:
+ LogoutHandler();
+
+ void handleMessage(MessageIn &msg);
+
+ void setScenario(unsigned short scenario,
+ std::string* passToken = NULL);
+
+ void reset();
+
+ void setAccountLoggedOut(){ mLoggedOutAccount = true; }
+ void setGameLoggedOut(){ mLoggedOutGame = true; }
+ void setChatLoggedOut(){ mLoggedOutChat = true; }
+
+ private:
+ std::string* mPassToken;
+ unsigned short mScenario;
+ bool mLoggedOutAccount;
+ bool mLoggedOutGame;
+ bool mLoggedOutChat;
+};
+
+#endif
diff --git a/src/net/messagehandler.cpp b/src/net/messagehandler.cpp
index f1561a31..a765d0e8 100644
--- a/src/net/messagehandler.cpp
+++ b/src/net/messagehandler.cpp
@@ -22,21 +22,33 @@
#include <cassert>
#include "messagehandler.h"
+#ifdef TMWSERV_SUPPORT
#include "network.h"
+#else
+#include "ea/network.h"
+#endif
-MessageHandler::MessageHandler():
- mNetwork(0)
+MessageHandler::MessageHandler()
+#ifdef EATHENA_SUPPORT
+ : mNetwork(0)
+#endif
{
}
MessageHandler::~MessageHandler()
{
+#ifdef TMWSERV_SUPPORT
+ Net::unregisterHandler(this);
+#else
if (mNetwork)
mNetwork->unregisterHandler(this);
+#endif
}
+#ifdef EATHENA_SUPPORT
void MessageHandler::setNetwork(Network *network)
{
assert(!(network && mNetwork));
mNetwork = network;
}
+#endif
diff --git a/src/net/messagehandler.h b/src/net/messagehandler.h
index 45cdf8cd..261a8351 100644
--- a/src/net/messagehandler.h
+++ b/src/net/messagehandler.h
@@ -25,8 +25,13 @@
#include <SDL_types.h>
class MessageIn;
+#ifdef EATHENA_SUPPORT
class Network;
+#endif
+/**
+ * \ingroup Network
+ */
class MessageHandler
{
public:
@@ -35,12 +40,14 @@ class MessageHandler
MessageHandler();
virtual ~MessageHandler();
- virtual void handleMessage(MessageIn *msg) =0;
+ virtual void handleMessage(MessageIn &msg) = 0;
+#ifdef EATHENA_SUPPORT
void setNetwork(Network *network);
protected:
Network *mNetwork;
+#endif
};
#endif
diff --git a/src/net/messagein.cpp b/src/net/messagein.cpp
index a288d936..813b440f 100644
--- a/src/net/messagein.cpp
+++ b/src/net/messagein.cpp
@@ -19,11 +19,16 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <cassert>
+#include "messagein.h"
+
+#ifdef TMWSERV_SUPPORT
+#include <enet/enet.h>
+#else
#include <SDL.h>
#include <SDL_endian.h>
+#endif
-#include "messagein.h"
+#include <cassert>
#define MAKEWORD(low,high) \
((unsigned short)(((unsigned char)(low)) | \
@@ -38,32 +43,68 @@ MessageIn::MessageIn(const char *data, unsigned int length):
mId = readInt16();
}
-Sint8 MessageIn::readInt8()
+int MessageIn::readInt8()
{
- assert(mPos < mLength);
- return mData[mPos++];
+ int value = -1;
+ if (mPos < mLength)
+ {
+ value = (unsigned char) mData[mPos];
+ }
+ mPos += 1;
+ return value;
}
-Sint16 MessageIn::readInt16()
+int MessageIn::readInt16()
{
- assert(mPos + 2 <= mLength);
- mPos += 2;
+ int value = -1;
+ if (mPos + 2 <= mLength)
+ {
+#ifdef TMWSERV_SUPPORT
+ uint16_t t;
+ memcpy(&t, mData + mPos, 2);
+ value = (unsigned short) ENET_NET_TO_HOST_16(t);
+#else
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- return SDL_Swap16(*(Sint16*)(mData + (mPos - 2)));
+ value = SDL_Swap16(*(Sint16*)(mData + mPos));
#else
- return (*(Sint16*)(mData + (mPos - 2)));
+ value = (*(Sint16*)(mData + mPos));
#endif
+#endif // TMWSERV_SUPPORT
+ }
+ mPos += 2;
+ return value;
}
-Sint32 MessageIn::readInt32()
+int MessageIn::readInt32()
{
- assert(mPos + 4 <= mLength);
- mPos += 4;
+ int value = -1;
+ if (mPos + 4 <= mLength)
+ {
+#ifdef TMWSERV_SUPPORT
+ uint32_t t;
+ memcpy(&t, mData + mPos, 4);
+ value = ENET_NET_TO_HOST_32(t);
+#else
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- return SDL_Swap32(*(Sint32*)(mData + (mPos - 4)));
+ value = SDL_Swap32(*(Sint32*)(mData + mPos));
#else
- return (*(Sint32*)(mData + (mPos - 4)));
+ value = (*(Sint32*)(mData + mPos));
#endif
+#endif // TMWSERV_SUPPORT
+ }
+ mPos += 4;
+ 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 = p[0] | ((p[1] & 0x07) << 8);
+ y = (p[1] >> 3) | ((p[2] & 0x3F) << 5);
+ }
+ mPos += 3;
}
void MessageIn::readCoordinates(Uint16 &x, Uint16 &y, Uint8 &direction)
@@ -164,21 +205,3 @@ std::string MessageIn::readString(int length)
mPos += length;
return readString;
}
-
-Sint8& operator<<(Sint8 &lhs, MessageIn &msg)
-{
- lhs = msg.readInt8();
- return lhs;
-}
-
-Sint16& operator<<(Sint16 &lhs, MessageIn &msg)
-{
- lhs = msg.readInt16();
- return lhs;
-}
-
-Sint32& operator<<(Sint32 &lhs, MessageIn &msg)
-{
- lhs = msg.readInt32();
- return lhs;
-}
diff --git a/src/net/messagein.h b/src/net/messagein.h
index 0ff6e78a..0d0e9981 100644
--- a/src/net/messagein.h
+++ b/src/net/messagein.h
@@ -27,13 +27,11 @@
/**
* Used for parsing an incoming message.
+ *
+ * \ingroup Network
*/
class MessageIn
{
- friend Sint8& operator<<(Sint8 &lhs, MessageIn &msg);
- friend Sint16& operator<<(Sint16 &lhs, MessageIn &msg);
- friend Sint32& operator<<(Sint32 &lhs, MessageIn &msg);
-
public:
/**
* Constructor.
@@ -43,16 +41,27 @@ class MessageIn
/**
* Returns the message ID.
*/
- short getId() { return mId; }
+ int getId() const { return mId; }
/**
* Returns the message length.
*/
- unsigned int getLength() { return mLength; }
+ unsigned int getLength() const { return mLength; }
+
+ /**
+ * Returns the length of unread data.
+ */
+ unsigned int getUnreadLength() const { return mLength - mPos; }
- Sint8 readInt8(); /**< Reads a byte. */
- Sint16 readInt16(); /**< Reads a short. */
- Sint32 readInt32(); /**< Reads a long. */
+ int readInt8(); /**< Reads a byte. */
+ int readInt16(); /**< Reads a short. */
+ int readInt32(); /**< Reads a long. */
+
+ /**
+ * Reads a 3-byte block containing tile-based coordinates. Used by
+ * tmwserv.
+ */
+ void readCoordinates(Uint16 &x, Uint16 &y);
/**
* Reads a special 3 byte block used by eAthena, containing x and y
@@ -82,8 +91,14 @@ class MessageIn
private:
const char* mData; /**< The message data. */
unsigned int mLength; /**< The length of the data. */
- unsigned int mPos; /**< The position in the data. */
- short mId; /**< The message ID. */
+ 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
diff --git a/src/net/messageout.cpp b/src/net/messageout.cpp
index bf4957be..f7ab6b41 100644
--- a/src/net/messageout.cpp
+++ b/src/net/messageout.cpp
@@ -19,95 +19,131 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <cstring>
+#include "messageout.h"
+
+#ifdef TMWSERV_SUPPORT
+#include <enet/enet.h>
+#else
+#include "ea/network.h"
#include <SDL.h>
#include <SDL_endian.h>
-#include <string>
+#endif
-#include "messageout.h"
-#include "network.h"
+#include <cstring>
+#include <string>
+#ifdef TMWSERV_SUPPORT
+MessageOut::MessageOut(short id):
+ mData(0),
+#else
MessageOut::MessageOut(Network *network):
mNetwork(network),
- mData(0),
+#endif
mDataSize(0),
mPos(0)
{
+#ifdef TMWSERV_SUPPORT
+ writeInt16(id);
+#else
mData = mNetwork->mOutBuffer + mNetwork->mOutSize;
+#endif
+}
+
+#ifdef TMWSERV_SUPPORT
+MessageOut::~MessageOut()
+{
+ free(mData);
+}
+
+void MessageOut::expand(size_t bytes)
+{
+ mData = (char*)realloc(mData, bytes);
+ mDataSize = bytes;
}
+#endif
void MessageOut::writeInt8(Sint8 value)
{
+#ifdef TMWSERV_SUPPORT
+ expand(mPos + 1);
+#else
+ mNetwork->mOutSize += 1;
+#endif
mData[mPos] = value;
- mPos += sizeof(Sint8);
- mNetwork->mOutSize+= sizeof(Sint8);
+ mPos += 1;
}
void MessageOut::writeInt16(Sint16 value)
{
+#ifdef TMWSERV_SUPPORT
+ expand(mPos + 2);
+ uint16_t t = ENET_HOST_TO_NET_16(value);
+ memcpy(mData + mPos, &t, 2);
+#else
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
(*(Sint16 *)(mData + mPos)) = SDL_Swap16(value);
#else
(*(Sint16 *)(mData + mPos)) = value;
#endif
- mPos += sizeof(Sint16);
- mNetwork->mOutSize += sizeof(Sint16);
+ mNetwork->mOutSize += 2;
+#endif // TMWSERV_SUPPORT
+ mPos += 2;
}
void MessageOut::writeInt32(Sint32 value)
{
+#ifdef TMWSERV_SUPPORT
+ expand(mPos + 4);
+ uint32_t t = ENET_HOST_TO_NET_32(value);
+ memcpy(mData + mPos, &t, 4);
+#else
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
(*(Sint32 *)(mData + mPos)) = SDL_Swap32(value);
#else
(*(Sint32 *)(mData + mPos)) = value;
#endif
- mPos += sizeof(Sint32);
- mNetwork->mOutSize += sizeof(Sint32);
+ mNetwork->mOutSize += 4;
+#endif // TMWSERV_SUPPORT
+ mPos += 4;
}
void MessageOut::writeString(const std::string &string, int length)
{
- std::string toWrite = string;
-
+ int stringLength = string.length();
if (length < 0)
{
// Write the length at the start if not fixed
- writeInt16(string.length());
+ writeInt16(stringLength);
+ length = stringLength;
}
- else
+ else if (length < stringLength)
{
// Make sure the length of the string is no longer than specified
- toWrite = string.substr(0, length);
+ stringLength = length;
}
+#ifdef TMWSERV_SUPPORT
+ expand(mPos + length);
+#else
+ mNetwork->mOutSize += length;
+#endif
// Write the actual string
- memcpy(&mData[mPos], (void*)toWrite.c_str(), toWrite.length());
- mPos += toWrite.length();
- mNetwork->mOutSize += toWrite.length();
+ memcpy(mData + mPos, string.c_str(), stringLength);
// Pad remaining space with zeros
- if (length > (int)toWrite.length())
+ if (length > stringLength)
{
- memset(&mData[mPos], '\0', length - toWrite.length());
- mPos += length - toWrite.length();
- mNetwork->mOutSize += length - toWrite.length();
+ memset(mData + mPos + stringLength, '\0', length - stringLength);
}
+ mPos += length;
}
-MessageOut& operator<<(MessageOut &msg, const Sint8 &rhs)
-{
- msg.writeInt8(rhs);
- return msg;
-}
-
-MessageOut& operator<<(MessageOut &msg, const Sint16 &rhs)
+char *MessageOut::getData() const
{
- msg.writeInt16(rhs);
- return msg;
+ return mData;
}
-MessageOut& operator<<(MessageOut &msg, const Sint32 &rhs)
+unsigned int MessageOut::getDataSize() const
{
- msg.writeInt32(rhs);
- return msg;
+ return mDataSize;
}
diff --git a/src/net/messageout.h b/src/net/messageout.h
index b3a4ef68..bc701b92 100644
--- a/src/net/messageout.h
+++ b/src/net/messageout.h
@@ -25,26 +25,38 @@
#include <iosfwd>
#include <SDL_types.h>
+#ifdef EATHENA_SUPPORT
class Network;
+#endif
/**
* Used for building an outgoing message.
+ *
+ * With tmwserv, the message is sent using Net::Connection::send() when
+ * finished.
+ *
+ * \ingroup Network
*/
class MessageOut
{
- friend MessageOut& operator<<(MessageOut &msg, const Sint8 &rhs);
- friend MessageOut& operator<<(MessageOut &msg, const Sint16 &rhs);
- friend MessageOut& operator<<(MessageOut &msg, const Sint32 &rhs);
-
public:
/**
* Constructor.
*/
+#ifdef TMWSERV_SUPPORT
+ MessageOut(short id);
+
+ /**
+ * Destructor.
+ */
+ ~MessageOut();
+#else
MessageOut(Network *network);
+#endif
void writeInt8(Sint8 value); /**< Writes a byte. */
void writeInt16(Sint16 value); /**< Writes a short. */
- void writeInt32(Sint32 value); /**< Writes a long. */
+ void writeInt32(Sint32 value); /**< Writes a long. */
/**
* Writes a string. If a fixed length is not given (-1), it is stored
@@ -52,8 +64,29 @@ class MessageOut
*/
void writeString(const std::string &string, int length = -1);
+ /**
+ * Returns the content of the message.
+ */
+ char *getData() const;
+
+ /**
+ * Returns the length of the data.
+ */
+ unsigned int getDataSize() const;
+
private:
+#ifdef TMWSERV_SUPPORT
+ /**
+ * 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);
+#else
Network *mNetwork;
+#endif
char *mData; /**< Data building up. */
unsigned int mDataSize; /**< Size of data. */
diff --git a/src/net/network.cpp b/src/net/network.cpp
index 3fa046c4..57e368e2 100644
--- a/src/net/network.cpp
+++ b/src/net/network.cpp
@@ -1,436 +1,169 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sstream>
-
-#include "messagehandler.h"
-#include "messagein.h"
#include "network.h"
-#include "../log.h"
-#include "../utils/stringutils.h"
-
-/** 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, 0, 0, -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,
-// #0x200
- 26, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-};
-
-const unsigned int BUFFER_SIZE = 65536;
+#include <enet/enet.h>
-int networkThread(void *data)
-{
- Network *network = static_cast<Network*>(data);
-
- if (!network->realConnect())
- return -1;
+#include <map>
- network->receive();
+#include "connection.h"
+#include "internal.h"
+#include "messagehandler.h"
+#include "messagein.h"
- return 0;
-}
+#include "../log.h"
-Network::Network():
- mSocket(0),
- mAddress(), mPort(0),
- mInBuffer(new char[BUFFER_SIZE]),
- mOutBuffer(new char[BUFFER_SIZE]),
- mInSize(0), mOutSize(0),
- mToSkip(0),
- mState(IDLE),
- mWorkerThread(0)
-{
- mMutex = SDL_CreateMutex();
+/**
+ * The local host which is shared for all outgoing connections.
+ */
+namespace {
+ ENetHost *client;
}
-Network::~Network()
-{
- clearHandlers();
-
- if (mState != IDLE && mState != NET_ERROR)
- disconnect();
-
- SDL_DestroyMutex(mMutex);
-
- delete[] mInBuffer;
- delete[] mOutBuffer;
-}
+typedef std::map<unsigned short, MessageHandler*> MessageHandlers;
+typedef MessageHandlers::iterator MessageHandlerIterator;
+static MessageHandlers mMessageHandlers;
-bool Network::connect(const std::string &address, short port)
+void Net::initialize()
{
- if (mState != IDLE && mState != NET_ERROR)
+ if (enet_initialize())
{
- logger->log("Tried to connect an already connected socket!");
- return false;
+ logger->error("Failed to initialize ENet.");
}
- if (address.empty())
+ client = enet_host_create(NULL, 3, 0, 0);
+
+ if (!client)
{
- setError("Empty address given to Network::connect()!");
- return false;
+ logger->error("Failed to create the local host.");
}
+}
- logger->log("Network::Connecting to %s:%i", address.c_str(), port);
-
- mAddress = address;
- mPort = port;
-
- // Reset to sane values
- mOutSize = 0;
- mInSize = 0;
- mToSkip = 0;
+void Net::finalize()
+{
+ if (!client)
+ return; // Wasn't initialized at all
- mState = CONNECTING;
- mWorkerThread = SDL_CreateThread(networkThread, this);
- if (!mWorkerThread)
- {
- setError("Unable to create network worker thread");
- return false;
+ if (Net::connections) {
+ logger->error("Tried to shutdown the network subsystem while there "
+ "are network connections left!");
}
- return true;
+ clearHandlers();
+ enet_deinitialize();
}
-void Network::disconnect()
+Net::Connection *Net::getConnection()
{
- mState = IDLE;
-
- if (mWorkerThread)
+ if (!client)
{
- SDL_WaitThread(mWorkerThread, NULL);
- mWorkerThread = NULL;
+ logger->error("Tried to instantiate a network object before "
+ "initializing the network subsystem!");
}
- if (mSocket)
- {
- SDLNet_TCP_Close(mSocket);
- mSocket = 0;
- }
+ return new Net::Connection(client);
}
-void Network::registerHandler(MessageHandler *handler)
+void Net::registerHandler(MessageHandler *handler)
{
for (const Uint16 *i = handler->handledMessages; *i; i++)
{
mMessageHandlers[*i] = handler;
}
-
- handler->setNetwork(this);
}
-void Network::unregisterHandler(MessageHandler *handler)
+void Net::unregisterHandler(MessageHandler *handler)
{
for (const Uint16 *i = handler->handledMessages; *i; i++)
{
mMessageHandlers.erase(*i);
}
-
- handler->setNetwork(0);
}
-void Network::clearHandlers()
+void Net::clearHandlers()
{
- MessageHandlerIterator i;
- for (i = mMessageHandlers.begin(); i != mMessageHandlers.end(); i++)
- {
- i->second->setNetwork(0);
- }
mMessageHandlers.clear();
}
-void Network::dispatchMessages()
-{
- while (messageReady())
- {
- MessageIn msg = getNextMessage();
-
- MessageHandlerIterator iter = mMessageHandlers.find(msg.getId());
-
- if (iter != mMessageHandlers.end())
- 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;
-
- SDL_mutexP(mMutex);
- if (mInSize >= 2)
- {
- len = packet_lengths[readWord(0)];
-
- if (len == -1 && mInSize > 4)
- len = readWord(2);
-
- }
-
- bool ret = (mInSize >= static_cast<unsigned int>(len));
- SDL_mutexV(mMutex);
-
- return ret;
-}
-
-MessageIn Network::getNextMessage()
+/**
+ * Dispatches a message to the appropriate message handler and
+ * destroys it afterwards.
+ */
+namespace
{
- while (!messageReady())
+ void dispatchMessage(ENetPacket *packet)
{
- if (mState == NET_ERROR)
- break;
- }
-
- SDL_mutexP(mMutex);
- int msgId = readWord(0);
- int len = packet_lengths[msgId];
-
- if (len == -1)
- len = readWord(2);
-
-#ifdef DEBUG
- logger->log("Received packet 0x%x of length %d", msgId, len);
-#endif
+ MessageIn msg((const char *)packet->data, packet->dataLength);
- MessageIn msg(mInBuffer, len);
- SDL_mutexV(mMutex);
-
- return msg;
-}
-
-bool Network::realConnect()
-{
- IPaddress ipAddress;
-
- if (SDLNet_ResolveHost(&ipAddress, mAddress.c_str(), mPort) == -1)
- {
- std::string error = "Unable to resolve host \"" + mAddress + "\"";
- setError(error);
- logger->log("SDLNet_ResolveHost: %s", error.c_str());
- return false;
- }
+ MessageHandlerIterator iter = mMessageHandlers.find(msg.getId());
- mState = CONNECTING;
+ 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());
+ }
- mSocket = SDLNet_TCP_Open(&ipAddress);
- if (!mSocket)
- {
- logger->log("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError());
- setError(SDLNet_GetError());
- return false;
+ // Clean up the packet now that we're done using it.
+ enet_packet_destroy(packet);
}
-
- logger->log("Network::Started session with %s:%i",
- ipToString(ipAddress.host), ipAddress.port);
-
- mState = CONNECTED;
-
- return true;
}
-void Network::receive()
+void Net::flush()
{
- 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()));
- }
+ ENetEvent event;
- while (mState == CONNECTED)
+ // Wait up to 10 milliseconds for an event.
+ while (enet_host_service(client, &event, 10) > 0)
{
- // 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)
+ switch (event.type)
{
- case -1:
- logger->log("Error: SDLNet_CheckSockets");
- // FALLTHROUGH
- case 0:
+ 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 1:
- // Receive data from the socket
- SDL_mutexP(mMutex);
- ret = SDLNet_TCP_Recv(mSocket, mInBuffer + mInSize, BUFFER_SIZE - mInSize);
+ case ENET_EVENT_TYPE_RECEIVE:
+ dispatchMessage(event.packet);
+ break;
+
+ case ENET_EVENT_TYPE_DISCONNECT:
+ logger->log("Disconnected.");
+ // Reset the server information.
+ event.peer->data = 0;
+ break;
- if (!ret)
- {
- // We got disconnected
- mState = IDLE;
- logger->log("Disconnected.");
- }
- else if (ret < 0)
- {
- setError("Error in SDLNet_TCP_Recv(): " +
- 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);
+ case ENET_EVENT_TYPE_NONE:
+ logger->log("No event during 10 milliseconds.");
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());
+ logger->log("Unhandled enet event.");
break;
}
}
-
- if (SDLNet_TCP_DelSocket(set, mSocket) == -1)
- {
- logger->log("Error in SDLNet_DelSocket(): %s", SDLNet_GetError());
- }
-
- SDLNet_FreeSocketSet(set);
-}
-
-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
}
diff --git a/src/net/network.h b/src/net/network.h
index 02fe7538..13576e79 100644
--- a/src/net/network.h
+++ b/src/net/network.h
@@ -1,118 +1,79 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef NETWORK_
-#define NETWORK_
+#ifndef _TMW_NET_NETWORK_H
+#define _TMW_NET_NETWORK_H
-#include <map>
-#include <SDL_net.h>
-#include <SDL_thread.h>
-#include <string>
+#include <iosfwd>
/**
- * Protocol version, reported to the eAthena char and mapserver who can adjust
- * the protocol accordingly.
+ * \defgroup Network Core network layer
*/
-#define CLIENT_PROTOCOL_VERSION 1
class MessageHandler;
-class MessageIn;
+class MessageOut;
-class Network;
-
-class Network
+/**
+ * \ingroup Network
+ */
+namespace Net
{
- public:
- friend int networkThread(void *data);
- friend class MessageOut;
-
- Network();
-
- ~Network();
-
- bool connect(const std::string &address, short port);
-
- void disconnect();
-
- 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,
- CONNECTED,
- CONNECTING,
- DATA,
- NET_ERROR
- };
-
- protected:
- void setError(const std::string& error);
-
- Uint16 readWord(int pos);
-
- bool realConnect();
-
- void receive();
-
- TCPsocket mSocket;
-
- std::string mAddress;
- short mPort;
-
- 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;
-};
+ 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 clearHandlers();
+
+ /*
+ * Handles all events and dispatches incoming messages to the
+ * registered handlers
+ */
+ void flush();
+}
#endif
diff --git a/src/net/npchandler.cpp b/src/net/npchandler.cpp
index a6dc216b..30507537 100644
--- a/src/net/npchandler.cpp
+++ b/src/net/npchandler.cpp
@@ -1,111 +1,86 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "messagein.h"
#include "npchandler.h"
+
+#include "messagein.h"
#include "protocol.h"
#include "../beingmanager.h"
-#include "../localplayer.h"
#include "../npc.h"
-#include "../gui/npc_text.h"
-#include "../gui/npcintegerdialog.h"
#include "../gui/npclistdialog.h"
-#include "../gui/npcstringdialog.h"
+#include "../gui/npcpostdialog.h"
+#include "../gui/npc_text.h"
-extern NpcIntegerDialog *npcIntegerDialog;
extern NpcListDialog *npcListDialog;
extern NpcTextDialog *npcTextDialog;
-extern NpcStringDialog *npcStringDialog;
+extern NpcPostDialog *npcPostDialog;
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,
+ GPMSG_NPC_CHOICE,
+ GPMSG_NPC_POST,
+ GPMSG_NPC_MESSAGE,
+ GPMSG_NPC_ERROR,
0
};
handledMessages = _messages;
}
-void NPCHandler::handleMessage(MessageIn *msg)
+void NPCHandler::handleMessage(MessageIn &msg)
{
- int id;
-
- switch (msg->getId())
+ Being *being = beingManager->findBeing(msg.readInt16());
+ if (!being || being->getType() != Being::NPC)
{
- case SMSG_NPC_CHOICE:
- msg->readInt16(); // length
- id = msg->readInt32();
- player_node->setAction(LocalPlayer::STAND);
- current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id));
- npcListDialog->parseItems(msg->readString(msg->getLength() - 8));
- npcListDialog->setVisible(true);
- break;
-
- case SMSG_NPC_MESSAGE:
- msg->readInt16(); // length
- id = msg->readInt32();
- player_node->setAction(LocalPlayer::STAND);
- current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id));
- npcTextDialog->addText(msg->readString(msg->getLength() - 8));
- npcListDialog->setVisible(false);
- npcTextDialog->setVisible(true);
- break;
+ return;
+ }
- case SMSG_NPC_CLOSE:
- id = msg->readInt32();
- current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id));
- npcTextDialog->showCloseButton();
- break;
+ current_npc = static_cast< NPC * >(being);
- case SMSG_NPC_NEXT:
- // Next button in NPC dialog, currently unused
- id = msg->readInt32();
- current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id));
- npcTextDialog->showNextButton();
+ switch (msg.getId())
+ {
+ case GPMSG_NPC_CHOICE:
+ npcListDialog->reset();
+ while (msg.getUnreadLength())
+ {
+ npcListDialog->addItem(msg.readString());
+ }
+ npcListDialog->setVisible(true);
break;
- case SMSG_NPC_INT_INPUT:
- // Request for an integer
- id = msg->readInt32();
- current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id));
- npcIntegerDialog->setRange(0, 2147483647);
- npcIntegerDialog->setDefaultValue(0);
- npcIntegerDialog->setVisible(true);
- npcIntegerDialog->requestFocus();
+ case GPMSG_NPC_POST:
+ npcTextDialog->setVisible(false);
+ npcPostDialog->clear();
+ npcPostDialog->setVisible(true);
break;
- case SMSG_NPC_STR_INPUT:
- // Request for a string
- id = msg->readInt32();
- current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id));
- npcStringDialog->setValue("");
- npcStringDialog->setVisible(true);
- npcStringDialog->requestFocus();
+ case GPMSG_NPC_ERROR:
+ current_npc = NULL;
+ case GPMSG_NPC_MESSAGE:
+ npcTextDialog->addText(msg.readString(msg.getUnreadLength()));
+ npcListDialog->setVisible(false);
+ npcTextDialog->setVisible(true);
+ npcPostDialog->setVisible(false);
break;
}
}
diff --git a/src/net/npchandler.h b/src/net/npchandler.h
index 7ac962eb..18ab0a05 100644
--- a/src/net/npchandler.h
+++ b/src/net/npchandler.h
@@ -29,7 +29,7 @@ class NPCHandler : public MessageHandler
public:
NPCHandler();
- void handleMessage(MessageIn *msg);
+ void handleMessage(MessageIn &msg);
};
#endif
diff --git a/src/net/partyhandler.cpp b/src/net/partyhandler.cpp
index 96200add..60c51821 100644
--- a/src/net/partyhandler.cpp
+++ b/src/net/partyhandler.cpp
@@ -1,122 +1,103 @@
/*
* The Mana World
- * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net>
+ * Copyright 2008 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <guichan/actionlistener.hpp>
-
+#include <iostream>
#include "partyhandler.h"
+
#include "protocol.h"
#include "messagein.h"
-#include "../gui/chat.h"
-#include "../gui/confirm_dialog.h"
+#include "chatserver/chatserver.h"
-#include "../beingmanager.h"
-#include "../party.h"
+#include "../gui/chat.h"
+#include "../gui/partywindow.h"
+#include "../log.h"
+#include "../localplayer.h"
-PartyHandler::PartyHandler(Party *party) : mParty(party)
+PartyHandler::PartyHandler()
{
static const Uint16 _messages[] = {
- SMSG_PARTY_CREATE,
- SMSG_PARTY_INFO,
- SMSG_PARTY_INVITE,
- SMSG_PARTY_INVITED,
- SMSG_PARTY_SETTINGS,
- SMSG_PARTY_MEMBER_INFO,
- SMSG_PARTY_LEAVE,
- SMSG_PARTY_UPDATE_HP,
- SMSG_PARTY_UPDATE_COORDS,
- SMSG_PARTY_MESSAGE,
+ CPMSG_PARTY_INVITE_RESPONSE,
+ CPMSG_PARTY_INVITED,
+ CPMSG_PARTY_ACCEPT_INVITE_RESPONSE,
+ CPMSG_PARTY_QUIT_RESPONSE,
+ CPMSG_PARTY_NEW_MEMBER,
+ CPMSG_PARTY_MEMBER_LEFT,
0
};
handledMessages = _messages;
+
}
-void PartyHandler::handleMessage(MessageIn *msg)
+void PartyHandler::handleMessage(MessageIn &msg)
{
- switch (msg->getId())
+ switch (msg.getId())
{
- case SMSG_PARTY_CREATE:
- mParty->createResponse(msg->readInt8());
- break;
- case SMSG_PARTY_INFO:
- break;
- case SMSG_PARTY_INVITE:
+ case CPMSG_PARTY_INVITE_RESPONSE:
+ {
+ if (msg.readInt8() == ERRMSG_OK)
{
- std::string nick = msg->readString(24);
- int status = msg->readInt8();
- mParty->inviteResponse(nick, status);
- break;
+
}
- case SMSG_PARTY_INVITED:
+ } break;
+
+ case CPMSG_PARTY_INVITED:
+ {
+ std::string inviter = msg.readString();
+ partyWindow->showPartyInvite(inviter);
+ } break;
+
+ case CPMSG_PARTY_ACCEPT_INVITE_RESPONSE:
+ {
+ if (msg.readInt8() == ERRMSG_OK)
{
- int id = msg->readInt32();
- Being *being = beingManager->findBeing(id);
- if (!being)
- {
- break;
- }
- std::string nick;
- int gender = 0;
- std::string partyName = "";
- if (being->getType() != Being::PLAYER)
- {
- nick = "";
- }
- else
- {
- nick = being->getName();
- gender = being->getGender();
- partyName = msg->readString(24);
- }
- mParty->invitedAsk(nick, gender, partyName);
- break;
+ player_node->setInParty(true);
+ chatWindow->chatLog("Joined party");
}
- case SMSG_PARTY_SETTINGS:
- break;
- case SMSG_PARTY_MEMBER_INFO:
- break;
- case SMSG_PARTY_LEAVE:
+ }
+
+ case CPMSG_PARTY_QUIT_RESPONSE:
+ {
+ if (msg.readInt8() == ERRMSG_OK)
{
- /*int id = */msg->readInt32();
- std::string nick = msg->readString(24);
- /*int fail = */msg->readInt8();
- mParty->leftResponse(nick);
- break;
- }
- case SMSG_PARTY_UPDATE_HP:
- break;
- case SMSG_PARTY_UPDATE_COORDS:
- break;
- case SMSG_PARTY_MESSAGE:
- { // new block to enable local variables
- int msgLength = msg->readInt16() - 8;
- if (msgLength <= 0)
- {
- return;
- }
- int id = msg->readInt32();
- Being *being = beingManager->findBeing(id);
- std::string chatMsg = msg->readString(msgLength);
- mParty->receiveChat(being, chatMsg);
+ player_node->setInParty(false);
}
- break;
+ } break;
+
+ case CPMSG_PARTY_NEW_MEMBER:
+ {
+ msg.readInt16(); // being id
+ std::string name = msg.readString();
+
+ chatWindow->chatLog(name + " joined the party");
+
+ if (!player_node->getInParty())
+ player_node->setInParty(true);
+
+ partyWindow->addPartyMember(name);
+ } break;
+
+ case CPMSG_PARTY_MEMBER_LEFT:
+ {
+ partyWindow->removePartyMember(msg.readString());
+ } break;
}
}
diff --git a/src/net/partyhandler.h b/src/net/partyhandler.h
index fc02bf0a..b4257c34 100644
--- a/src/net/partyhandler.h
+++ b/src/net/partyhandler.h
@@ -1,39 +1,41 @@
/*
* The Mana World
- * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net>
+ * Copyright 2008 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef PARTYHANDLER_H
-#define PARTYHANDLER_H
+#ifndef _TMW_NET_PARTYHANDLER_H
+#define _TMW_NET_PARTYHANDLER_H
#include "messagehandler.h"
-class Party;
+#include <string>
class PartyHandler : public MessageHandler
{
- public:
- PartyHandler(Party *party);
+public:
+ PartyHandler();
+
+ void handleMessage(MessageIn &msg);
+
+protected:
- void handleMessage(MessageIn *msg);
- private:
- Party *mParty;
};
#endif
+
diff --git a/src/net/playerhandler.cpp b/src/net/playerhandler.cpp
index fc3506fb..b4e2f328 100644
--- a/src/net/playerhandler.cpp
+++ b/src/net/playerhandler.cpp
@@ -1,33 +1,34 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "messagein.h"
#include "playerhandler.h"
+
+#include "messagein.h"
#include "protocol.h"
#include "../engine.h"
#include "../localplayer.h"
#include "../log.h"
+#include "../particle.h"
#include "../npc.h"
-#include "../units.h"
#include "../gui/buy.h"
#include "../gui/chat.h"
@@ -39,9 +40,6 @@
#include "../gui/skill.h"
#include "../gui/viewport.h"
-#include "../utils/stringutils.h"
-#include "../utils/gettext.h"
-
// TODO Move somewhere else
OkDialog *weightNotice = NULL;
OkDialog *deathNotice = NULL;
@@ -52,9 +50,10 @@ extern BuyDialog *buyDialog;
extern SellDialog *sellDialog;
extern Window *buySellDialog;
-// 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;
+/* 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 * 32;
/**
* Listener used for handling the overweigth message.
@@ -86,7 +85,7 @@ namespace {
buyDialog->setVisible(false);
sellDialog->setVisible(false);
buySellDialog->setVisible(false);
- if (current_npc) current_npc->handleDeath();
+ current_npc = 0;
}
} deathListener;
}
@@ -94,317 +93,192 @@ namespace {
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,
+ 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,
0
};
handledMessages = _messages;
}
-void PlayerHandler::handleMessage(MessageIn *msg)
+void PlayerHandler::handleMessage(MessageIn &msg)
{
- switch (msg->getId())
+ 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.
- */
+ case GPMSG_PLAYER_MAP_CHANGE:
+ handleMapChangeMessage(msg);
break;
- case SMSG_PLAYER_WARP:
- {
- std::string mapPath = msg->readString(16);
- bool nearby;
- Uint16 x = msg->readInt16();
- Uint16 y = msg->readInt16();
-
- logger->log("Warping to %s (%d, %d)", mapPath.c_str(), x, y);
-
- /*
- * We must clear the local player's target *before* the call
- * to changeMap, as it deletes all beings.
- */
- player_node->stopAttack();
-
- nearby = (engine->getCurrentMapName() == mapPath);
-
- // Switch the actual map, deleting the previous one if necessary
- engine->changeMap(mapPath);
+ 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;
- if (current_npc) current_npc->handleDeath();
-
- float scrollOffsetX = 0.0f;
- float scrollOffsetY = 0.0f;
+ case GPMSG_PLAYER_ATTRIBUTE_CHANGE:
+ {
+ logger->log("ATTRIBUTE UPDATE:");
+ while (msg.getUnreadLength())
+ {
+ int stat = msg.readInt8();
+ int base = msg.readInt16();
+ int value = msg.readInt16();
+ logger->log("%d set to %d %d", stat, base, value);
- /* Scroll if neccessary */
- if (!nearby
- || (abs(x - player_node->mX) > MAP_TELEPORT_SCROLL_DISTANCE)
- || (abs(y - player_node->mY) > MAP_TELEPORT_SCROLL_DISTANCE))
+ if (stat == BASE_ATTR_HP)
{
- scrollOffsetX = (x - player_node->mX) * 32;
- scrollOffsetY = (y - player_node->mY) * 32;
+ player_node->setMaxHp(base);
+ player_node->setHp(value);
}
+ else if (stat < NB_CHARACTER_ATTRIBUTES)
+ {
+ if (stat >= CHAR_SKILL_BEGIN && stat < CHAR_SKILL_END
+ && player_node->getAttributeBase(stat) < base
+ && player_node->getAttributeBase(stat) > -1)
+ {
+ Particle* effect = particleEngine->addEffect("graphics/particles/skillup.particle.xml", 0, 0);
+ player_node->controlParticle(effect);
+ }
- player_node->setAction(Being::STAND);
- player_node->mFrame = 0;
- player_node->mX = x;
- player_node->mY = y;
-
- logger->log("Adjust scrolling by %d:%d",
- (int)scrollOffsetX,
- (int)scrollOffsetY);
-
- viewport->scrollBy(scrollOffsetX, scrollOffsetY);
+ player_node->setAttributeBase(stat, base);
+ player_node->setAttributeEffective(stat, value);
+ }
+ else
+ {
+ logger->log("Warning: server wants to update unknown "
+ "attribute %d to %d", stat, value);
+ }
}
- break;
+ } break;
- case SMSG_PLAYER_STAT_UPDATE_1:
+ case GPMSG_PLAYER_EXP_CHANGE:
+ {
+ logger->log("EXP Update");
+ while (msg.getUnreadLength())
{
- Sint16 type = msg->readInt16();
- Uint32 value = msg->readInt32();
+ int skill = msg.readInt8();
+ int current = msg.readInt32();
+ int next = msg.readInt32();
- switch (type)
+ if (skill < CHAR_SKILL_NB)
{
- //case 0x0000:
- // player_node->setWalkSpeed(msg->readInt32());
- // break;
- case 0x0005: player_node->mHp = value; break;
- case 0x0006: player_node->mMaxHp = value; break;
- case 0x0007: player_node->mMp = value; break;
- case 0x0008: player_node->mMaxMp = value; break;
- case 0x0009:
- player_node->mStatsPointsToAttribute = value;
- break;
- case 0x000b: player_node->mLevel = value; break;
- case 0x000c:
- player_node->mSkillPoint = value;
- skillDialog->update();
- break;
- case 0x0018:
- if (value >= player_node->mMaxWeight / 2 &&
- player_node->mTotalWeight <
- player_node->mMaxWeight / 2)
- {
- weightNotice = new OkDialog(_("Message"),
- _("You are carrying more than "
- "half your weight. You are "
- "unable to regain health."));
- weightNotice->addActionListener(
- &weightListener);
- }
- player_node->mTotalWeight = value;
- break;
- case 0x0019: player_node->mMaxWeight = value; break;
- case 0x0029: player_node->ATK = value; break;
- case 0x002b: player_node->MATK = value; break;
- case 0x002d: player_node->DEF = value; break;
- case 0x002e: player_node->DEF_BONUS = value; break;
- case 0x002f: player_node->MDEF = value; break;
- case 0x0031: player_node->HIT = value; break;
- case 0x0032: player_node->FLEE = value; break;
- case 0x0035: player_node->mAttackSpeed = value; break;
- case 0x0037: player_node->mJobLevel = value; break;
+ player_node->setExperience(skill, current, next);
}
-
- if (player_node->mHp == 0 && !deathNotice)
+ else
{
- 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!"),
- _("Insert coin to continue"),
- _("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."),
- // NetHack reference:
- _("Do you want your possessions identified?"),
- // Secret of Mana reference:
- _("Sadly, no trace of you was ever found..."),
- // Final Fantasy VI reference:
- _("Annihilated."),
- // Earthbound reference:
- _("Looks like you got your head handed to you."),
- // Leisure Suit Larry 1 reference:
- _("You screwed up again, dump your body down the tubes "
- "and get you another one."),
- // Monty Python references (Dead Parrot sketch mostly):
- _("You're not dead yet. You're just resting."),
- _("You are no more."),
- _("You have ceased to be."),
- _("You've expired and gone to meet your maker."),
- _("You're a stiff."),
- _("Bereft of life, you rest in peace."),
- _("If you weren't so animated, you'd be pushing up the "
- "daisies."),
- _("Your metabolic processes are now history."),
- _("You're off the twig."),
- _("You've kicked the bucket."),
- _("You've shuffled off your mortal coil, run down the "
- "curtain and joined the bleedin' choir invisibile."),
- _("You are an ex-player."),
- _("You're pining for the fjords.")
- };
- std::string message(deadMsg[rand()%27]);
-
- deathNotice = new OkDialog(_("Message"), message);
- deathNotice->addActionListener(&deathListener);
- player_node->setAction(Being::DEAD);
+ logger->log("Warning: server wants to update experience of unknown "
+ "skill %d to %d / %d", skill, current, next);
}
}
- break;
+ } break;
- case SMSG_PLAYER_STAT_UPDATE_2:
- switch (msg->readInt16()) {
- case 0x0001:
- player_node->setXp(msg->readInt32());
- break;
- case 0x0002:
- player_node->mJobXp = msg->readInt32();
- break;
- case 0x0014: {
- Uint32 curGp = player_node->mGp;
- player_node->mGp = msg->readInt32();
- if (player_node->mGp > curGp)
- chatWindow->chatLog(_("You picked up ") +
- Units::formatCurrency(player_node->mGp
- - curGp), BY_SERVER);
- }
- break;
- case 0x0016:
- player_node->mXpForNextLevel = msg->readInt32();
- break;
- case 0x0017:
- player_node->mJobXpForNextLevel = msg->readInt32();
- break;
- }
- break;
-
- case SMSG_PLAYER_STAT_UPDATE_3:
- {
- Sint32 type = msg->readInt32();
- Sint32 base = msg->readInt32();
- Sint32 bonus = msg->readInt32();
- Sint32 total = base + bonus;
+ case GPMSG_LEVELUP:
+ {
+ player_node->setLevel(msg.readInt16());
+ player_node->setCharacterPoints(msg.readInt16());
+ player_node->setCorrectionPoints(msg.readInt16());
+ Particle* effect = particleEngine->addEffect("graphics/particles/levelup.particle.xml", 0, 0);
+ player_node->controlParticle(effect);
+ } break;
- switch (type) {
- case 0x000d: player_node->mAttr[LocalPlayer::STR] = total;
- break;
- case 0x000e: player_node->mAttr[LocalPlayer::AGI] = total;
- break;
- case 0x000f: player_node->mAttr[LocalPlayer::VIT] = total;
- break;
- case 0x0010: player_node->mAttr[LocalPlayer::INT] = total;
- break;
- case 0x0011: player_node->mAttr[LocalPlayer::DEX] = total;
- break;
- case 0x0012: player_node->mAttr[LocalPlayer::LUK] = total;
- break;
- }
- }
- break;
- case SMSG_PLAYER_STAT_UPDATE_4:
- {
- Sint16 type = msg->readInt16();
- Sint8 fail = msg->readInt8();
- Sint8 value = msg->readInt8();
+ case GPMSG_LEVEL_PROGRESS:
+ {
+ logger->log("Level Progress Update");
+ player_node->setLevelProgress(msg.readInt8());
+ } break;
- if (fail != 1)
- break;
- switch (type) {
- case 0x000d: player_node->mAttr[LocalPlayer::STR] = value;
- break;
- case 0x000e: player_node->mAttr[LocalPlayer::AGI] = value;
- break;
- case 0x000f: player_node->mAttr[LocalPlayer::VIT] = value;
- break;
- case 0x0010: player_node->mAttr[LocalPlayer::INT] = value;
- break;
- case 0x0011: player_node->mAttr[LocalPlayer::DEX] = value;
- break;
- case 0x0012: player_node->mAttr[LocalPlayer::LUK] = value;
- break;
- }
+ case GPMSG_RAISE_ATTRIBUTE_RESPONSE:
+ {
+ int errCode = msg.readInt8();
+ int attrNum = msg.readInt8() - CHAR_ATTR_BEGIN;
+ 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 = player_node->getAttributeBase(attrNum) - 1;
+ player_node->setCharacterPoints(0);
+ player_node->setAttributeBase(attrNum, attrValue);
+ } break;
+ case ATTRIBMOD_DENIED:
+ {
+ // undo attribute change
+ logger->log("Warning: Server denied increase of attribute %d (reason unknown) ", attrNum);
+ int points = player_node->getCharacterPoints() - 1;
+ player_node->setCharacterPoints(points);
+ int attrValue = player_node->getAttributeBase(attrNum) - 1;
+ player_node->setAttributeBase(attrNum, attrValue);
+ } break;
}
- break;
-
- // Updates stats and status points
- case SMSG_PLAYER_STAT_UPDATE_5:
- player_node->mStatsPointsToAttribute = msg->readInt16();
- player_node->mAttr[LocalPlayer::STR] = msg->readInt8();
- player_node->mAttrUp[LocalPlayer::STR] = msg->readInt8();
- player_node->mAttr[LocalPlayer::AGI] = msg->readInt8();
- player_node->mAttrUp[LocalPlayer::AGI] = msg->readInt8();
- player_node->mAttr[LocalPlayer::VIT] = msg->readInt8();
- player_node->mAttrUp[LocalPlayer::VIT] = msg->readInt8();
- player_node->mAttr[LocalPlayer::INT] = msg->readInt8();
- player_node->mAttrUp[LocalPlayer::INT] = msg->readInt8();
- player_node->mAttr[LocalPlayer::DEX] = msg->readInt8();
- player_node->mAttrUp[LocalPlayer::DEX] = msg->readInt8();
- player_node->mAttr[LocalPlayer::LUK] = msg->readInt8();
- player_node->mAttrUp[LocalPlayer::LUK] = msg->readInt8();
- player_node->ATK = msg->readInt16(); // ATK
- player_node->ATK_BONUS = msg->readInt16(); // ATK bonus
- player_node->MATK = msg->readInt16(); // MATK max
- player_node->MATK_BONUS = msg->readInt16(); // MATK min
- player_node->DEF = msg->readInt16(); // DEF
- player_node->DEF_BONUS = msg->readInt16(); // DEF bonus
- player_node->MDEF = msg->readInt16(); // MDEF
- player_node->MDEF_BONUS = msg->readInt16(); // MDEF bonus
- player_node->HIT = msg->readInt16(); // HIT
- player_node->FLEE = msg->readInt16(); // FLEE
- player_node->FLEE_BONUS = msg->readInt16(); // FLEE bonus
- msg->readInt16(); // critical
- msg->readInt16(); // unknown
- break;
+ } break;
- case SMSG_PLAYER_STAT_UPDATE_6:
- switch (msg->readInt16()) {
- case 0x0020:
- player_node->mAttrUp[LocalPlayer::STR] = msg->readInt8();
- break;
- case 0x0021:
- player_node->mAttrUp[LocalPlayer::AGI] = msg->readInt8();
- break;
- case 0x0022:
- player_node->mAttrUp[LocalPlayer::VIT] = msg->readInt8();
- break;
- case 0x0023:
- player_node->mAttrUp[LocalPlayer::INT] = msg->readInt8();
- break;
- case 0x0024:
- player_node->mAttrUp[LocalPlayer::DEX] = msg->readInt8();
- break;
- case 0x0025:
- player_node->mAttrUp[LocalPlayer::LUK] = msg->readInt8();
+ case GPMSG_LOWER_ATTRIBUTE_RESPONSE:
+ {
+ int errCode = msg.readInt8();
+ int attrNum = msg.readInt8() - CHAR_ATTR_BEGIN;
+ 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 = player_node->getAttributeBase(attrNum) + 1;
+ player_node->setCorrectionPoints(0);
+ player_node->setAttributeBase(attrNum, attrValue);
break;
+ } break;
+ case ATTRIBMOD_DENIED:
+ {
+ // undo attribute change
+ logger->log("Warning: Server denied reduction of attribute %d (reason unknown) ", attrNum);
+ int charaPoints = player_node->getCharacterPoints() - 1;
+ player_node->setCharacterPoints(charaPoints);
+ int correctPoints = player_node->getCorrectionPoints() + 1;
+ player_node->setCorrectionPoints(correctPoints);
+ int attrValue = player_node->getAttributeBase(attrNum) + 1;
+ player_node->setAttributeBase(attrNum, attrValue);
+ } break;
}
- break;
+ } break;
+ /*
case SMSG_PLAYER_ARROW_MESSAGE:
{
- Sint16 type = msg->readInt16();
+ Sint16 type = msg.readInt16();
switch (type) {
case 0:
- chatWindow->chatLog(_("Equip arrows first"),
+ chatWindow->chatLog("Equip arrows first",
BY_SERVER);
break;
default:
@@ -413,5 +287,41 @@ void PlayerHandler::handleMessage(MessageIn *msg)
}
}
break;
+ */
}
}
+
+void
+PlayerHandler::handleMapChangeMessage(MessageIn &msg)
+{
+ const std::string mapName = msg.readString();
+ const unsigned short x = msg.readInt16();
+ const unsigned short y = msg.readInt16();
+ const bool nearby = (engine->getCurrentMapName() == mapName);
+
+ logger->log("Changing map to %s (%d, %d)", mapName.c_str(), x, y);
+
+ // Switch the actual map, deleting the previous one
+ engine->changeMap(mapName);
+
+ current_npc = 0;
+
+ const Vector &playerPos = player_node->getPosition();
+ float scrollOffsetX = 0.0f;
+ float scrollOffsetY = 0.0f;
+
+ /* Scroll if neccessary */
+ if (!nearby
+ || (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);
+
+ logger->log("Adjust scrolling by %d,%d", (int) scrollOffsetX,
+ (int) scrollOffsetY);
+ viewport->scrollBy(scrollOffsetX, scrollOffsetY);
+}
diff --git a/src/net/playerhandler.h b/src/net/playerhandler.h
index 1a7c8da3..fef767da 100644
--- a/src/net/playerhandler.h
+++ b/src/net/playerhandler.h
@@ -29,7 +29,10 @@ class PlayerHandler : public MessageHandler
public:
PlayerHandler();
- void handleMessage(MessageIn *msg);
+ void handleMessage(MessageIn &msg);
+
+ private:
+ void handleMapChangeMessage(MessageIn &msg);
};
#endif
diff --git a/src/net/protocol.h b/src/net/protocol.h
index e9053451..73dd65e0 100644
--- a/src/net/protocol.h
+++ b/src/net/protocol.h
@@ -1,162 +1,295 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef PROTOCOL_
-#define PROTOCOL_
-
-/*********************************
- * Packets from server to client *
- *********************************/
-#define SMSG_LOGIN_SUCCESS 0x0073 /**< Contains starting location */
-#define SMSG_SERVER_PING 0x007f /**< Contains server tick */
-#define SMSG_CONNECTION_PROBLEM 0x0081
-#define SMSG_UPDATE_HOST 0x0063 /**< Custom update host packet */
-#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_SKILL_FAILED 0x0110
-#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_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 0x00fd
-#define SMSG_PARTY_INVITED 0x00fe
-#define SMSG_PARTY_SETTINGS 0x0102
-#define SMSG_PARTY_MEMBER_INFO 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 */
-
-/**********************************
- * Packets from client to server *
- **********************************/
-#define CMSG_CLIENT_PING 0x007e /**< Send to server with tick */
-#define CMSG_TRADE_RESPONSE 0x00e6
-#define CMSG_ITEM_PICKUP 0x009f
-#define CMSG_MAP_LOADED 0x007d
-#define CMSG_NPC_BUY_REQUEST 0x00c8
-#define CMSG_NPC_BUY_SELL_REQUEST 0x00c5
-#define CMSG_CHAT_MESSAGE 0x008c
-#define CMSG_CHAT_WHISPER 0x0096
-#define CMSG_CHAT_ANNOUNCE 0x0099
-#define CMSG_CHAT_WHO 0x00c1
-#define CMSG_NPC_LIST_CHOICE 0x00b8
-#define CMSG_NPC_NEXT_REQUEST 0x00b9
-#define CMSG_NPC_SELL_REQUEST 0x00c9
-#define CMSG_NPC_INT_RESPONSE 0x0143
-#define CMSG_NPC_STR_RESPONSE 0x01d5
-#define CMSG_SKILL_LEVELUP_REQUEST 0x0112
-#define CMSG_STAT_UPDATE_REQUEST 0x00bb
-#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_NPC_TALK 0x0090
-#define CMSG_TRADE_REQUEST 0x00e4
-#define CMSG_PLAYER_INVENTORY_USE 0x00a7
-#define CMSG_PLAYER_INVENTORY_DROP 0x00a2
-#define CMSG_PLAYER_EQUIP 0x00a9
-#define CMSG_PLAYER_UNEQUIP 0x00ab
-
-#define CMSG_PARTY_CREATE 0x00f9
-#define CMSG_PARTY_INVITE 0x00fc
-#define CMSG_PARTY_INVITED 0x00ff
-#define CMSG_PARTY_LEAVE 0x0100 /** Undocumented */
-#define CMSG_PARTY_SETTINGS 0x0101
-#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 */
-
-/** Encodes coords and direction in 3 bytes data */
-void set_coordinates(char *data, unsigned short x, unsigned short y, unsigned char direction);
+#ifndef _TMW_PROTOCOL_
+#define _TMW_PROTOCOL_
+
+/**
+ * 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
+ *
+ * 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)
+ */
+enum {
+ // Login/Register
+ PAMSG_REGISTER = 0x0000, // L version, S username, S password, S email
+ APMSG_REGISTER_RESPONSE = 0x0002, // B error [, S updatehost]
+ PAMSG_UNREGISTER = 0x0003, // -
+ APMSG_UNREGISTER_RESPONSE = 0x0004, // B error
+ PAMSG_LOGIN = 0x0010, // L version, S username, S password
+ APMSG_LOGIN_RESPONSE = 0x0012, // B error [, S updatehost]
+ PAMSG_LOGOUT = 0x0013, // -
+ APMSG_LOGOUT_RESPONSE = 0x0014, // B error
+ PAMSG_CHAR_CREATE = 0x0020, // S name, B hair style, B hair color, B gender, W*6 stats
+ APMSG_CHAR_CREATE_RESPONSE = 0x0021, // B error
+ PAMSG_CHAR_DELETE = 0x0022, // B index
+ APMSG_CHAR_DELETE_RESPONSE = 0x0023, // B error
+ APMSG_CHAR_INFO = 0x0024, // B index, S name, B gender, B hair style, B hair color, W level, W character points, W correction points, D money, W*6 stats
+ PAMSG_CHAR_SELECT = 0x0026, // B index
+ 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, // { B slot, W item id [, B amount] }*
+ GPMSG_INVENTORY_FULL = 0x0121, // { B slot, W item id [, B amount] }*
+ GPMSG_PLAYER_ATTRIBUTE_CHANGE = 0x0130, // { B attribute, W base value, W modified value }*
+ GPMSG_PLAYER_EXP_CHANGE = 0x0140, // { B skill, D exp got, D exp needed }*
+ GPMSG_LEVELUP = 0x0150, // W new level
+ GPMSG_LEVEL_PROGRESS = 0x0151, // B percent completed to next levelup
+ PGMSG_RAISE_ATTRIBUTE = 0x0160, // B attribute
+ GPMSG_RAISE_ATTRIBUTE_RESPONSE = 0x0161, // B error, B attribute
+ PGMSG_LOWER_ATTRIBUTE = 0x0170, // B attribute
+ GPMSG_LOWER_ATTRIBUTE_RESPONSE = 0x0171, // B error, B attribute
+ PGMSG_RESPAWN = 0x0180, // -
+ GPMSG_BEING_ENTER = 0x0200, // B type, W being id, B action, W*2 position
+ // player: 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_BEINGS_MOVE = 0x0280, // { W being id, B flags [, C position, B speed] [, W*2 destination] }*
+ GPMSG_ITEMS = 0x0281, // { W item id, W*2 position }*
+ PGMSG_ATTACK = 0x0290, // B direction
+ PGMSG_USE_SPECIAL = 0x0292, // B specialID
+ GPMSG_BEING_ATTACK = 0x0291, // W being id
+ 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_POST = 0x02D0, // W being id
+ PGMSG_NPC_POST_SEND = 0x02D1, // S name, S text, W item id
+ GPMSG_NPC_POST_GET = 0x02D2, // W being id, { S name, S text, W item 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_ACCEPT = 0x02C6, // -
+ GPMSG_TRADE_ACCEPT = 0x02C7, // -
+ PGMSG_TRADE_ADD_ITEM = 0x02C8, // B slot, B amount
+ GPMSG_TRADE_ADD_ITEM = 0x02C9, // W item id, B amount
+ PGMSG_TRADE_SET_MONEY = 0x02CA, // L amount
+ GPMSG_TRADE_SET_MONEY = 0x02CB, // L amount
+ PGMSG_USE_ITEM = 0x0300, // B slot
+ GPMSG_USE_RESPONSE = 0x0301, // B error
+ GPMSG_BEINGS_DAMAGE = 0x0310, // { W being id, W amount }*
+ GPMSG_CREATE_EFFECT = 0x0320, // W effect id, W*2 position
+
+ // 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
+
+ CPMSG_GUILD_INVITED = 0x0370, // S char name, S guild name, W id
+ CPMSG_GUILD_REJOIN = 0x0371, // S name, W guild, W rights, W channel, S announce
+
+ // Party
+ PCMSG_PARTY_INVITE = 0x03A0, // S name
+ CPMSG_PARTY_INVITE_RESPONSE = 0x03A1, // B error
+ 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_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
+ // -- 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 mode
+ PCMSG_USER_MODE = 0x0465, // W channel id, S name, B mode
+ PCMSG_KICK_USER = 0x0466, // W channel id, S name
+
+ 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
+};
+
+// Login specific return values
+enum {
+ LOGIN_INVALID_VERSION = 0x40, // the user is using an incompatible protocol
+ LOGIN_SERVER_FULL // the server is overloaded
+};
+
+// 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
+};
+
+// Character creation specific return values
+enum {
+ CREATE_INVALID_HAIRSTYLE = 0x40,
+ CREATE_INVALID_HAIRCOLOR,
+ CREATE_INVALID_GENDER,
+ CREATE_RAW_STATS_TOO_HIGH,
+ CREATE_RAW_STATS_TOO_LOW,
+ CREATE_RAW_STATS_EQUAL_TO_ZERO,
+ CREATE_EXISTS_NAME,
+ CREATE_TOO_MUCH_CHARACTERS
+};
+
+// 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 {
+ // A simple item
+ OBJECT_ITEM = 0,
+ // An item that can be activated (doors, switchs, sign, ...)
+ OBJECT_ACTOR,
+ // Non-Playable-Character is an actor capable of movement and maybe actions
+ OBJECT_NPC,
+ // A monster (moving actor with AI. able to toggle map/quest actions, too)
+ OBJECT_MONSTER,
+ // A player
+ OBJECT_PLAYER
+};
+
+// 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
+};
#endif
diff --git a/src/net/tradehandler.cpp b/src/net/tradehandler.cpp
index c5465835..5d93016f 100644
--- a/src/net/tradehandler.cpp
+++ b/src/net/tradehandler.cpp
@@ -1,40 +1,41 @@
/*
* The Mana World
- * Copyright (C) 2004 The Mana World Development Team
+ * Copyright 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
+ * The Mana World is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * The Mana World is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
+ * along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include "tradehandler.h"
+
#include "messagein.h"
#include "protocol.h"
-#include "tradehandler.h"
-#include "../inventory.h"
+#include "gameserver/player.h"
+
+#include "../beingmanager.h"
#include "../item.h"
#include "../localplayer.h"
-#include "../player_relations.h"
#include "../gui/chat.h"
#include "../gui/confirm_dialog.h"
#include "../gui/trade.h"
-#include "../utils/gettext.h"
-
std::string tradePartnerName;
+int tradePartnerID;
/**
* Listener for request trade dialogs
@@ -44,174 +45,90 @@ namespace {
{
void action(const gcn::ActionEvent &event)
{
- player_node->tradeReply(event.getId() == "yes");
- };
+ if (event.getId() == "yes")
+ Net::GameServer::Player::requestTrade(tradePartnerID);
+ else
+ Net::GameServer::Player::acceptTrade(false);
+ }
} listener;
}
-TradeHandler::TradeHandler()
+TradeHandler::TradeHandler():
+ mAcceptTradeRequests(true)
{
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,
+ GPMSG_TRADE_REQUEST,
+ GPMSG_TRADE_CANCEL,
+ GPMSG_TRADE_START,
+ GPMSG_TRADE_COMPLETE,
+ GPMSG_TRADE_ACCEPT,
+ GPMSG_TRADE_ADD_ITEM,
+ GPMSG_TRADE_SET_MONEY,
0
};
handledMessages = _messages;
}
+void TradeHandler::setAcceptTradeRequests(bool acceptTradeRequests)
+{
+ mAcceptTradeRequests = acceptTradeRequests;
+ if (mAcceptTradeRequests) {
+ chatWindow->chatLog("Accepting incoming trade requests", BY_SERVER);
+ } else {
+ chatWindow->chatLog("Ignoring incoming trade requests", BY_SERVER);
+ }
+}
-void TradeHandler::handleMessage(MessageIn *msg)
+void TradeHandler::handleMessage(MessageIn &msg)
{
- switch (msg->getId())
+ 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.
- tradePartnerName = msg->readString(24);
-
- if (player_relations.hasPermission(tradePartnerName, PlayerRelation::TRADE))
- {
- if (!player_node->tradeRequestOk())
- {
- player_node->tradeReply(false);
- break;
- }
-
- player_node->setTrading(true);
- ConfirmDialog *dlg;
- dlg = new ConfirmDialog(_("Request for trade"),
- tradePartnerName +
- _(" wants to trade with you, do you accept?"));
- dlg->addActionListener(&listener);
- }
- else
- {
- player_node->tradeReply(false);
- break;
- }
- break;
-
- case SMSG_TRADE_RESPONSE:
- switch (msg->readInt8())
+ case GPMSG_TRADE_REQUEST:
+ {
+ Being *being = beingManager->findBeing(msg.readInt16());
+ if (!being || !mAcceptTradeRequests)
{
- case 0: // Too far away
- chatWindow->chatLog(_("Trading isn't possible. Trade partner is too far away."),
- BY_SERVER);
- break;
- case 1: // Character doesn't exist
- chatWindow->chatLog(_("Trading isn't possible. Character doesn't exist."),
- BY_SERVER);
- break;
- case 2: // Invite request check failed...
- chatWindow->chatLog(_("Trade cancelled due to an unknown reason."),
- BY_SERVER);
- break;
- case 3: // Trade accepted
- tradeWindow->reset();
- tradeWindow->setCaption(
- _("Trade: You and ") + tradePartnerName);
- tradeWindow->setVisible(true);
- break;
- case 4: // Trade cancelled
- if (player_relations.hasPermission(tradePartnerName,
- PlayerRelation::SPEECH_LOG))
- chatWindow->chatLog(_("Trade with ") + tradePartnerName +
- _(" cancelled"), BY_SERVER);
- // otherwise ignore silently
-
- tradeWindow->setVisible(false);
- player_node->setTrading(false);
- break;
- default: // Shouldn't happen as well, but to be sure
- chatWindow->chatLog(_("Unhandled trade cancel packet"),
- BY_SERVER);
- break;
+ Net::GameServer::Player::acceptTrade(false);
+ break;
}
- break;
+ player_node->setTrading(true);
+ tradePartnerName = being->getName();
+ tradePartnerID = being->getId();
+ ConfirmDialog *dlg = new ConfirmDialog("Request for trade",
+ tradePartnerName + " wants to trade with you, do you accept?");
+ dlg->addActionListener(&listener);
+ } break;
+
+ case GPMSG_TRADE_ADD_ITEM:
+ {
+ int type = msg.readInt16();
+ int amount = msg.readInt8();
+ tradeWindow->addItem(type, false, amount);
+ } break;
- case SMSG_TRADE_ITEM_ADD:
- {
- Sint32 amount = msg->readInt32();
- Sint16 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 (type == 0) {
- tradeWindow->addMoney(amount);
- } else {
- tradeWindow->addItem(type, false, amount, false);
- }
- }
+ case GPMSG_TRADE_SET_MONEY:
+ tradeWindow->setMoney(msg.readInt32());
break;
- case SMSG_TRADE_ITEM_ADD_RESPONSE:
- // Trade: New Item add response (was 0x00ea, now 01b1)
- {
- const int index = msg->readInt16();
- Item *item = player_node->getInventory()->getItem(index);
- if (!item)
- {
- tradeWindow->receivedOk(true);
- return;
- }
- Sint16 quantity = msg->readInt16();
-
- switch (msg->readInt8())
- {
- case 0:
- // Successfully added item
- if (item->isEquipment() && item->isEquipped())
- {
- player_node->unequipItem(item);
- }
- tradeWindow->addItem(item->getId(), true, quantity,
- item->isEquipment());
- item->increaseQuantity(-quantity);
- break;
- case 1:
- // Add item failed - player overweighted
- chatWindow->chatLog(_("Failed adding item. Trade partner is over weighted."),
- BY_SERVER);
- break;
- case 2:
- // Add item failed - player has no free slot
- chatWindow->chatLog(_("Failed adding item. Trade partner has no free slot."),
- BY_SERVER);
- break;
- default:
- chatWindow->chatLog(_("Failed adding item for unknown reason."),
- BY_SERVER);
- break;
- }
- }
+ case GPMSG_TRADE_START:
+ tradeWindow->reset();
+ tradeWindow->setCaption("Trading with " + tradePartnerName);
+ tradeWindow->setVisible(true);
break;
- case SMSG_TRADE_OK:
- // 0 means ok from myself, 1 means ok from other;
- tradeWindow->receivedOk(msg->readInt8() == 0);
+ case GPMSG_TRADE_ACCEPT:
+ tradeWindow->receivedOk();
break;
- case SMSG_TRADE_CANCEL:
- chatWindow->chatLog(_("Trade canceled."), BY_SERVER);
+ case GPMSG_TRADE_CANCEL:
+ chatWindow->chatLog("Trade canceled.", BY_SERVER);
tradeWindow->setVisible(false);
tradeWindow->reset();
player_node->setTrading(false);
break;
- case SMSG_TRADE_COMPLETE:
- chatWindow->chatLog(_("Trade completed."), BY_SERVER);
+ case GPMSG_TRADE_COMPLETE:
+ chatWindow->chatLog("Trade completed.", BY_SERVER);
tradeWindow->setVisible(false);
tradeWindow->reset();
player_node->setTrading(false);
diff --git a/src/net/tradehandler.h b/src/net/tradehandler.h
index d479e43f..6ffe17b5 100644
--- a/src/net/tradehandler.h
+++ b/src/net/tradehandler.h
@@ -24,14 +24,30 @@
#include "messagehandler.h"
-class Network;
-
class TradeHandler : public MessageHandler
{
public:
TradeHandler();
- void handleMessage(MessageIn *msg);
+ void handleMessage(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);
+
+ private:
+ bool mAcceptTradeRequests;
};
#endif