Files
SLikeNet/DependentExtensions/RPC3/RPC3_Boost.h
2025-11-24 14:19:51 +05:30

713 lines
18 KiB
C++

/*
* 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-2020, 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<Function,Sequence,N,CBI,RandomAccess>' : 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 <boost/fusion/container/generation/make_vector.hpp>
#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<bool, boost::function<InvokeResultCodes (InvokeArgs)> > 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 <class templateType>
inline const templateType& Deref(const templateType & t) {
__RPC3_Tag_AddHead(RPC3Tag((void*)t,1,RPC3_TAG_FLAG_DEREF));
return t;
}
template <class templateType>
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 <typename T>
struct ReadPtr
{
template <typename T2>
static inline void applyArray(SLNet::BitStream &bitStream, T2 *t) {bitStream >> (*t);}
template <typename T2>
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 a null pointer" && (nullptr != 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<T*, SLNet::BitStream*>,
ReadBitstream,
ReadPtr >::type type;
};
template< typename T >
struct ReadWithoutNetworkIDNoPtr
{
static InvokeResultCodes apply(InvokeArgs &args, T &t)
{
// printf("ReadWithoutNetworkIDNoPtr\n");
DoRead< typename boost::remove_pointer<T>::type >::type::apply(* (args.bitStream),&t);
return IRC_SUCCESS;
}
// typedef boost::mpl::false_ Cleanup;
template< typename T2 >
static void Cleanup(T2 &t)
{
// unused parameters
(void)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<T>::type >::type::apply(* (args.bitStream),t);
}
else
{
// Skip data!
args.bitStream->IgnoreBits(bitsUsed);
}
}
}
return IRC_SUCCESS;
}
template< typename T2 >
static void Cleanup(T2 &t)
{
// unused parameters
(void)t;
}
};
template< typename T >
struct ReadWithoutNetworkIDPtr
{
template <typename T2>
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<T>::type >::type::applyArray(* (args.bitStream),t+i);
}
}
else
{
DoRead< typename boost::remove_pointer<T>::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)
{
// unused parameters
(void)t;
}
};
/*
template< typename T >
struct ReadWithNetworkID
{
typedef typename boost::mpl::if_<
boost::is_pointer<T>
, typename ReadWithNetworkIDPtr<T> // true
, typename ReadWithNetworkIDNoPtr<T>
>::type type;
};
*/
template< typename T >
struct ReadWithoutNetworkID
{
typedef typename boost::mpl::if_<
boost::is_pointer<T>
, ReadWithoutNetworkIDPtr<T> // true
, ReadWithoutNetworkIDNoPtr<T>
>::type type;
};
template< typename T >
struct identity
{
typedef T type;
};
template< typename T >
struct IsRPC3Ptr
{
typedef typename boost::mpl::if_<
boost::is_convertible<T,RPC3*>,
boost::mpl::true_,
boost::mpl::false_>::type type;
};
template< typename T >
struct ShouldReadNetworkID
{
/*
typedef typename boost::mpl::if_<
boost::is_pointer<T>,
typename identity<T>::type,
boost::add_pointer<T>>::type typeWithPtr;
typedef typename boost::mpl::if_<
boost::is_convertible<typeWithPtr,NetworkIDObject*>,
boost::mpl::true_,
boost::mpl::false_>::type type;
*/
typedef typename boost::mpl::if_<
boost::is_convertible<T,NetworkIDObject*>,
boost::mpl::true_,
boost::mpl::false_>::type type;
};
template< typename T >
struct GetReadFunction
{
/*
typedef typename boost::mpl::if_<
typename ShouldReadNetworkID<T>::type
, typename ReadWithNetworkID<T>::type
, typename ReadWithoutNetworkID<T>::type
>::type type;
*/
typedef typename boost::mpl::if_<
typename ShouldReadNetworkID<T>::type
, ReadWithNetworkIDPtr<T>
, typename ReadWithoutNetworkID<T>::type
>::type type;
};
template< typename T >
struct ProcessArgType
{
typedef typename boost::mpl::if_<
typename IsRPC3Ptr<T>::type
, SetRPC3Ptr<T>
, typename GetReadFunction<T>::type
>::type type;
};
template< typename Function
, class From = typename boost::mpl::begin< boost::function_types::parameter_types<Function> >::type
, class To = typename boost::mpl::end< boost::function_types::parameter_types<Function> >::type
>
struct BoostRPCInvoker
{
// add an argument to a Fusion cons-list for each parameter type
template<typename Args>
static inline
InvokeResultCodes apply(Function func, InvokeArgs &functionArgs, Args const &args)
{
typedef typename boost::mpl::deref<From>::type arg_type;
typedef typename boost::mpl::next<From>::type next_iter_type;
typedef typename boost::remove_reference<arg_type>::type arg_type_no_ref;
arg_type_no_ref argType;
ProcessArgType< arg_type_no_ref >::type::apply(functionArgs, argType);
InvokeResultCodes irc = BoostRPCInvoker<Function, next_iter_type, To>::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<Function> >::type
, class To = typename boost::mpl::end< boost::function_types::parameter_types<Function> >::type
>
struct BoostRPCInvoker_ThisPtr
{
// add an argument to a Fusion cons-list for each parameter type
template<typename Args>
static inline
InvokeResultCodes apply(Function func, InvokeArgs &functionArgs, Args const &args)
{
typedef typename boost::mpl::deref<From>::type arg_type;
typedef typename boost::mpl::next<From>::type next_iter_type;
arg_type argType = (arg_type) *(functionArgs.thisPtr);
return BoostRPCInvoker<Function, next_iter_type, To>::apply
( func, functionArgs, boost::fusion::push_back(args, boost::ref(argType) ) );
}
};
template<typename Function, class To>
struct BoostRPCInvoker<Function,To,To>
{
// the argument list is complete, now call the function
template<typename Args>
static inline
InvokeResultCodes apply(Function func, InvokeArgs&, Args const &args)
{
boost::fusion::invoke(func,args);
return IRC_SUCCESS;
}
};
template <typename T>
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 <typename T>
struct WritePtr
{
template <typename T2>
static inline void applyArray(SLNet::BitStream &bitStream, T2 *t) {bitStream << (*t);}
template <typename T2>
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<T*, SLNet::BitStream*>,
WriteBitstream,
WritePtr >::type type;
};
template <typename T>
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<T>::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 <typename T>
struct WriteWithoutNetworkIDNoPtr
{
static void apply(SLNet::BitStream &bitStream, T& t)
{
DoWrite< typename boost::remove_pointer<T>::type >::type::apply(bitStream,&t);
}
};
template <typename T>
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<T>::type >::type::applyArray(bitStream,t+i);
}
else
{
DoWrite< typename boost::remove_pointer<T>::type >::type::apply(bitStream,t);
}
}
};
template <typename T>
struct SerializeCallParameterBranch
{
typedef typename boost::mpl::if_<
typename IsRPC3Ptr<T>::type
, DoNothing<T>
, WriteWithoutNetworkIDPtr<T>
>::type typeCheck1;
typedef typename boost::mpl::if_<
boost::is_pointer<T>
, typeCheck1
, WriteWithoutNetworkIDNoPtr<T>
>::type typeCheck2;
typedef typename boost::mpl::if_<
typename ShouldReadNetworkID<T>::type
, WriteWithNetworkIDPtr<T>
, typeCheck2
>::type type;
};
template<typename Function>
struct GetBoundPointer_C
{
// typedef typename GetBoundPointer_C type;
static FunctionPointer GetBoundPointer(Function f)
{
return FunctionPointer(false, boost::bind( & BoostRPCInvoker<Function>::template apply<boost::fusion::nil>, f, _1, boost::fusion::nil() ));
}
};
template<typename Function>
struct GetBoundPointer_CPP
{
// typedef typename GetBoundPointer_CPP type;
static FunctionPointer GetBoundPointer(Function f)
{
return FunctionPointer(true, boost::bind( & BoostRPCInvoker_ThisPtr<Function>::template apply<boost::fusion::nil>, f, _1, boost::fusion::nil() ));
}
};
template<typename Function>
FunctionPointer GetBoundPointer(Function f)
{
return boost::mpl::if_<
boost::is_member_function_pointer<Function>
, GetBoundPointer_CPP<Function>
, GetBoundPointer_C<Function>
>::type::GetBoundPointer(f);
// return FunctionPointer(true, boost::bind( & BoostRPCInvoker<Function>::template apply<boost::fusion::nil>, f, _1, boost::fusion::nil() ) );
}
}
}
#endif