// 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 */