/* * Original work: Copyright (c) 2014, Oculus VR, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * RakNet License.txt file in the licenses directory of this source tree. An additional grant * of patent rights can be found in the RakNet Patents.txt file in the same directory. * * * Modified work: Copyright (c) 2017, SLikeSoft UG (haftungsbeschränkt) * * This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style * license found in the license.txt file in the root directory of this source tree. */ #ifndef __RPC3_BOOST_H #define __RPC3_BOOST_H // Fixes // error C2504: 'boost::fusion::detail::invoke_impl' : base class undefined // This defines the maximum number of parameters you can have #ifndef BOOST_FUSION_INVOKE_MAX_ARITY #define BOOST_FUSION_INVOKE_MAX_ARITY 10 #endif // Boost dependencies // Boost is assumed to be at C:\boost_1_43_0 based on the project settings // If this is not where you downloaded boost, change the project settings Configuration Properties / C/C++ / General / Additional Include Directories // If you don't have boost, get it from http://www.boost.org/users/download/ // If you don't want to use boost, use RPC4 instead which relies on assembly but has fewer features #include "boost/type_traits.hpp" #include "boost/function.hpp" #include "boost/bind.hpp" #include "boost/mpl/if.hpp" #include "boost/mpl/apply.hpp" #include "boost/function_types/parameter_types.hpp" #include "boost/fusion/container/list/cons.hpp" // boost::fusion::nil #include "boost/fusion/include/push_back.hpp" #include "boost/fusion/include/invoke.hpp" #include "boost/fusion/tuple/tuple.hpp" #include "boost/fusion/tuple/make_tuple.hpp" #include "boost/fusion/functional/invocation/invoke.hpp" #include "boost/type_traits/is_array.hpp" // Not needed? //#include #include "slikenet/NetworkIDManager.h" #include "slikenet/NetworkIDObject.h" #include "slikenet/BitStream.h" namespace SLNet { class RPC3; class BitStream; namespace _RPC3 { enum InvokeResultCodes { IRC_SUCCESS, IRC_NEED_BITSTREAM, IRC_NEED_NETWORK_ID_MANAGER, IRC_NEED_NETWORK_ID, IRC_NEED_CLASS_OBJECT, }; struct InvokeArgs { // Bitstream to use to deserialize SLNet::BitStream *bitStream; // NetworkIDManager to use to lookup objects NetworkIDManager *networkIDManager; // C++ class member object NetworkID classMemberObjectId; // The calling plugin RPC3 *caller; // The this pointer for C++ NetworkIDObject *thisPtr; }; typedef boost::fusion::tuple > FunctionPointer; struct StrWithDestructor { char *c; ~StrWithDestructor() {if (c) delete c;} }; enum RPC3TagFlag { RPC3_TAG_FLAG_DEREF=1, RPC3_TAG_FLAG_ARRAY=2, }; struct RPC3Tag { RPC3Tag() {} RPC3Tag(void *_v, unsigned int _count, RPC3TagFlag _flag) : v(_v), count(_count), flag((unsigned char)_flag) {} void* v; unsigned int count; unsigned char flag; }; // Track the pointers tagged with SLNet::_RPC3::Deref static RPC3Tag __RPC3TagPtrs[BOOST_FUSION_INVOKE_MAX_ARITY+1]; static int __RPC3TagHead=0; static int __RPC3TagTail=0; // If this assert hits, then SLNet::_RPC3::Deref was called more times than the argument was passed to the function static void __RPC3_Tag_AddHead(const RPC3Tag &p) { // Update tag if already in array int i; for (i=__RPC3TagTail; i!=__RPC3TagHead; i=(i+1)%BOOST_FUSION_INVOKE_MAX_ARITY) { if (__RPC3TagPtrs[i].v==p.v) { if (p.flag==RPC3_TAG_FLAG_ARRAY) { __RPC3TagPtrs[i].count=p.count; } __RPC3TagPtrs[i].flag|=p.flag; return; } } __RPC3TagPtrs[__RPC3TagHead]=p; __RPC3TagHead = (__RPC3TagHead + 1) % BOOST_FUSION_INVOKE_MAX_ARITY; assert(__RPC3TagHead!=__RPC3TagTail); } static void __RPC3ClearTail(void) { while (__RPC3TagTail!=__RPC3TagHead) { if (__RPC3TagPtrs[__RPC3TagTail].v==0) __RPC3TagTail = (__RPC3TagTail+1) % BOOST_FUSION_INVOKE_MAX_ARITY; else return; } } static bool __RPC3ClearPtr(void* p, RPC3Tag *tag) { int i; for (i=__RPC3TagTail; i!=__RPC3TagHead; i=(i+1)%BOOST_FUSION_INVOKE_MAX_ARITY) { if (__RPC3TagPtrs[i].v==p) { *tag=__RPC3TagPtrs[i]; __RPC3TagPtrs[i].v=0; __RPC3ClearTail(); return true; } } tag->flag=0; tag->count=1; return false; } template inline const templateType& Deref(const templateType & t) { __RPC3_Tag_AddHead(RPC3Tag((void*)t,1,RPC3_TAG_FLAG_DEREF)); return t; } template inline const templateType& PtrToArray(unsigned int count, const templateType & t) { __RPC3_Tag_AddHead(RPC3Tag((void*)t,count,RPC3_TAG_FLAG_ARRAY)); return t; } struct ReadBitstream { static void applyArray(SLNet::BitStream &bitStream, SLNet::BitStream* t){apply(bitStream,t);} static void apply(SLNet::BitStream &bitStream, SLNet::BitStream* t) { BitSize_t numBitsUsed; bitStream.ReadCompressed(numBitsUsed); bitStream.Read(t,numBitsUsed); } }; //template struct ReadPtr { template static inline void applyArray(SLNet::BitStream &bitStream, T2 *t) {bitStream >> (*t);} template static inline void apply(SLNet::BitStream &bitStream, T2 *t) {bitStream >> (*t);} static inline void apply(SLNet::BitStream &bitStream, char *&t) {applyStr(bitStream, (char *&) t);} static inline void apply(SLNet::BitStream &bitStream, unsigned char *&t) {applyStr(bitStream, (char *&) t);} static inline void apply(SLNet::BitStream &bitStream, const char *&t) {applyStr(bitStream, (char *&) t);} static inline void apply(SLNet::BitStream &bitStream, const unsigned char *&t) {applyStr(bitStream, (char *&) t);} static inline void applyStr(SLNet::BitStream &bitStream, char *&t) { SLNet::RakString rs; bitStream >> rs; size_t len = rs.GetLength()+1; // The caller should have already allocated memory, so we need to free // it and allocate a new buffer. RakAssert("Expected allocated array, got NULL" && (NULL != t)); delete [] t; t = new char [len]; memcpy(t,rs.C_String(),len); } }; template< typename T > struct DoRead { typedef typename boost::mpl::if_< boost::is_convertible, ReadBitstream, ReadPtr >::type type; }; template< typename T > struct ReadWithoutNetworkIDNoPtr { static InvokeResultCodes apply(InvokeArgs &args, T &t) { // printf("ReadWithoutNetworkIDNoPtr\n"); DoRead< typename boost::remove_pointer::type >::type::apply(* (args.bitStream),&t); return IRC_SUCCESS; } // typedef boost::mpl::false_ Cleanup; template< typename T2 > static void Cleanup(T2 &t) {} }; template< typename T > struct ReadWithNetworkIDPtr { static InvokeResultCodes apply(InvokeArgs &args, T &t) { // printf("ReadWithNetworkIDPtr\n"); // Read the network ID bool isNull; args.bitStream->Read(isNull); if (isNull) { t=0; return IRC_SUCCESS; } bool deref, isArray; args.bitStream->Read(deref); args.bitStream->Read(isArray); unsigned int count; if (isArray) args.bitStream->ReadCompressed(count); else count=1; NetworkID networkId; for (unsigned int i=0; i < count; i++) { args.bitStream->Read(networkId); t = args.networkIDManager->GET_OBJECT_FROM_ID< T >(networkId); if (deref) { BitSize_t bitsUsed; args.bitStream->AlignReadToByteBoundary(); args.bitStream->Read(bitsUsed); if (t) { DoRead< typename boost::remove_pointer::type >::type::apply(* (args.bitStream),t); } else { // Skip data! args.bitStream->IgnoreBits(bitsUsed); } } } return IRC_SUCCESS; } template< typename T2 > static void Cleanup(T2 &t) {} }; template< typename T > struct ReadWithoutNetworkIDPtr { template static InvokeResultCodes apply(InvokeArgs &args, T2 &t) { // printf("ReadWithoutNetworkIDPtr\n"); bool isNull=false; args.bitStream->Read(isNull); if (isNull) { t=0; return IRC_SUCCESS; } typedef typename boost::remove_pointer< T >::type ActualObjectType; bool isArray=false; unsigned int count; args.bitStream->Read(isArray); if (isArray) args.bitStream->ReadCompressed(count); else count=1; t = new ActualObjectType[count](); if (isArray) { for (unsigned int i=0; i < count; i++) { DoRead< typename boost::remove_pointer::type >::type::applyArray(* (args.bitStream),t+i); } } else { DoRead< typename boost::remove_pointer::type >::type::apply(* (args.bitStream),t); } return IRC_SUCCESS; } template< typename T2 > static void Cleanup(T2 &t) { if (t) delete [] t; } }; template< typename T > struct SetRPC3Ptr { static InvokeResultCodes apply(InvokeArgs &args, T &obj) { obj=args.caller; return IRC_SUCCESS; } //typedef boost::mpl::false_ Cleanup; template< typename T2 > static void Cleanup(T2 &t) {} }; /* template< typename T > struct ReadWithNetworkID { typedef typename boost::mpl::if_< boost::is_pointer , typename ReadWithNetworkIDPtr // true , typename ReadWithNetworkIDNoPtr >::type type; }; */ template< typename T > struct ReadWithoutNetworkID { typedef typename boost::mpl::if_< boost::is_pointer , ReadWithoutNetworkIDPtr // true , ReadWithoutNetworkIDNoPtr >::type type; }; template< typename T > struct identity { typedef T type; }; template< typename T > struct IsRPC3Ptr { typedef typename boost::mpl::if_< boost::is_convertible, boost::mpl::true_, boost::mpl::false_>::type type; }; template< typename T > struct ShouldReadNetworkID { /* typedef typename boost::mpl::if_< boost::is_pointer, typename identity::type, boost::add_pointer>::type typeWithPtr; typedef typename boost::mpl::if_< boost::is_convertible, boost::mpl::true_, boost::mpl::false_>::type type; */ typedef typename boost::mpl::if_< boost::is_convertible, boost::mpl::true_, boost::mpl::false_>::type type; }; template< typename T > struct GetReadFunction { /* typedef typename boost::mpl::if_< typename ShouldReadNetworkID::type , typename ReadWithNetworkID::type , typename ReadWithoutNetworkID::type >::type type; */ typedef typename boost::mpl::if_< typename ShouldReadNetworkID::type , ReadWithNetworkIDPtr , typename ReadWithoutNetworkID::type >::type type; }; template< typename T > struct ProcessArgType { typedef typename boost::mpl::if_< typename IsRPC3Ptr::type , SetRPC3Ptr , typename GetReadFunction::type >::type type; }; template< typename Function , class From = typename boost::mpl::begin< boost::function_types::parameter_types >::type , class To = typename boost::mpl::end< boost::function_types::parameter_types >::type > struct BoostRPCInvoker { // add an argument to a Fusion cons-list for each parameter type template static inline InvokeResultCodes apply(Function func, InvokeArgs &functionArgs, Args const &args) { typedef typename boost::mpl::deref::type arg_type; typedef typename boost::mpl::next::type next_iter_type; typedef typename boost::remove_reference::type arg_type_no_ref; arg_type_no_ref argType; ProcessArgType< arg_type_no_ref >::type::apply(functionArgs, argType); InvokeResultCodes irc = BoostRPCInvoker::apply ( func, functionArgs, boost::fusion::push_back(args, boost::ref(argType) ) ); ProcessArgType< arg_type_no_ref >::type::Cleanup(argType); return irc; } }; template< typename Function , class From = typename boost::mpl::begin< boost::function_types::parameter_types >::type , class To = typename boost::mpl::end< boost::function_types::parameter_types >::type > struct BoostRPCInvoker_ThisPtr { // add an argument to a Fusion cons-list for each parameter type template static inline InvokeResultCodes apply(Function func, InvokeArgs &functionArgs, Args const &args) { typedef typename boost::mpl::deref::type arg_type; typedef typename boost::mpl::next::type next_iter_type; arg_type argType = (arg_type) *(functionArgs.thisPtr); return BoostRPCInvoker::apply ( func, functionArgs, boost::fusion::push_back(args, boost::ref(argType) ) ); } }; template struct BoostRPCInvoker { // the argument list is complete, now call the function template static inline InvokeResultCodes apply(Function func, InvokeArgs&, Args const &args) { boost::fusion::invoke(func,args); return IRC_SUCCESS; } }; template struct DoNothing { static void apply(SLNet::BitStream &bitStream, T& t) { (void) bitStream; (void) t; // printf("DoNothing\n"); } }; struct WriteBitstream { static void applyArray(SLNet::BitStream &bitStream, SLNet::BitStream* t) {apply(bitStream,t);} static void apply(SLNet::BitStream &bitStream, SLNet::BitStream* t) { BitSize_t oldReadOffset = t->GetReadOffset(); t->ResetReadPointer(); bitStream.WriteCompressed(t->GetNumberOfBitsUsed()); bitStream.Write(t); t->SetReadOffset(oldReadOffset); } }; //template struct WritePtr { template static inline void applyArray(SLNet::BitStream &bitStream, T2 *t) {bitStream << (*t);} template static inline void apply(SLNet::BitStream &bitStream, T2 *t) {bitStream << (*t);} // template <> static inline void apply(SLNet::BitStream &bitStream, char *t) {bitStream << t;} // template <> static inline void apply(SLNet::BitStream &bitStream, unsigned char *t) {bitStream << t;} // template <> static inline void apply(SLNet::BitStream &bitStream, const char *t) {bitStream << t;} // template <> static inline void apply(SLNet::BitStream &bitStream, const unsigned char *t) {bitStream << t;} }; template< typename T > struct DoWrite { typedef typename boost::mpl::if_< boost::is_convertible, WriteBitstream, WritePtr >::type type; }; template struct WriteWithNetworkIDPtr { static void apply(SLNet::BitStream &bitStream, T& t) { bool isNull; isNull=(t==0); bitStream.Write(isNull); if (isNull) return; RPC3Tag tag; __RPC3ClearPtr(t, &tag); bool deref = (tag.flag & RPC3_TAG_FLAG_DEREF) !=0; bool isArray = (tag.flag & RPC3_TAG_FLAG_ARRAY) !=0; bitStream.Write(deref); bitStream.Write(isArray); if (isArray) { bitStream.WriteCompressed(tag.count); } for (unsigned int i=0; i < tag.count; i++) { NetworkID inNetworkID=t->GetNetworkID(); bitStream << inNetworkID; if (deref) { // skip bytes, write data, go back, write number of bits written, reset cursor bitStream.AlignWriteToByteBoundary(); BitSize_t writeOffset1 = bitStream.GetWriteOffset(); BitSize_t bitsUsed1=bitStream.GetNumberOfBitsUsed(); bitStream.Write(bitsUsed1); bitsUsed1=bitStream.GetNumberOfBitsUsed(); DoWrite< typename boost::remove_pointer::type >::type::apply(bitStream,t); BitSize_t writeOffset2 = bitStream.GetWriteOffset(); BitSize_t bitsUsed2=bitStream.GetNumberOfBitsUsed(); bitStream.SetWriteOffset(writeOffset1); bitStream.Write(bitsUsed2-bitsUsed1); bitStream.SetWriteOffset(writeOffset2); } } } }; template struct WriteWithoutNetworkIDNoPtr { static void apply(SLNet::BitStream &bitStream, T& t) { DoWrite< typename boost::remove_pointer::type >::type::apply(bitStream,&t); } }; template struct WriteWithoutNetworkIDPtr { static void apply(SLNet::BitStream &bitStream, T& t) { bool isNull; isNull=(t==0); bitStream.Write(isNull); if (isNull) return; RPC3Tag tag; __RPC3ClearPtr((void*) t, &tag); bool isArray = (tag.flag & RPC3_TAG_FLAG_ARRAY) !=0; bitStream.Write(isArray); if (isArray) { bitStream.WriteCompressed(tag.count); } if (isArray) { for (unsigned int i=0; i < tag.count; i++) DoWrite< typename boost::remove_pointer::type >::type::applyArray(bitStream,t+i); } else { DoWrite< typename boost::remove_pointer::type >::type::apply(bitStream,t); } } }; template struct SerializeCallParameterBranch { typedef typename boost::mpl::if_< typename IsRPC3Ptr::type , DoNothing , WriteWithoutNetworkIDPtr >::type typeCheck1; typedef typename boost::mpl::if_< boost::is_pointer , typeCheck1 , WriteWithoutNetworkIDNoPtr >::type typeCheck2; typedef typename boost::mpl::if_< typename ShouldReadNetworkID::type , WriteWithNetworkIDPtr , typeCheck2 >::type type; }; template struct GetBoundPointer_C { // typedef typename GetBoundPointer_C type; static FunctionPointer GetBoundPointer(Function f) { return FunctionPointer(false, boost::bind( & BoostRPCInvoker::template apply, f, _1, boost::fusion::nil() )); } }; template struct GetBoundPointer_CPP { // typedef typename GetBoundPointer_CPP type; static FunctionPointer GetBoundPointer(Function f) { return FunctionPointer(true, boost::bind( & BoostRPCInvoker_ThisPtr::template apply, f, _1, boost::fusion::nil() )); } }; template FunctionPointer GetBoundPointer(Function f) { return boost::mpl::if_< boost::is_member_function_pointer , GetBoundPointer_CPP , GetBoundPointer_C >::type::GetBoundPointer(f); // return FunctionPointer(true, boost::bind( & BoostRPCInvoker::template apply, f, _1, boost::fusion::nil() ) ); } } } #endif