logo
Main

Forums

Downloads

Unreal-Netiquette

Donate for Oldunreal:
Donate

borderline

Links to our wiki:
Wiki

Walkthrough

Links

Tutorials

Unreal Reference

Usermaps

borderline

Contact us:
Submit News
Page Index Toggle Pages: 1 Send TopicPrint
Normal Topic Issue #68. ClientUpdatePosition doesn't handle dodge moves correctly (Read 210 times)
Masterkent
Developer Team
Offline



Posts: 1279
Location: Russia
Joined: Apr 5th, 2013
Gender: Male
Issue #68. ClientUpdatePosition doesn't handle dodge moves correctly
Jan 3rd, 2019 at 10:46am
Print Post  
There are two major issues with reproducing dodge moves in online game when ClientUpdatePosition is called:

Issue #68.1. MoveAutonomous doesn't use relevant player rotation to evaluate direction of dodge moves.

MoveAutonomous evaluates all dodge moves based on the most recent player's Rotation instead of the player's Rotation that would match the corresponding TimeStamp in CurrentMove. This causes very noticeable errors in client-side prediction when playing with ping above 100. For a moment, you move in a wrong direction and then your location is adjusted by the next call to ClientUpdatePosition. If you ever used diagonal dodges with high ping, you probably know how ugly this looks:
https://www.youtube.com/watch?v=YnK8Z-TA5a0

Issue #68.2. MoveAutonomous may erroneously suppress further dodge moves.

That is, you double-tap a movement button and simply don't get the expected dodge move. This may happen for two reasons:

1) Your first press may be "forgotten", because it's stored in DodgeDir and ClientUpdatePosition may change DodgeDir to DODGE_Done when handling your previous landing (this function doesn't care about restoring the original value of DodgeDir).

2) The client may erroneously decide that you're trying to initiate a new dodge move too quickly since your last dodge move, so the expected dodge move is rejected as premature. This happens when MoveAutonomous resets DodgeClickTimer (in Landed).

Suggested resolution:

Change class SavedMove as indicated below:

Code
Select All
-	// also stores info in Acceleration attribute
+	// also stores info in Acceleration, Rotation, and DodgeDir attributes
	var SavedMove NextMove;		// Next move in linked list.
	var float TimeStamp;		// Time of this move.
	var float Delta;			// Distance moved.
	var EDodgeDir DodgeMove;	// Dodge info.
+	var float	DodgeClickTimer; // Extra movement info, valid only if ExtraInfoTimeStamp == TimeStamp
	var bool	bRun;
	var bool	bDuck;
	var bool	bPressedJump;
	var bool	bSent;			// Whether sent.
	var bool	bIsReducedCrouch;
	var class<CustomPlayerStateInfo> CustomPlayerState;
+	var float	ExtraInfoTimeStamp; // is used for validation of extra movement info (Rotation, DodgeDir, DodgeClickTimer)

final function Clear()
{
	TimeStamp = 0;
	Delta = 0;
	bSent = false;
	DodgeMove = DODGE_None;
	Acceleration = vect(0,0,0);
	bIsReducedCrouch = false;
+	ExtraInfoTimeStamp = 0;
} 


In class PlayerPawn:

- change function ReplicateMove as indicated below:

Code
Select All
	// Set this move's data.
	NewMove.DodgeMove = DodgeMove;
+	NewMove.DodgeDir = DodgeDir;
+	NewMove.DodgeClickTimer = DodgeClickTimer;
	NewMove.TimeStamp = Level.TimeSeconds;
	NewMove.bRun = (bRun > 0);
	NewMove.bDuck = (bDuck > 0);
	NewMove.bIsReducedCrouch = bIsReducedCrouch;
	NewMove.bPressedJump = bPressedJump;
+	NewMove.SetRotation(Rotation);
	if (CustomPlayerStateInfo != none && IsInState('CustomPlayerState'))
		NewMove.CustomPlayerState = CustomPlayerStateInfo.Class;
	else
		NewMove.CustomPlayerState = none;
+
+	NewMove.ExtraInfoTimeStamp = NewMove.TimeStamp; 


- change function ClientAdjustState as indicated below:

Code
Select All
function ClientAdjustPosition
(
	float TimeStamp,
	name newState,
	EPhysics newPhysics,
	float NewLocX,
	float NewLocY,
	float NewLocZ,
	float NewVelX,
	float NewVelY,
	float NewVelZ,
	Actor NewBase
)
{
	local Decoration Carried;
	local vector OldLoc, NewLocation;

	if ( CurrentTimeStamp > TimeStamp )
		return;
	CurrentTimeStamp = TimeStamp;

	NewLocation.X = NewLocX;
	NewLocation.Y = NewLocY;
	NewLocation.Z = NewLocZ;
	Velocity.X = NewVelX;
	Velocity.Y = NewVelY;
	Velocity.Z = NewVelZ;

	SetBase(NewBase);
	if ( Mover(NewBase) != None )
		NewLocation += NewBase.Location;

	//log("Client "$Role$" adjust "$self$" stamp "$TimeStamp$" location "$Location$" new location "$NewLocation);
	Carried = CarriedDecoration;
	OldLoc = Location;
	SetLocation(NewLocation);

	if ( Carried != None )
	{
		CarriedDecoration = Carried;
		CarriedDecoration.SetLocation(NewLocation + CarriedDecoration.Location - OldLoc);
		CarriedDecoration.SetPhysics(PHYS_None);
		CarriedDecoration.SetBase(self);
	}
	SetPhysics(newPhysics);
	//FIXME - don't do this state update if client dead???
	ClientAdjustState(newState);

	bUpdatePosition = true;
} 


Code
Select All
final function ClientAdjustState(name NewState)
{
	local SavedMove CurrentMove;

	if (!IsInState(NewState))
		GotoState(NewState);

	CurrentMove = SavedMoves;
	while (CurrentMove != none && CurrentMove.TimeStamp < CurrentTimeStamp)
		CurrentMove = CurrentMove.NextMove;
	if (CurrentMove == none)
		return;

+	if (CurrentMove.ExtraInfoTimeStamp == CurrentMove.TimeStamp)
+	{
+		DodgeDir = CurrentMove.DodgeDir;
+		DodgeClickTimer = CurrentMove.DodgeClickTimer;
+	}
+
	if (CurrentMove.CustomPlayerState != none && IsInState('CustomPlayerState'))
		GotoCustomPlayerState(CurrentMove.CustomPlayerState);

	if (SupportsRealCrouching())
	{
		bIsReducedCrouch = CurrentMove.bIsReducedCrouch;
		bIsCrouching = bIsReducedCrouch && IsInState('PlayerWalking');
		RealCrouchInfo.ClientAdjustCrouch();
	}
} 


- change function ClientUpdatePosition as indicated below:

Code
Select All
	while ( CurrentMove != None )
	{
		if ( CurrentMove.TimeStamp <= CurrentTimeStamp )
		{
			SavedMoves = CurrentMove.NextMove;
			CurrentMove.NextMove = FreeMoves;
			FreeMoves = CurrentMove;
			FreeMoves.Clear();
			CurrentMove = SavedMoves;
		}
		else
		{
+			if (CurrentMove.ExtraInfoTimeStamp == CurrentMove.TimeStamp) // making sure that the extra info is actual
+			{
+				SetRotation(CurrentMove.Rotation);
+				PlayerDodgeAutonomous(CurrentMove);
+			}
			MoveAutonomous(CurrentMove.Delta, CurrentMove.bRun, CurrentMove.bDuck, CurrentMove.bPressedJump, CurrentMove.DodgeMove, CurrentMove.Acceleration, rot(0,0,0));
			CurrentMove = CurrentMove.NextMove;
		}
	} 


- add new function PlayerDodgeAutonomous:

Code
Select All
// 227j client-side reproducing of dodge moves in online game:
// this function is used to evaluate relevant values of DodgeDir and DodgeClickTimer in ClientUpdatePosition
final function PlayerDodgeAutonomous(SavedMove CurrentMove)
{
	if (DodgeClickTime <= 0)
		return;

	if (DodgeDir < DODGE_Active &&
		CurrentMove.DodgeDir != DODGE_None && CurrentMove.DodgeDir < DODGE_Active)
	{
		if (DodgeDir != CurrentMove.DodgeDir)
			DodgeClickTimer = DodgeClickTime + 0.5 * CurrentMove.Delta;
		DodgeDir = CurrentMove.DodgeDir;
	}

	if (DodgeDir == DODGE_Active && Physics == PHYS_Walking)
	{
		// force dodge completion in case if PHYS_Walking was set without calling Landed
		DodgeDir = DODGE_Done;
		DodgeClickTimer = 0;
	}

	if (DodgeDir == DODGE_Done)
	{
		DodgeClickTimer -= CurrentMove.Delta;
		if (DodgeClickTimer < -0.35)
		{
			DodgeDir = DODGE_None;
			DodgeClickTimer = DodgeClickTime;
		}
	}
	else if (DodgeDir != DODGE_None && DodgeDir != DODGE_Active)
	{
		DodgeClickTimer -= CurrentMove.Delta;
		if (DodgeClickTimer < 0)
		{
			DodgeDir = DODGE_None;
			DodgeClickTimer = DodgeClickTime;
		}
	}
} 


- change function PlayerWalking.PlayerMove as indicated below:

Code
Select All
			if ( DodgeDir < DODGE_Active )
			{
				OldDodge = DodgeDir;
				DodgeDir = DODGE_None;
				if (bEdgeForward && bWasForward)
					DodgeDir = DODGE_Forward;
				if (bEdgeBack && bWasBack)
					DodgeDir = DODGE_Back;
				if (bEdgeLeft && bWasLeft)
					DodgeDir = DODGE_Left;
				if (bEdgeRight && bWasRight)
					DodgeDir = DODGE_Right;
				if ( DodgeDir == DODGE_None)
					DodgeDir = OldDodge;
				else if ( DodgeDir != OldDodge )
					DodgeClickTimer = DodgeClickTime + 0.5 * DeltaTime;
				else
					DodgeMove = DodgeDir;
			}

+			if (DodgeDir == DODGE_Active && Physics == PHYS_Walking)
+			{
+				// force dodge completion in case if PHYS_Walking was set without calling Landed
+				DodgeDir = DODGE_Done;
+				DodgeClickTimer = 0;
+			}

			if (DodgeDir == DODGE_Done)
			{
				DodgeClickTimer -= DeltaTime;
				if (DodgeClickTimer < -0.35)
				{
					DodgeDir = DODGE_None;
					DodgeClickTimer = DodgeClickTime;
				}
			} 


ExtraInfoTimeStamp is used for backward compatibility with possible modded PlayerPawns that don't call ReplicateMove. The values of Rotation, DodgeDir, and DodgeClickTimer in a SavedMove object are presumed to be actual only when ExtraInfoTimeStamp == TimeStamp.

Before fixing: https://www.youtube.com/watch?v=YnK8Z-TA5a0
After fixing: https://www.youtube.com/watch?v=IXCyu5eW-Lg
  
Back to top
 
IP Logged
 
Page Index Toggle Pages: 1
Send TopicPrint
Bookmarks: del.icio.us Digg Facebook Google Google+ Linked in reddit StumbleUpon Twitter Yahoo