// 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 . #ifndef NL_TRANSPORT_CLASS_H #define NL_TRANSPORT_CLASS_H // // Includes // #include "nel/misc/types_nl.h" #include "nel/misc/stream.h" #include "nel/misc/entity_id.h" #include "nel/misc/sheet_id.h" #include "nel/misc/variable.h" #include "unified_network.h" #include "message.h" #include #include namespace NLNET { // // Macros // /** * Use this macro to register a class that can be transported in the init of your program. */ #define TRANSPORT_CLASS_REGISTER(_c) \ static _c _c##Instance; \ CTransportClass::registerClass (_c##Instance); #define NETTC_INFO if (!VerboseNETTC.get()) {} else nlinfo #define NETTC_DEBUG if (!VerboseNETTC.get()) {} else nldebug extern NLMISC::CVariable VerboseNETTC; // // Classes // /** * You have to inherit this class and implement description() and callback() method. * For an example of use, take a look at nel/samples/class_transport sample. * \author Vianney Lecroart * \author Nevrax France * \date 2002 */ class CTransportClass { public: virtual ~CTransportClass() {} /** Different types that we can use in a Transport class * warning: if you add/change a prop, change also in CTransportClass::init() * warning: PropUKN must be the last value (used to resize a vector) */ enum TProp { PropUInt8, PropUInt16, PropUInt32, PropUInt64, PropSInt8, PropSInt16, PropSInt32, PropSInt64, PropBool, PropFloat, PropDouble, PropString, PropDataSetRow, PropSheetId, PropUCString, PropUKN }; // PropBool, PropFloat, PropDouble, PropString, PropDataSetRow, PropEntityId, PropSheetId, PropUKN }; // // Static methods // /// Init the transport class system (must be called one time, in the IService5::init() for example) static void init (); /// Release the transport class system (must be called one time, in the IService5::release() for example) static void release (); /** Call this function to register a new transport class. * \param instance A reference to a GLOBAL space of the instance of this transport class. It will be used when receive this class from network. */ static void registerClass (CTransportClass &instance); /// Display registered transport class (debug purpose) static void displayLocalRegisteredClass (); // // Virtual methods // /** You have to implement this function with the description of your class. This description * is used to send the class accross the network and read it. It must contains a class name and * a set properties. * Example: *\code virtual void description () { className ("SharedClass"); property ("i1", PropUInt32, (uint32)11, i1); } *\endcode */ virtual void description () = 0; /** This function will be call when we receive this class from the network. It will use the instance given at the * registration process. By default, it does nothing. */ virtual void callback (const std::string &/* name */, NLNET::TServiceId /* sid */) { } // // Other methods // /// send the transport class to a specified service using the service id void send (NLNET::TServiceId sid); /// send the transport class to a specified service using the service name void send (const std::string &serviceName); /** The name of the transport class. Must be unique for each class. */ void className (const std::string &name); /** Return the name of the transport class. * The result is valid only AFTER calling of REGISTER_TRANSPORT_CLASS. */ const std::string &className() { return Name; } /** One property of the class. Look description() for an example of use. * \param name The name of the property * \param type Type of the property * \param defaultValue The value you want to be set when a message comes without this property * \param value Reference to the value where the property will be read or write */ template void property (const std::string &name, TProp type, T defaultValue, T &value) { switch (type) { case PropUInt8: case PropSInt8: case PropBool: nlassert(sizeof(T) == sizeof (uint8)); break; case PropUInt16: case PropSInt16: nlassert(sizeof(T) == sizeof (uint16)); break; case PropUInt32: case PropSInt32: case PropDataSetRow: nlassert(sizeof(T) == sizeof (uint32)); break; case PropUInt64: case PropSInt64: nlassert(sizeof(T) == sizeof (uint64)); break; case PropFloat: nlassert(sizeof(T) == sizeof (float)); break; case PropDouble: nlassert(sizeof(T) == sizeof (double)); break; case PropString: nlassert(sizeof(T) == sizeof (std::string)); break; // case PropEntityId: nlassert(sizeof(T) == sizeof (NLMISC::CEntityId)); break; case PropSheetId: nlassert(sizeof(T) == sizeof (NLMISC::CSheetId)); break; case PropUCString: nlassert(sizeof(T) == sizeof (ucstring)); break; default: nlerror ("property %s have unknown type %d", name.c_str(), type); } if (Mode == 2) // write { // send only if needed // todo manage unknown prop TempMessage.serial (value); } else if (Mode == 3) // register { // add a new prop to the current class nlassert (TempRegisteredClass.Instance != NULL); TempRegisteredClass.Instance->Prop.push_back (new CRegisteredProp (name, type, defaultValue, &value)); } else if (Mode == 4) // display { std::string val; val = "defval: "; val += NLMISC::toString (defaultValue); val += " val: "; val += NLMISC::toString (value); NETTC_DEBUG ("NETTC: prop %s %d: %s", name.c_str(), type, val.c_str()); } else { nlstop; } } template void propertyVector (const std::string &name, TProp type, std::vector &value) { if (Mode == 2) // write { // send only if needed // todo manage unknown prop TempMessage.serialCont (value); } else if (Mode == 3) // register { // add a new prop to the current class nlassert (TempRegisteredClass.Instance != NULL); TempRegisteredClass.Instance->Prop.push_back (new CRegisteredPropCont > (name, type, &value)); } else if (Mode == 4) // display { typedef typename std::vector::iterator __iterator; std::string val; for (__iterator it = value.begin (); it != value.end(); it++) { val += NLMISC::toString (T(*it)); val += " "; } NETTC_DEBUG ("NETTC: prop %s %d: %d elements ( %s)", name.c_str(), type, value.size(), val.c_str()); } else { nlstop; } } template void propertyCont (const std::string &name, TProp type, T &value) { if (Mode == 2) // write { // send only if needed // todo manage unknown prop TempMessage.serialCont (value); } else if (Mode == 3) // register { // add a new prop to the current class nlassert (TempRegisteredClass.Instance != NULL); TempRegisteredClass.Instance->Prop.push_back (new CRegisteredPropCont (name, type, &value)); } else if (Mode == 4) // display { typedef typename T::iterator __iterator; std::string val; for (__iterator it = value.begin (); it != value.end(); it++) { val += NLMISC::toString (*it); val += " "; } NETTC_DEBUG ("NETTC: prop %s %d: %d elements ( %s)", name.c_str(), type, value.size(), val.c_str()); } else { nlstop; } } // Read the header (first part of the transport class message, currently only className) static void readHeader(CMessage& msgin, std::string& className) { msgin.serial(className); } /// Display with nlinfo the content of the class (debug purpose) void display (); protected: // // Structures // struct CRegisteredBaseProp { CRegisteredBaseProp () : Type(PropUKN) { } virtual ~CRegisteredBaseProp() {} CRegisteredBaseProp (const std::string &name, TProp type) : Name(name), Type(type) { } std::string Name; TProp Type; virtual void serialDefaultValue (NLMISC::IStream &/* f */) { } virtual void serialValue (NLMISC::IStream &/* f */) { } virtual void setDefaultValue () { } }; typedef std::vector > > TOtherSideRegisteredClass; struct CRegisteredClass { CTransportClass *Instance; CRegisteredClass () { clear (); } void clear () { Instance = NULL; } }; typedef std::map TRegisteredClass; template struct CRegisteredProp : public CRegisteredBaseProp { CRegisteredProp () : Value(NULL) { } CRegisteredProp (const std::string &name, TProp type, T defaultValue, T *value = NULL) : CRegisteredBaseProp (name, type), DefaultValue(defaultValue), Value (value) { } T DefaultValue, *Value; virtual void serialDefaultValue (NLMISC::IStream &f) { f.serial (DefaultValue); } virtual void serialValue (NLMISC::IStream &f) { nlassert (Value != NULL); f.serial (*Value); } virtual void setDefaultValue () { nlassert (Value != NULL); *Value = DefaultValue; } }; template struct CRegisteredPropCont : public CRegisteredBaseProp { CRegisteredPropCont () : Value(NULL) { } CRegisteredPropCont (const std::string &name, TProp type, T *value = NULL) : CRegisteredBaseProp (name, type), Value (value) { } T *Value; virtual void serialDefaultValue (NLMISC::IStream &/* f */) { // nothing } virtual void serialValue (NLMISC::IStream &f) { nlassert (Value != NULL); f.serialCont (*Value); } virtual void setDefaultValue () { nlassert (Value != NULL); Value->clear (); } }; // // Variables // // Name of the class std::string Name; // States to decode the stream from the network std::vector > > States; // Contains all propterties for this class std::vector Prop; // // Methods // // Read the TempMessage and call the callback bool read (const std::string &name, NLNET::TServiceId sid); // Used to create a TempMessage with this class NLNET::CMessage &write (); // // Static Variables // // Used to serialize unused properties from the TempMessage static std::vector DummyProp; // Select what the description() must do static uint Mode; // 0=nothing 1=read 2=write 3=register 4=display // Contains all registered transport class static TRegisteredClass LocalRegisteredClass; // registered class that are in my program // The registered class that is currently filled (before put in LocalRegisteredClass) static CRegisteredClass TempRegisteredClass; // The message that is currently filled/emptyed static NLNET::CMessage TempMessage; static bool Init; // // Static methods // // Called by release() to delete all structures static void unregisterClass (); // Fill the States merging local and other side class static void registerOtherSideClass (NLNET::TServiceId sid, TOtherSideRegisteredClass &osrc); // Create a message with local transport classes to send to the other side static void createLocalRegisteredClassMessage (); // Send the local transport classes to another service using the service id static void sendLocalRegisteredClass (NLNET::TServiceId sid) { nlassert (Init); NETTC_DEBUG ("NETTC: sendLocalRegisteredClass to %hu", sid.get()); createLocalRegisteredClassMessage (); NLNET::CUnifiedNetwork::getInstance()->send (sid, TempMessage); } // Display a specific registered class (debug purpose) static void displayLocalRegisteredClass (CRegisteredClass &c); static void displayDifferentClass (NLNET::TServiceId sid, const std::string &className, const std::vector &otherClass, const std::vector &myClass); // // Friends // friend void cbTCReceiveMessage (NLNET::CMessage &msgin, const std::string &name, NLNET::TServiceId sid); friend void cbTCUpService (const std::string &serviceName, NLNET::TServiceId sid, void *arg); friend void cbTCReceiveOtherSideClass (NLNET::CMessage &msgin, const std::string &name, NLNET::TServiceId sid); }; /** * 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: * - msgin.getPos() 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 ); // // Inlines // inline void CTransportClass::className (const std::string &name) { if (Mode == 2) // write { TempMessage.serial (const_cast (name)); } else if (Mode == 3) // register { // add a new entry in my registered class nlassert (TempRegisteredClass.Instance != NULL); TempRegisteredClass.Instance->Name = name; } else if (Mode == 4) // display { NETTC_DEBUG ("NETTC: class %s:", name.c_str()); } else { nlstop; } } inline void CTransportClass::send (NLNET::TServiceId sid) { nlassert (Init); NLNET::CUnifiedNetwork::getInstance()->send (sid, write ()); } inline void CTransportClass::send (const std::string &serviceName) { nlassert (Init); NLNET::CUnifiedNetwork::getInstance()->send (serviceName, write ()); } inline void CTransportClass::display () { nlassert (Mode == 0); // set the mode to register Mode = 4; description (); // set to mode none Mode = 0; } inline NLNET::CMessage &CTransportClass::write () { nlassert (Init); nlassert (Mode == 0); #ifndef FINAL_VERSION // Did the programmer forget to register the transport class? Forbid sending then. nlassert( LocalRegisteredClass.find( className() ) != LocalRegisteredClass.end() ); #endif // set the mode to register Mode = 2; TempMessage.clear (); if (TempMessage.isReading()) TempMessage.invert(); TempMessage.setType ("CT_MSG"); description (); // set to mode none Mode = 0; display (); return TempMessage; } inline bool CTransportClass::read (const std::string &name, NLNET::TServiceId sid) { nlassert (Init); nlassert (Mode == 0); // there's no info about how to read this message from this sid, give up if (sid.get() >= States.size()) return false; // set flag of all prop std::vector bitfield; bitfield.resize (Prop.size(), 0); // init prop from the stream uint i; for (i = 0; i < States[sid.get()].size(); i++) { if (States[sid.get()][i].first == -1) { // skip the value from the stream DummyProp[States[sid.get()][i].second]->serialDefaultValue (TempMessage); } else { // get the good value Prop[States[sid.get()][i].first]->serialValue (TempMessage); bitfield[States[sid.get()][i].first] = 1; } } // set default value for unknown prop for (i = 0; i < Prop.size(); i++) { if (bitfield[i] == 0) { Prop[i]->setDefaultValue (); } } display (); // call the user callback callback (name, sid); return true; } } // NLNET #endif // NL_TRANSPORT_CLASS_H /* End of transport_class.h */