// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . // // Includes // #include "stdnet.h" #include "nel/net/naming_client.h" #include "nel/net/callback_client.h" #include "nel/net/service.h" // // Namespaces // using namespace std; using namespace NLMISC; namespace NLNET { // // Variables // CCallbackClient *CNamingClient::_Connection = NULL; CNamingClient::TRegServices CNamingClient::_RegisteredServices; static TBroadcastCallback _RegistrationBroadcastCallback = NULL; static TBroadcastCallback _UnregistrationBroadcastCallback = NULL; TServiceId CNamingClient::_MySId(0); std::list CNamingClient::RegisteredServices; NLMISC::CMutex CNamingClient::RegisteredServicesMutex("CNamingClient::RegisteredServicesMutex"); // // // void CNamingClient::setRegistrationBroadcastCallback (TBroadcastCallback cb) { _RegistrationBroadcastCallback = cb; } void CNamingClient::setUnregistrationBroadcastCallback (TBroadcastCallback cb) { _UnregistrationBroadcastCallback = cb; } // // static bool Registered; static bool RegisteredSuccess; static TServiceId *RegisteredSID = NULL; static string Reason; void cbRegisterBroadcast (CMessage &msgin, TSockId from, CCallbackNetBase &netbase); static void cbRegister (CMessage &msgin, TSockId from, CCallbackNetBase &netbase) { nlassert(RegisteredSID != NULL); msgin.serial (RegisteredSuccess); if (RegisteredSuccess) { msgin.serial (*RegisteredSID); // decode the registered services at the register process cbRegisterBroadcast (msgin, from, netbase); } else { msgin.serial( Reason ); } Registered = true; } // static bool QueryPort; static uint16 QueryPortPort; static void cbQueryPort (CMessage &msgin, TSockId /* from */, CCallbackNetBase &/* netbase */) { msgin.serial (QueryPortPort); QueryPort = true; } // //static bool FirstRegisteredBroadcast; void cbRegisterBroadcast (CMessage &msgin, TSockId /* from */, CCallbackNetBase &/* netbase */) { TServiceId::size_type size; string name; TServiceId sid; vector addr; msgin.serial (size); for (TServiceId::size_type i = 0; i < size; i++) { msgin.serial (name); msgin.serial (sid); msgin.serialCont (addr); // add it in the list std::vector addrs; CNamingClient::find (sid, addrs); if (addrs.size() == 0) { CNamingClient::RegisteredServicesMutex.enter (); CNamingClient::RegisteredServices.push_back (CNamingClient::CServiceEntry (name, sid, addr)); CNamingClient::RegisteredServicesMutex.leave (); nlinfo ("NC: Registration Broadcast of the service %s-%hu '%s'", name.c_str(), sid.get(), vectorCInetAddressToString(addr).c_str()); if (_RegistrationBroadcastCallback != NULL) _RegistrationBroadcastCallback (name, sid, addr); } else if (addrs.size() == 1) { CNamingClient::RegisteredServicesMutex.enter (); for (std::list::iterator it = CNamingClient::RegisteredServices.begin(); it != CNamingClient::RegisteredServices.end (); it++) { if (sid == (*it).SId) { (*it).Name = name; (*it).Addr = addr; break; } } CNamingClient::RegisteredServicesMutex.leave (); nlinfo ("NC: Registration Broadcast (update) of the service %s-%hu '%s'", name.c_str(), sid.get(), addr[0].asString().c_str()); } else { nlstop; } } // FirstRegisteredBroadcast = true; //CNamingClient::displayRegisteredServices (); } // void cbUnregisterBroadcast (CMessage &msgin, TSockId /* from */, CCallbackNetBase &/* netbase */) { string name; TServiceId sid; vector addrs; msgin.serial (name); msgin.serial (sid); // remove it in the list, if the service is not found, ignore it CNamingClient::RegisteredServicesMutex.enter (); for (std::list::iterator it = CNamingClient::RegisteredServices.begin(); it != CNamingClient::RegisteredServices.end (); it++) { CNamingClient::CServiceEntry &serviceEntry = *it; if (serviceEntry.SId == sid) { // check the structure nlassertex (serviceEntry.Name == name, ("%s %s",serviceEntry.Name.c_str(), name.c_str())); addrs = serviceEntry.Addr; CNamingClient::RegisteredServices.erase (it); break; } } CNamingClient::RegisteredServicesMutex.leave (); nlinfo ("NC: Unregistration Broadcast of the service %s-%hu", name.c_str(), sid.get()); // send the ACK to the NS CMessage msgout ("ACK_UNI"); msgout.serial (sid); CNamingClient::_Connection->send (msgout); // oh my god, it s my sid! but i m alive, why this f*cking naming service want to kill me? ok, i leave it alone! if(CNamingClient::_MySId == sid) { nlwarning ("NC: Naming Service asked me to leave, I leave!"); IService::getInstance()->exit(); return; } if (_UnregistrationBroadcastCallback != NULL) _UnregistrationBroadcastCallback (name, sid, addrs); //CNamingClient::displayRegisteredServices (); } // static TCallbackItem NamingClientCallbackArray[] = { { "RG", cbRegister }, { "QP", cbQueryPort }, { "RGB", cbRegisterBroadcast }, { "UNB", cbUnregisterBroadcast } }; void CNamingClient::connect( const CInetAddress &addr, CCallbackNetBase::TRecordingState rec, const vector &/* addresses */ ) { nlassert (_Connection == NULL || (_Connection != NULL && !_Connection->connected ())); if (_Connection == NULL) { _Connection = new CCallbackClient( rec, "naming_client.nmr" ); _Connection->addCallbackArray (NamingClientCallbackArray, sizeof (NamingClientCallbackArray) / sizeof (NamingClientCallbackArray[0])); } _Connection->connect (addr); /* // send the available addresses CMessage msgout ("RS"); msgout.serialCont (const_cast&>(addresses)); _Connection->send (msgout); // wait the message that contains all already connected services FirstRegisteredBroadcast = false; while (!FirstRegisteredBroadcast && _Connection->connected ()) { _Connection->update (-1); nlSleep (1); } */} void CNamingClient::disconnect () { if (_Connection != NULL) { if (_Connection->connected ()) { _Connection->disconnect (); } delete _Connection; _Connection = NULL; } // we don't call unregisterAllServices because when the naming service will see the disconnection, // it'll automatically unregister all services registered by this client. } string CNamingClient::info () { string res; if (connected ()) { res = "connected to "; res += _Connection->remoteAddress().asString(); } else { res = "Not connected"; } return res; } bool CNamingClient::registerService (const std::string &name, const std::vector &addr, TServiceId &sid) { nlassert (_Connection != NULL && _Connection->connected ()); CMessage msgout ("RG"); msgout.serial (const_cast(name)); msgout.serialCont (const_cast&>(addr)); sid.set(0); msgout.serial (sid); _Connection->send (msgout); // wait the answer of the naming service "RG" Registered = false; RegisteredSID = &sid; while (!Registered) { _Connection->update (-1); nlSleep (1); } if (RegisteredSuccess) { _MySId = sid; _RegisteredServices.insert (make_pair (*RegisteredSID, name)); nldebug ("NC: Registered service %s-%hu at %s", name.c_str(), sid.get(), addr[0].asString().c_str()); } else { nldebug ("NC: Naming service refused to register service %s at %s", name.c_str(), addr[0].asString().c_str()); nlwarning ("NC: Startup denied: %s", Reason.c_str()); Reason.clear(); } RegisteredSID = NULL; return RegisteredSuccess; } bool CNamingClient::registerServiceWithSId (const std::string &name, const std::vector &addr, TServiceId sid) { nlassert (_Connection != NULL && _Connection->connected ()); CMessage msgout ("RG"); msgout.serial (const_cast(name)); msgout.serialCont (const_cast&>(addr)); msgout.serial (sid); _Connection->send (msgout); // wait the answer of the naming service "RGI" Registered = false; RegisteredSID = &sid; while (!Registered) { _Connection->update (-1); nlSleep (1); } if (RegisteredSuccess) { _MySId = sid; _RegisteredServices.insert (make_pair (*RegisteredSID, name)); nldebug ("NC: Registered service with sid %s-%hu at %s", name.c_str(), RegisteredSID->get(), addr[0].asString().c_str()); } else { nlerror ("NC: Naming service refused to register service with sid %s at %s", name.c_str(), addr[0].asString().c_str()); } return RegisteredSuccess == 1; } void CNamingClient::resendRegisteration (const std::string &name, const std::vector &addr, TServiceId sid) { nlassert (_Connection != NULL && _Connection->connected ()); CMessage msgout ("RRG"); msgout.serial (const_cast(name)); msgout.serialCont (const_cast&>(addr)); msgout.serial (sid); _Connection->send (msgout); } void CNamingClient::unregisterService (TServiceId sid) { nlassert (_Connection != NULL && _Connection->connected ()); CMessage msgout ("UNI"); msgout.serial (sid); _Connection->send (msgout); nldebug ("NC: Unregistering service %s-%hu", _RegisteredServices[sid].c_str(), sid.get()); _RegisteredServices.erase (sid); } void CNamingClient::unregisterAllServices () { nlassert (_Connection != NULL && _Connection->connected ()); while (!_RegisteredServices.empty()) { TRegServices::iterator irs = _RegisteredServices.begin(); TServiceId sid = (*irs).first; unregisterService (sid); } } uint16 CNamingClient::queryServicePort () { nlassert (_Connection != NULL && _Connection->connected ()); CMessage msgout ("QP"); _Connection->send (msgout); // wait the answer of the naming service "QP" QueryPort = false; while (!QueryPort) { _Connection->update (-1); nlSleep (1); } nlinfo ("NC: Received the answer of the query port (%hu)", QueryPortPort); return QueryPortPort; } bool CNamingClient::lookup (const std::string &name, CInetAddress &addr) { nlassert (_Connection != NULL && _Connection->connected ()); vector addrs; find (name, addrs); if (addrs.empty()) return false; nlassert (addrs.size()==1); addr = addrs[0]; return true; } bool CNamingClient::lookup (TServiceId sid, CInetAddress &addr) { nlassert (_Connection != NULL && _Connection->connected ()); vector addrs; find (sid, addrs); if (addrs.empty()) return false; nlassert (addrs.size()==1); addr = addrs[0]; return true; } bool CNamingClient::lookupAlternate (const std::string &name, CInetAddress &addr) { nlassert (_Connection != NULL && _Connection->connected ()); // remove it from his local list RegisteredServicesMutex.enter (); for (std::list::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((*it).Addr[0] == addr) { RegisteredServices.erase (it); break; } } RegisteredServicesMutex.leave (); vector addrs; find (name, addrs); if (addrs.empty()) return false; nlassert (addrs.size()==1); addr = addrs[0]; return true; } void CNamingClient::lookupAll (const std::string &name, std::vector &addrs) { nlassert (_Connection != NULL && _Connection->connected ()); find (name, addrs); } bool CNamingClient::lookupAndConnect (const std::string &name, CCallbackClient &sock) { nlassert (_Connection != NULL && _Connection->connected ()); // look up for service CInetAddress servaddr; // if service not found, return false if (!CNamingClient::lookup (name, servaddr)) return false; for(;;) { try { // try to connect to the server sock.connect (servaddr); // connection succeeded return true; } catch (const ESocketConnectionFailed &e) { nldebug( "NC: Connection to %s failed: %s, tring another service if available", servaddr.asString().c_str(), e.what() ); // try another server and if service is not found, return false if (!CNamingClient::lookupAlternate (name, servaddr)) return false; } } } void CNamingClient::update () { // get message for naming service (new registration for example) if (_Connection != NULL && _Connection->connected ()) _Connection->update (); } // // Commands // NLMISC_CATEGORISED_COMMAND(nel, services, "displays registered services", "") { nlunreferenced(rawCommandString); nlunreferenced(quiet); nlunreferenced(human); if(args.size() != 0) return false; CNamingClient::displayRegisteredServices (&log); return true; } } // NLNET