00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <cassert>
00023 #include <map>
00024
00025 #include "game-server/gamehandler.hpp"
00026
00027 #include "common/transaction.hpp"
00028 #include "game-server/accountconnection.hpp"
00029 #include "game-server/buysell.hpp"
00030 #include "game-server/commandhandler.hpp"
00031 #include "game-server/inventory.hpp"
00032 #include "game-server/item.hpp"
00033 #include "game-server/itemmanager.hpp"
00034 #include "game-server/map.hpp"
00035 #include "game-server/mapcomposite.hpp"
00036 #include "game-server/npc.hpp"
00037 #include "game-server/postman.hpp"
00038 #include "game-server/state.hpp"
00039 #include "game-server/trade.hpp"
00040 #include "net/messagein.hpp"
00041 #include "net/messageout.hpp"
00042 #include "net/netcomputer.hpp"
00043 #include "utils/logger.h"
00044 #include "utils/tokendispenser.hpp"
00045
00046 const unsigned int TILES_TO_BE_NEAR = 7;
00047
00048 GameHandler::GameHandler():
00049 mTokenCollector(this)
00050 {
00051 }
00052
00053 bool GameHandler::startListen(enet_uint16 port)
00054 {
00055 LOG_INFO("Game handler started:");
00056 return ConnectionHandler::startListen(port);
00057 }
00058
00059 NetComputer *GameHandler::computerConnected(ENetPeer *peer)
00060 {
00061 return new GameClient(peer);
00062 }
00063
00064 void GameHandler::computerDisconnected(NetComputer *comp)
00065 {
00066 GameClient &computer = *static_cast< GameClient * >(comp);
00067
00068 if (computer.status == CLIENT_QUEUED)
00069 {
00070 mTokenCollector.deletePendingClient(&computer);
00071 }
00072 else if (Character *ch = computer.character)
00073 {
00074 accountHandler->sendCharacterData(ch);
00075 GameState::remove(ch);
00076 ch->disconnected();
00077 delete ch;
00078 }
00079 delete &computer;
00080 }
00081
00082 void GameHandler::kill(Character *ch)
00083 {
00084 GameClient *client = ch->getClient();
00085 assert(client != NULL);
00086 client->character = NULL;
00087 client->status = CLIENT_LOGIN;
00088 ch->setClient(NULL);
00089 }
00090
00091 void GameHandler::prepareServerChange(Character *ch)
00092 {
00093 GameClient *client = ch->getClient();
00094 assert(client != NULL);
00095 client->status = CLIENT_CHANGE_SERVER;
00096 }
00097
00098 void GameHandler::completeServerChange(int id, const std::string &token,
00099 const std::string &address, int port)
00100 {
00101 for (NetComputers::const_iterator i = clients.begin(),
00102 i_end = clients.end(); i != i_end; ++i)
00103 {
00104 GameClient *c = static_cast< GameClient * >(*i);
00105 if (c->status == CLIENT_CHANGE_SERVER &&
00106 c->character->getDatabaseID() == id)
00107 {
00108 MessageOut msg(GPMSG_PLAYER_SERVER_CHANGE);
00109 msg.writeString(token, MAGIC_TOKEN_LENGTH);
00110 msg.writeString(address);
00111 msg.writeShort(port);
00112 c->send(msg);
00113 c->character->disconnected();
00114 delete c->character;
00115 c->character = NULL;
00116 c->status = CLIENT_LOGIN;
00117 return;
00118 }
00119 }
00120 }
00121
00122 void GameHandler::updateCharacter(int charid, int partyid)
00123 {
00124 for (NetComputers::const_iterator i = clients.begin(),
00125 i_end = clients.end(); i != i_end; ++i)
00126 {
00127 GameClient *c = static_cast< GameClient * >(*i);
00128 if (c->character->getDatabaseID() == charid)
00129 {
00130 c->character->setParty(partyid);
00131 }
00132 }
00133 }
00134
00135 static Actor *findActorNear(Actor *p, int id)
00136 {
00137 MapComposite *map = p->getMap();
00138 const Point &ppos = p->getPosition();
00139
00140 const int pixelDist = DEFAULT_TILE_WIDTH * TILES_TO_BE_NEAR;
00141 for (ActorIterator i(map->getAroundPointIterator(ppos, pixelDist)); i; ++i)
00142 {
00143 Actor *a = *i;
00144 if (a->getPublicID() != id)
00145 continue;
00146 return ppos.inRangeOf(a->getPosition(), pixelDist) ? a : 0;
00147 }
00148 return 0;
00149 }
00150
00151 static Character *findCharacterNear(Actor *p, int id)
00152 {
00153 MapComposite *map = p->getMap();
00154 const Point &ppos = p->getPosition();
00155
00156 const int pixelDist = DEFAULT_TILE_WIDTH * TILES_TO_BE_NEAR;
00157 for (CharacterIterator i(map->getAroundPointIterator(ppos,
00158 pixelDist)); i; ++i)
00159 {
00160 Character *c = *i;
00161 if (c->getPublicID() != id)
00162 continue;
00163 return ppos.inRangeOf(c->getPosition(), pixelDist) ? c : 0;
00164 }
00165 return 0;
00166 }
00167
00168 void GameHandler::processMessage(NetComputer *comp, MessageIn &message)
00169 {
00170 GameClient &computer = *static_cast< GameClient * >(comp);
00171 MessageOut result;
00172
00173 if (computer.status == CLIENT_LOGIN)
00174 {
00175 if (message.getId() != PGMSG_CONNECT)
00176 return;
00177
00178 std::string magic_token = message.readString(MAGIC_TOKEN_LENGTH);
00179 computer.status = CLIENT_QUEUED;
00180 mTokenCollector.addPendingClient(magic_token, &computer);
00181 return;
00182 }
00183 else if (computer.status != CLIENT_CONNECTED)
00184 {
00185 return;
00186 }
00187
00188 switch (message.getId())
00189 {
00190 case PGMSG_SAY:
00191 {
00192 std::string say = message.readString();
00193 if (say.empty()) break;
00194
00195 if (say[0] == '@')
00196 {
00197 CommandHandler::handleCommand(computer.character, say);
00198 break;
00199 }
00200 GameState::sayAround(computer.character, say);
00201 std::string msg = computer.character->getName() + " said " + say;
00202 accountHandler->sendTransaction(computer.character->getDatabaseID(), TRANS_MSG_PUBLIC, msg);
00203 } break;
00204
00205 case PGMSG_NPC_TALK:
00206 case PGMSG_NPC_TALK_NEXT:
00207 case PGMSG_NPC_SELECT:
00208 {
00209 int id = message.readShort();
00210 Actor *o = findActorNear(computer.character, id);
00211 if (!o || o->getType() != OBJECT_NPC)
00212 {
00213 sendError(comp, id, "Not close enough to NPC\n");
00214 break;
00215 }
00216
00217 NPC *q = static_cast< NPC * >(o);
00218 if (message.getId() == PGMSG_NPC_SELECT)
00219 {
00220 q->select(computer.character, message.readByte());
00221 }
00222 else
00223 {
00224 q->prompt(computer.character, message.getId() == PGMSG_NPC_TALK);
00225 }
00226 } break;
00227
00228 case PGMSG_PICKUP:
00229 {
00230 int x = message.readShort();
00231 int y = message.readShort();
00232 Point ppos = computer.character->getPosition();
00233
00234
00235 if (std::abs(x - ppos.x) + std::abs(y - ppos.y) < 48)
00236 {
00237 MapComposite *map = computer.character->getMap();
00238 Point ipos(x, y);
00239 for (FixedActorIterator i(map->getAroundPointIterator(ipos, 0)); i; ++i)
00240 {
00241 Actor *o = *i;
00242 Point opos = o->getPosition();
00243 if (o->getType() == OBJECT_ITEM && opos.x == x && opos.y == y)
00244 {
00245 Item *item = static_cast< Item * >(o);
00246 ItemClass *ic = item->getItemClass();
00247 Inventory(computer.character)
00248 .insert(ic->getDatabaseID(), item->getAmount());
00249 GameState::remove(item);
00250
00251 std::stringstream str;
00252 str << "User picked up item " << ic->getDatabaseID()
00253 << " at " << opos.x << "x" << opos.y;
00254 accountHandler->sendTransaction(computer.character->getDatabaseID(),
00255 TRANS_ITEM_PICKUP, str.str());
00256 break;
00257 }
00258 }
00259 }
00260 } break;
00261
00262 case PGMSG_USE_ITEM:
00263 {
00264 int slot = message.readByte();
00265 Inventory inv(computer.character);
00266 if (ItemClass *ic = ItemManager::getItem(inv.getItem(slot)))
00267 {
00268 if (ic->use(computer.character))
00269 {
00270 inv.removeFromSlot(slot, 1);
00271
00272 std::stringstream str;
00273 str << "User used item " << ic->getDatabaseID()
00274 << " from slot " << slot;
00275 accountHandler->sendTransaction(computer.character->getDatabaseID(),
00276 TRANS_ITEM_USED, str.str());
00277 }
00278 }
00279 } break;
00280
00281 case PGMSG_DROP:
00282 {
00283 int slot = message.readByte();
00284 int amount = message.readByte();
00285 Inventory inv(computer.character);
00286 if (ItemClass *ic = ItemManager::getItem(inv.getItem(slot)))
00287 {
00288 int nb = inv.removeFromSlot(slot, amount);
00289 Item *item = new Item(ic, amount - nb);
00290 item->setMap(computer.character->getMap());
00291 item->setPosition(computer.character->getPosition());
00292 if (!GameState::insert(item))
00293 {
00294
00295 inv.insert(ic->getDatabaseID(), amount - nb);
00296 delete item;
00297 break;
00298 }
00299
00300 Point pt = computer.character->getPosition();
00301 std::stringstream str;
00302 str << "User dropped item " << ic->getDatabaseID()
00303 << " at " << pt.x << "x" << pt.y;
00304 accountHandler->sendTransaction(computer.character->getDatabaseID(),
00305 TRANS_ITEM_DROP, str.str());
00306 }
00307 } break;
00308
00309 case PGMSG_WALK:
00310 {
00311 handleWalk(&computer, message);
00312 } break;
00313
00314 case PGMSG_EQUIP:
00315 {
00316 int slot = message.readByte();
00317 Inventory(computer.character).equip(slot);
00318 } break;
00319
00320 case PGMSG_UNEQUIP:
00321 {
00322 int slot = message.readByte();
00323 if (slot >= 0 && slot < EQUIP_PROJECTILE_SLOT)
00324 {
00325 Inventory(computer.character).unequip(slot);
00326 }
00327 } break;
00328
00329 case PGMSG_MOVE_ITEM:
00330 {
00331 int slot1 = message.readByte();
00332 int slot2 = message.readByte();
00333 int amount = message.readByte();
00334 Inventory(computer.character).move(slot1, slot2, amount);
00335
00336 std::stringstream str;
00337 str << "User moved item "
00338 << " from slot " << slot1 << " to slot " << slot2;
00339 accountHandler->sendTransaction(computer.character->getDatabaseID(),
00340 TRANS_ITEM_MOVE, str.str());
00341 } break;
00342
00343 case PGMSG_ATTACK:
00344 {
00345 LOG_DEBUG("Character " << computer.character->getPublicID()
00346 << " attacks");
00347 computer.character->setDirection(message.readByte());
00348 computer.character->setAction(Being::ATTACK);
00349 } break;
00350
00351 case PGMSG_USE_SPECIAL:
00352 {
00353 int specialID = message.readByte();
00354 LOG_DEBUG("Character " << computer.character->getPublicID()
00355 << " tries to use his special attack "<<specialID);
00356 computer.character->useSpecial(specialID);
00357 }
00358
00359 case PGMSG_ACTION_CHANGE:
00360 {
00361 Being::Action action = (Being::Action)message.readByte();
00362 Being::Action current = (Being::Action)computer.character->getAction();
00363
00364 switch (action)
00365 {
00366 case Being::STAND:
00367 {
00368 if (current == Being::SIT)
00369 computer.character->setAction(Being::STAND);
00370 } break;
00371 case Being::SIT:
00372 {
00373 if (current == Being::STAND)
00374 computer.character->setAction(Being::SIT);
00375 } break;
00376 default:
00377 break;
00378 }
00379
00380
00381 std::stringstream str;
00382 str << "User changed action from " << current
00383 << " to " << action;
00384 accountHandler->sendTransaction(computer.character->getDatabaseID(),
00385 TRANS_ACTION_CHANGE, str.str());
00386
00387 } break;
00388
00389 case PGMSG_DIRECTION_CHANGE:
00390 {
00391 computer.character->setDirection(message.readByte());
00392 } break;
00393
00394 case PGMSG_DISCONNECT:
00395 {
00396 bool reconnectAccount = (bool) message.readByte();
00397
00398 result.writeShort(GPMSG_DISCONNECT_RESPONSE);
00399 result.writeByte(ERRMSG_OK);
00400
00401 if (reconnectAccount)
00402 {
00403 std::string magic_token(utils::getMagicToken());
00404 result.writeString(magic_token, MAGIC_TOKEN_LENGTH);
00405
00406 accountHandler->playerReconnectAccount(
00407 computer.character->getDatabaseID(),
00408 magic_token);
00409 }
00410
00411 GameState::remove(computer.character);
00412
00413 accountHandler->sendCharacterData(computer.character);
00414
00415
00416 computer.character->disconnected();
00417 delete computer.character;
00418 computer.character = NULL;
00419 computer.status = CLIENT_LOGIN;
00420 } break;
00421
00422 case PGMSG_TRADE_REQUEST:
00423 {
00424 int id = message.readShort();
00425
00426 if (Trade *t = computer.character->getTrading())
00427 {
00428 if (t->request(computer.character, id)) break;
00429 }
00430
00431 Character *q = findCharacterNear(computer.character, id);
00432 if (!q || q->isBusy())
00433 {
00434 result.writeShort(GPMSG_TRADE_CANCEL);
00435 break;
00436 }
00437
00438 new Trade(computer.character, q);
00439
00440
00441 std::string str;
00442 str = "User requested trade with " + q->getName();
00443 accountHandler->sendTransaction(computer.character->getDatabaseID(),
00444 TRANS_TRADE_REQUEST, str);
00445 } break;
00446
00447 case PGMSG_TRADE_CANCEL:
00448 case PGMSG_TRADE_AGREED:
00449 case PGMSG_TRADE_CONFIRM:
00450 case PGMSG_TRADE_ADD_ITEM:
00451 case PGMSG_TRADE_SET_MONEY:
00452 {
00453 std::stringstream str;
00454 Trade *t = computer.character->getTrading();
00455 if (!t) break;
00456
00457 switch (message.getId())
00458 {
00459 case PGMSG_TRADE_CANCEL:
00460 t->cancel();
00461 break;
00462 case PGMSG_TRADE_CONFIRM:
00463 t->confirm(computer.character);
00464 break;
00465 case PGMSG_TRADE_AGREED:
00466 t->agree(computer.character);
00467
00468 accountHandler->sendTransaction(computer.character->getDatabaseID(),
00469 TRANS_TRADE_END, "User finished trading");
00470 break;
00471 case PGMSG_TRADE_SET_MONEY:
00472 {
00473 int money = message.readLong();
00474 t->setMoney(computer.character, money);
00475
00476 str << "User added " << money << " money to trade.";
00477 accountHandler->sendTransaction(computer.character->getDatabaseID(),
00478 TRANS_TRADE_MONEY, str.str());
00479 } break;
00480 case PGMSG_TRADE_ADD_ITEM:
00481 {
00482 int slot = message.readByte();
00483 t->addItem(computer.character, slot, message.readByte());
00484
00485 str << "User add item from slot " << slot;
00486 accountHandler->sendTransaction(computer.character->getDatabaseID(),
00487 TRANS_TRADE_ITEM, str.str());
00488 } break;
00489 }
00490 } break;
00491
00492 case PGMSG_NPC_BUYSELL:
00493 {
00494 BuySell *t = computer.character->getBuySell();
00495 if (!t) break;
00496 int id = message.readShort();
00497 int amount = message.readShort();
00498 t->perform(id, amount);
00499 } break;
00500
00501 case PGMSG_RAISE_ATTRIBUTE:
00502 {
00503 int attribute = message.readByte();
00504 AttribmodResponseCode retCode;
00505 retCode = computer.character->useCharacterPoint(attribute);
00506 result.writeShort(GPMSG_RAISE_ATTRIBUTE_RESPONSE);
00507 result.writeByte(retCode);
00508 result.writeByte(attribute);
00509
00510 if (retCode == ATTRIBMOD_OK )
00511 {
00512 accountHandler->updateCharacterPoints(
00513 computer.character->getDatabaseID(),
00514 computer.character->getCharacterPoints(),
00515 computer.character->getCorrectionPoints(),
00516 attribute,
00517 computer.character->getAttribute(attribute));
00518
00519
00520 std::stringstream str;
00521 str << "User increased attribute " << attribute;
00522 accountHandler->sendTransaction(computer.character->getDatabaseID(),
00523 TRANS_ATTR_INCREASE, str.str());
00524 }
00525 } break;
00526
00527 case PGMSG_LOWER_ATTRIBUTE:
00528 {
00529 int attribute = message.readByte();
00530 AttribmodResponseCode retCode;
00531 retCode = computer.character->useCorrectionPoint(attribute);
00532 result.writeShort(GPMSG_LOWER_ATTRIBUTE_RESPONSE);
00533 result.writeByte(retCode);
00534 result.writeByte(attribute);
00535
00536 if (retCode == ATTRIBMOD_OK )
00537 {
00538 accountHandler->updateCharacterPoints(
00539 computer.character->getDatabaseID(),
00540 computer.character->getCharacterPoints(),
00541 computer.character->getCorrectionPoints(),
00542 attribute,
00543 computer.character->getAttribute(attribute));
00544
00545
00546 std::stringstream str;
00547 str << "User decreased attribute " << attribute;
00548 accountHandler->sendTransaction(computer.character->getDatabaseID(),
00549 TRANS_ATTR_DECREASE, str.str());
00550 }
00551 } break;
00552
00553 case PGMSG_RESPAWN:
00554 {
00555 computer.character->respawn();
00556 } break;
00557
00558 case PGMSG_NPC_POST_SEND:
00559 {
00560 handleSendPost(&computer, message);
00561 } break;
00562
00563 default:
00564 LOG_WARN("Invalid message type");
00565 result.writeShort(XXMSG_INVALID);
00566 break;
00567 }
00568
00569 if (result.getLength() > 0)
00570 computer.send(result);
00571 }
00572
00573 void GameHandler::sendTo(Character *beingPtr, MessageOut &msg)
00574 {
00575 GameClient *client = beingPtr->getClient();
00576 assert(client && client->status == CLIENT_CONNECTED);
00577 client->send(msg);
00578 }
00579
00580 void GameHandler::addPendingCharacter(const std::string &token, Character *ch)
00581 {
00582
00583
00584
00585
00586 int id = ch->getDatabaseID();
00587 for (NetComputers::const_iterator i = clients.begin(),
00588 i_end = clients.end(); i != i_end; ++i)
00589 {
00590 GameClient *c = static_cast< GameClient * >(*i);
00591 Character *old_ch = c->character;
00592 if (old_ch && old_ch->getDatabaseID() == id)
00593 {
00594 if (c->status != CLIENT_CONNECTED)
00595 {
00596
00597
00598
00599 return;
00600 }
00601
00602
00603
00604
00605
00606 delete ch;
00607 GameState::remove(old_ch);
00608 kill(old_ch);
00609 ch = old_ch;
00610 break;
00611 }
00612 }
00613
00614
00615 mTokenCollector.addPendingConnect(token, ch);
00616 }
00617
00618 void GameHandler::tokenMatched(GameClient* computer, Character* character)
00619 {
00620 computer->character = character;
00621 computer->status = CLIENT_CONNECTED;
00622
00623 character->setClient(computer);
00624
00625 MessageOut result(GPMSG_CONNECT_RESPONSE);
00626
00627 if (!GameState::insert(character))
00628 {
00629 result.writeByte(ERRMSG_SERVER_FULL);
00630 kill(character);
00631 delete character;
00632 computer->disconnect(result);
00633 return;
00634 }
00635
00636 result.writeByte(ERRMSG_OK);
00637 computer->send(result);
00638
00639
00640 Inventory(character).sendFull();
00641 for (int i = 0; i < NB_CHARACTER_ATTRIBUTES; ++i)
00642 {
00643 character->modifiedAttribute(i);
00644 }
00645 }
00646
00647 void GameHandler::deletePendingClient(GameClient* computer)
00648 {
00649
00650 if (computer->status != CLIENT_QUEUED) return;
00651
00652 MessageOut msg(GPMSG_CONNECT_RESPONSE);
00653 msg.writeByte(ERRMSG_TIME_OUT);
00654
00655
00656 computer->disconnect(msg);
00657 }
00658
00659 void GameHandler::deletePendingConnect(Character* character)
00660 {
00661 delete character;
00662 }
00663
00664 GameClient *GameHandler::getClientByNameSlow(const std::string &name)
00665 {
00666 for (NetComputers::const_iterator i = clients.begin(),
00667 i_end = clients.end(); i != i_end; ++i)
00668 {
00669 GameClient *c = static_cast< GameClient * >(*i);
00670 Character *ch = c->character;
00671 if (ch && ch->getName() == name)
00672 {
00673 return c;
00674 }
00675 }
00676 return NULL;
00677 }
00678
00679 void GameHandler::sendError(NetComputer *computer, int id, std::string errorMsg)
00680 {
00681 MessageOut msg(GPMSG_NPC_ERROR);
00682 msg.writeShort(id);
00683 msg.writeString(errorMsg, errorMsg.size());
00684 computer->send(msg);
00685 }
00686
00687 void GameHandler::handleWalk(GameClient *client, MessageIn &message)
00688 {
00689 int x = message.readShort();
00690 int y = message.readShort();
00691
00692 Point dst(x, y);
00693 client->character->setDestination(dst);
00694
00695 }
00696
00697 void GameHandler::handleSendPost(GameClient *client, MessageIn &message)
00698 {
00699
00700 postMan->addCharacter(client->character);
00701 accountHandler->sendPost(client->character, message);
00702 }