// ====================================================================== // // DataLookup.cpp // copyright (c) 2001 Sony Online Entertainment // // ====================================================================== #include "serverDatabase/FirstServerDatabase.h" #include "serverDatabase/DataLookup.h" #include "UnicodeUtils.h" #include "serverDatabase/ConfigServerDatabase.h" #include "serverDatabase/DatabaseProcess.h" #include "serverDatabase/GameServerConnection.h" #include "serverDatabase/TaskCheckCharacterName.h" #include "serverDatabase/TaskGetStructuresForPurge.h" #include "serverDatabase/TaskGetBiography.h" #include "serverDatabase/TaskSetBiography.h" #include "serverNetworkMessages/BiographyMessage.h" #include "serverNetworkMessages/CentralGameServerMessages.h" #include "serverNetworkMessages/RequestBiographyMessage.h" #include "serverNetworkMessages/RetrievedItemLoadMessage.h" #include "sharedDatabaseInterface/DbTaskQueue.h" #include "sharedFoundation/Crc.h" #include "sharedFoundation/ExitChain.h" #include "sharedLog/Log.h" #include "sharedNetworkMessages/GenericValueTypeMessage.h" #include "sharedNetworkMessages/NameErrors.h" #include "unicodeArchive/UnicodeArchive.h" //----------------------------------------------------------------------- DataLookup *DataLookup::ms_theInstance = NULL; //----------------------------------------------------------------------- void DataLookup::install() { DEBUG_FATAL(ms_theInstance,("Called DataLookup::install twice.\n")); ms_theInstance = new DataLookup; ExitChain::add(&remove, "DataLookup::remove"); } // ---------------------------------------------------------------------- void DataLookup::remove() { DEBUG_FATAL(!ms_theInstance,("DataLookup was not installed.\n")); delete ms_theInstance; ms_theInstance = 0; } // ---------------------------------------------------------------------- DataLookup::DataLookup() : MessageDispatch::Receiver(), taskQueue(new DB::TaskQueue(1,DatabaseProcess::getInstance().getDBServer(),2)), m_reservations() { connectToMessage("BiographyMessage"); connectToMessage("PurgeStructuresForAccountMessage"); connectToMessage("ReleaseNameMessage"); connectToMessage("RequestBiographyMessage"); connectToMessage("VerifyNameRequest"); connectToMessage("WarnStructuresAboutPurgeMessage"); } //----------------------------------------------------------------------- DataLookup::~DataLookup() { DEBUG_FATAL(taskQueue,("Call shutdown() before deleting DataLookup.\n")); while (!m_reservations.empty()) { deleteReservationList(m_reservations.begin()->first); } } // ---------------------------------------------------------------------- void DataLookup::update(real uptime) { UNREF(uptime); taskQueue->update(ConfigServerDatabase::getDefaultQueueUpdateTimeLimit()); } // ---------------------------------------------------------------------- void DataLookup::receiveMessage(const MessageDispatch::Emitter &source, const MessageDispatch::MessageBase &message) { UNREF(source); if (message.isType("VerifyNameRequest")) { const GameServerConnection * g = safe_cast(&source); Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); VerifyNameRequest * vnr = new VerifyNameRequest(ri); LOG("TraceCharacterCreation", ("%d received VerifyNameRequest", vnr->getStationId())); verifyName(g->getProcessId(), vnr); } else if (message.isType("ReleaseNameMessage")) { Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); ReleaseNameMessage rnm(ri); LOG("TraceCharacterCreation", ("%d received ReleaseNameMessage", rnm.getStationId())); releaseName(rnm.getStationId(), rnm.getCharacterId()); } else if (message.isType("RequestBiographyMessage")) { Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); RequestBiographyMessage msg(ri); const GameServerConnection *conn = dynamic_cast(&source); if (conn) getBiography(msg.getOwner(),conn->getProcessId()); else DEBUG_WARNING(true,("Got RequestBiographyMessage from something that wasn't a GameServerConnection.\n")); } else if (message.isType("BiographyMessage")) { Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); BiographyMessage msg(ri); setBiography(msg.getOwner(),msg.getBio()); } else if (message.isType("PurgeStructuresForAccountMessage")) { Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); GenericValueTypeMessage msg(ri); getStructuresForPurge(msg.getValue(), false); } else if (message.isType("WarnStructuresAboutPurgeMessage")) { Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); GenericValueTypeMessage msg(ri); getStructuresForPurge(msg.getValue(), true); } else { DEBUG_REPORT_LOG(true,("Unrecognized message recieved by DataLookup.\n")); } } // ---------------------------------------------------------------------- void DataLookup::onRetrieveItemComplete(NetworkId const &ownerId, NetworkId const &itemId) const { RetrievedItemLoadMessage msg(ownerId, itemId); std::vector gameServerIds; DatabaseProcess::getInstance().getGameServerProcessIds(gameServerIds); for (std::vector::iterator i = gameServerIds.begin(); i != gameServerIds.end(); ++i) DatabaseProcess::getInstance().getConnectionByProcess(*i)->send(msg, true); } // ---------------------------------------------------------------------- void DataLookup::verifyName(uint32 gameServer, VerifyNameRequest * request) { DEBUG_REPORT_LOG(true, ("verifyName: stationId %lu, process %lu, name %s: ", request->getStationId(), gameServer, Unicode::wideToNarrow(request->getCharacterName()).c_str())); Unicode::String name = normalizeName(request->getCharacterName()); reservationList * rl = getReservationList(request->getStationId()); // if request is to verify name for character rename, free up all // reservations that have been made for character create; there's // no possibility that a character create is happening if there's // a rename happening if (rl && request->getCharacterId().isValid()) { releaseName(request->getStationId(), NetworkId::cms_invalid); rl = getReservationList(request->getStationId()); } if (rl) // already have m_reservations for this character { // see if we have any equivalent m_reservations reservationList::iterator i; for (i=rl->begin(); i!=rl->end(); ++i) { // equivalent reservation; if it has been approved, // response immediately; otherwise make a new reservation, // but don't request a verification. When the original // is approved, all matching m_reservations will be approved if ((i->name == name) && (request->getCharacterId() == i->request->getCharacterId())) { if (i->approved) { DEBUG_REPORT_LOG(true, ("Reserved and already approved.\n")); GameServerConnection * g = DatabaseProcess::getInstance().getConnectionByProcess(gameServer); VerifyNameResponse vnr(request->getStationId(), request->getCharacterId(), request->getCreatureTemplate(), request->getCharacterName(), NameErrors::nameApproved); LOG("TraceCharacterCreation", ("%d Name reservation already approved, sending VeryfyNameResponse(%d, %s, %s) to game server", request->getStationId(), request->getStationId(), request->getCreatureTemplate().c_str(), Unicode::wideToNarrow(request->getCharacterName()).c_str())); if (g) g->send(vnr, true); // now move the reservation to the front of the queue reservation temp(*i); IGNORE_RETURN(rl->erase(i)); addReservation(rl, temp); delete request; return; } else { LOG("TraceCharacterCreation", ("%d Name reservation not yet approved", request->getStationId())); DEBUG_REPORT_LOG(true, ("Not yet approved.\n")); reservation temp; temp.gameServer = gameServer; temp.name = name; temp.approved = false; temp.request = request; addReservation(rl, temp); return; } } } } DEBUG_REPORT_LOG(true, ("No matching reservation.\n")); // If we get here, there are no matching m_reservations for our client. But there // may be m_reservations from other clients conflicting with ours. std::map::iterator j; for (j=m_reservations.begin(); j != m_reservations.end(); ++j) { reservationList * otherReservationList = j->second; reservationList::iterator i; for (i = otherReservationList->begin(); i!=otherReservationList->end(); ++i) { if (i->name == name) { LOG("TraceCharacterCreation", ("Name (%s) requested by station ID %lu is already reserved by another station ID %lu and/or another character on the same station ID", Unicode::wideToNarrow(request->getCharacterName()).c_str(), request->getStationId(), j->first)); DEBUG_REPORT_LOG(true, ("Name (%s) requested by station ID %lu is already reserved by another station ID %lu and/or another character on the same station ID\n", Unicode::wideToNarrow(request->getCharacterName()).c_str(), request->getStationId(), j->first)); GameServerConnection * g = DatabaseProcess::getInstance().getConnectionByProcess(gameServer); VerifyNameResponse vnr(request->getStationId(), request->getCharacterId(), request->getCreatureTemplate(), request->getCharacterName(), NameErrors::nameDeclinedInUse); g->send(vnr, true); delete request; return; } } } // nothing else has this name reserved reservation temp; temp.gameServer = gameServer; temp.name = name; temp.request = request; temp.approved = false; if (!rl) rl = newReservationList(request->getStationId()); addReservation(rl, temp); LOG("TraceCharacterCreation", ("%d Reserving name and verifying it against the DB", request->getStationId())); DEBUG_REPORT_LOG(true, ("Reserving and verifying\n")); TaskCheckCharacterName *task = new TaskCheckCharacterName(request->getStationId(), name); taskQueue->asyncRequest(task); } //-------------------------------------------------------------------- void DataLookup::onCharacterNameChecked(uint32 stationId, const Unicode::String &name, int resultCode) { StringId reason(NameErrors::nameApproved); switch (resultCode) { case 0: break; case 1: reason = NameErrors::nameDeclinedInUse; break; default: reason = NameErrors::nameDeclinedInternalError; break; } reservationList * rl = getReservationList(stationId); if (!rl) { DEBUG_REPORT_LOG(true, ("Received onCharacterNameChecked (%s) for non-pending name stationid %lu\n", reason.getText().c_str(), stationId)); return; } reservationList::iterator i; for (i=rl->begin(); i!=rl->end(); ++i) { if (i->name == name) { LOG("TraceCharacterCreation", ("sending VerifyNameResponse(%d, %s, %s, ?) to game server", stationId, i->request->getCreatureTemplate().c_str(), Unicode::wideToNarrow(i->request->getCharacterName()).c_str())); VerifyNameResponse vnr(stationId, i->request->getCharacterId(), i->request->getCreatureTemplate(), i->request->getCharacterName(), reason); GameServerConnection * g = DatabaseProcess::getInstance().getConnectionByProcess(i->gameServer); if (g) g->send(vnr, true); else DEBUG_REPORT_LOG(true,("VerifyNameResponse for %lu dropped because GameServer has dropped connection.\n",stationId)); } } if (reason != NameErrors::nameApproved) { LOG("TraceCharacterCreation", ("%d name released", stationId)); DEBUG_REPORT_LOG(true, ("Name %s verified for stationId %lu: %s.\n", Unicode::wideToNarrow(name).c_str(), stationId, reason.getText().c_str())); releaseName(stationId, name); } else { LOG("TraceCharacterCreation", ("%d name %s verified", stationId, Unicode::wideToNarrow(name).c_str())); DEBUG_REPORT_LOG(true, ("Name %s verified for stationId %lu: %s, holding reservation.\n", Unicode::wideToNarrow(name).c_str(), stationId, reason.getText().c_str())); approveName(stationId, name); } } //-------------------------------------------------------------------- void DataLookup::releaseName(uint32 stationId, const NetworkId & characterId) { reservationList * rl = getReservationList(stationId); if (!rl) { WARNING(true, ("Releasing names for stationId %lu, characterId %s, but no reservations found.\n", stationId, characterId.getValueString().c_str())); return; } reservationList newReservations; reservationList::iterator i; int erased = 0; for (i=rl->begin(); i!=rl->end(); ++i) { if (i->request->getCharacterId() != characterId) { newReservations.push_back(*i); } else { delete const_cast(i->request); // non-const for Lint ++erased; } } DEBUG_REPORT_LOG(true, ("Releasing %d names for stationId %lu, characterId %s\n", erased, stationId, characterId.getValueString().c_str())); rl->swap(newReservations); if (rl->empty()) { deleteReservationList(stationId); } } //-------------------------------------------------------------------- void DataLookup::releaseName(uint32 stationId, const Unicode::String &i_name) { reservationList * rl = getReservationList(stationId); if (!rl) { WARNING(true, ("Releasing name [%s] for stationId %lu, but no reservations found.\n", Unicode::wideToNarrow(i_name).c_str(), stationId)); return; } reservationList newReservations; reservationList::iterator i; int erased = 0; for (i=rl->begin(); i!=rl->end(); ++i) { if (i->name != i_name) { newReservations.push_back(*i); } else { delete const_cast(i->request); // non-const for Lint ++erased; } } DEBUG_REPORT_LOG(true, ("Releasing %d names for stationId %lu, name (%s)\n", erased, stationId, Unicode::wideToNarrow(i_name).c_str())); rl->swap(newReservations); if (rl->empty()) { deleteReservationList(stationId); } } //-------------------------------------------------------------------- void DataLookup::approveName(uint32 stationId, const Unicode::String &i_name) { reservationList * rl = getReservationList(stationId); if (!rl) { WARNING_STRICT_FATAL(true, ("Approving name for stationId %lu, but name not found.\n", stationId)); return; } reservationList::iterator i; for (i=rl->begin(); i!=rl->end(); ++i) { if (i->name == i_name) { i->approved = true; } } } //-------------------------------------------------------------------- void DataLookup::releaseNamesForProcess(uint32 gameServer) { DEBUG_REPORT_LOG(true, ("Release name m_reservations for process %lu\n", gameServer)); std::map newReservations; std::map::iterator i; for (i = m_reservations.begin(); i!=m_reservations.end(); ++i) { reservationList * rl = i->second; reservationList newReservationListLocal; reservationList::iterator j; for (j=rl->begin(); j!=rl->end(); ++j) { if (j->gameServer != gameServer) { newReservationListLocal.push_back(*j); } else { delete const_cast(j->request); // const-cast to make Lint happy } } rl->swap(newReservationListLocal); if (rl->empty()) { delete rl; } else { newReservations[i->first] = rl; } } m_reservations.swap(newReservations); } //-------------------------------------------------------------------- const Unicode::String DataLookup::normalizeName(const Unicode::String &name) const { Unicode::String result = Unicode::toLower(name); result = Unicode::String(result, 0, result.find(' ')); return result; } //-------------------------------------------------------------------- /* const std::string DataLookup::getNameByStationId(uint32 stationId) const { std::map::const_iterator i=m_reservations.find(stationId); if (i == m_reservations.end()) { WARNING_STRICT_FATAL(true, ("asked for name that wasn't in the reservation list")); return std::string(); } return Unicode::wideToNarrow(i->second.originalName); } //-------------------------------------------------------------------- const std::string DataLookup::getNormalizedNameByStationId(uint32 stationId) const { std::map::const_iterator i=m_reservations.find(stationId); if (i == m_reservations.end()) { WARNING_STRICT_FATAL(true, ("asked for name that wasn't in the reservation list")); return std::string(); } return Unicode::wideToNarrow(i->second.name); } //-------------------------------------------------------------------- */ DataLookup::reservationList * DataLookup::getReservationList(uint32 stationId) { std::map::iterator i = m_reservations.find(stationId); if (i == m_reservations.end()) return 0; else return i->second; } //-------------------------------------------------------------------- DataLookup::reservationList * DataLookup::newReservationList(uint32 stationId) { reservationList * result = new reservationList; DEBUG_FATAL(m_reservations.find(stationId) != m_reservations.end(), ("DataLookup::newReservationList called, but there is already a reservationList for this stationId %lu.", stationId)); m_reservations[stationId] = result; return result; } //-------------------------------------------------------------------- void DataLookup::addReservation(DataLookup::reservationList * rl, const DataLookup::reservation &temp) { rl->push_back(temp); while (rl->size() > 16) { delete const_cast(rl->begin()->request); rl->pop_front(); } } //-------------------------------------------------------------------- void DataLookup::deleteReservationList(uint32 stationId) { std::map::iterator rlIter = m_reservations.find(stationId); if (rlIter == m_reservations.end()) return; reservationList * rl = rlIter->second; if (!rl) { WARNING_STRICT_FATAL(true, ("Reservation list for stationID %lu is NULL", stationId)); return; } reservationList::iterator i; for (i=rl->begin(); i!=rl->end(); ++i) { delete const_cast(i->request); } delete rl; IGNORE_RETURN(m_reservations.erase(stationId)); } // ---------------------------------------------------------------------- void DataLookup::getBiography(const NetworkId &owner, uint32 requestingProcess) { NOT_NULL(taskQueue); TaskGetBiography *task = new TaskGetBiography(owner,requestingProcess); taskQueue->asyncRequest(task); } // ---------------------------------------------------------------------- void DataLookup::setBiography(const NetworkId &owner, const Unicode::String &bio) { NOT_NULL(taskQueue); TaskSetBiography *task = new TaskSetBiography(owner,bio); taskQueue->asyncRequest(task); } // ---------------------------------------------------------------------- void DataLookup::shutdown() { NOT_NULL(taskQueue); taskQueue->cancel(); delete taskQueue; taskQueue=0; } // ---------------------------------------------------------------------- bool DataLookup::isIdle() { return taskQueue->isIdle(); } // ---------------------------------------------------------------------- void DataLookup::getStructuresForPurge(StationId account, bool warnOnly) { TaskGetStructuresForPurge *task = new TaskGetStructuresForPurge(account, warnOnly); taskQueue->asyncRequest(task); } // ======================================================================