diff options
author | Guillaume Melquiond <guillaume.melquiond@gmail.com> | 2006-12-30 23:25:42 +0000 |
---|---|---|
committer | Guillaume Melquiond <guillaume.melquiond@gmail.com> | 2006-12-30 23:25:42 +0000 |
commit | 723e7454e5d164b9d438a45b0446b56b72f36d83 (patch) | |
tree | c942bfe1dc7087c856b9024b251f39cfcfa91f6d /src | |
parent | 7f1c499e4e09ab939d6d31188aaaaf785250a1d5 (diff) | |
download | manaserv-723e7454e5d164b9d438a45b0446b56b72f36d83.tar.gz manaserv-723e7454e5d164b9d438a45b0446b56b72f36d83.tar.bz2 manaserv-723e7454e5d164b9d438a45b0446b56b72f36d83.tar.xz manaserv-723e7454e5d164b9d438a45b0446b56b72f36d83.zip |
Almost a complete implementation for warping players between servers.
Diffstat (limited to 'src')
-rw-r--r-- | src/account-server/serverhandler.cpp | 44 | ||||
-rw-r--r-- | src/defines.h | 8 | ||||
-rw-r--r-- | src/game-server/accountconnection.cpp | 28 | ||||
-rw-r--r-- | src/game-server/accountconnection.hpp | 7 | ||||
-rw-r--r-- | src/game-server/gamehandler.cpp | 125 | ||||
-rw-r--r-- | src/game-server/gamehandler.hpp | 37 | ||||
-rw-r--r-- | src/game-server/state.cpp | 136 | ||||
-rw-r--r-- | src/game-server/state.hpp | 36 |
8 files changed, 319 insertions, 102 deletions
diff --git a/src/account-server/serverhandler.cpp b/src/account-server/serverhandler.cpp index 7de37126..1b9baa44 100644 --- a/src/account-server/serverhandler.cpp +++ b/src/account-server/serverhandler.cpp @@ -124,6 +124,50 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) } } break; + case GAMSG_PLAYER_DATA: + { + int id = msg.readLong(); + /* + // TODO: get correct character and account from database + 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()); + */ + } break; + + case GAMSG_REDIRECT: + { + int id = msg.readLong(); + std::string magic_token(32, ' '); + for (int i = 0; i < 32; ++i) + { + magic_token[i] = 1 + (int)(127 * (rand() / (RAND_MAX + 1.0))); + } + /* + // TODO: get correct character and account from database + std::string address; + short port; + if (serverHandler->getGameServerFromMap(ptr->getMapId(), address, port)) + { + registerGameClient(magic_token, ptr); + result.writeShort(AGMSG_REDIRECT_RESPONSE); + result.writeLong(ptr->getDatabaseID()); + result.writeString(magic_token, 32); + result.writeString(address); + result.writeShort(port); + } + */ + } break; + default: LOG_WARN("Invalid message type.", 0); result.writeShort(XXMSG_INVALID); diff --git a/src/defines.h b/src/defines.h index 0a5f383c..b5a44898 100644 --- a/src/defines.h +++ b/src/defines.h @@ -141,8 +141,8 @@ enum { CPMSG_CONNECT_RESPONSE = 0x0054, // B error // Game - GPMSG_PLAYER_MAP_CHANGE = 0x0100, // S filename, W x, W y, B newserv - // [, S32 token, S server, W port] + 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, GPMSG_PICKUP_RESPONSE = 0x0111, GPMSG_BEING_ENTER = 0x0200, // B type, W being id @@ -185,6 +185,10 @@ enum { AGMSG_ACTIVE_MAP = 0x501, // W map id AGMSG_PLAYER_ENTER = 0x510, // L id, S name, B gender, B hair style, B hair color, B level, W money, // W*6 stats, W x, W y, W map id, B*32 token + GAMSG_PLAYER_DATA = 0x520, // L id, B gender, B hair style, B hair color, B level, W money, + // W*6 stats, W x, W y, W map id + GAMSG_REDIRECT = 0x530, // L id + AGMSG_REDIRECT_RESPONSE = 0x531, // L id, B*32 token, S game address, W game port XXMSG_INVALID = 0x7FFF }; diff --git a/src/game-server/accountconnection.cpp b/src/game-server/accountconnection.cpp index ee00a440..ceb0b5dd 100644 --- a/src/game-server/accountconnection.cpp +++ b/src/game-server/accountconnection.cpp @@ -25,6 +25,7 @@ #include "defines.h" #include "player.h" #include "game-server/accountconnection.hpp" +#include "game-server/gamehandler.hpp" #include "game-server/mapmanager.hpp" #include "net/messagein.hpp" #include "net/messageout.hpp" @@ -52,6 +53,24 @@ bool AccountConnection::start() return true; } +void AccountConnection::sendPlayerData(Player *p) +{ + MessageOut msg(GAMSG_PLAYER_DATA); + msg.writeLong(p->getDatabaseID()); + msg.writeByte(p->getGender()); + msg.writeByte(p->getHairStyle()); + msg.writeByte(p->getHairColor()); + msg.writeByte(p->getLevel()); + msg.writeShort(p->getMoney()); + for (int j = 0; j < NB_RSTAT; ++j) + msg.writeShort(p->getRawStat(j)); + Point pos = p->getPosition(); + msg.writeShort(pos.x); + msg.writeShort(pos.y); + msg.writeShort(p->getMapId()); + send(msg); +} + void AccountConnection::processMessage(MessageIn &msg) { switch (msg.getId()) @@ -84,6 +103,15 @@ void AccountConnection::processMessage(MessageIn &msg) mapManager->raiseActive(id); } break; + case AGMSG_REDIRECT_RESPONSE: + { + int id = msg.readLong(); + std::string token = msg.readString(32); + std::string address = msg.readString(); + int port = msg.readShort(); + gameHandler->completeServerChange(id, token, address, port); + } break; + default: LOG_WARN("Invalid message type", 0); break; diff --git a/src/game-server/accountconnection.hpp b/src/game-server/accountconnection.hpp index 97412215..9722fe69 100644 --- a/src/game-server/accountconnection.hpp +++ b/src/game-server/accountconnection.hpp @@ -26,6 +26,8 @@ #include "net/connection.hpp" +class Player; + /** * A connection to the account server. */ @@ -38,6 +40,11 @@ class AccountConnection: public Connection */ bool start(); + /** + * Sends data of given player. + */ + void sendPlayerData(Player *); + protected: /** * Processes server messages. diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp index 4347886f..c8e22350 100644 --- a/src/game-server/gamehandler.cpp +++ b/src/game-server/gamehandler.cpp @@ -32,10 +32,19 @@ #include "net/netcomputer.hpp" #include "utils/logger.h" +enum +{ + CLIENT_LOGIN = 0, + CLIENT_CONNECTED, + CLIENT_CHANGE_SERVER +}; + struct GameClient: NetComputer { - GameClient(ENetPeer *peer): NetComputer(peer), character(NULL) {} + GameClient(ENetPeer *peer) + : NetComputer(peer), character(NULL), status(CLIENT_LOGIN) {} Player *character; + int status; }; struct GamePendingLogin @@ -62,6 +71,21 @@ static GamePendingLogins pendingLogins; static GamePendingClients pendingClients; /** + * Links a client to a character. + */ +static void linkCharacter(GameClient *computer, Player *ch) +{ + computer->character = ch; + computer->status = CLIENT_CONNECTED; + ch->setClient(computer); + gameState->insertObject(ch); + MessageOut result; + result.writeShort(GPMSG_CONNECT_RESPONSE); + result.writeByte(ERRMSG_OK); + computer->send(result); +} + +/** * Notification that a particular token has been given to allow a certain * player to enter the game. */ @@ -70,15 +94,8 @@ void registerGameClient(std::string const &token, Player *ch) GamePendingClients::iterator i = pendingClients.find(token); if (i != pendingClients.end()) { - GameClient *computer = i->second; - computer->character = ch; - ch->setClient(computer); - gameState->addObject(ch); + linkCharacter(i->second, ch); pendingClients.erase(i); - MessageOut result; - result.writeShort(GPMSG_CONNECT_RESPONSE); - result.writeByte(ERRMSG_OK); - computer->send(result); } else { @@ -89,31 +106,12 @@ void registerGameClient(std::string const &token, Player *ch) } } -bool -GameHandler::startListen(enet_uint16 port) +bool GameHandler::startListen(enet_uint16 port) { LOG_INFO("Game handler started:", 0); return ConnectionHandler::startListen(port); } -void GameHandler::removeOutdatedPending() -{ - GamePendingLogins::iterator i = pendingLogins.begin(); - - while (i != pendingLogins.end()) - { - if (--i->second.timeout <= 0) - { - delete i->second.character; - pendingLogins.erase(i++); - } - else - { - ++i; - } - } -} - NetComputer *GameHandler::computerConnected(ENetPeer *peer) { return new GameClient(peer); @@ -138,10 +136,62 @@ void GameHandler::computerDisconnected(NetComputer *computer) delete computer; } +void GameHandler::kill(Player *ch) +{ + GameClient *client = ch->getClient(); + assert(client != NULL); + client->character = NULL; + client->status = CLIENT_LOGIN; +} + +void GameHandler::prepareServerChange(Player *ch) +{ + GameClient *client = ch->getClient(); + assert(client != NULL); + client->status = CLIENT_CHANGE_SERVER; +} + +void GameHandler::completeServerChange(int id, std::string const &token, + std::string const &address, int port) +{ + for (NetComputers::const_iterator i = clients.begin(), + i_end = clients.end(); i != i_end; ++i) + { + GameClient *c = static_cast< GameClient * >(*i); + if (c->status == CLIENT_CHANGE_SERVER && + c->character->getDatabaseID() == id) + { + MessageOut msg(GPMSG_PLAYER_SERVER_CHANGE); + msg.writeString(token, 32); + msg.writeString(address); + msg.writeShort(port); + c->send(msg); + delete c->character; + c->character = NULL; + c->status = CLIENT_LOGIN; + return; + } + } +} + void GameHandler::process() { ConnectionHandler::process(); - removeOutdatedPending(); + + // Removes characters that have been left unconnected for too long. + GamePendingLogins::iterator i = pendingLogins.begin(); + while (i != pendingLogins.end()) + { + if (--i->second.timeout <= 0) + { + delete i->second.character; + pendingLogins.erase(i++); + } + else + { + ++i; + } + } } void GameHandler::processMessage(NetComputer *comp, MessageIn &message) @@ -149,7 +199,7 @@ void GameHandler::processMessage(NetComputer *comp, MessageIn &message) GameClient &computer = *static_cast< GameClient * >(comp); MessageOut result; - if (computer.character == NULL) + if (computer.status == CLIENT_LOGIN) { if (message.getId() != PGMSG_CONNECT) return; std::string magic_token = message.readString(32); @@ -164,13 +214,12 @@ void GameHandler::processMessage(NetComputer *comp, MessageIn &message) pendingClients.insert(std::make_pair(magic_token, &computer)); return; } - computer.character = i->second.character; - computer.character->setClient(&computer); - gameState->addObject(computer.character); + linkCharacter(&computer, i->second.character); pendingLogins.erase(i); - result.writeShort(GPMSG_CONNECT_RESPONSE); - result.writeByte(ERRMSG_OK); - computer.send(result); + return; + } + else if (computer.status != CLIENT_CONNECTED) + { return; } @@ -256,6 +305,6 @@ void GameHandler::processMessage(NetComputer *comp, MessageIn &message) void GameHandler::sendTo(Player *beingPtr, MessageOut &msg) { GameClient *client = beingPtr->getClient(); - assert(client != NULL); + assert(client && client->status == CLIENT_CONNECTED); client->send(msg); } diff --git a/src/game-server/gamehandler.hpp b/src/game-server/gamehandler.hpp index ceb37cd8..f574d9ec 100644 --- a/src/game-server/gamehandler.hpp +++ b/src/game-server/gamehandler.hpp @@ -27,38 +27,51 @@ #include "player.h" #include "net/connectionhandler.hpp" -class GameClient; - -/* - * Manage connections to game server. +/** + * Manages connections to game client. */ class GameHandler: public ConnectionHandler { public: + /** + * Processes messages and cleans outdated characters. + */ void process(); /** - * Start the handler + * Starts the handler */ - bool - startListen(enet_uint16 port); + bool startListen(enet_uint16 port); /** - * Send message to the given player. + * Sends message to the given player. */ void sendTo(Player *, MessageOut &msg); + /** + * Kills connection with given player. + */ + void kill(Player *); + + /** + * Prepares a server change for given player. + */ + void prepareServerChange(Player *); + + /** + * Completes a server change for given player ID. + */ + void completeServerChange(int id, std::string const &token, + std::string const &address, int port); + protected: NetComputer *computerConnected(ENetPeer *); void computerDisconnected(NetComputer *); /** - * Process messages related to core game events. + * Processes messages related to core game events. */ void processMessage(NetComputer *computer, MessageIn &message); - - private: - void removeOutdatedPending(); }; extern GameHandler *gameHandler; diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp index 5fade89a..f7c12d98 100644 --- a/src/game-server/state.cpp +++ b/src/game-server/state.cpp @@ -27,6 +27,7 @@ #include "defines.h" #include "map.h" #include "point.h" +#include "game-server/accountconnection.hpp" #include "game-server/gamehandler.hpp" #include "game-server/mapcomposite.hpp" #include "game-server/mapmanager.hpp" @@ -44,14 +45,14 @@ State::State() being->setMapId(1); Point pos = { 720, 900 }; being->setPosition(pos); - addObject(being); + DelayedEvent e = { EVENT_INSERT }; + enqueueEvent(being, e); } } State::~State() { - for (std::map< unsigned, MapComposite * >::iterator i = maps.begin(), - i_end = maps.end(); i != i_end; ++i) + for (Maps::iterator i = maps.begin(), i_end = maps.end(); i != i_end; ++i) { delete i->second; } @@ -81,6 +82,32 @@ void State::updateMap(MapComposite *map) (*i)->move(); } map->update(); + + // 4. Just for fun. Should be replaced by a real trigger system. + for (MovingObjectIterator i(map->getWholeMapIterator()); i; ++i) + { + Object *o = *i; + if (o->getType() != OBJECT_PLAYER) continue; + Point pos = o->getPosition(); + int x = pos.x / 32, y = pos.y / 32; + int m = 0; + switch (o->getMapId()) + { + case 1: + if (x >= 56 && x <= 60 && y == 12) + { m = 3; x = 44; y = 80; } + break; + case 3: + if (x >= 42 && x <= 46 && y == 88) + { m = 1; x = 58; y = 17; } + break; + } + if (m != 0) + { + DelayedEvent e = { EVENT_WARP, m, x * 32 + 16, y * 32 + 16 }; + enqueueEvent(o, e); + } + } } void State::informPlayer(MapComposite *map, Player *p) @@ -214,19 +241,12 @@ void State::informPlayer(MapComposite *map, Player *p) 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) + // Update game state (update AI, etc.) + for (Maps::iterator m = maps.begin(), m_end = maps.end(); m != m_end; ++m) { MapComposite *map = m->second; - updateMap(map); - /* - * Inform clients about changes in the game state - */ for (PlayerIterator p(map->getWholeMapIterator()); p; ++p) { informPlayer(map, *p); @@ -242,42 +262,60 @@ void State::update() static_cast< Being * >(o)->clearHitsTaken(); } } + } - // Just for fun. Should be replaced by a real trigger system. - for (size_t i = map->getObjects().size(); i-- > 0;) + // Take care of events that were delayed because of their side effects. + for (DelayedEvents::iterator i = delayedEvents.begin(), + i_end = delayedEvents.end(); i != i_end; ++i) + { + DelayedEvent const &e = i->second; + Object *o = i->first; + switch (e.type) { - Object *o = map->getObjects()[i]; - if (o->getType() != OBJECT_PLAYER) continue; - Point pos = o->getPosition(); - int x = pos.x / 32, y = pos.y / 32; - int m = 0; - switch (o->getMapId()) + case EVENT_REMOVE: { - case 1: - if (x >= 56 && x <= 60 && y == 12) - { m = 3; x = 44; y = 80; } - break; - case 3: - if (x >= 42 && x <= 46 && y == 88) - { m = 1; x = 58; y = 17; } - break; - } - if (m != 0) + removeObject(o); + if (o->getType() == OBJECT_PLAYER) + { + gameHandler->kill(static_cast< Player * >(o)); + } + delete o; + } break; + + case EVENT_INSERT: + { + insertObject(o); + } break; + + case EVENT_WARP: { removeObject(o); - o->setMapId(m); - pos.x = x * 32; pos.y = y * 32; + o->setMapId(e.map); + Point pos = { e.x, e.y }; o->setPosition(pos); - addObject(o); - } + if (mapManager->isActive(e.map)) + { + insertObject(o); + } + else + { + assert(o->getType() == OBJECT_PLAYER); + Player *p = static_cast< Player * >(o); + accountHandler->sendPlayerData(p); + MessageOut msg(GAMSG_REDIRECT); + msg.writeLong(p->getDatabaseID()); + accountHandler->send(msg); + gameHandler->prepareServerChange(p); + } + } break; } } + delayedEvents.clear(); } -void -State::addObject(Object *objectPtr) +void State::insertObject(Object *objectPtr) { - unsigned mapId = objectPtr->getMapId(); + int mapId = objectPtr->getMapId(); MapComposite *map = loadMap(mapId); if (!map || !map->insert(objectPtr)) { @@ -300,11 +338,10 @@ State::addObject(Object *objectPtr) gameHandler->sendTo(playerPtr, mapChangeMessage); } -void -State::removeObject(Object *objectPtr) +void State::removeObject(Object *objectPtr) { - unsigned mapId = objectPtr->getMapId(); - std::map< unsigned, MapComposite * >::iterator m = maps.find(mapId); + int mapId = objectPtr->getMapId(); + Maps::iterator m = maps.find(mapId); if (m == maps.end()) return; MapComposite *map = m->second; @@ -328,9 +365,20 @@ State::removeObject(Object *objectPtr) map->remove(objectPtr); } -MapComposite *State::loadMap(unsigned mapId) +void State::enqueueEvent(Object *ptr, DelayedEvent const &e) +{ + std::pair< DelayedEvents::iterator, bool > p = + delayedEvents.insert(std::make_pair(ptr, e)); + // Delete events take precedence over other events. + if (!p.second && e.type == EVENT_REMOVE) + { + p.first->second.type = EVENT_REMOVE; + } +} + +MapComposite *State::loadMap(int mapId) { - std::map< unsigned, MapComposite * >::iterator m = maps.find(mapId); + Maps::iterator m = maps.find(mapId); if (m != maps.end()) return m->second; Map *map = mapManager->getMap(mapId); assert(map); @@ -354,7 +402,7 @@ void State::sayAround(Object *obj, std::string text) msg.writeShort(id); msg.writeString(text); - std::map< unsigned, MapComposite * >::iterator m = maps.find(obj->getMapId()); + Maps::iterator m = maps.find(obj->getMapId()); if (m == maps.end()) return; MapComposite *map = m->second; Point speakerPosition = obj->getPosition(); diff --git a/src/game-server/state.hpp b/src/game-server/state.hpp index 95ba73b5..d759da88 100644 --- a/src/game-server/state.hpp +++ b/src/game-server/state.hpp @@ -27,9 +27,20 @@ #include <map> #include <string> -#include "object.h" - class MapComposite; +class Object; + +enum +{ + EVENT_REMOVE = 0, + EVENT_INSERT, + EVENT_WARP +}; + +struct DelayedEvent +{ + unsigned short type, map, x, y; +}; /** * State class contains all information/procedures associated with the game @@ -37,10 +48,18 @@ class MapComposite; */ class State { + typedef std::map< int, MapComposite * > Maps; + typedef std::map< Object *, DelayedEvent > DelayedEvents; + /** * List of maps. */ - std::map<unsigned int, MapComposite *> maps; + Maps maps; + + /** + * List of delayed events. + */ + DelayedEvents delayedEvents; /** * Updates object states on the map. @@ -64,12 +83,12 @@ class State /** * Loads map into game world. */ - MapComposite *loadMap(unsigned mapId); + MapComposite *loadMap(int mapId); /** - * Adds object to the map. + * Inserts an object on the map. */ - void addObject(Object *); + void insertObject(Object *); /** * Removes an object from the map. @@ -77,6 +96,11 @@ class State void removeObject(Object *); /** + * Enqueues an event. It will be executed at end of update. + */ + void enqueueEvent(Object *, DelayedEvent const &); + + /** * Says something around an object. */ void sayAround(Object *, std::string text); |