CooloffTime

CooloffTime

#Overview

name: CooloffTime

The value of this variable can be defined or overridden in .ini config files. 9 .ini config files referencing this setting variable.

It is referenced in 22 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of CooloffTime is to control the de-escalation of severity levels in various network-related protection systems within Unreal Engine 5. It represents the amount of time, in seconds, that must pass before the current severity state cools off and de-escalates to a lower level.

This setting variable is primarily used in network security and performance-related subsystems, including:

  1. RPC DoS (Denial of Service) Detection
  2. Network Fault Recovery
  3. DDoS (Distributed Denial of Service) Detection
  4. General Network Escalation Management

The value of CooloffTime is typically set in configuration files (e.g., .ini files) and can be customized for different severity states or protection systems.

CooloffTime often interacts with other variables such as:

Developers should be aware of the following when using this variable:

  1. CooloffTime should be less than AutoEscalateTime to prevent conflicting behavior
  2. It directly affects the responsiveness and sensitivity of the protection systems
  3. Setting it too low might cause frequent state changes, while setting it too high could leave the system in a heightened state for too long

Best practices for using CooloffTime include:

  1. Carefully balance it with other timing variables to create a smooth and effective protection system
  2. Test different values in various network conditions to find the optimal setting
  3. Consider the specific needs of your game or application when configuring this value
  4. Regularly review and adjust the value based on real-world performance and security incident data

#Setting Variables

#References In INI files

Location: <Workspace>/Engine/Config/BaseEngine.ini:1672, section: [DDoSDetection.PersistentBurst]

Location: <Workspace>/Engine/Config/BaseEngine.ini:1680, section: [DDoSDetection.DDoS]

Location: <Workspace>/Engine/Config/BaseEngine.ini:1688, section: [DDoSDetection.ExpensiveDDoS]

Location: <Workspace>/Engine/Config/BaseEngine.ini:1695, section: [DDoSDetection.DebilitatingDDoS]

Location: <Workspace>/Engine/Config/BaseEngine.ini:1738, section: [RPCDoSDetection.Hitch]

Location: <Workspace>/Engine/Config/BaseEngine.ini:1746, section: [RPCDoSDetection.Burst]

Location: <Workspace>/Engine/Config/BaseEngine.ini:1759, section: [RPCDoSDetection.PersistentBurst]

Location: <Workspace>/Engine/Config/BaseEngine.ini:1775, section: [RPCDoSDetection.DoS]

Location: <Workspace>/Engine/Config/BaseEngine.ini:1791, section: [RPCDoSDetection.ExpensiveDoS]

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:291

Scope (from outer to inner):

file
function     void FRPCDoSStateConfig::ValidateConfig

Source code excerpt:

						(RPCRepeatLimitPerPeriod > 0 || RPCRepeatLimitMSPerPeriod > 0));

	if (AutoEscalateTime > 0 && AutoEscalateTime < CooloffTime)
	{
		UE_LOG(LogNet, Warning, TEXT("FRPCDoSStateConfig: AutoEscalateTime must be larger than CooloffTime."));
	}
}

int8 FRPCDoSStateConfig::GetHighestTimePeriod() const

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:321

Scope (from outer to inner):

file
function     void FRPCDoSStateConfig::ApplyState

Source code excerpt:

	Target.RPCRepeatLimitMSPerPeriod		= RPCRepeatLimitMSPerPeriod;
	Target.RPCRepeatLimitTimePeriod			= RPCRepeatLimitTimePeriod;
	Target.CooloffTime						= CooloffTime;
	Target.AutoEscalateTime					= AutoEscalateTime;

	Target.ApplyImpliedValues();
}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:451

Scope (from outer to inner):

file
function     void FRPCDoSDetection::InitConfig

Source code excerpt:

						const int8 HighestTimePeriod = CurState.GetHighestTimePeriod();

						HighestHistoryRequirment = FMath::Max(HighestHistoryRequirment, (int32)(CurState.CooloffTime + HighestTimePeriod));
						MaxRPCTrackingPeriod = FMath::Max(MaxRPCTrackingPeriod, HighestTimePeriod);
					}
					else
					{
						UE_LOG(LogNet, Warning, TEXT("RPC DoS Detection failed to load ini section: %s"), *CurSection);
					}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:540

Scope (from outer to inner):

file
function     void FRPCDoSDetection::UpdateSeverity_Private

Source code excerpt:

				FRPCDoSStateConfig& PrevState = DetectionSeverity[NewStateIdx-1];
				FRPCDoSStateConfig& CurState = DetectionSeverity[NewStateIdx];
				int32 CurStateCooloffTime = CurState.CooloffTime;
				const TArray<int8>& CurStateTimePeriods = CurState.GetAllTimePeriods();
				FRPCDoSCounters CurPerPeriodHistory[16] = {};

				check(CounterPerSecHistory.Num() >= CurStateCooloffTime);

				// Count backwards through every second of CounterPerSecHistory, whose starting index wraps around like a circular buffer

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:790

Scope (from outer to inner):

file
function     void FRPCDoSDetection::PreTickDispatch

Source code excerpt:

			double ActiveStateTime = TimeSeconds - LastMetEscalationConditions;

			if (CooloffTime > 0 && ActiveStateTime > CooloffTime)
			{
				TickScope.UpdateSeverity(*this, ERPCDoSSeverityUpdate::Deescalate, ERPCDoSEscalateReason::Deescalate);
			}
			else if (AutoEscalateTime > 0 && ActiveStateTime > AutoEscalateTime)
			{
				TickScope.UpdateSeverity(*this, ERPCDoSSeverityUpdate::AutoEscalate, ERPCDoSEscalateReason::AutoEscalate);

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/Net/RPCDoSDetection.h:239

Scope: file

Source code excerpt:

	/** The amount of time, in seconds, before the current DoS severity category cools off and de-escalates */
	UPROPERTY(config)
	int16 CooloffTime					= -1;

	/** The amount of time, in seconds, spent in the current DoS severity category before it automatically escalates to the next category */
	UPROPERTY(config)
	int16 AutoEscalateTime				= -1;

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/Net/RPCDoSDetection.h:1196

Scope: file

Source code excerpt:

	double LastPerSecQuotaBegin										= 0.0;

	/** Stores enough per second quota history, to allow all DetectionSeverity states to recalculate if their CooloffTime is reached */
	TArray<FRPCDoSCounters> CounterPerSecHistory;

	/** The last written index of CounterPerSecHistory */
	int32 LastCounterPerSecHistoryIdx								= 0;

	/** Counter history which is precalculated/cached from CounterPerSecHistory, for tracking per-period time (up to a maximum of 16s) */

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Connection/EscalationStates.cpp:15

Scope (from outer to inner):

file
function     void FEscalationState::ValidateConfigInternal

Source code excerpt:

	Super::ValidateConfigInternal();

	if (AutoEscalateTime > 0 && AutoEscalateTime < CooloffTime)
	{
		UE_LOG(LogNetCore, Warning, TEXT("FEscalationState: AutoEscalateTime must be larger than CooloffTime."));
	}
}

void FEscalationState::ValidateTimePeriod(int8& Value, const TCHAR* PropertyName, EValidateTime Requirement/*=EValidateTime::Optional*/)

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Connection/EscalationStates.cpp:196

Scope (from outer to inner):

file
namespace    UE
namespace    Net
function     EEscalateResult FEscalationManager::UpdateSeverity

Source code excerpt:

					const TStructOnScope<FEscalationState>& PrevState = EscalationSeverity[NewStateIdx-1];
					const TStructOnScope<FEscalationState>& CurState = EscalationSeverity[NewStateIdx];
					int32 CurStateCooloffTime = CurState->CooloffTime;

					check(CountersPerSecHistory.Num() >= CurStateCooloffTime);

					for (FEscalationCounter& CurCounterAlloc : WorkingPerPeriodAlloc)
					{
						CurCounterAlloc.ResetCounters();

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Connection/EscalationStates.cpp:325

Scope (from outer to inner):

file
namespace    UE
namespace    Net
function     void FEscalationManager::TickRealtime

Source code excerpt:

			{
				double ActiveStateTime = TimeSeconds - LastMetEscalationConditions;
				const double& CooloffTime = State->CooloffTime;
				const double& AutoEscalateTime = State->AutoEscalateTime;
				bool bUpdatedState = false;

				if (CooloffTime > 0 && ActiveStateTime > CooloffTime)
				{
					bUpdatedState = UpdateSeverity(ESeverityUpdate::Deescalate, EEscalateReason::Deescalate) != EEscalateResult::NoChange;
				}

				if (!bUpdatedState && AutoEscalateTime > 0 && ActiveStateTime > AutoEscalateTime)
				{

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Connection/EscalationStates.cpp:423

Scope (from outer to inner):

file
namespace    UE
namespace    Net
function     int32 FEscalationManager::GetHighestHistoryRequirement

Source code excerpt:

			const int8 HighestTimePeriod = CurSeverityConfig->GetHighestTimePeriod();

			ReturnVal = FMath::Max(ReturnVal, (int32)(CurSeverityConfig->CooloffTime + HighestTimePeriod));
		}
	}

	return ReturnVal;
}

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Connection/NetConnectionFaultRecoveryBase.cpp:51

Scope (from outer to inner):

file
function     EInitStateDefaultsResult FNetFaultState::InitConfigDefaultsInternal

Source code excerpt:

		DescalateQuotaFaultsPerPeriod = 6;
		EscalateQuotaTimePeriod = 8;
		CooloffTime = 10;
		AutoEscalateTime = 11;
	}
	else if (SeverityCategory == TEXT("PersistentFault"))
	{
		EscalateQuotaFaultsPerPeriod = 64;
		EscalateQuotaFaultPercentPerPeriod = 50;

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Connection/NetConnectionFaultRecoveryBase.cpp:61

Scope (from outer to inner):

file
function     EInitStateDefaultsResult FNetFaultState::InitConfigDefaultsInternal

Source code excerpt:

		DescalateQuotaFaultPercentPerPeriod = 40;
		EscalateQuotaTimePeriod = 8;
		CooloffTime = 10;
		AutoEscalateTime = 20;
		bLogEscalate = true;
	}
	else if (SeverityCategory == TEXT("DisconnectCountdown"))
	{
		EscalateQuotaFaultPercentPerPeriod = 70;
		DescalateQuotaFaultPercentPerPeriod = 56;
		EscalateQuotaTimePeriod = 16;
		CooloffTime = 10;
		AutoEscalateTime = 60;
		bLogEscalate = true;
	}
	else if (SeverityCategory == TEXT("Disconnect"))
	{
		bCloseConnection = true;

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Misc/DDoSDetection.cpp:140

Scope (from outer to inner):

file
function     void FDDoSDetection::InitConfig

Source code excerpt:

				GConfig->GetInt(*CurSection, TEXT("PacketTimeLimitMSPerFrame"), CurState.PacketTimeLimitMSPerFrame, GEngineIni);
				GConfig->GetInt(*CurSection, TEXT("NetConnPacketTimeLimitMSPerFrame"), CurState.NetConnPacketTimeLimitMSPerFrame, GEngineIni);
				GConfig->GetInt(*CurSection, TEXT("CooloffTime"), CurState.CooloffTime, GEngineIni);

				if (GConfig->GetInt(*CurSection, TEXT("EscalateTimeQuotaMSPerFrame"), EscalateTime32, GEngineIni))
				{
					CurState.EscalateTimeQuotaMSPerFrame = IntCastChecked<int16>(EscalateTime32);
				}

				HighestCooloffTime = FMath::Max(HighestCooloffTime, CurState.CooloffTime);
			}
			else
			{
				UE_LOG(LogNetCore, Warning, TEXT("DDoS detection could not find ini section: %s"), *CurSection);
			}
		}

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Misc/DDoSDetection.cpp:196

Scope (from outer to inner):

file
function     void FDDoSDetection::UpdateSeverity

Source code excerpt:

			{
				FDDoSStateConfig& PrevState = DetectionSeverity[NewState-1];
				int32 CurStateCooloffTime = DetectionSeverity[NewState].CooloffTime;

				check(CounterPerSecHistory.Num() >= CurStateCooloffTime);

				for (int32 SecondsDelta=0; SecondsDelta<CurStateCooloffTime; SecondsDelta++)
				{
					int32 CurIdx = LastCounterPerSecHistoryIdx - SecondsDelta;

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Misc/DDoSDetection.cpp:286

Scope (from outer to inner):

file
function     void FDDoSDetection::PreFrameReceive

Source code excerpt:

		FrameAdjustment = static_cast<float>((HitchFrameCount > 0 && HitchFrameCount > HitchFrameTolerance) ? 1.0 : (double)DeltaTime / ExpectedFrameTime);

		if (ActiveState > 0 && CooloffTime > 0 && (float)(StartFrameRecvTimestamp - LastMetEscalationConditions) > (float)CooloffTime)
		{
			UpdateSeverity(false);
		}

		DetectionSeverity[ActiveState].ApplyAdjustedState(*this, FMath::Max(0.25f, FrameAdjustment));

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Public/Net/Core/Connection/EscalationStates.h:182

Scope: file

Source code excerpt:

	/** The amount of time, in seconds, before the current severity state cools off and de-escalates */
	UPROPERTY(config)
	int16 CooloffTime				= -1;

	/** The amount of time, in seconds, spent in the current severity state before it automatically escalates to the next state */
	UPROPERTY(config)
	int16 AutoEscalateTime			= -1;

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Public/Net/Core/Connection/EscalationStates.h:584

Scope (from outer to inner):

file
namespace    UE
namespace    Net
class        class FEscalationManager

Source code excerpt:

	double LastPerSecQuotaBegin																	= 0.0;

	/** Stores enough per second quota history, to allow all EscalationSeverity states to recalculate if their CooloffTime is reached */
	TArray<TArrayView<FEscalationCounter>> CountersPerSecHistory;

	/** Allocation for the above array, to reduce indirection */
	TArray<FEscalationCounter> CountersPerSecHistoryAlloc;

	/** The last written index of CountersPerSecHistory */

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Public/Net/Core/Misc/DDoSDetection.h:89

Scope: file

Source code excerpt:


	/** The amount of time, in seconds, before the current DDoS severity category cools off and de-escalates */
	int32 CooloffTime;


	FDDoSState()
		: bSendEscalateAnalytics(true)
		, EscalateQuotaPacketsPerSec(-1)
		, EscalateQuotaDisconnPacketsPerSec(-1)

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Public/Net/Core/Misc/DDoSDetection.h:101

Scope (from outer to inner):

file
function     FDDoSState

Source code excerpt:

		, PacketTimeLimitMSPerFrame(-1)
		, NetConnPacketTimeLimitMSPerFrame(-1)
		, CooloffTime(-1)
	{
	}

	/**
	 * Whether or not the specified counters and time passed has hit any of the quota's
	 *

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Public/Net/Core/Misc/DDoSDetection.h:147

Scope (from outer to inner):

file
function     void ApplyState

Source code excerpt:

		Target.PacketTimeLimitMSPerFrame			= PacketTimeLimitMSPerFrame;
		Target.NetConnPacketTimeLimitMSPerFrame		= NetConnPacketTimeLimitMSPerFrame;
		Target.CooloffTime							= CooloffTime;
	}

	/**
	 * Applies only the per-frame adjusted state (based on expected vs actual framerate), to active DDoS protection.
	 * ApplyState should be called, first.
	 */

#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Public/Net/Core/Misc/DDoSDetection.h:342

Scope (from outer to inner):

file
class        class FDDoSDetection : protected FDDoSPacketCounters, protected FDDoSState

Source code excerpt:

	double LastPerSecQuotaBegin;

	/** Stores enough per second quota history, to allow all DetectionSeverity states to recalculate if their CooloffTime is reached */
	TArray<FDDoSPacketCounters> CounterPerSecHistory;

	/** The last written index of CounterPerSecHistory */
	int32 LastCounterPerSecHistoryIdx;