summaryrefslogtreecommitdiff
path: root/src/game-server
diff options
context:
space:
mode:
authorGuillaume Melquiond <guillaume.melquiond@gmail.com>2006-12-29 13:43:24 +0000
committerGuillaume Melquiond <guillaume.melquiond@gmail.com>2006-12-29 13:43:24 +0000
commit291ad04d5b5c4ab08d85eadde116f968cd579b77 (patch)
treee4dced5715a5d9792cfdc0455a6b3ee6d3116079 /src/game-server
parent3d404e743105bb9168c89e3451cf35d7d59120b1 (diff)
downloadmanaserv-291ad04d5b5c4ab08d85eadde116f968cd579b77.tar.gz
manaserv-291ad04d5b5c4ab08d85eadde116f968cd579b77.tar.bz2
manaserv-291ad04d5b5c4ab08d85eadde116f968cd579b77.tar.xz
manaserv-291ad04d5b5c4ab08d85eadde116f968cd579b77.zip
Physically split the server into one tmwserv-acount program (account +
chat + database) and multiple tmwserv-game programs (selected with respect to the maps). Cleaned the repository by moving server-specific source files into dedicated directories.
Diffstat (limited to 'src/game-server')
-rw-r--r--src/game-server/accountconnection.cpp85
-rw-r--r--src/game-server/accountconnection.hpp50
-rw-r--r--src/game-server/gameclient.cpp57
-rw-r--r--src/game-server/gameclient.hpp70
-rw-r--r--src/game-server/gamehandler.cpp245
-rw-r--r--src/game-server/gamehandler.hpp66
-rw-r--r--src/game-server/itemmanager.cpp155
-rw-r--r--src/game-server/itemmanager.hpp90
-rw-r--r--src/game-server/main-game.cpp360
-rw-r--r--src/game-server/mapmanager.cpp112
-rw-r--r--src/game-server/mapmanager.hpp83
-rw-r--r--src/game-server/state.cpp331
-rw-r--r--src/game-server/state.hpp77
13 files changed, 1781 insertions, 0 deletions
diff --git a/src/game-server/accountconnection.cpp b/src/game-server/accountconnection.cpp
new file mode 100644
index 00000000..51fc13f9
--- /dev/null
+++ b/src/game-server/accountconnection.cpp
@@ -0,0 +1,85 @@
+/*
+ * The Mana World
+ * Copyright 2006 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
+ *
+ * $Id:$
+ */
+
+#include "configuration.h"
+#include "defines.h"
+#include "player.h"
+#include "game-server/accountconnection.hpp"
+#include "game-server/mapmanager.hpp"
+#include "net/messagein.hpp"
+#include "net/messageout.hpp"
+#include "utils/logger.h"
+
+extern void registerGameClient(std::string const &, PlayerPtr);
+
+bool AccountConnection::start()
+{
+ if (!Connection::start(config.getValue("accountServerAddress", "localhost"),
+ int(config.getValue("accountServerPort", DEFAULT_SERVER_PORT)) + 1))
+ {
+ return false;
+ }
+ LOG_INFO("Connection established to the account server.", 0);
+ MessageOut msg(GAMSG_REGISTER);
+ msg.writeString(config.getValue("gameServerAddress", "localhost"));
+ msg.writeShort(int(config.getValue("gameServerPort", DEFAULT_SERVER_PORT + 3)));
+ MapManager::Maps const &m = mapManager->getMaps();
+ for (MapManager::Maps::const_iterator i = m.begin(), i_end = m.end(); i != i_end; ++i)
+ {
+ msg.writeShort(i->first);
+ }
+ send(msg);
+ return true;
+}
+
+void AccountConnection::processMessage(MessageIn &msg)
+{
+ switch (msg.getId())
+ {
+ case AGMSG_PLAYER_ENTER:
+ {
+ int id = msg.readLong();
+ std::string name = msg.readString();
+ Player *ptr = new Player(name, id);
+ ptr->setGender((Gender)msg.readByte());
+ ptr->setHairStyle(msg.readByte());
+ ptr->setHairColor(msg.readByte());
+ ptr->setLevel(msg.readByte());
+ ptr->setMoney(msg.readShort());
+ for (int j = 0; j < NB_RSTAT; ++j)
+ ptr->setRawStat(j, msg.readShort());
+ int x = msg.readShort();
+ int y = msg.readShort();
+ Point pos = { x, y };
+ ptr->setPosition(pos);
+ ptr->setMapId(msg.readShort());
+ ptr->setSpeed(150); // TODO
+ std::string token = msg.readString(32);
+ registerGameClient(token, PlayerPtr(ptr));
+ } break;
+
+ default:
+ LOG_WARN("Invalid message type", 0);
+ break;
+ }
+}
diff --git a/src/game-server/accountconnection.hpp b/src/game-server/accountconnection.hpp
new file mode 100644
index 00000000..97412215
--- /dev/null
+++ b/src/game-server/accountconnection.hpp
@@ -0,0 +1,50 @@
+/*
+ * The Mana World
+ * Copyright 2006 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
+ *
+ * $Id$
+ */
+
+#ifndef _TMW_ACCOUNTCONNECTION_H_
+#define _TMW_ACCOUNTCONNECTION_H_
+
+#include "net/connection.hpp"
+
+/**
+ * A connection to the account server.
+ */
+class AccountConnection: public Connection
+{
+ public:
+ /**
+ * Initializes a connection to the account server described in the
+ * configuration file. Registers the maps known by MapManager.
+ */
+ bool start();
+
+ protected:
+ /**
+ * Processes server messages.
+ */
+ virtual void processMessage(MessageIn &);
+};
+
+extern AccountConnection *accountHandler;
+
+#endif
diff --git a/src/game-server/gameclient.cpp b/src/game-server/gameclient.cpp
new file mode 100644
index 00000000..bd5d5492
--- /dev/null
+++ b/src/game-server/gameclient.cpp
@@ -0,0 +1,57 @@
+/*
+ * The Mana World Server
+ * 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
+ *
+ * $Id$
+ */
+
+#include <cassert>
+
+#include "game-server/gameclient.hpp"
+#include "game-server/gamehandler.hpp"
+#include "game-server/state.hpp"
+
+GameClient::GameClient(ENetPeer *peer):
+ NetComputer(peer),
+ mCharacterPtr(NULL)
+{
+}
+
+GameClient::~GameClient()
+{
+ unsetCharacter();
+}
+
+void GameClient::setCharacter(PlayerPtr ch)
+{
+ assert(mCharacterPtr.get() == NULL);
+ mCharacterPtr = ch;
+ assert(mCharacterPtr->mClient == NULL);
+ mCharacterPtr->mClient = this;
+ gameState->addObject(ObjectPtr(mCharacterPtr));
+}
+
+void GameClient::unsetCharacter()
+{
+ if (mCharacterPtr.get() == NULL) return;
+ // remove being from world
+ gameState->removeObject(ObjectPtr(mCharacterPtr));
+ mCharacterPtr->mClient = NULL;
+ mCharacterPtr = PlayerPtr(NULL);
+}
diff --git a/src/game-server/gameclient.hpp b/src/game-server/gameclient.hpp
new file mode 100644
index 00000000..14f084db
--- /dev/null
+++ b/src/game-server/gameclient.hpp
@@ -0,0 +1,70 @@
+/*
+ * The Mana World Server
+ * 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
+ *
+ * $Id$
+ */
+
+#ifndef _TMWSERV_GAMECLIENT_H_
+#define _TMWSERV_GAMECLIENT_H_
+
+#include <enet/enet.h>
+
+#include "player.h"
+#include "net/netcomputer.hpp"
+
+class GameHandler;
+
+/**
+ * A connected computer with an associated character.
+ */
+class GameClient: public NetComputer
+{
+ public:
+ /**
+ * Constructor.
+ */
+ GameClient(ENetPeer *peer);
+
+ /**
+ * Destructor.
+ */
+ ~GameClient();
+
+ /**
+ * Set the selected character associated with connection.
+ */
+ void setCharacter(PlayerPtr ch);
+
+ /**
+ * Deselect the character associated with connection.
+ */
+ void unsetCharacter();
+
+ /**
+ * Get character associated with the connection.
+ */
+ PlayerPtr getCharacter() { return mCharacterPtr; }
+
+ private:
+ /** Character associated with the conneciton. */
+ PlayerPtr mCharacterPtr;
+};
+
+#endif
diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp
new file mode 100644
index 00000000..b0b036e3
--- /dev/null
+++ b/src/game-server/gamehandler.cpp
@@ -0,0 +1,245 @@
+/*
+ * The Mana World Server
+ * 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
+ *
+ * $Id$
+ */
+
+#include <cassert>
+#include <map>
+
+#include "map.h"
+#include "game-server/gameclient.hpp"
+#include "game-server/gamehandler.hpp"
+#include "game-server/state.hpp"
+#include "net/messagein.hpp"
+#include "net/messageout.hpp"
+#include "net/netcomputer.hpp"
+#include "utils/logger.h"
+
+struct GamePendingLogin
+{
+ PlayerPtr character;
+ int timeout;
+};
+
+typedef std::map< std::string, GamePendingLogin > GamePendingLogins;
+typedef std::map< std::string, GameClient * > GamePendingClients;
+
+/**
+ * The pending logins represent clients who were given a magic token by the
+ * account server but who have not yet logged in to the game server.
+ */
+static GamePendingLogins pendingLogins;
+
+/**
+ * The pending clients represent clients who tried to login to the game server,
+ * but for which no magic token is available yet. This can happen when the
+ * communication between the account server and client went faster than the
+ * communication between the account server and the game server.
+ */
+static GamePendingClients pendingClients;
+
+/**
+ * Notification that a particular token has been given to allow a certain
+ * player to enter the game.
+ */
+void registerGameClient(std::string const &token, PlayerPtr ch)
+{
+ GamePendingClients::iterator i = pendingClients.find(token);
+ if (i != pendingClients.end())
+ {
+ GameClient *computer = i->second;
+ computer->setCharacter(ch);
+ pendingClients.erase(i);
+ MessageOut result;
+ result.writeShort(GPMSG_CONNECT_RESPONSE);
+ result.writeByte(ERRMSG_OK);
+ computer->send(result);
+ }
+ else
+ {
+ GamePendingLogin p;
+ p.character = ch;
+ p.timeout = 300; // world ticks
+ pendingLogins.insert(std::make_pair(token, p));
+ }
+}
+
+bool
+GameHandler::startListen(enet_uint16 port)
+{
+ LOG_INFO("Game handler started:", 0);
+ return ConnectionHandler::startListen(port);
+}
+
+void GameHandler::removeOutdatedPending()
+{
+ GamePendingLogins::iterator i = pendingLogins.begin();
+ GamePendingLogins::iterator next;
+
+ while (i != pendingLogins.end())
+ {
+ next = i;
+ ++next;
+ if (--i->second.timeout <= 0)
+ {
+ pendingLogins.erase(i);
+ }
+ i = next;
+ }
+}
+
+NetComputer *GameHandler::computerConnected(ENetPeer *peer)
+{
+ return new GameClient(peer);
+}
+
+void GameHandler::computerDisconnected(NetComputer *computer)
+{
+ GamePendingClients::iterator i;
+ for (i = pendingClients.begin(); i != pendingClients.end(); ++i)
+ {
+ if (i->second == computer)
+ {
+ pendingClients.erase(i);
+ break;
+ }
+ }
+ delete computer;
+}
+
+void GameHandler::process()
+{
+ ConnectionHandler::process();
+ removeOutdatedPending();
+}
+
+void GameHandler::processMessage(NetComputer *comp, MessageIn &message)
+{
+ GameClient &computer = *static_cast< GameClient * >(comp);
+ MessageOut result;
+
+ if (computer.getCharacter().get() == NULL) {
+ if (message.getId() != PGMSG_CONNECT) return;
+ std::string magic_token = message.readString(32);
+ GamePendingLogins::iterator i = pendingLogins.find(magic_token);
+ if (i == pendingLogins.end())
+ {
+ GamePendingClients::iterator i;
+ for (i = pendingClients.begin(); i != pendingClients.end(); ++i)
+ {
+ if (i->second == &computer) return;
+ }
+ pendingClients.insert(std::make_pair(magic_token, &computer));
+ return;
+ }
+ computer.setCharacter(i->second.character);
+ pendingLogins.erase(i);
+ result.writeShort(GPMSG_CONNECT_RESPONSE);
+ result.writeByte(ERRMSG_OK);
+ computer.send(result);
+ return;
+ }
+
+ switch (message.getId())
+ {
+ case PGMSG_SAY:
+ {
+ std::string say = message.readString();
+ gameState->sayAround(computer.getCharacter().get(), say);
+ } break;
+
+ /*
+ case PGMSG_PICKUP:
+ {
+ // add item to inventory (this is too simplistic atm)
+ unsigned int itemId = message.readLong();
+
+ // remove the item from world map
+
+ // send feedback
+ computer.getCharacter()->addItem(itemId);
+ result.writeShort(GPMSG_PICKUP_RESPONSE);
+ result.writeByte(ERRMSG_OK);
+ } break;
+
+ case PGMSG_USE_ITEM:
+ {
+ unsigned int itemId = message.readLong();
+
+ result.writeShort(GPMSG_USE_RESPONSE);
+
+ if (computer.getCharacter()->hasItem(itemId)) {
+ // use item
+ // this should execute a script which will do the appropriate action
+ // (the script will determine if the item is 1 use only)
+ result.writeByte(ERRMSG_OK);
+ } else {
+ result.writeByte(ERRMSG_FAILURE);
+ }
+ } break;
+ */
+
+ case PGMSG_WALK:
+ {
+ unsigned x = message.readShort();
+ unsigned y = message.readShort();
+ Point dst = {x, y};
+ computer.getCharacter()->setDestination(dst);
+
+ // no response should be required
+ } break;
+
+ /*
+ case PGMSG_EQUIP:
+ {
+ message.readLong(); // ItemId: Not useful, the inventory knows it
+ char slot = message.readByte();
+
+ result.writeShort(GPMSG_EQUIP_RESPONSE);
+ result.writeByte(computer.getCharacter()->equip(slot) ?
+ ERRMSG_OK : ERRMSG_FAILURE);
+ } break;
+ */
+
+ case PGMSG_ATTACK:
+ {
+ LOG_DEBUG("Player " << computer.getCharacter()->getPublicID()
+ << " attacks", 0);
+ computer.getCharacter()->setDirection(message.readByte());
+ computer.getCharacter()->setAttacking(true);
+ } break;
+
+ default:
+ LOG_WARN("Invalid message type", 0);
+ result.writeShort(XXMSG_INVALID);
+ break;
+ }
+
+ if (result.getLength() > 0)
+ computer.send(result);
+}
+
+void GameHandler::sendTo(Player *beingPtr, MessageOut &msg)
+{
+ GameClient *client = beingPtr->getClient();
+ assert(client != NULL);
+ client->send(msg);
+}
diff --git a/src/game-server/gamehandler.hpp b/src/game-server/gamehandler.hpp
new file mode 100644
index 00000000..ceb37cd8
--- /dev/null
+++ b/src/game-server/gamehandler.hpp
@@ -0,0 +1,66 @@
+/*
+ * The Mana World Server
+ * 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
+ *
+ * $Id$
+ */
+
+#ifndef _TMW_SERVER_GAMEHANDLER_
+#define _TMW_SERVER_GAMEHANDLER_
+
+#include "player.h"
+#include "net/connectionhandler.hpp"
+
+class GameClient;
+
+/*
+ * Manage connections to game server.
+ */
+class GameHandler: public ConnectionHandler
+{
+ public:
+ void process();
+
+ /**
+ * Start the handler
+ */
+ bool
+ startListen(enet_uint16 port);
+
+ /**
+ * Send message to the given player.
+ */
+ void sendTo(Player *, MessageOut &msg);
+
+ protected:
+ NetComputer *computerConnected(ENetPeer *);
+ void computerDisconnected(NetComputer *);
+
+ /**
+ * Process messages related to core game events.
+ */
+ void processMessage(NetComputer *computer, MessageIn &message);
+
+ private:
+ void removeOutdatedPending();
+};
+
+extern GameHandler *gameHandler;
+
+#endif
diff --git a/src/game-server/itemmanager.cpp b/src/game-server/itemmanager.cpp
new file mode 100644
index 00000000..3ce8350c
--- /dev/null
+++ b/src/game-server/itemmanager.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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
+ *
+ * $Id:$
+ */
+
+#include "resourcemanager.h"
+#include "game-server/itemmanager.hpp"
+#include "utils/logger.h"
+#include "utils/xml.hpp"
+
+ItemManager::ItemManager(std::string const &itemReferenceFile)
+{
+ ResourceManager *resman = ResourceManager::getInstance();
+ int size;
+ char *data = (char *)resman->loadFile(itemReferenceFile, size);
+
+ if (!data) {
+ LOG_ERROR("Item Manager: Could not find " << itemReferenceFile << "!", 0);
+ free(data);
+ return;
+ }
+
+ xmlDocPtr doc = xmlParseMemory(data, size);
+ free(data);
+
+ if (!doc)
+ {
+ LOG_ERROR("Item Manager: Error while parsing item database ("
+ << itemReferenceFile << ")!", 0);
+ return;
+ }
+
+ xmlNodePtr node = xmlDocGetRootElement(doc);
+ if (!node || !xmlStrEqual(node->name, BAD_CAST "items"))
+ {
+ LOG_ERROR("Item Manager: " << itemReferenceFile
+ << " is not a valid database file!", 0);
+ xmlFreeDoc(doc);
+ return;
+ }
+
+ LOG_INFO("Loading item reference...", 0);
+ unsigned nbItems = 0;
+ for (node = node->xmlChildrenNode; node != NULL; node = node->next)
+ {
+ if (!xmlStrEqual(node->name, BAD_CAST "item")) {
+ continue;
+ }
+
+ unsigned id = XML::getProperty(node, "id", 0);
+
+ if (id == 0)
+ {
+ LOG_WARN("Item Manager: An (ignored) item has no ID in "
+ << itemReferenceFile << "!", 0);
+ continue;
+ }
+
+ int itemType = XML::getProperty(node, "type", 0);
+ int weight = XML::getProperty(node, "weight", 0);
+ int value = XML::getProperty(node, "value", 0);
+ int maxPerSlot = XML::getProperty(node, "max_per_slot", 0);
+ std::string scriptName = XML::getProperty(node, "script_name", std::string());
+
+ Modifiers modifiers;
+ modifiers.element = (Element)XML::getProperty(node, "element", 0);
+ modifiers.lifetime = XML::getProperty(node, "lifetime", 0);
+ modifiers.rawStats[STAT_STRENGTH] = XML::getProperty(node, "strength", 0);
+ modifiers.rawStats[STAT_AGILITY] = XML::getProperty(node, "agility", 0);
+ modifiers.rawStats[STAT_VITALITY] = XML::getProperty(node, "vitality", 0);
+ modifiers.rawStats[STAT_INTELLIGENCE] = XML::getProperty(node, "intelligence", 0);
+ modifiers.rawStats[STAT_DEXTERITY] = XML::getProperty(node, "dexterity", 0);
+ modifiers.rawStats[STAT_LUCK] = XML::getProperty(node, "luck", 0);
+ modifiers.computedStats[STAT_HEAT] = XML::getProperty(node, "heat", 0);
+ modifiers.computedStats[STAT_ATTACK] = XML::getProperty(node, "attack", 0);
+ modifiers.computedStats[STAT_DEFENCE] = XML::getProperty(node, "defence", 0);
+ modifiers.computedStats[STAT_MAGIC] = XML::getProperty(node, "magic", 0);
+ modifiers.computedStats[STAT_ACCURACY] = XML::getProperty(node, "accuracy", 0);
+ modifiers.computedStats[STAT_SPEED] = XML::getProperty(node, "speed", 0);
+ modifiers.hp = XML::getProperty(node, "hp", 0);
+ modifiers.mp = XML::getProperty(node, "mp", 0);
+ modifiers.range = XML::getProperty(node, "range", 0);
+ modifiers.weaponType = (WeaponType)XML::getProperty(node, "weapon_type", 0);
+ modifiers.beingStateEffect = (BeingStateEffect)XML::getProperty(node, "status_effect", 0);
+
+ ItemPtr item(new Item(modifiers, itemType, weight,
+ value, scriptName, maxPerSlot));
+ mItemReference[id] = item;
+ nbItems++;
+
+ if (maxPerSlot == 0)
+ {
+ LOG_WARN("Item Manager: Missing max per slot properties for item: "
+ << id << " in " << itemReferenceFile << ".", 0);
+ }
+ if (weight == 0)
+ {
+ LOG_WARN("Item Manager: Missing weight for item: "
+ << id << " in " << itemReferenceFile << ".", 0);
+ }
+
+ LOG_INFO("Item: ID: " << id << ", itemType: " << itemType
+ << ", weight: " << weight << ", value: " << value <<
+ ", scriptName: " << scriptName << ", maxPerSlot: " << maxPerSlot << ".", 3);
+ // Log level 5
+ LOG_INFO("Modifiers:: element: " << modifiers.element <<
+ ", lifetime: " << modifiers.lifetime
+ << std::endl <<
+ ", strength: " << modifiers.rawStats[STAT_STRENGTH] <<
+ ", agility: " << modifiers.rawStats[STAT_AGILITY] <<
+ ", vitality: " << modifiers.rawStats[STAT_VITALITY]
+ << std::endl <<
+ ", intelligence: " << modifiers.rawStats[STAT_INTELLIGENCE] <<
+ ", dexterity: " << modifiers.rawStats[STAT_DEXTERITY] <<
+ ", luck: " << modifiers.rawStats[STAT_LUCK]
+ << std::endl <<
+ ", heat: " << modifiers.computedStats[STAT_HEAT] <<
+ ", attack: " << modifiers.computedStats[STAT_ATTACK] <<
+ ", defence: " << modifiers.computedStats[STAT_DEFENCE]
+ << std::endl <<
+ ", magic: " << modifiers.computedStats[STAT_MAGIC] <<
+ ", accuracy: " << modifiers.computedStats[STAT_ACCURACY] <<
+ ", speed: " << modifiers.computedStats[STAT_SPEED] <<
+ std::endl <<
+ ", hp: " << modifiers.hp <<
+ ", mp: " << modifiers.mp <<
+ std::endl <<
+ ", range: " << modifiers.range <<
+ ", weapon_type: " << modifiers.weaponType <<
+ ", status_effect: " << modifiers.beingStateEffect, 5);
+ }
+
+ LOG_INFO("Loaded " << nbItems << " items from "
+ << itemReferenceFile << ".", 0);
+
+ xmlFreeDoc(doc);
+}
diff --git a/src/game-server/itemmanager.hpp b/src/game-server/itemmanager.hpp
new file mode 100644
index 00000000..a5ebc8f3
--- /dev/null
+++ b/src/game-server/itemmanager.hpp
@@ -0,0 +1,90 @@
+/*
+ * 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
+ *
+ * $Id$
+ */
+
+#ifndef _TMW_ITEMMANAGER_H
+#define _TMW_ITEMMANAGER_H
+
+#include <map>
+
+#include "item.h"
+
+/**
+ * The Item Manager loads the item reference database
+ * and also offers an API to items information, and more.
+ * For item objects, see the WorldItem class.
+ */
+class ItemManager
+{
+ public:
+ /**
+ * Constructor (loads item reference file)
+ */
+ ItemManager(const std::string &itemReferenceFile);
+
+ /**
+ * Gives an Item having the demanded information.
+ */
+ ItemPtr getItem(const unsigned int itemId)
+ { return mItemReference[itemId]; };
+
+ bool use(BeingPtr beingPtr, const unsigned int itemId)
+ { return mItemReference[itemId].get()->use(beingPtr); };
+
+ /**
+ * Return item Type
+ */
+ unsigned short getItemType(const unsigned int itemId)
+ { return mItemReference[itemId].get()->getItemType(); };
+
+ /**
+ * Return Weight of item
+ */
+ unsigned int getWeight(const unsigned int itemId)
+ { return mItemReference[itemId].get()->getWeight(); };
+
+ /**
+ * Return gold value of item
+ */
+ unsigned int getGoldValue(const unsigned int itemId)
+ { return mItemReference[itemId].get()->getGoldValue(); };
+
+ /**
+ * Return max item per slot
+ */
+ unsigned short getMaxPerSlot(const unsigned int itemId)
+ { return mItemReference[itemId].get()->getMaxPerSlot(); };
+
+ /**
+ * Return item's modifiers
+ */
+ Modifiers
+ getItemModifiers(const unsigned int itemId)
+ { return mItemReference[itemId].get()->getItemModifiers(); };
+
+ private:
+ std::map<unsigned int, ItemPtr> mItemReference; /**< Item reference */
+};
+
+extern ItemManager *itemManager;
+
+#endif
diff --git a/src/game-server/main-game.cpp b/src/game-server/main-game.cpp
new file mode 100644
index 00000000..cfc07c8d
--- /dev/null
+++ b/src/game-server/main-game.cpp
@@ -0,0 +1,360 @@
+/*
+ * The Mana World Server
+ * 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
+ *
+ * $Id$
+ */
+
+#include <cstdlib>
+#include <getopt.h>
+#include <iostream>
+#include <signal.h>
+#include <physfs.h>
+#include <enet/enet.h>
+
+#if (defined __USE_UNIX98 || defined __FreeBSD__)
+#include "../config.h"
+#elif defined WIN32
+#include "../tmwserv_private.h"
+#define PACKAGE_VERSION PRODUCT_VERSION
+#endif
+
+#include "configuration.h"
+#include "resourcemanager.h"
+#include "skill.h"
+#include "game-server/accountconnection.hpp"
+#include "game-server/gamehandler.hpp"
+#include "game-server/itemmanager.hpp"
+#include "game-server/mapmanager.hpp"
+#include "game-server/state.hpp"
+#include "net/connectionhandler.hpp"
+#include "net/messageout.hpp"
+#include "utils/logger.h"
+#include "utils/stringfilter.h"
+#include "utils/timer.h"
+
+// Scripting
+#ifdef SCRIPT_SUPPORT
+
+extern "C" void Init_Tmw();
+
+#if defined (SQUIRREL_SUPPORT)
+std::string scriptLanguage = "squirrel";
+#elif defined (RUBY_SUPPORT)
+#include <ruby.h>
+int rubyStatus;
+std::string scriptLanguage = "ruby";
+#elif defined (LUA_SUPPORT)
+std::string scriptLanguage = "lua";
+#else
+#error "Scripting enabled, but no language selected"
+#endif
+
+#else
+std::string scriptLanugage = "none";
+#endif // SCRIPT_SUPPORT
+
+// Default options that automake should be able to override.
+#define DEFAULT_LOG_FILE "tmwserv.log"
+#define DEFAULT_CONFIG_FILE "tmwserv.xml"
+#define DEFAULT_ITEMSDB_FILE "items.xml"
+#define DEFAULT_MAPSDB_FILE "maps.xml"
+
+utils::Timer worldTimer(100, false); /**< Timer for world tics set to 100 ms */
+int worldTime = 0; /**< Current world time in 100ms ticks */
+bool running = true; /**< Determines if server keeps running */
+
+Skill skillTree("base"); /**< Skill tree */
+
+Configuration config; /**< XML config reader */
+
+utils::StringFilter *stringFilter; /**< Slang's Filter */
+
+/** Item manager */
+ItemManager *itemManager;
+
+/** Map manager */
+MapManager *mapManager;
+
+/** Core game message handler */
+GameHandler *gameHandler;
+
+/** Account server message handler */
+AccountConnection *accountHandler;
+
+/** Global game state */
+State *gameState;
+
+/**
+ * Initializes the server.
+ */
+void initialize()
+{
+
+ // Reset to default segmentation fault handling for debugging purposes
+ signal(SIGSEGV, SIG_DFL);
+
+ // Set enet to quit on exit.
+ atexit(enet_deinitialize);
+
+ /*
+ * If the path values aren't defined, we set the default
+ * depending on the platform.
+ */
+ // The config path
+#if defined CONFIG_FILE
+ std::string configPath = CONFIG_FILE;
+#else
+
+#if (defined __USE_UNIX98 || defined __FreeBSD__)
+ std::string configPath = getenv("HOME");
+ configPath += "/.";
+ configPath += DEFAULT_CONFIG_FILE;
+#else // Win32, ...
+ std::string configPath = DEFAULT_CONFIG_FILE;
+#endif
+
+#endif // defined CONFIG_FILE
+
+ // The log path
+#if defined LOG_FILE
+ std::string logPath = LOG_FILE;
+#else
+
+#if (defined __USE_UNIX98 || defined __FreeBSD__)
+ std::string logPath = getenv("HOME");
+ logPath += "/.";
+ logPath += DEFAULT_LOG_FILE;
+#else // Win32, ...
+ std::string logPath = DEFAULT_LOG_FILE;
+#endif
+
+#endif // defined LOG_FILE
+
+ // Initialize PhysicsFS
+ PHYSFS_init("");
+
+ // Initialize the logger.
+ using namespace utils;
+ Logger::instance().setLogFile(logPath);
+
+ // write the messages to both the screen and the log file.
+ Logger::instance().setTeeMode(true);
+
+ config.init(configPath);
+ LOG_INFO("Using Config File: " << configPath, 0);
+ LOG_INFO("Using Log File: " << logPath, 0);
+
+ // --- Initialize the managers
+ // Initialize the slang's and double quotes filter.
+ stringFilter = new StringFilter(&config);
+ // Initialize the map manager
+ mapManager = new MapManager(DEFAULT_MAPSDB_FILE);
+ // Initialize the item manager
+ itemManager = new ItemManager(DEFAULT_ITEMSDB_FILE);
+
+ // --- Initialize the global handlers
+ // FIXME: Make the global handlers global vars or part of a bigger
+ // singleton or a local variable in the event-loop
+ gameHandler = new GameHandler();
+ accountHandler = new AccountConnection();
+
+ // --- Initialize enet.
+ if (enet_initialize() != 0) {
+ LOG_FATAL("An error occurred while initializing ENet", 0);
+ exit(2);
+ }
+
+ // --- Initialize scripting subsystem.
+#ifdef RUBY_SUPPORT
+ LOG_INFO("Script Language: " << scriptLanguage, 0);
+
+ // Initialize ruby
+ ruby_init();
+ ruby_init_loadpath();
+ ruby_script("tmw");
+
+ // Initialize bindings
+ Init_Tmw();
+
+ // Run test script
+ rb_load_file("scripts/init.rb");
+ rubyStatus = ruby_exec();
+#else
+ LOG_WARN("No Scripting Language Support.", 0);
+#endif
+}
+
+
+/**
+ * Deinitializes the server.
+ */
+void deinitialize()
+{
+ delete stringFilter;
+ // Write configuration file
+ config.write();
+
+ // Stop world timer
+ worldTimer.stop();
+
+ // Quit ENet
+ enet_deinitialize();
+
+#ifdef RUBY_SUPPORT
+ // Finish up ruby
+ ruby_finalize();
+ ruby_cleanup(rubyStatus);
+#endif
+
+ // Destroy message handlers
+ delete gameHandler;
+ delete accountHandler;
+
+ // Destroy Managers
+ delete itemManager;
+ delete mapManager;
+
+ PHYSFS_deinit();
+}
+
+
+/**
+ * Show command line arguments
+ */
+void printHelp()
+{
+ std::cout << "tmwserv" << std::endl << std::endl
+ << "Options: " << std::endl
+ << " -h --help : Display this help" << std::endl
+ << " --verbosity <n> : Set the verbosity level" << std::endl
+ << " --port <n> : Set the default port to listen on" << std::endl;
+ exit(0);
+}
+
+/**
+ * Parse the command line arguments
+ */
+void parseOptions(int argc, char *argv[])
+{
+ const char *optstring = "h";
+
+ const struct option long_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "verbosity", required_argument, 0, 'v' },
+ { "port", required_argument, 0, 'p' },
+ { 0 }
+ };
+
+ while (optind < argc) {
+ int result = getopt_long(argc, argv, optstring, long_options, NULL);
+
+ if (result == -1) {
+ break;
+ }
+
+ switch (result) {
+ default: // Unknown option
+ case 'h':
+ // Print help
+ printHelp();
+ break;
+ case 'v':
+ // Set Verbosity to level
+ unsigned short verbosityLevel;
+ verbosityLevel = atoi(optarg);
+ utils::Logger::instance().setVerbosity(verbosityLevel);
+ LOG_INFO("Setting Log Verbosity Level to " << verbosityLevel, 0);
+ break;
+ case 'p':
+ // Change the port to listen on.
+ unsigned short portToListenOn;
+ portToListenOn = atoi(optarg);
+ config.setValue("gameServerPort", portToListenOn);
+ LOG_INFO("Setting Default Port to " << portToListenOn, 0);
+ break;
+ }
+ }
+}
+
+
+/**
+ * Main function, initializes and runs server.
+ */
+int main(int argc, char *argv[])
+{
+ int elapsedWorldTicks;
+
+ LOG_INFO("The Mana World Server v" << PACKAGE_VERSION, 0);
+
+ // Parse Command Line Options
+ parseOptions(argc, argv);
+
+ // General Initialization
+ initialize();
+
+ if (!accountHandler->start()) {
+ LOG_ERROR("Unable to create a connection to an account server.", 0);
+ return 3;
+ }
+
+ if (!gameHandler->startListen(int(config.getValue("gameServerPort", DEFAULT_SERVER_PORT + 3)))) {
+ LOG_ERROR("Unable to create an ENet server host.", 0);
+ return 3;
+ }
+
+ // Create state machine
+ gameState = new State;
+
+ // Initialize world timer
+ worldTimer.start();
+
+ while (running) {
+ elapsedWorldTicks = worldTimer.poll();
+ if (elapsedWorldTicks > 0) {
+ worldTime += elapsedWorldTicks;
+
+ if (elapsedWorldTicks > 1)
+ {
+ LOG_WARN(elapsedWorldTicks -1 << " World Tick(s) skipped "
+ "because of insufficient time. please buy a faster "
+ "machine ;-)", 0);
+ };
+
+ // Print world time at 10 second intervals to show we're alive
+ if (worldTime % 100 == 0) {
+ LOG_INFO("World time: " << worldTime, 0);
+ }
+
+ // Handle all messages that are in the message queues
+ accountHandler->process();
+ gameHandler->process();
+ // Update all active objects/beings
+ gameState->update();
+ // Send potentially urgent outgoing messages
+ gameHandler->flush();
+ }
+ worldTimer.sleep();
+ }
+
+ LOG_INFO("Received: Quit signal, closing down...", 0);
+ gameHandler->stopListen();
+ accountHandler->stop();
+ delete gameState;
+ deinitialize();
+}
diff --git a/src/game-server/mapmanager.cpp b/src/game-server/mapmanager.cpp
new file mode 100644
index 00000000..3b2caedf
--- /dev/null
+++ b/src/game-server/mapmanager.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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
+ *
+ * $Id$
+ */
+
+#include <cassert>
+
+#include "map.h"
+#include "mapreader.h"
+#include "resourcemanager.h"
+#include "game-server/mapmanager.hpp"
+#include "utils/logger.h"
+#include "utils/xml.hpp"
+
+MapManager::MapManager(std::string const &mapReferenceFile)
+{
+ ResourceManager *resman = ResourceManager::getInstance();
+ int size;
+ char *data = (char *)resman->loadFile(mapReferenceFile, size);
+
+ if (!data) {
+ LOG_ERROR("Map Manager: Could not find " << mapReferenceFile << "!", 0);
+ free(data);
+ return;
+ }
+
+ xmlDocPtr doc = xmlParseMemory(data, size);
+ free(data);
+
+ if (!doc)
+ {
+ LOG_ERROR("Map Manager: Error while parsing map database ("
+ << mapReferenceFile << ")!", 0);
+ return;
+ }
+
+ xmlNodePtr node = xmlDocGetRootElement(doc);
+ if (!node || !xmlStrEqual(node->name, BAD_CAST "maps"))
+ {
+ LOG_ERROR("Map Manager: " << mapReferenceFile
+ << " is not a valid database file!", 0);
+ xmlFreeDoc(doc);
+ return;
+ }
+
+ LOG_INFO("Loading map reference...", 0);
+ for (node = node->xmlChildrenNode; node != NULL; node = node->next)
+ {
+ if (!xmlStrEqual(node->name, BAD_CAST "map")) {
+ continue;
+ }
+
+ unsigned id = XML::getProperty(node, "id", 0);
+ std::string name = XML::getProperty(node, "name", std::string());
+ // TODO: load only local maps
+ loadMap(id, name);
+ }
+
+ xmlFreeDoc(doc);
+}
+
+MapManager::~MapManager()
+{
+ for (Maps::iterator i = maps.begin(), i_end = maps.end(); i != i_end; ++i)
+ {
+ delete i->second.map;
+ }
+}
+
+void MapManager::loadMap(unsigned mapId, std::string const &mapFile)
+{
+ LoadedMap m = { mapFile, MapReader::readMap("maps/" + mapFile) };
+ if (m.map == NULL)
+ {
+ LOG_ERROR("Unable to load map \"" << mapFile << "\" (id " << mapId << ")", 0);
+ return;
+ }
+ LOG_INFO("Loaded map \"" << mapFile << "\" (id " << mapId << ")", 0);
+ maps[mapId] = m;
+}
+
+Map *MapManager::getMap(unsigned mapId)
+{
+ Maps::iterator i = maps.find(mapId);
+ assert(i != maps.end());
+ return i->second.map;
+}
+
+std::string MapManager::getMapName(unsigned mapId)
+{
+ Maps::iterator i = maps.find(mapId);
+ assert(i != maps.end());
+ return i->second.fileName;
+}
diff --git a/src/game-server/mapmanager.hpp b/src/game-server/mapmanager.hpp
new file mode 100644
index 00000000..1090a850
--- /dev/null
+++ b/src/game-server/mapmanager.hpp
@@ -0,0 +1,83 @@
+/*
+ * 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
+ *
+ * $Id$
+ */
+
+#ifndef _TMW_MAPMANAGER_H
+#define _TMW_MAPMANAGER_H
+
+#include <map>
+#include <string>
+
+class Map;
+
+struct LoadedMap
+{
+ std::string fileName;
+ Map *map;
+};
+
+/**
+ * MapManager loads/unloads maps
+ */
+class MapManager
+{
+ public:
+ typedef std::map< unsigned, LoadedMap > Maps;
+
+ /**
+ * Constructor (loads map reference file).
+ */
+ MapManager(std::string const &);
+
+ /**
+ * Returns the requested map.
+ */
+ Map *getMap(unsigned);
+
+ /**
+ * Returns the requested map name.
+ */
+ std::string getMapName(unsigned);
+
+ /**
+ * Returns all the maps.
+ */
+ Maps const &getMaps() { return maps; }
+
+ /**
+ * Destructor.
+ */
+ ~MapManager();
+
+ private:
+ /**
+ * Loads the specified map.
+ */
+ void loadMap(unsigned, std::string const &);
+
+ // Hold all the loaded maps.
+ Maps maps;
+};
+
+extern MapManager *mapManager;
+
+#endif
diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp
new file mode 100644
index 00000000..3bb2e478
--- /dev/null
+++ b/src/game-server/state.cpp
@@ -0,0 +1,331 @@
+/*
+ * The Mana World Server
+ * 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
+ *
+ * $Id$
+ */
+
+#include <cassert>
+
+#include "controller.h"
+#include "map.h"
+#include "mapcomposite.h"
+#include "point.h"
+#include "game-server/gamehandler.hpp"
+#include "game-server/mapmanager.hpp"
+#include "game-server/state.hpp"
+#include "net/messageout.hpp"
+#include "utils/logger.h"
+
+State::State()
+{
+ // Create 10 maggots for testing purposes
+ for (int i = 0; i < 10; i++)
+ {
+ Being *being = new Controlled(OBJECT_MONSTER);
+ being->setSpeed(150);
+ being->setMapId(1);
+ Point pos = { 720, 900 };
+ being->setPosition(pos);
+ addObject(ObjectPtr(being));
+ }
+}
+
+State::~State()
+{
+ for (std::map< unsigned, MapComposite * >::iterator i = maps.begin(),
+ i_end = maps.end(); i != i_end; ++i)
+ {
+ delete i->second;
+ }
+}
+
+void
+State::update()
+{
+ /*
+ * Update game state (update AI, etc.)
+ */
+ for (std::map< unsigned, MapComposite * >::iterator m = maps.begin(),
+ m_end = maps.end(); m != m_end; ++m)
+ {
+ MapComposite *map = m->second;
+
+ for (ObjectIterator o(map->getWholeMapIterator()); o; ++o)
+ {
+ (*o)->update();
+ if ((*o)->getType() == OBJECT_PLAYER || (*o)->getType() == OBJECT_MONSTER)
+ {
+ static_cast< Being * >(*o)->clearHitsTaken();
+ }
+ }
+
+ for (MovingObjectIterator o(map->getWholeMapIterator()); o; ++o)
+ {
+ if ((*o)->getUpdateFlags() & ATTACK)
+ {
+ static_cast< Being * >(*o)->performAttack(m->second);
+ }
+ }
+
+ for (MovingObjectIterator o(map->getWholeMapIterator()); o; ++o)
+ {
+ (*o)->move();
+ }
+
+ map->update();
+
+ /*
+ * Inform clients about changes in the game state
+ */
+ for (PlayerIterator p(map->getWholeMapIterator()); p; ++p)
+ {
+ MessageOut moveMsg(GPMSG_BEINGS_MOVE);
+ MessageOut damageMsg(GPMSG_BEINGS_DAMAGE);
+
+ for (MovingObjectIterator o(map->getAroundPlayerIterator(*p)); o; ++o)
+ {
+
+ Point os = (*o)->getOldPosition();
+ Point on = (*o)->getPosition();
+
+ int flags = 0;
+
+ // Send attack messages
+ if ( (*o)->getUpdateFlags() & ATTACK
+ && (*o)->getPublicID() != (*p)->getPublicID()
+ && (*p)->getPosition().inRangeOf(on)
+ )
+ {
+ MessageOut AttackMsg (GPMSG_BEING_ATTACK);
+ AttackMsg.writeShort((*o)->getPublicID());
+
+ LOG_DEBUG("Sending attack packet from " << (*o)->getPublicID()
+ << " to " << (*p)->getPublicID(), 0);
+
+ gameHandler->sendTo(*p, AttackMsg);
+ }
+
+ // Send damage messages
+
+ if ((*o)->getType() == OBJECT_PLAYER || (*o)->getType() == OBJECT_MONSTER)
+ {
+ Being *victim = static_cast< Being * >(*o);
+ Hits const &hits = victim->getHitsTaken();
+ for (Hits::const_iterator i = hits.begin(),
+ i_end = hits.end(); i != i_end; ++i)
+ {
+ damageMsg.writeShort(victim->getPublicID());
+ damageMsg.writeShort((*i));
+ }
+ }
+
+ // Send move messages
+
+ /* Check whether this player and this moving object were around
+ * the last time and whether they will be around the next time.
+ */
+ bool wereInRange = (*p)->getOldPosition().inRangeOf(os) &&
+ !(((*p)->getUpdateFlags() | (*o)->getUpdateFlags()) & NEW_ON_MAP);
+ bool willBeInRange = (*p)->getPosition().inRangeOf(on);
+ if (!wereInRange)
+ {
+ // o was outside p's range.
+ if (!willBeInRange)
+ {
+ // Nothing to report: o will not be inside p's range.
+ continue;
+ }
+ flags |= MOVING_DESTINATION;
+
+ int type = (*o)->getType();
+ MessageOut enterMsg(GPMSG_BEING_ENTER);
+ enterMsg.writeByte(type);
+ enterMsg.writeShort((*o)->getPublicID());
+ switch (type) {
+ case OBJECT_PLAYER:
+ {
+ Player *q = static_cast< Player * >(*o);
+ enterMsg.writeString(q->getName());
+ enterMsg.writeByte(q->getHairStyle());
+ enterMsg.writeByte(q->getHairColor());
+ enterMsg.writeByte(q->getGender());
+ } break;
+ case OBJECT_MONSTER:
+ {
+ enterMsg.writeShort(0); // TODO: The monster ID
+ } break;
+ default:
+ assert(false); // TODO
+ }
+ gameHandler->sendTo(*p, enterMsg);
+ }
+ else if (!willBeInRange)
+ {
+ // o is no longer visible from p.
+ MessageOut leaveMsg(GPMSG_BEING_LEAVE);
+ leaveMsg.writeShort((*o)->getPublicID());
+ gameHandler->sendTo(*p, leaveMsg);
+ continue;
+ }
+ else if (os.x == on.x && os.y == on.y)
+ {
+ // o does not move, nothing to report.
+ continue;
+ }
+
+ /* At this point, either o has entered p's range, either o is
+ moving inside p's range. Report o's movements. */
+
+ Point od = (*o)->getDestination();
+ if (on.x != od.x || on.y != od.y)
+ {
+ flags |= MOVING_POSITION;
+ if ((*o)->getUpdateFlags() & NEW_DESTINATION)
+ {
+ flags |= MOVING_DESTINATION;
+ }
+ }
+ else
+ {
+ // no need to synchronize on the very last step
+ flags |= MOVING_DESTINATION;
+ }
+
+ moveMsg.writeShort((*o)->getPublicID());
+ moveMsg.writeByte(flags);
+ if (flags & MOVING_POSITION)
+ {
+ moveMsg.writeCoordinates(on.x / 32, on.y / 32);
+ }
+ if (flags & MOVING_DESTINATION)
+ {
+ moveMsg.writeShort(od.x);
+ moveMsg.writeShort(od.y);
+ }
+ }
+
+ // Don't send a packet if nothing happened in p's range.
+ if (moveMsg.getLength() > 2)
+ gameHandler->sendTo(*p, moveMsg);
+
+ if (damageMsg.getLength() > 2)
+ gameHandler->sendTo(*p, damageMsg);
+ }
+
+ for (ObjectIterator o(map->getWholeMapIterator()); o; ++o)
+ {
+ (*o)->clearUpdateFlags();
+ }
+ }
+}
+
+void
+State::addObject(ObjectPtr objectPtr)
+{
+ unsigned mapId = objectPtr->getMapId();
+ MapComposite *map = loadMap(mapId);
+ if (!map || !map->insert(objectPtr))
+ {
+ // TODO: Deal with failure to place Object on the map.
+ return;
+ }
+ objectPtr->raiseUpdateFlags(NEW_ON_MAP);
+ if (objectPtr->getType() != OBJECT_PLAYER) return;
+ Player *playerPtr = static_cast< Player * >(objectPtr.get());
+
+ /* Since the player doesn't know yet where on the world he is after
+ * connecting to the map server, we send him an initial change map message.
+ */
+ MessageOut mapChangeMessage(GPMSG_PLAYER_MAP_CHANGE);
+ mapChangeMessage.writeString(mapManager->getMapName(mapId));
+ Point pos = playerPtr->getPosition();
+ mapChangeMessage.writeShort(pos.x);
+ mapChangeMessage.writeShort(pos.y);
+ mapChangeMessage.writeByte(0);
+ gameHandler->sendTo(playerPtr, mapChangeMessage);
+}
+
+void
+State::removeObject(ObjectPtr objectPtr)
+{
+ unsigned mapId = objectPtr->getMapId();
+ std::map< unsigned, MapComposite * >::iterator m = maps.find(mapId);
+ if (m == maps.end()) return;
+ MapComposite *map = m->second;
+
+ int type = objectPtr->getType();
+ if (type == OBJECT_MONSTER || type == OBJECT_PLAYER || type == OBJECT_NPC)
+ {
+ MovingObject *obj = static_cast< MovingObject * >(objectPtr.get());
+ MessageOut msg(GPMSG_BEING_LEAVE);
+ msg.writeShort(obj->getPublicID());
+ Point objectPos = obj->getPosition();
+
+ for (PlayerIterator p(map->getAroundObjectIterator(obj)); p; ++p)
+ {
+ if (*p != obj && objectPos.inRangeOf((*p)->getPosition()))
+ {
+ gameHandler->sendTo(*p, msg);
+ }
+ }
+ }
+
+ map->remove(objectPtr);
+}
+
+MapComposite *State::loadMap(unsigned mapId)
+{
+ std::map< unsigned, MapComposite * >::iterator m = maps.find(mapId);
+ if (m != maps.end()) return m->second;
+ Map *map = mapManager->getMap(mapId);
+ assert(map);
+ MapComposite *tmp = new MapComposite(map);
+ maps[mapId] = tmp;
+
+ // will need to load extra map related resources here also
+
+ return tmp;
+}
+
+void State::sayAround(Object *obj, std::string text)
+{
+ unsigned short id = 65535;
+ int type = obj->getType();
+ if (type == OBJECT_PLAYER || type == OBJECT_NPC || type == OBJECT_MONSTER)
+ {
+ id = static_cast< MovingObject * >(obj)->getPublicID();
+ }
+ MessageOut msg(GPMSG_SAY);
+ msg.writeShort(id);
+ msg.writeString(text);
+
+ std::map< unsigned, MapComposite * >::iterator m = maps.find(obj->getMapId());
+ if (m == maps.end()) return;
+ MapComposite *map = m->second;
+ Point speakerPosition = obj->getPosition();
+
+ for (PlayerIterator i(map->getAroundObjectIterator(obj)); i; ++i)
+ {
+ if (speakerPosition.inRangeOf((*i)->getPosition()))
+ {
+ gameHandler->sendTo(*i, msg);
+ }
+ }
+}
diff --git a/src/game-server/state.hpp b/src/game-server/state.hpp
new file mode 100644
index 00000000..36bd87d0
--- /dev/null
+++ b/src/game-server/state.hpp
@@ -0,0 +1,77 @@
+/*
+ * The Mana World Server
+ * 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
+ *
+ * $Id$
+ */
+
+#ifndef _TMW_SERVER_STATE_
+#define _TMW_SERVER_STATE_
+
+#include <map>
+#include <string>
+
+#include "object.h"
+
+class MapComposite;
+
+/**
+ * State class contains all information/procedures associated with the game
+ * world's state.
+ */
+class State
+{
+ /**
+ * List of maps.
+ */
+ std::map<unsigned int, MapComposite *> maps;
+
+ public:
+ State();
+ ~State();
+
+ /**
+ * Update game state (contains core server logic).
+ */
+ void update();
+
+ /**
+ * Load map into game world.
+ */
+ MapComposite *loadMap(unsigned mapId);
+
+ /**
+ * Add object to the map.
+ */
+ void addObject(ObjectPtr objectPtr);
+
+ /**
+ * Remove an object from the map.
+ */
+ void removeObject(ObjectPtr objectPtr);
+
+ /**
+ * Say around an object.
+ */
+ void sayAround(Object *, std::string text);
+};
+
+extern State *gameState;
+
+#endif