So I finally dived deeper into adding dynamic array support for Deus Ex.
Getting P_GET macros for FArray/TArray is straight forward, as FString is more
less just a fancy TArray, so one can just go from there.
Code: Select all
#define P_GET_FARRAY(var) FArray var; Stack.Step( Stack.Object, &var );
#define P_GET_FARRAY_OPTX(var,def) FArray var(def); Stack.Step( Stack.Object, &var );
#define P_GET_FARRAY_REF(var) FArray var##T; GPropAddr=0; Stack.Step( Stack.Object, &var##T ); FArray* var = GPropAddr ? (FArray*)GPropAddr:&var##T;
#define P_GET_TARRAY(typ,var) TArray var; Stack.Step( Stack.Object, &var );
#define P_GET_TARRAY_OPTX(typ,var,def)TArray var(def); Stack.Step( Stack.Object, &var );
#define P_GET_TARRAY_REF(typ,var) TArray var##T; GPropAddr=0; Stack.Step( Stack.Object, &var##T ); TArray* var = GPropAddr ? (TArray*)GPropAddr:&var##T;
Note that when using Objects with the P_GET_TARRAY macro you want to the pointer type for
typ.
We can now go ahead and use P_GET_ARRAY_REF macro to implement a native function to retrieve the array count for any type.
Code: Select all
native(3100) static final function int DynIntArrayCount( Array Array );
void UHXObject::execDynIntArrayCount( FFrame& Stack, RESULT_DECL )
{
guard(UHXObject::execDynArrayCount); // Missing "Int" is intentional.
P_GET_TARRAY_REF(INT,Array)
P_FINISH;
*(INT*)Result = Array->Num();
unguardexec;
}
Note that the P_GET_TARRAY_REF may not the best implementation, as one can potentially avoid creating the extra fallback copy, but I'm not yet comfortable enough with the script execution system to just head for this.
You can do macros to speed up the process. Macros in UnrealScript would probably be also helpful here. ^^'
Code: Select all
#define IMPLEMENT_DYNARRAYCOUNT(cls,type) \
void cls::execDyn##type##ArrayCount( FFrame& Stack, RESULT_DECL ) \
{ \
((UHXObject*)this)->UHXObject::execDynIntArrayCount( Stack, Result ); \
}
IMPLEMENT_DYNARRAYCOUNT(UHXObject,Byte);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,Float);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,String);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,Name);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,Object);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,Guid);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,Vector);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,Plane);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,Rotator);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,Coords);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,Scale);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,Color);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,BoundingBox);
IMPLEMENT_DYNARRAYCOUNT(UHXObject,BoundingVolume);
However, a HUGE downside is that the native function needs to be per type, however we can at least get away with just having to supply DynObjectArrayCount as it also works for any subclass, and well the next best way is to declare it for any other type we may want to use. Including structs also declared in Core.Object (Deus Ex):
Code: Select all
native(3100) static final function int DynIntArrayCount( Array Array );
native(3101) static final function int DynByteArrayCount( Array Array );
native(3102) static final function int DynFloatArrayCount( Array Array );
native(3103) static final function int DynStringArrayCount( Array Array );
native(3104) static final function int DynNameArrayCount( Array Array );
native(3105) static final function int DynObjectArrayCount( Array Array );
native(3106) static final function int DynGuidArrayCount( Array Array );
native(3107) static final function int DynVectorArrayCount( Array Array );
native(3108) static final function int DynPlaneArrayCount( Array Array );
native(3109) static final function int DynRotatorArrayCount( Array Array );
native(3110) static final function int DynCoordsArrayCount( Array Array );
native(3111) static final function int DynScaleArrayCount( Array Array );
native(3112) static final function int DynColorArrayCount( Array Array );
native(3113) static final function int DynBoundingBoxArrayCount( Array Array );
native(3114) static final function int DynBoundingVolumeArrayCount( Array Array );
Note that I have not yet checked if whether the baseclass for a struct is enough, say beeing able to use Array with DynVectorArrayCount. I also haven't checked whether the DynByteArrayCount also works with enums.
And now for some good news, we can use the operator system to wrap these all under
a common name:
Code: Select all
static final preoperator int DynArrayCount( Array Array ) { return DynIntArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynFloatArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynStringArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynNameArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynObjectArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynGuidArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynVectorArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynPlaneArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynRotatorArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynCoordsArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynScaleArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynColorArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynBoundingBoxArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return DynBoundingVolumeArrayCount(Array); }
As this is just pure unrealscript, we can also just implement the operators into any other, even pure unrealscript class:
Code: Select all
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynIntArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynFloatArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynStringArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynNameArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynObjectArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynGuidArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynVectorArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynPlaneArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynRotatorArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynCoordsArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynScaleArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynColorArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynBoundingBoxArrayCount (Array); }
static final preoperator int DynArrayCount( Array Array ) { return Class'HXObject'.static.DynBoundingVolumeArrayCount(Array); }
So one could start from there and start to add structs declared in Actor, etc. but the approach is the same. It kinda sucks, but it should actually be good enough for a lot of tasks. I started to use it to specifcy an arbitrary amount of sounds in defaultproperties, to pick a random selected one out of it.
Also, one can always continue and start implementing += operators to add items, etc.
When you use dynamic arrays in native classes, you run into the issues with them not getting exported as TArrayNoInit, so you would end up having to maintain your constructors to deal with them, but you can hook UArrayProperty::ExportCppItem, while solves this issue in an automated way.
Code: Select all
//
// Did always export as TArray, but it needs to be either TArrayNoInit
// inside classes or the E_NoInit constructor needs to be called,
// as otherwise it would empty the array.
//
// Needless to say that manually calling E_NoInit isn't an option.
//
void UArrayPropertySubstitute::ExportCppItem( FOutputDevice& Ar ) const
{
guard(UArrayPropertySubstitute::ExportCppItem);
Ar.Log( (PropertyFlags&CPF_Parm) ? TEXT("TArray") );
unguardobj;
}