void ChatHandler::processMessage(NetComputer *comp, MessageIn &message) { ChatClient &computer = *static_cast< ChatClient * >(comp); MessageOut result; if (computer.characterName.empty()) { if (message.getId() != PCMSG_CONNECT) return; std::string magic_token = message.readString(MAGIC_TOKEN_LENGTH); mTokenCollector.addPendingClient(magic_token, &computer); sendGuildRejoin(computer); return; } switch (message.getId()) { case PCMSG_CHAT: handleChatMessage(computer, message); break; case PCMSG_ANNOUNCE: handleAnnounceMessage(computer, message); break; case PCMSG_PRIVMSG: handlePrivMsgMessage(computer, message); break; case PCMSG_WHO: handleWhoMessage(computer); break; case PCMSG_ENTER_CHANNEL: handleEnterChannelMessage(computer, message); break; case PCMSG_USER_MODE: handleModeChangeMessage(computer, message); break; case PCMSG_KICK_USER: handleKickUserMessage(computer, message); case PCMSG_QUIT_CHANNEL: handleQuitChannelMessage(computer, message); break; case PCMSG_LIST_CHANNELS: handleListChannelsMessage(computer, message); break; case PCMSG_LIST_CHANNELUSERS: handleListChannelUsersMessage(computer, message); break; case PCMSG_TOPIC_CHANGE: handleTopicChange(computer, message); break; case PCMSG_DISCONNECT: handleDisconnectMessage(computer, message); break; case PCMSG_GUILD_CREATE: handleGuildCreation(computer, message); break; case PCMSG_GUILD_INVITE: handleGuildInvitation(computer, message); break; case PCMSG_GUILD_ACCEPT: handleGuildAcceptInvite(computer, message); break; case PCMSG_GUILD_GET_MEMBERS: handleGuildRetrieveMembers(computer, message); break; case PCMSG_GUILD_PROMOTE_MEMBER: handleGuildMemberLevelChange(computer, message); break; case PCMSG_GUILD_KICK_MEMBER: handleGuildMemberKick(computer, message); case PCMSG_GUILD_QUIT: handleGuildQuit(computer, message); break; case PCMSG_PARTY_INVITE: handlePartyInvite(computer, message); break; case PCMSG_PARTY_ACCEPT_INVITE: handlePartyAcceptInvite(computer, message); break; case PCMSG_PARTY_QUIT: handlePartyQuit(computer); break; case PCMSG_PARTY_REJECT_INVITE: handlePartyRejection(computer, message); break; default: LOG_WARN("ChatHandler::processMessage, Invalid message type" << message.getId()); result.writeShort(XXMSG_INVALID); break; } if (result.getLength() > 0) computer.send(result); }
void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) { MessageOut result; GameServer *server = static_cast<GameServer *>(comp); switch (msg.getId()) { case GAMSG_REGISTER: { LOG_DEBUG("GAMSG_REGISTER"); // TODO: check the credentials of the game server server->address = msg.readString(); server->port = msg.readInt16(); const std::string password = msg.readString(); // checks the version of the remote item database with our local copy unsigned int dbversion = msg.readInt32(); LOG_INFO("Game server uses itemsdatabase with version " << dbversion); LOG_DEBUG("AGMSG_REGISTER_RESPONSE"); MessageOut outMsg(AGMSG_REGISTER_RESPONSE); if (dbversion == storage->getItemDatabaseVersion()) { LOG_DEBUG("Item databases between account server and " "gameserver are in sync"); outMsg.writeInt16(DATA_VERSION_OK); } else { LOG_DEBUG("Item database of game server has a wrong version"); outMsg.writeInt16(DATA_VERSION_OUTDATED); } if (password == Configuration::getValue("net_password", "changeMe")) { outMsg.writeInt16(PASSWORD_OK); comp->send(outMsg); // transmit global world state variables std::map<std::string, std::string> variables; variables = storage->getAllWorldStateVars(0); for (std::map<std::string, std::string>::iterator i = variables.begin(); i != variables.end(); i++) { outMsg.writeString(i->first); outMsg.writeString(i->second); } } else { LOG_INFO("The password given by " << server->address << ':' << server->port << " was bad."); outMsg.writeInt16(PASSWORD_BAD); comp->disconnect(outMsg); break; } LOG_INFO("Game server " << server->address << ':' << server->port << " wants to register " << (msg.getUnreadLength() / 2) << " maps."); while (msg.getUnreadLength()) { int id = msg.readInt16(); LOG_INFO("Registering map " << id << '.'); if (GameServer *s = getGameServerFromMap(id)) { LOG_ERROR("Server Handler: map is already registered by " << s->address << ':' << s->port << '.'); } else { MessageOut outMsg(AGMSG_ACTIVE_MAP); outMsg.writeInt16(id); std::map<std::string, std::string> variables; variables = storage->getAllWorldStateVars(id); for (std::map<std::string, std::string>::iterator i = variables.begin(); i != variables.end(); i++) { outMsg.writeString(i->first); outMsg.writeString(i->second); } comp->send(outMsg); MapStatistics &m = server->maps[id]; m.nbThings = 0; m.nbMonsters = 0; } } } break; case GAMSG_PLAYER_DATA: { LOG_DEBUG("GAMSG_PLAYER_DATA"); int id = msg.readInt32(); if (Character *ptr = storage->getCharacter(id, NULL)) { deserializeCharacterData(*ptr, msg); if (!storage->updateCharacter(ptr)) { LOG_ERROR("Failed to update character " << id << '.'); } delete ptr; } else { LOG_ERROR("Received data for non-existing character " << id << '.'); } } break; case GAMSG_PLAYER_SYNC: { LOG_DEBUG("GAMSG_PLAYER_SYNC"); GameServerHandler::syncDatabase(msg); } break; case GAMSG_REDIRECT: { LOG_DEBUG("GAMSG_REDIRECT"); int id = msg.readInt32(); std::string magic_token(utils::getMagicToken()); if (Character *ptr = storage->getCharacter(id, NULL)) { int mapId = ptr->getMapId(); if (GameServer *s = getGameServerFromMap(mapId)) { registerGameClient(s, magic_token, ptr); result.writeInt16(AGMSG_REDIRECT_RESPONSE); result.writeInt32(id); result.writeString(magic_token, MAGIC_TOKEN_LENGTH); result.writeString(s->address); result.writeInt16(s->port); } else { LOG_ERROR("Server Change: No game server for map " << mapId << '.'); } delete ptr; } else { LOG_ERROR("Received data for non-existing character " << id << '.'); } } break; case GAMSG_PLAYER_RECONNECT: { LOG_DEBUG("GAMSG_PLAYER_RECONNECT"); int id = msg.readInt32(); std::string magic_token = msg.readString(MAGIC_TOKEN_LENGTH); if (Character *ptr = storage->getCharacter(id, NULL)) { int accountID = ptr->getAccountID(); AccountClientHandler::prepareReconnect(magic_token, accountID); delete ptr; } else { LOG_ERROR("Received data for non-existing character " << id << '.'); } } break; case GAMSG_GET_VAR_CHR: { int id = msg.readInt32(); std::string name = msg.readString(); std::string value = storage->getQuestVar(id, name); result.writeInt16(AGMSG_GET_VAR_CHR_RESPONSE); result.writeInt32(id); result.writeString(name); result.writeString(value); } break; case GAMSG_SET_VAR_CHR: { int id = msg.readInt32(); std::string name = msg.readString(); std::string value = msg.readString(); storage->setQuestVar(id, name, value); } break; case GAMSG_SET_VAR_WORLD: { std::string name = msg.readString(); std::string value = msg.readString(); // save the new value to the database storage->setWorldStateVar(name, value); // relay the new value to all gameservers for (ServerHandler::NetComputers::iterator i = clients.begin(); i != clients.end(); i++) { MessageOut varUpdateMessage(AGMSG_SET_VAR_WORLD); varUpdateMessage.writeString(name); varUpdateMessage.writeString(value); (*i)->send(varUpdateMessage); } } break; case GAMSG_SET_VAR_MAP: { int mapid = msg.readInt32(); std::string name = msg.readString(); std::string value = msg.readString(); storage->setWorldStateVar(name, mapid, value); } break; case GAMSG_BAN_PLAYER: { int id = msg.readInt32(); int duration = msg.readInt32(); storage->banCharacter(id, duration); } break; case GAMSG_CHANGE_PLAYER_LEVEL: { int id = msg.readInt32(); int level = msg.readInt16(); storage->setPlayerLevel(id, level); } break; case GAMSG_CHANGE_ACCOUNT_LEVEL: { int id = msg.readInt32(); int level = msg.readInt16(); // get the character so we can get the account id Character *c = storage->getCharacter(id, NULL); if (c) { storage->setAccountLevel(c->getAccountID(), level); } } break; case GAMSG_STATISTICS: { while (msg.getUnreadLength()) { int mapId = msg.readInt16(); ServerStatistics::iterator i = server->maps.find(mapId); if (i == server->maps.end()) { LOG_ERROR("Server " << server->address << ':' << server->port << " should not be sending stati" "stics for map " << mapId << '.'); // Skip remaining data. break; } MapStatistics &m = i->second; m.nbThings = msg.readInt16(); m.nbMonsters = msg.readInt16(); int nb = msg.readInt16(); m.players.resize(nb); for (int j = 0; j < nb; ++j) { m.players[j] = msg.readInt32(); } } } break; case GCMSG_REQUEST_POST: { // Retrieve the post for user LOG_DEBUG("GCMSG_REQUEST_POST"); result.writeInt16(CGMSG_POST_RESPONSE); // get the character id int characterId = msg.readInt32(); // send the character id of sender result.writeInt32(characterId); // get the character based on the id Character *ptr = storage->getCharacter(characterId, NULL); if (!ptr) { // Invalid character LOG_ERROR("Error finding character id for post"); break; } // get the post for that character Post *post = postalManager->getPost(ptr); // send the post if valid if (post) { for (unsigned int i = 0; i < post->getNumberOfLetters(); ++i) { // get each letter, send the sender's name, // the contents and any attachments Letter *letter = post->getLetter(i); result.writeString(letter->getSender()->getName()); result.writeString(letter->getContents()); std::vector<InventoryItem> items = letter->getAttachments(); for (unsigned int j = 0; j < items.size(); ++j) { result.writeInt16(items[j].itemId); result.writeInt16(items[j].amount); } } // clean up postalManager->clearPost(ptr); } } break; case GCMSG_STORE_POST: { // Store the letter for the user LOG_DEBUG("GCMSG_STORE_POST"); result.writeInt16(CGMSG_STORE_POST_RESPONSE); // get the sender and receiver int senderId = msg.readInt32(); std::string receiverName = msg.readString(); // for sending it back result.writeInt32(senderId); // get their characters Character *sender = storage->getCharacter(senderId, NULL); Character *receiver = storage->getCharacter(receiverName); if (!sender || !receiver) { // Invalid character LOG_ERROR("Error finding character id for post"); result.writeInt8(ERRMSG_INVALID_ARGUMENT); break; } // get the letter contents std::string contents = msg.readString(); std::vector< std::pair<int, int> > items; while (msg.getUnreadLength()) { items.push_back(std::pair<int, int>(msg.readInt16(), msg.readInt16())); } // save the letter LOG_DEBUG("Creating letter"); Letter *letter = new Letter(0, sender, receiver); letter->addText(contents); for (unsigned int i = 0; i < items.size(); ++i) { InventoryItem item; item.itemId = items[i].first; item.amount = items[i].second; letter->addAttachment(item); } postalManager->addLetter(letter); result.writeInt8(ERRMSG_OK); } break; case GAMSG_TRANSACTION: { LOG_DEBUG("TRANSACTION"); int id = msg.readInt32(); int action = msg.readInt32(); std::string message = msg.readString(); Transaction trans; trans.mCharacterId = id; trans.mAction = action; trans.mMessage = message; storage->addTransaction(trans); } break; case GCMSG_PARTY_INVITE: chatHandler->handlePartyInvite(msg); break; default: LOG_WARN("ServerHandler::processMessage, Invalid message type: " << msg.getId()); result.writeInt16(XXMSG_INVALID); break; } // return result if (result.getLength() > 0) comp->send(result); }
void GameHandler::processMessage(NetComputer *comp, MessageIn &message) { GameClient &computer = *static_cast< GameClient * >(comp); MessageOut result; if (computer.status == CLIENT_LOGIN) { if (message.getId() != PGMSG_CONNECT) return; std::string magic_token = message.readString(MAGIC_TOKEN_LENGTH); computer.status = CLIENT_QUEUED; // Before the addPendingClient mTokenCollector.addPendingClient(magic_token, &computer); return; } else if (computer.status != CLIENT_CONNECTED) { return; } switch (message.getId()) { case PGMSG_SAY: { std::string say = message.readString(); if (say.empty()) break; if (say[0] == '@') { CommandHandler::handleCommand(computer.character, say); break; } GameState::sayAround(computer.character, say); std::string msg = computer.character->getName() + " said " + say; accountHandler->sendTransaction(computer.character->getDatabaseID(), TRANS_MSG_PUBLIC, msg); } break; case PGMSG_NPC_TALK: case PGMSG_NPC_TALK_NEXT: case PGMSG_NPC_SELECT: case PGMSG_NPC_NUMBER: case PGMSG_NPC_STRING: { int id = message.readShort(); Actor *o = findActorNear(computer.character, id); if (!o || o->getType() != OBJECT_NPC) { sendError(comp, id, "Not close enough to NPC\n"); break; } NPC *q = static_cast< NPC * >(o); if (message.getId() == PGMSG_NPC_SELECT) { q->select(computer.character, message.readByte()); } else if (message.getId() == PGMSG_NPC_NUMBER) { q->integerReceived(computer.character, message.readLong()); } else if (message.getId() == PGMSG_NPC_STRING) { q->stringReceived(computer.character, message.readString()); } else { q->prompt(computer.character, message.getId() == PGMSG_NPC_TALK); } } break; case PGMSG_PICKUP: { int x = message.readShort(); int y = message.readShort(); Point ppos = computer.character->getPosition(); // TODO: use a less arbitrary value. if (std::abs(x - ppos.x) + std::abs(y - ppos.y) < 48) { MapComposite *map = computer.character->getMap(); Point ipos(x, y); for (FixedActorIterator i(map->getAroundPointIterator(ipos, 0)); i; ++i) { Actor *o = *i; Point opos = o->getPosition(); if (o->getType() == OBJECT_ITEM && opos.x == x && opos.y == y) { Item *item = static_cast< Item * >(o); ItemClass *ic = item->getItemClass(); Inventory(computer.character) .insert(ic->getDatabaseID(), item->getAmount()); GameState::remove(item); // log transaction std::stringstream str; str << "User picked up item " << ic->getDatabaseID() << " at " << opos.x << "x" << opos.y; accountHandler->sendTransaction(computer.character->getDatabaseID(), TRANS_ITEM_PICKUP, str.str()); break; } } } } break; case PGMSG_USE_ITEM: { int slot = message.readByte(); Inventory inv(computer.character); if (ItemClass *ic = ItemManager::getItem(inv.getItem(slot))) { if (ic->use(computer.character)) { inv.removeFromSlot(slot, 1); // log transaction std::stringstream str; str << "User used item " << ic->getDatabaseID() << " from slot " << slot; accountHandler->sendTransaction(computer.character->getDatabaseID(), TRANS_ITEM_USED, str.str()); } } } break; case PGMSG_DROP: { int slot = message.readByte(); int amount = message.readByte(); Inventory inv(computer.character); if (ItemClass *ic = ItemManager::getItem(inv.getItem(slot))) { int nb = inv.removeFromSlot(slot, amount); Item *item = new Item(ic, amount - nb); item->setMap(computer.character->getMap()); item->setPosition(computer.character->getPosition()); if (!GameState::insert(item)) { // The map is full. Put back into inventory. inv.insert(ic->getDatabaseID(), amount - nb); delete item; break; } // log transaction Point pt = computer.character->getPosition(); std::stringstream str; str << "User dropped item " << ic->getDatabaseID() << " at " << pt.x << "x" << pt.y; accountHandler->sendTransaction(computer.character->getDatabaseID(), TRANS_ITEM_DROP, str.str()); } } break; case PGMSG_WALK: { handleWalk(&computer, message); } break; case PGMSG_EQUIP: { int slot = message.readByte(); Inventory(computer.character).equip(slot); } break; case PGMSG_UNEQUIP: { int slot = message.readByte(); if (slot >= 0 && slot < EQUIP_PROJECTILE_SLOT) { Inventory(computer.character).unequip(slot); } } break; case PGMSG_MOVE_ITEM: { int slot1 = message.readByte(); int slot2 = message.readByte(); int amount = message.readByte(); Inventory(computer.character).move(slot1, slot2, amount); // log transaction std::stringstream str; str << "User moved item " << " from slot " << slot1 << " to slot " << slot2; accountHandler->sendTransaction(computer.character->getDatabaseID(), TRANS_ITEM_MOVE, str.str()); } break; case PGMSG_ATTACK: { int id = message.readShort(); LOG_DEBUG("Character " << computer.character->getPublicID() << " attacked being " << id); Actor *o = findActorNear(computer.character, id); if (o && o->getType() != OBJECT_NPC) { Being *being = static_cast<Being*>(o); computer.character->setTarget(being); computer.character->setAction(Being::ATTACK); } } break; case PGMSG_USE_SPECIAL: { int specialID = message.readByte(); LOG_DEBUG("Character " << computer.character->getPublicID() << " tries to use his special attack "<<specialID); computer.character->useSpecial(specialID); } case PGMSG_ACTION_CHANGE: { Being::Action action = (Being::Action)message.readByte(); Being::Action current = (Being::Action)computer.character->getAction(); bool logActionChange = true; switch (action) { case Being::STAND: { if (current == Being::SIT) { computer.character->setAction(Being::STAND); logActionChange = false; } } break; case Being::SIT: { if (current == Being::STAND) { computer.character->setAction(Being::SIT); logActionChange = false; } } break; default: break; } // Log the action change only when this is relevant. if (logActionChange) { // log transaction std::stringstream str; str << "User changed action from " << current << " to " << action; accountHandler->sendTransaction( computer.character->getDatabaseID(), TRANS_ACTION_CHANGE, str.str()); } } break; case PGMSG_DIRECTION_CHANGE: { computer.character->setDirection(message.readByte()); } break; case PGMSG_DISCONNECT: { bool reconnectAccount = (bool) message.readByte(); result.writeShort(GPMSG_DISCONNECT_RESPONSE); result.writeByte(ERRMSG_OK); // It is, when control reaches here if (reconnectAccount) { std::string magic_token(utils::getMagicToken()); result.writeString(magic_token, MAGIC_TOKEN_LENGTH); // No accountserver data, the client should remember that accountHandler->playerReconnectAccount( computer.character->getDatabaseID(), magic_token); } // TODO: implement a delayed remove GameState::remove(computer.character); accountHandler->sendCharacterData(computer.character); // Done with the character computer.character->disconnected(); delete computer.character; computer.character = NULL; computer.status = CLIENT_LOGIN; } break; case PGMSG_TRADE_REQUEST: { int id = message.readShort(); if (Trade *t = computer.character->getTrading()) { if (t->request(computer.character, id)) break; } Character *q = findCharacterNear(computer.character, id); if (!q || q->isBusy()) { result.writeShort(GPMSG_TRADE_CANCEL); break; } new Trade(computer.character, q); // log transaction std::string str; str = "User requested trade with " + q->getName(); accountHandler->sendTransaction(computer.character->getDatabaseID(), TRANS_TRADE_REQUEST, str); } break; case PGMSG_TRADE_CANCEL: case PGMSG_TRADE_AGREED: case PGMSG_TRADE_CONFIRM: case PGMSG_TRADE_ADD_ITEM: case PGMSG_TRADE_SET_MONEY: { std::stringstream str; Trade *t = computer.character->getTrading(); if (!t) break; switch (message.getId()) { case PGMSG_TRADE_CANCEL: t->cancel(); break; case PGMSG_TRADE_CONFIRM: t->confirm(computer.character); break; case PGMSG_TRADE_AGREED: t->agree(computer.character); // log transaction accountHandler->sendTransaction(computer.character->getDatabaseID(), TRANS_TRADE_END, "User finished trading"); break; case PGMSG_TRADE_SET_MONEY: { int money = message.readLong(); t->setMoney(computer.character, money); // log transaction str << "User added " << money << " money to trade."; accountHandler->sendTransaction(computer.character->getDatabaseID(), TRANS_TRADE_MONEY, str.str()); } break; case PGMSG_TRADE_ADD_ITEM: { int slot = message.readByte(); t->addItem(computer.character, slot, message.readByte()); // log transaction str << "User add item from slot " << slot; accountHandler->sendTransaction(computer.character->getDatabaseID(), TRANS_TRADE_ITEM, str.str()); } break; } } break; case PGMSG_NPC_BUYSELL: { BuySell *t = computer.character->getBuySell(); if (!t) break; int id = message.readShort(); int amount = message.readShort(); t->perform(id, amount); } break; case PGMSG_RAISE_ATTRIBUTE: { int attribute = message.readByte(); AttribmodResponseCode retCode; retCode = computer.character->useCharacterPoint(attribute); result.writeShort(GPMSG_RAISE_ATTRIBUTE_RESPONSE); result.writeByte(retCode); result.writeByte(attribute); if (retCode == ATTRIBMOD_OK ) { accountHandler->updateCharacterPoints( computer.character->getDatabaseID(), computer.character->getCharacterPoints(), computer.character->getCorrectionPoints(), attribute, computer.character->getAttribute(attribute)); // log transaction std::stringstream str; str << "User increased attribute " << attribute; accountHandler->sendTransaction(computer.character->getDatabaseID(), TRANS_ATTR_INCREASE, str.str()); } } break; case PGMSG_LOWER_ATTRIBUTE: { int attribute = message.readByte(); AttribmodResponseCode retCode; retCode = computer.character->useCorrectionPoint(attribute); result.writeShort(GPMSG_LOWER_ATTRIBUTE_RESPONSE); result.writeByte(retCode); result.writeByte(attribute); if (retCode == ATTRIBMOD_OK ) { accountHandler->updateCharacterPoints( computer.character->getDatabaseID(), computer.character->getCharacterPoints(), computer.character->getCorrectionPoints(), attribute, computer.character->getAttribute(attribute)); // log transaction std::stringstream str; str << "User decreased attribute " << attribute; accountHandler->sendTransaction(computer.character->getDatabaseID(), TRANS_ATTR_DECREASE, str.str()); } } break; case PGMSG_RESPAWN: { computer.character->respawn(); // plausibility check is done by character class } break; case PGMSG_NPC_POST_SEND: { handleSendPost(&computer, message); } break; default: LOG_WARN("Invalid message type"); result.writeShort(XXMSG_INVALID); break; } if (result.getLength() > 0) computer.send(result); }