// 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 . /* * Limitations: Not threadsafe, not reentrant. */ // // Includes // #include "stdnet.h" #include "nel/misc/types_nl.h" #include "nel/misc/debug.h" #include "nel/misc/entity_id.h" #include "nel/misc/sheet_id.h" #include "nel/net/unified_network.h" #include "nel/net/transport_class.h" // // Namespace // using namespace std; using namespace NLMISC; using namespace NLNET; namespace NLNET { // // Globals // NLMISC::CVariable VerboseNETTC("nel","VerboseNETTC","Enable verbose logging in CTransportClass operations",true,0,true); // // Variables // uint CTransportClass::Mode = 0; // 0=nothing 1=read 2=write 3=register map CTransportClass::LocalRegisteredClass; // registered class that are in my program CTransportClass::CRegisteredClass CTransportClass::TempRegisteredClass; NLNET::CMessage CTransportClass::TempMessage; vector CTransportClass::DummyProp; bool CTransportClass::Init = false; // // Functions // string typeToString (CTransportClass::TProp type) { string conv[] = { "PropUInt8", "PropUInt16", "PropUInt32", "PropUInt64", "PropSInt8", "PropSInt16", "PropSInt32", "PropSInt64", "PropBool", "PropFloat", "PropDouble", "PropString", "PropDataSetRow", "PropSheetId", "PropUKN" }; // "PropBool", "PropFloat", "PropDouble", "PropString", "PropDataSetRow", "PropEntityId", "PropSheetId", "PropUKN" }; if (type > CTransportClass::PropUKN) return ""; return conv[type]; } void CTransportClass::displayDifferentClass (TServiceId sid, const string &className, const vector &otherClass, const vector &myClass) { NETTC_INFO ("NETTC: Service with sid %hu send me the TransportClass '%s' with differents properties:", sid.get(), className.c_str()); NETTC_INFO ("NETTC: My local TransportClass is:"); for (uint i = 0; i < myClass.size(); i++) { NETTC_INFO ("NETTC: Property: %d Name: '%s' type: '%s'", i, myClass[i]->Name.c_str(), typeToString(myClass[i]->Type).c_str()); } NETTC_INFO ("NETTC: The other side TransportClass is:"); for (uint i = 0; i < otherClass.size(); i++) { NETTC_INFO ("NETTC: Property: %d Name: '%s' type: '%s'", i, otherClass[i].Name.c_str(), typeToString(otherClass[i].Type).c_str()); } } void CTransportClass::registerOtherSideClass (TServiceId sid, TOtherSideRegisteredClass &osrc) { for (TOtherSideRegisteredClass::iterator it = osrc.begin(); it != osrc.end (); it++) { // find the class name in the map TRegisteredClass::iterator res = LocalRegisteredClass.find ((*it).first); if (res == LocalRegisteredClass.end ()) { // The other service knows a class that we don't // there was previously an nlwarning here but that was wrong because it is quite normal for this to happen when one service // ueses different transport classes to communicate with several different services, so the message has been changed to an nldebug NETTC_DEBUG ("NETTC: the other side class '%s' declared from service %d is not registered in my system, skip it", (*it).first.c_str(),(uint32)sid.get()); continue; } if (sid.get() >= (*res).second.Instance->States.size ()) (*res).second.Instance->States.resize (sid.get()+1); (*res).second.Instance->States[sid.get()].clear (); for (sint j = 0; j < (sint)(*it).second.size (); j++) { // check each prop to see the correspondance // try to find the prop name in the array uint k; for (k = 0; k < (*res).second.Instance->Prop.size(); k++) { if ((*it).second[j].Name == (*res).second.Instance->Prop[k]->Name) { if ((*it).second[j].Type != (*res).second.Instance->Prop[k]->Type) { nlwarning ("NETTC: Property '%s' of the class '%s' have not the same type in the 2 sides (%s %s)", (*it).second[j].Name.c_str(), (*it).first.c_str(), typeToString((*it).second[j].Type).c_str(), typeToString((*res).second.Instance->Prop[k]->Type).c_str()); } break; } } if (k == (*res).second.Instance->Prop.size()) { // not found, put -1 (*res).second.Instance->States[sid.get()].push_back (make_pair (-1, (*it).second[j].Type)); } else { // same, store the index (*res).second.Instance->States[sid.get()].push_back (make_pair (k, PropUKN)); } } // check if the version are the same if ((*it).second.size () != (*res).second.Instance->Prop.size ()) { // 2 class don't have the same number of prop => different class => display class displayDifferentClass (sid, (*it).first.c_str(), (*it).second, (*res).second.Instance->Prop); } else { // check if the prop are same for (uint i = 0; i < (*res).second.Instance->Prop.size (); i++) { if ((*res).second.Instance->Prop[i]->Name != (*it).second[i].Name) { // different name => different class => display class displayDifferentClass (sid, (*it).first.c_str(), (*it).second, (*res).second.Instance->Prop); break; } else if ((*res).second.Instance->Prop[i]->Type != (*it).second[i].Type) { // different type => different class => display class displayDifferentClass (sid, (*it).first.c_str(), (*it).second, (*res).second.Instance->Prop); break; } } } } displayLocalRegisteredClass (); } void CTransportClass::registerClass (CTransportClass &instance) { nlassert (Init); nlassert (Mode == 0); // set the mode to register Mode = 3; // clear the current class TempRegisteredClass.clear (); // set the instance pointer TempRegisteredClass.Instance = &instance; // fill name and props TempRegisteredClass.Instance->description (); // add the new registered class in the array LocalRegisteredClass[TempRegisteredClass.Instance->Name] = TempRegisteredClass; // set to mode none Mode = 0; } void CTransportClass::unregisterClass () { for (TRegisteredClass::iterator it = LocalRegisteredClass.begin(); it != LocalRegisteredClass.end (); it++) { for (uint j = 0; j < (*it).second.Instance->Prop.size (); j++) { delete (*it).second.Instance->Prop[j]; } (*it).second.Instance->Prop.clear (); (*it).second.Instance = NULL; } LocalRegisteredClass.clear (); } void CTransportClass::displayLocalRegisteredClass (CRegisteredClass &c) { NETTC_DEBUG ("NETTC: > %s", c.Instance->Name.c_str()); for (uint j = 0; j < c.Instance->Prop.size (); j++) { NETTC_DEBUG ("NETTC: > %s %s", c.Instance->Prop[j]->Name.c_str(), typeToString(c.Instance->Prop[j]->Type).c_str()); } for (uint l = 0; l < c.Instance->States.size (); l++) { if (c.Instance->States[l].size () != 0) { NETTC_DEBUG ("NETTC: > sid: %u", l); for (uint k = 0; k < c.Instance->States[l].size (); k++) { NETTC_DEBUG ("NETTC: - %d type : %s", c.Instance->States[l][k].first, typeToString(c.Instance->States[l][k].second).c_str()); } } } } void CTransportClass::displayLocalRegisteredClass () { NETTC_DEBUG ("NETTC:> LocalRegisteredClass:"); for (TRegisteredClass::iterator it = LocalRegisteredClass.begin(); it != LocalRegisteredClass.end (); it++) { displayLocalRegisteredClass ((*it).second); } } void cbTCReceiveMessage (CMessage &msgin, const string &name, TServiceId sid) { NETTC_DEBUG ("NETTC: cbReceiveMessage"); CTransportClass::TempMessage.clear(); CTransportClass::TempMessage.assignFromSubMessage( msgin ); string className; CTransportClass::readHeader(CTransportClass::TempMessage, className); CTransportClass::TRegisteredClass::iterator it = CTransportClass::LocalRegisteredClass.find (className); if (it == CTransportClass::LocalRegisteredClass.end ()) { nlwarning ("NETTC: Receive unknown transport class '%s' received from %s-%hu", className.c_str(), name.c_str(), sid.get()); return; } nlassert ((*it).second.Instance != NULL); if (!(*it).second.Instance->read (name, sid)) { nlwarning ("NETTC: Can't read the transportclass '%s' received from %s-%hu (probably not registered on sender service)", className.c_str(), name.c_str(), sid.get()); } } void cbTCReceiveOtherSideClass (CMessage &msgin, const string &/* name */, TServiceId sid) { NETTC_DEBUG ("NETTC: cbReceiveOtherSideClass"); CTransportClass::TOtherSideRegisteredClass osrc; uint32 nbClass; msgin.serial (nbClass); NETTC_DEBUG ("NETTC: %d class", nbClass); for (uint i = 0; i < nbClass; i++) { string className; msgin.serial (className); osrc.push_back(make_pair (className, vector())); uint32 nbProp; msgin.serial (nbProp); NETTC_DEBUG ("NETTC: %s (%d prop)", className.c_str(), nbProp); for (uint j = 0; j < nbProp; j++) { CTransportClass::CRegisteredBaseProp prop; msgin.serial (prop.Name); msgin.serialEnum (prop.Type); NETTC_DEBUG ("NETTC: %s %s", prop.Name.c_str(), typeToString(prop.Type).c_str()); osrc[osrc.size()-1].second.push_back (prop); } } // we have the good structure CTransportClass::registerOtherSideClass (sid, osrc); } static TUnifiedCallbackItem CallbackArray[] = { { "CT_LRC", cbTCReceiveOtherSideClass }, { "CT_MSG", cbTCReceiveMessage }, }; void cbTCUpService (const std::string &serviceName, TServiceId sid, void * /* arg */) { NETTC_DEBUG ("NETTC: CTransportClass Service %s %hu is up", serviceName.c_str(), sid.get()); if (sid.get() >= 256) return; CTransportClass::sendLocalRegisteredClass (sid); } void CTransportClass::init () { // this isn't an error! if (Init) return; CUnifiedNetwork::getInstance()->addCallbackArray (CallbackArray, sizeof (CallbackArray) / sizeof (CallbackArray[0])); // create an instance of all d'ifferent prop types DummyProp.resize (PropUKN); nlassert (PropUInt8 < PropUKN); DummyProp[PropUInt8] = new CTransportClass::CRegisteredProp; nlassert (PropUInt16 < PropUKN); DummyProp[PropUInt16] = new CTransportClass::CRegisteredProp; nlassert (PropUInt32 < PropUKN); DummyProp[PropUInt32] = new CTransportClass::CRegisteredProp; nlassert (PropUInt64 < PropUKN); DummyProp[PropUInt64] = new CTransportClass::CRegisteredProp; nlassert (PropSInt8 < PropUKN); DummyProp[PropSInt8] = new CTransportClass::CRegisteredProp; nlassert (PropSInt16 < PropUKN); DummyProp[PropSInt16] = new CTransportClass::CRegisteredProp; nlassert (PropSInt32 < PropUKN); DummyProp[PropSInt32] = new CTransportClass::CRegisteredProp; nlassert (PropSInt64 < PropUKN); DummyProp[PropSInt64] = new CTransportClass::CRegisteredProp; nlassert (PropBool < PropUKN); DummyProp[PropBool] = new CTransportClass::CRegisteredProp; nlassert (PropFloat < PropUKN); DummyProp[PropFloat] = new CTransportClass::CRegisteredProp; nlassert (PropDouble < PropUKN); DummyProp[PropDouble] = new CTransportClass::CRegisteredProp; nlassert (PropString < PropUKN); DummyProp[PropString] = new CTransportClass::CRegisteredProp; // nlassert (PropDataSetRow < PropUKN); DummyProp[PropDataSetRow] = new CTransportClass::CRegisteredProp; // nlassert (PropEntityId < PropUKN); DummyProp[PropEntityId] = new CTransportClass::CRegisteredProp; nlassert (PropSheetId < PropUKN); DummyProp[PropSheetId] = new CTransportClass::CRegisteredProp; // we have to know when a service comes, so add callback (put the callback before all other one because we have to send this message first) CUnifiedNetwork::getInstance()->setServiceUpCallback("*", cbTCUpService, NULL, false); Init = true; } void CTransportClass::release () { unregisterClass (); for (uint i = 0; i < DummyProp.size (); i++) { delete DummyProp[i]; } DummyProp.clear (); } void CTransportClass::createLocalRegisteredClassMessage () { TempMessage.clear (); if (TempMessage.isReading()) TempMessage.invert(); TempMessage.setType ("CT_LRC"); uint32 nbClass = LocalRegisteredClass.size (); TempMessage.serial (nbClass); for (TRegisteredClass::iterator it = LocalRegisteredClass.begin(); it != LocalRegisteredClass.end (); it++) { nlassert ((*it).first == (*it).second.Instance->Name); TempMessage.serial ((*it).second.Instance->Name); uint32 nbProp = (*it).second.Instance->Prop.size (); TempMessage.serial (nbProp); for (uint j = 0; j < (*it).second.Instance->Prop.size (); j++) { // send the name and the type of the prop TempMessage.serial ((*it).second.Instance->Prop[j]->Name); TempMessage.serialEnum ((*it).second.Instance->Prop[j]->Type); } } } /* * Get the name of message (for displaying), or extract the class name if it is a transport class. * * Preconditions: * - msgin is an input message that contains a valid message * * Postconditions: * - the pos in msgin was modified * - msgName contains "msg %s" or "transport class %s" where %s is the name of message, or the name * transport class is the message is a CT_MSG. */ void getNameOfMessageOrTransportClass( NLNET::CMessage& msgin, std::string& msgName ) { if ( msgin.getName() == "CT_MSG" ) { try { msgin.seek( msgin.getHeaderSize(), NLMISC::IStream::begin ); msgin.serial( msgName ); } catch ( EStreamOverflow& ) { msgName = ""; } msgName = "transport class " + msgName; } else { msgName = "msg " + msgin.getName(); } } } // NLNET