В стандартной сетевой архитектуре Unreal Engine, сервер проверяет, изменение значения у реплицированной переменной Actor класса, и в случае отличия, значение синхронизируется между сервером и клиентом. Когда объем данных для синхронизации не больших размеров, особых проблем с производительностью нет.

Однако, игра часто может состоять из огромного множества Актеров и переменных, которые необходимо реплицировать одному или нескольким клиентам, и это уже может стать проблемным местом.

UE уже предоставляет такие функции как: NetUpdateFrequency, NetCullDistanceSquared и т.д. Основная задача которых состоит в том, что бы устранить из общей картины репликации, как можно больше актеров не нуждающихся в постоянно синхронизации данных.

PushModel, пока что экспериментальная фича, которая может позволить разработчикам активно отмечать необходимость синхронизации свойства, для этого предоставляется несколько макросов:

#define MARK_PROPERTY_DIRTY(Object, Property) 
#define MARK_PROPERTY_DIRTY_STATIC_ARRAY_INDEX(Object, RepIndex, ArrayIndex) 
#define MARK_PROPERTY_DIRTY_STATIC_ARRAY(Object, RepIndex, ArrayIndex) 

#define MARK_PROPERTY_DIRTY_FROM_NAME(ClassName, PropertyName, Object) 
#define MARK_PROPERTY_DIRTY_FROM_NAME_STATIC_ARRAY_INDEX(ClassName, PropertyName, ArrayIndex, Object) 
#define MARK_PROPERTY_DIRTY_FROM_NAME_STATIC_ARRAY(ClassName, PropertyName, ArrayIndex, Object) 

Эти макросы устанавливают определенной переменной метку необходимости синхронизации, а сервер в свою очередь убирает необходимость постоянно проверять изменение значения.

Как настроить PushModel. В первую очередь в Build.cs, необходимо добавить, что б исключить проблемы компиляции

PublicDependencyModuleNames.AddRange(new string[]  {"NetCore"});

Во вторых добавить в DefaultEngine.ini в папке config добавить:

[SystemSettings]
net.IsPushModelEnabled=1

В третьих отметить необходимые перменные UPROPERTY(Replicated) или (ReplicatedUsing)

В четвертых, в функции GetLifetimeReplicatedProps, нам необходимо использовать DOREPLIFETIME_WITH_PARAMS или DOREPLIFETIME_WITH_PARAMS_FAST, вместо DOREPLIFETIME или DOREPLIFETIME_CONDITION, что бы отметить переменную для репликации. Пример:

void ASomeActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
  Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	FDoRepLifetimeParams SharedParams;
	SharedParams.bIsPushBased = true;
	SharedParams.Condition = COND_OwnerOnly;
	DOREPLIFETIME_WITH_PARAMS_FAST(ASomeActor, SomeVar, SharedParams);
}

FDoRepLifetimeParams имеет в себе 3 прекрасный параметра:

struct ENGINE_API FDoRepLifetimeParams
{
	/** Replication Condition. The property will only be replicated to connections where this condition is met. */
	ELifetimeCondition Condition = COND_None;
	/**  * RepNotify Condition. The property will only trigger a RepNotify if this condition is met, and has been  * properly set up to handle RepNotifies.  */
	ELifetimeRepNotifyCondition RepNotifyCondition = REPNOTIFY_OnChanged;
	/** Whether or not this property uses Push Model. See PushModel.h */
	bool bIsPushBased = false;
};

В данной структуре bIsPushBased указывает, следует ли включать данную переменную в PushModel. В одном и том же Актере, несколько разных переменных, могут использовать данный функционал. Для того что бы оповестить сервер о необходимости репликации, необходимо вызвать макрос:

MARK_PROPERTY_DIRTY_FROM_NAME(ASomeActor, SomeVar, this);
SomeVar = SomeValue;

Тем самым, сервер синхронизирует значение переменной.

Заключение

Использование PushModel может здорово сократить количество данных, которые постоянно обрабатываются на серверной стороне, тем самым уменьшить количество потребляемых ресурсов.

Подробные примеры использования можно посмотреть в классе APlayerState в движке.

Всем спасибо за внимание и хорошего времяпровождения.