For direct access use https://forums.oldunreal.com
It's been quite a while since oldunreal had an overhaul, but we are moving to another server which require some updates and changes. The biggest change is the migration of our old reliable YaBB forum to phpBB. This system expects you to login with your username and old password known from YaBB.
If you experience any problems there is also the usual "password forgotten" function. Don't forget to clear your browser cache!
If you have any further concerns feel free to contact me: Smirftsch@oldunreal.com

[Snippet] Native replication

Post Reply
User avatar
han
Global Moderator
Posts: 686
Joined: Wed Dec 10, 2014 12:38 am

[Snippet] Native replication

Post by han »

In the UT2004 script source were some snippets of C++, which also included the native replication statements. This works in DeusEx, and likely other UE1 games. These are slightly modified compared to UT2004 script (See below).

Code: Select all

/*=============================================================================
	RevNet.h: Revision networking.
	Copyright 2015 Sebastian Kaufel. All Rights Reserved.

	Revision history:
		* Created by Sebastian Kaufel
=============================================================================*/

/*-----------------------------------------------------------------------------
	Replication (based on ut2004scripts3369).
-----------------------------------------------------------------------------*/

inline UBOOL NEQ( BITFIELD A, BITFIELD B, UPackageMap* Map )
{
	return A!=B;
}
inline UBOOL NEQ( BYTE A, BYTE B, UPackageMap* Map )
{
	return A!=B;
}
inline UBOOL NEQ( INT A, INT B, UPackageMap* Map )
{
	return A!=B;
}
inline UBOOL NEQ( FLOAT& A, FLOAT& B, UPackageMap* Map)
{
	return *(INT*)&A!=*(INT*)&B;
}
inline UBOOL NEQ( FVector& A, FVector& B, UPackageMap* Map)
{
	return ((INT*)&A)[0]!=((INT*)&B)[0]
			|| ((INT*)&A)[1]!=((INT*)&B)[1]
			|| ((INT*)&A)[2]!=((INT*)&B)[2];
}
inline UBOOL NEQ( FRotator& A, FRotator& B, UPackageMap* Map)
{
	return A.Pitch!=B.Pitch
			|| A.Yaw  !=B.Yaw
			|| A.Roll !=B.Roll;
}
inline UBOOL NEQ( UObject* A, UObject* B, UPackageMap* Map)
{
	return (Map->CanSerializeObject(A) ? A : NULL)!=B;
}
inline UBOOL NEQ( FName& A, FName B, UPackageMap* Map)
{
	return *(INT*)&A!=*(INT*)&B;
}
inline UBOOL NEQ( FColor& A, FColor& B, UPackageMap* Map)
{
	return *(INT*)&A!=*(INT*)&B;
}
inline UBOOL NEQ( FPlane& A, FPlane& B, UPackageMap* Map)
{
	return ((INT*)&A)[0]!=((INT*)&B)[0]
			|| ((INT*)&A)[1]!=((INT*)&B)[1]
			|| ((INT*)&A)[2]!=((INT*)&B)[2]
			|| ((INT*)&A)[3]!=((INT*)&B)[3];
}
inline UBOOL NEQ( const FString& A, const FString& B, UPackageMap* Map )
{
	return A!=B;
}

#define DOREP(c,v) \
	if( NEQ(v,((A##c*)Recent)->v,Map) ) \
	{ \
		static UProperty* sp##v = FindObjectChecked<UProperty>(A##c::StaticClass(),TEXT(#v)); \
		*Ptr++ = sp##v->RepIndex; \
	}
#define DOREPARRAY(c,v) \
	{static UProperty* sp##v = FindObjectChecked<UProperty>(A##c::StaticClass(),TEXT(#v)); \
	for( INT i=0; i<ARRAY_COUNT(v); i++ ) \
		if( NEQ(v[i],((A##c*)Recent)->v[i],Map) ) \
				*Ptr++ = sp##v->RepIndex+i;}

// Useful to replicate single BlendAnim slots.
#define DOREPARRAYINDEX(c,v,i) \
	{static UProperty* sp##v = FindObjectChecked<UProperty>(A##c::StaticClass(),TEXT(#v)); \
	if( NEQ(v[i],((A##c*)Recent)->v[i],Map) ) \
			*Ptr++ = sp##v->RepIndex+i;}

/*-----------------------------------------------------------------------------
	The End.
-----------------------------------------------------------------------------*/
The cool thing about native replication is, that you can override the whole native replication of the parent classes. In fact you can even disable the script replication. However, for native replication you *still* need a replication index, which gets created when loading the class. Thats on of the reason why there is an unrealscript replication statement in a nativereplication class. The other reason is that, when you replicate stuff to the server, the server switches netroles and runs the uc replication statement to see if he "wants" it. Another thing worth to mention is that native replication is just for variables, *never* for functions.

For nativer replication basically four major functions which are important:

Code: Select all

	// AActor interface.
	INT* GetOptimizedRepList( BYTE* InDefault, FPropertyRetirement* Retire, INT* Ptr, UPackageMap* Map );
	void PreNetReceive();
	void PostNetReceive();
	INT* GetOptimizedRepList( BYTE* InDefault, FPropertyRetirement* Retire, INT* Ptr, UPackageMap* Map );
GetOptimizedRepList() basically is where the main magic happens, and you decide which properties you want to replicate or not. It's not much different compared to the unrealscript replication statements.

Example:

Code: Select all

INT* AHXPlayerPawn::GetOptimizedRepList( BYTE* Recent, FPropertyRetirement* Retire, INT* Ptr, UPackageMap* Map )
{
	guard(AHXPlayerPawn::GetOptimizedRepList)

	Ptr = AActor::GetOptimizedRepList( Recent, Retire, Ptr, Map );

	// Actor Overrides.
	if ( Role==ROLE_Authority )
	{
		if( DrawType==DT_Mesh && ((RemoteRole<=ROLE_SimulatedProxy && (!bNetOwner || !bClientAnim)) || bDemoRecording) )
		{
			// Head Movement.
			DOREPARRAYINDEX(Actor,BlendAnimSequence,3);
			DOREPARRAYINDEX(Actor,SimBlendAnim,3);
			DOREPARRAYINDEX(Actor,BlendAnimMinRate,3);
		}
	}
	
	// [...]


	// Check if native replication keyword is that. That way you can easily toggle by setting the uc nativereplication keyword to toggle between native and script replication.
	if ( StaticClass()->ClassFlags & CLASS_NativeReplication ) 
	{
	}

	return Ptr;
	unguard;
}
So the above snippet basically just adds replication for some blend animation variables, ion luckily left inside the replication statement, but took out (or never implemented) the native one. So the next two suckers are PreNetReceive()/PostNetReceive(). They will be called before and after a property receives a network update. So I could use that in HX to catch the change of the properties and control the animations, and it turned out that network replication for that one blendanimation slot was really helpful.

Code: Select all

FPlane PreSimBlendAnim[4];

// NetReceive.
void AHXPlayerPawn::PreNetReceive()
{
	guard(AHXPlayerPawn::PreNetReceive);

	Super::PreNetReceive();

	// Head Movement only.
	PreSimBlendAnim[3] = SimBlendAnim[3];

	unguard;
}
void AHXPlayerPawn::PostNetReceive()
{
	guard(AHXPlayerPawn::PostNetReceive);

	Super::PostNetReceive();

	// Head Movement only.
	if ( SimBlendAnim[3]!=PreSimBlendAnim[3] )
	{
		BlendAnimFrame[3] = SimBlendAnim[3].X * 0.0001;
		BlendAnimRate[3]  = SimBlendAnim[3].Y * 0.0001;
		BlendTweenRate[3] = SimBlendAnim[3].Z * 0.001;
		BlendAnimLast[3]  = SimBlendAnim[3].W * 0.0001;

		if ( BlendAnimLast[3]<0.0 )
		{
			BlendAnimLast[3] *= -1.0;
			if ( BlendAnimMinRate[3]<0.5 )
				BlendAnimMinRate[3] = 0.5;
		}
	}
	unguard;
}
The forth function, ShouldDoScriptReplication() basically dictates whether script replication should be run at all. This is infamously used by Inventory class to cause some weired behaviour I still have trouble with. Keep in mind, if you disable script replication, it affects *ALL* subclasses, and if they are unreal script only, they have no way to enable it again.
Last edited by han on Fri Nov 06, 2015 2:53 pm, edited 1 time in total.
HX on Mod DB. Revision on Steam. Löffels on Patreon.
Post Reply

Return to “C++ Native Mods for UE1”