TeamSystem框架

基本架构

支持基本的加入、退出 队伍,以及维护队伍的基本数据(TeamIDMemberUIDs

提供继承扩展,维护自定义业务逻辑与队伍数据;

对于挂载组件:由 Manager 级别的 TeamManager ,与 Player 身上的 TeamComponent 组成;

以及队伍数据同步,自定义不同方式的数据同步;

classDiagram

    UTeamManager..>UTeamBase
    UTeamManager..>UTeamSyncDataBase
    class UTeamManager {
        SubSystemCollections : FGameSubSystemCollection~UTeamCommonSubSystemBase~
        Teams : TMap~uint64|UTeamBase*~ 
        TeamSyncDatas : TArray~UTeamSyncDataBase*~
        
        CreateTeam(TeamID)
        DestoryTeam(TeamID)
        GetTeam(TeamID)
        
        JoinTeam(TeamID, PlayerUID)
        LeaveTeam(PlayerUID)
    }
    
    UTeamComponent..>UTeamSyncDataBase
	class UTeamComponent {
        TeamID : uint64
        SubSystemCollection : FGameSubSystemCollection~UTeamPlayerSubSystemBase~
        UpdateTeamID()
        CreateSyncData()
        DestroySyncData()
    }
   
    
    class UTeamBase {
    	TeamID : uint64
    	MemberUIDs : TArray~uint64~
    	OnDataDirty
    	
    	Init(TeamID)
    	Uninit()
    	AddMember(PlayerUID)
    	RemoveMember(PlayerUID)
    	
    	MarkDirty()
    }
    
    class UTeamSyncDataBase {
    	BoundTeam : TWeakObjectPtr~UTeamBase~
    	TeamID : uint64
    	MemberUIDs : TArray~uint64~
    	
    	BindTeam(InTeam)
    	Unbind()
    	
    	OnDataDirty()
    	CollectSyncData(InTeam)
    }

TeamManager

持有 Teams 保存队伍的原始指针,提供 CreateTeamDestroyTeam 方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// TeamManager.cpp

#pragma region Team

UTeamBase* UTeamManager::GetTeam(uint64 TeamID)
{
return Teams.FindRef(TeamID);
}

const TMap<uint64, UTeamBase*>& UTeamManager::GetTeams() const
{
return Teams;
}

// ----------

uint64 UTeamManager::GetHashValue(uint64 Value)
{
uint64 HashValue = HashCombine( GetHashCode(), GetTypeHash(Value)));
LogD(TEXT("UTeamManager::GetHashValue, HashValue=%llu, [Value=%llu | HashCode=%llu]"), HashValue, Value, GetHashCode());
return HashValue;
}

UTeamBase* UTeamManager::GetOrCreateTeam(uint64 TeamID)
{
if (auto Team = GetTeam(TeamID); !IsValid(Team))
{
CreateTeam(TeamID);
}

return GetTeam(TeamID);
}

UTeamBase* UTeamManager::CreateTeam(uint64 TeamID)
{
if (GetTeam(TeamID) != nullptr)
{
LogW(TEXT("UTeamManager::CreateTeam, Team is existed! TeamID=%llu"), TeamID);
return nullptr;
}

LogD(TEXT("UTeamManager::CreateTeam, Begin, TeamID=%llu"), TeamID);

// CreateTeam
auto TeamClass = GetTeamClass();
if (!IsValid(TeamClass) || !TeamClass->IsChildOf(UTeamBase::StaticClass()))
{
LogE(TEXT("UTeamManager::CreateTeam, TeamClass is invalid! TeamID=%llu"), TeamID);
return nullptr;
}

UTeamBase* NewTeam = NewObject<UTeamBase>(this, GetTeamClass());
if (!IsValid(NewTeam))
{
LogE(TEXT("UTeamManager::CreateTeam, NewTeam is invalid! TeamID=%llu"), TeamID);
return nullptr;
}


LogD(TEXT("UTeamManager::CreateTeam, CreateTeam, TeamID=%llu"), TeamID);
NewTeam->Init(TeamID);
Teams.Add( TeamID, NewTeam );

// SyncData
{
auto NewTeamSyncData = UTeamUtils::CreateTeamSyncData( this, NewTeam, GetTeamSyncDataClass() );
if (IsValid(NewTeamSyncData))
{
LogD(TEXT("UTeamManager::CreateTeam, CreateTeamSyncData, TeamID=%llu"), TeamID);
TeamSyncDatas.Add( NewTeamSyncData );
MARK_PROPERTY_DIRTY_FROM_NAME(UTeamManager, TeamSyncDatas, this);
}
}

return NewTeam;
}

void UTeamManager::DestoryTeam(uint64 TeamID)
{
auto Team = GetTeam(TeamID);
if (!IsValid(Team)) return;
LogD(TEXT("UTeamManager::DestoryTeam, TeamID=%llu"), TeamID);

// SyncData
for (auto Iter = TeamSyncDatas.CreateIterator(); Iter; ++Iter)
{
auto SyncData = *Iter;
if (!IsValid(SyncData) || SyncData->GetTeamID() == TeamID)
{
UTeamUtils::DestoryTeamSyncData(SyncData);
Iter.RemoveCurrent();
}
}
MARK_PROPERTY_DIRTY_FROM_NAME(UTeamManager, TeamSyncDatas, this);

Team->Uninit();
Teams.Remove(TeamID);
}

#pragma endregion Team

提供 PlayerJoinTeamLeaveTeam

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// TeamManager.cpp

#pragma region Join/Leave

void UTeamManager::JoinTeam(uint64 TeamID, uint64 PlayerUID, bool bNeedHash)
{
if (!IsStandaloneOrDS(this)) return;

if (TeamID == 0)
{
LogD(TEXT("UTeamManager::JoinTeam, TeamID is 0! PlayerUID=%llu, TeamID=%llu, bNeedHash=%d"), PlayerUID, TeamID, bNeedHash);
return;
}

auto TeamComponent = UTeamUtils::GetComponentByUID(GetWorld(), PlayerUID);
if (!IsValid(TeamComponent)) return;

LogD(TEXT("UTeamManager::JoinTeam, PlayerUID=%llu, TeamID=%llu, bNeedHash=%d"), PlayerUID, TeamID, bNeedHash);
if (bNeedHash)
{
TeamID = GetHashValue(TeamID);
LogD(TEXT("UTeamManager::JoinTeam, ProcessID, PlayerUID=%llu, HashTeamID=%llu"), PlayerUID, TeamID);
}

// 如果玩家已经存在于某个队伍则无法再次加入
if (auto ExistTeam = GetTeam(TeamComponent->GetTeamID()); IsValid(ExistTeam))
{
LogE(TEXT("UTeamManager::JoinTeam, Player already exist Team! PlayerUID=%llu, ExistTeamID=%llu"), PlayerUID, ExistTeam->GetTeamID());
return;
}

auto Team = GetOrCreateTeam(TeamID);
if (!IsValid(Team))
{
LogE(TEXT("UTeamManager::JoinTeam, Team is invalid! PlayerUID=%llu, TeamID=%llu"), PlayerUID, TeamID);
return;
}

LogD(TEXT("UTeamManager::JoinTeam, Begin, PlayerUID=%llu, TeamID=%llu"), PlayerUID, TeamID);
TeamComponent->UpdateTeamID(Team->GetTeamID());
TeamComponent->CreateSyncData(Team);

Team->AddMember(PlayerUID);
}

void UTeamManager::LeaveTeam(uint64 PlayerUID)
{
if (!IsStandaloneOrDS(this)) return;

auto TeamComponent = UTeamUtils::GetComponentByUID(GetWorld(), PlayerUID);
if (!IsValid(TeamComponent)) return;

auto Team = GetTeam(TeamComponent->GetTeamID());
if (!IsValid(Team))
{
LogE(TEXT("UTeamManager::LeaveTeam, Player isn't in any team! PlayerUID=%llu"), PlayerUID);
return;
}

uint64 TeamID = Team->GetTeamID();
LogD(TEXT("UTeamManager::LeaveTeam, Begin, PlayerUID=%llu, TeamID=%llu"), PlayerUID, TeamID);
Team->RemoveMember(PlayerUID);
TeamComponent->UpdateTeamID(0);
TeamComponent->DestroySyncData();

if (Team->GetMemberUIDs().Num() == 0)
{
DestoryTeam( TeamID );
}
}

#pragma endregion Join/Leave

持有 TeamCommonSubSystem,支持业务扩展拆分为若干个子系统,参考:[UE]GameSubSystem 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// TeamManager.cpp

void UTeamManager::RegisterSubSystems()
{
if (!IsStandaloneOrDS(this)) return;
LogD(TEXT("UTeamManager::RegisterSubSystems"));

SubSystemCollection.Init( this, GetSubSystemClasses() );
}

void UTeamManager::UnregisterSubSystems()
{
if (!IsStandaloneOrDS(this)) return;
LogD(TEXT("UTeamManager::UnregisterSubSystems"));

SubSystemCollection.Uninit();
}

同步 TeamSyncDatas,其中类型为 TeamSyncData_Common,记录所有人都需要关心的队伍同步数据;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// TeamManager.cpp

#pragma region Replicate

void UTeamManager::OnRep_TeamSyncDatas(const TArray<UTeamSyncDataBase*>& LastTeamSyncDatas)
{
LogD(TEXT("UTeamManager::OnRep_TeamSyncDatas, TeamSyncDatas=%s, LastTeamSyncDatas=%s"), *UStringUtils::ToString(TeamSyncDatas), *UStringUtils::ToString(LastTeamSyncDatas));
}

bool UTeamManager::ReplicateSubobjects(UActorChannel* Channel, FOutBunch* Bunch, FReplicationFlags* RepFlags)
{
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);


if (IsValid(Channel) && RepFlags->bNetInitial == false)
{
for (const auto& SyncData : TeamSyncDatas)
{
WroteSomething |= Channel->ReplicateSubobject( SyncData, *Bunch, *RepFlags);
}
}

return WroteSomething;
}

#pragma endregion Replicate

TeamComponent

维护 TeamID

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// TeamComponent.cpp

#pragma region TeamID

void UTeamComponent::UpdateTeamID(uint64 InTeamID)
{
if (InTeamID == TeamID) return;
LogD(TEXT("UTeamComponent::UpdateTeamID, UID=%llu, TeamID=%llu"), GetUID(), InTeamID);
SetTeamID(InTeamID);
OnRep_TeamID();

if (IsDS(this))
{
S2C_NotifyTeamIDChanged(InTeamID);
}
}

void UTeamComponent::S2C_NotifyTeamIDChanged_Implementation(uint64 InTeamID)
{
if (!IsClient(this)) return;
if (InTeamID == TeamID) return;
LogD(TEXT("UTeamComponent::S2C_NotifyTeamIDChanged_Implementation, UID=%llu, TeamID=%llu"), GetUID(), TeamID);
SetTeamID(InTeamID);
OnRep_TeamID();
}

void UTeamComponent::OnRep_TeamID()
{
LogD(TEXT("UTeamComponent::OnRep_TeamID, UID=%llu, TeamID=%llu"), GetUID(), TeamID);
OnPlayerTeamIDChanged.Broadcast(TeamID);
UTeamDelegate::GetDelegate()->OnPlayerTeamIDChanged.Broadcast(GetUID(), TeamID);
}

#pragma endregion TeamID

持有 TeamPlayerSubSystem,支持业务扩展拆分为若干个子系统;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// TeamComponent.cpp

#pragma region SubSystem

void UTeamComponent::RegisterSubSystems()
{
if (!IsStandaloneOrDS(this)) return;
LogD(TEXT("UTeamComponent::RegisterSubSystems, UID=%llu"), GetUID());

SubSystemCollection.Init( this, GetSubSystemClasses() );
}

void UTeamComponent::UnregisterSubSystems()
{
if (!IsStandaloneOrDS(this)) return;
LogD(TEXT("UTeamComponent::UnregisterSubSystems, UID=%llu"), GetUID());

SubSystemCollection.Uninit();
}


// -----

UTeamPlayerSubSystemBase* UTeamComponent::GetSubSystem(UClass* InClass) const
{
if (!IsValid(InClass)) return nullptr;
return SubSystemCollection.GetSubSystem(InClass);
}

#pragma endregion SubSystem

同步 TeamSyncData,其中类型为 TeamSyncData_OwnerOnly,记录仅自己队伍成员关心的队伍同步数据;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ComponentBase.cpp

bool UComponentBase::ReplicateSubobjects(UActorChannel* Channel, FOutBunch* Bunch, FReplicationFlags* RepFlags)
{
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);

if (IsValid(Channel) && IsValid(Channel->Connection))
{
if (Channel->Connection->PlayerController == GetPlayerController())
{
WroteSomething |= ReplicateSubobjects_OwnerOnly(Channel, Bunch, RepFlags);
}
}

return WroteSomething;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// TeamComponent.cpp

#pragma region SyncData

bool UTeamComponent::ReplicateSubobjects_OwnerOnly(UActorChannel* Channel, FOutBunch* Bunch, FReplicationFlags* RepFlags)
{
bool WroteSomething = Super::ReplicateSubobjects_OwnerOnly(Channel, Bunch, RepFlags);

WroteSomething |= SubSystemCollection.ReplicateSubSystems(Channel, Bunch, RepFlags);

if (IsValid(Channel) && RepFlags->bNetInitial == false)
{
WroteSomething |= Channel->ReplicateSubobject( SyncData, *Bunch, *RepFlags);
}

return WroteSomething;
}

void UTeamComponent::CreateSyncData(UTeamBase* InTeam)
{
if (!IsValid(InTeam)) return;
LogD(TEXT("UTeamComponent::CreateSyncData, UID=%llu, InTeamID=%llu, TeamID=%llu"), GetUID(), InTeam->GetTeamID(), TeamID);

auto NewSyncData = UTeamUtils::CreateTeamSyncData( this, InTeam, GetSyncDataClass() );
if (IsValid(NewSyncData))
{
SetSyncData(NewSyncData);
LogD(TEXT("UTeamComponent::CreateSyncData, UID=%llu, SyncData=%s"), GetUID(), *SyncData->ToString());
OnRep_SyncData();
}
}


void UTeamComponent::DestroySyncData()
{
LogD(TEXT("UTeamComponent::DestroySyncData, UID=%llu, TeamID=%llu"), GetUID(), TeamID);
UTeamUtils::DestoryTeamSyncData(SyncData);
SetSyncData( nullptr );
}

void UTeamComponent::OnRep_SyncData()
{
LogD(TEXT("UTeamComponent::OnRep_SyncData, UID=%llu, SyncData=%s"), GetUID(), *UStringUtils::ToString(SyncData));
}

Team

  1. 维护队伍信息: TeamIDMemberUIDs,以及可能继承的属性;
  2. 当属性变化时,进行 MarkDirty 通知 OnDataDirty

完整实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// TeamMacro.h

#pragma region Property


#define TEAM_GENERATE_PROPERTY(Visibility, Type, Name) \
Visibility: Type Name{}; \
TEAM_GENERATE_PROPERTY_FUNCTIONS_INTERNAL(Name)

// ----

#define TEAM_GENERATE_PROPERTY_MARKDIRTY(Visibility, Type, Name) \
Visibility: Type Name{}; \
TEAM_GENERATE_PROPERTY_MARKDIRTY_FUNCTIONS(Name)


#pragma endregion Property


#pragma region Function

#define TEAM_GENERATE_PROPERTY_FUNCTIONS(Name) \
TEAM_EXPAND(TEAM_GENERATE_PROPERTY_FUNCTIONS_INTERNAL(decltype(Name), Name))

#define TEAM_GENERATE_PROPERTY_FUNCTIONS_INTERNAL(Type, Name) \
public: const Type& Get##Name() const { return Name; } \
public: void Set##Name(const Type& Value) { \
Name = Value; \
}


// -----

#define TEAM_GENERATE_PROPERTY_MARKDIRTY_FUNCTIONS(Name) \
TEAM_EXPAND(TEAM_GENERATE_PROPERTY_MARKDIRTY_FUNCTIONS_INTERNAL(decltype(Name), Name))

#define TEAM_GENERATE_PROPERTY_MARKDIRTY_FUNCTIONS_INTERNAL(Type, Name) \
public: const Type& Get##Name() const { return Name; } \
public: void Set##Name(const Type& Value) { \
if (Name == Value) return; \
Name = Value; \
this->MarkDirty(); \
}

#pragma endregion Function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Team.h

UCLASS(Blueprintable, BlueprintType)
class UTeamBase : public UObject
{
GENERATED_BODY()

public:
void Init(uint64 InTeamID);
void Uninit();

public:
virtual FString ToString() const;

#pragma region Dirty

public:
DECLARE_MULTICAST_DELEGATE(FDataDirty)
FDataDirty OnDataDirty;

protected:
virtual void MarkDirty();

#pragma endregion Dirty


#pragma region Member

public:
void AddMember(uint64 MemberUID);
void RemoveMember(uint64 MemberUID);

#pragma endregion Member

private:
TEAM_GENERATE_PROPERTY_MARKDIRTY(protected, uint64, TeamID);
TEAM_GENERATE_PROPERTY_MARKDIRTY(protected, TArray<uint64>, MemberUIDs);
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Team.cpp

void UTeamBase::Init(uint64 InTeamID)
{
TeamID = InTeamID;
LogD(TEXT("UTeamBase::Init, TeamID=%llu"), TeamID);
MemberUIDs.Empty();
}

void UTeamBase::Uninit()
{
LogD(TEXT("UTeamBase::Uninit, TeamID=%llu"), TeamID);
}


#pragma region Dirty

void UTeamBase::MarkDirty()
{
OnDataDirty.Broadcast();
}

#pragma endregion Dirty


#pragma region Member

void UTeamBase::AddMember(uint64 MemberUID)
{
if (MemberUIDs.Contains( MemberUID )) return;
MemberUIDs.Add( MemberUID );
LogD(TEXT("UTeamBase::AddMember, TeamID=%llu, MemberUID=%llu, MemberUIDs=%s"), TeamID, MemberUID, *UStringUtils::ToString(MemberUIDs));
MarkDirty();
}

void UTeamBase::RemoveMember(uint64 MemberUID)
{
if (!MemberUIDs.Contains( MemberUID )) return;
MemberUIDs.Remove( MemberUID );
LogD(TEXT("UTeamBase::RemoveMember, TeamID=%llu, MemberUID=%llu, MemberUIDs=%s"), TeamID, MemberUID, *UStringUtils::ToString(MemberUIDs));
MarkDirty();
}


#pragma endregion Member

TeamSyncData

与对应的 Team 进行绑定;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// TeamSyncData.cpp

#pragma region Bind

void UTeamSyncDataBase::BindTeam(UTeamBase* InTeam)
{
if (!IsValid(InTeam)) return;
BoundTeam = InTeam;
InTeam->OnDataDirty.AddUObject(this, &ThisClass::OnDataDirty);
CollectSyncData(InTeam);

LogD(TEXT("UTeamSyncDataBase::BindTeam, %s"), *ToString());
}

void UTeamSyncDataBase::Unbind()
{
if (!BoundTeam.IsValid()) return;
BoundTeam->OnDataDirty.RemoveAll(this);
LogD(TEXT("UTeamSyncDataBase::Unbind, %s"), *ToString());
}


#pragma endregion Bind

Team 进行 OnDataDirty 时,重新对 Team 进行 CollectSyncData 收集并设置同步数据;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// TeamSyncData.cpp

#pragma region CollectData

void UTeamSyncDataBase::OnDataDirty()
{
if (BoundTeam.IsValid())
{
CollectSyncData( BoundTeam.Get() );
}
}


void UTeamSyncDataBase::CollectSyncData(UTeamBase* InTeam)
{
if (!IsValid(InTeam)) return;

SetTeamID( InTeam->GetTeamID() );
SetMemberUIDs( InTeam->GetMemberUIDs() );

LogD(TEXT("UTeamSyncDataBase::CollectSyncData, %s"), *ToString());
}

#pragma endregion CollectData

提供 Create / Destroy TeamSyncData 的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// TeamUtils.cpp

UTeamSyncDataBase* UTeamUtils::CreateTeamSyncData(UObject* Outer, UTeamBase* InTeam, UClass* InClass)
{
if (!IsValid(Outer)) return nullptr;
if (!IsValid(InClass) || !InClass->IsChildOf(UTeamSyncDataBase::StaticClass())) return nullptr;

UTeamSyncDataBase* NewSyncData = NewObject<UTeamSyncDataBase>(Outer, InClass);
if (!IsValid(NewSyncData)) return nullptr;

NewSyncData->BindTeam( InTeam );
return NewSyncData;
}

void UTeamUtils::DestoryTeamSyncData(UTeamSyncDataBase* SyncData)
{
if (!IsValid(SyncData)) return;
SyncData->Unbind();
SyncData = nullptr;
}

完整实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// TeamSyncData.h

UCLASS()
class UTeamSyncDataBase : public UObject
{
GENERATED_BODY()


#pragma region Base

protected:
virtual bool IsSupportedForNetworking() const override { return true; }

protected:
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;


#pragma endregion Base



#pragma region Bind

public:
void BindTeam(UTeamBase* InTeam);
void Unbind();

private:
TWeakObjectPtr <UTeamBase> BoundTeam;

#pragma endregion Bind


#pragma region CollectData

private:
void OnDataDirty();

protected:
virtual void CollectSyncData(UTeamBase* InTeam);

#pragma endregion CollectData


#pragma region Data

public:
virtual FString ToString() const;

protected:
UFUNCTION()
virtual void OnRep_TeamID();

UFUNCTION()
virtual void OnRep_MemberUIDs(const TArray<uint64>& LastMemberUIDs);

private:

UPROPERTY(PushModelProperty, ReplicatedUsing=OnRep_TeamID)
uint64 TeamID;

UPROPERTY(PushModelProperty, ReplicatedUsing=OnRep_MemberUIDs)
TArray <uint64> MemberUIDs;

#pragma endregion Data


};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// TeamSyncData.cpp

#pragma region Base

void UTeamSyncDataBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);

FDoRepLifetimeParams SharedParams;
SharedParams.bIsPushBased = true;

DOREPLIFETIME_WITH_PARAMS_FAST(UTeamSyncDataBase, TeamID, SharedParams);
DOREPLIFETIME_WITH_PARAMS_FAST(UTeamSyncDataBase, MemberUIDs, SharedParams);
}


#pragma endregion Base


#pragma region Bind

void UTeamSyncDataBase::BindTeam(UTeamBase* InTeam)
{
if (!IsValid(InTeam)) return;
BoundTeam = InTeam;
InTeam->OnDataDirty.AddUObject(this, &ThisClass::OnDataDirty);
CollectSyncData(InTeam);

LogD(TEXT("UTeamSyncDataBase::BindTeam, %s"), *ToString());
}

void UTeamSyncDataBase::Unbind()
{
if (!BoundTeam.IsValid()) return;
BoundTeam->OnDataDirty.RemoveAll(this);
LogD(TEXT("UTeamSyncDataBase::Unbind, %s"), *ToString());
}



#pragma endregion Bind




#pragma region CollectData

void UTeamSyncDataBase::OnDataDirty()
{
if (BoundTeam.IsValid())
{
CollectSyncData( BoundTeam.Get() );
}
}


void UTeamSyncDataBase::CollectSyncData(UTeamBase* InTeam)
{
if (!IsValid(InTeam)) return;

SetTeamID( InTeam->GetTeamID() );
SetMemberUIDs( InTeam->GetMemberUIDs() );

LogD(TEXT("UTeamSyncDataBase::CollectSyncData, %s"), *ToString());
}

#pragma endregion CollectData



#pragma region Data

FString UTeamSyncDataBase::ToString() const
{
return FString::Printf(TEXT("UID=%llu, TeamID=%llu, Members=%s"), GetUID(), TeamID, *UStringUtils::ToString(MemberUIDs));
}


void UTeamSyncDataBase::OnRep_TeamID()
{
LogD(TEXT("UTeamSyncDataBase::OnRep_TeamID, %s"), *ToString());
}


void UTeamSyncDataBase::OnRep_MemberUIDs(const TArray<uint64>& LastMemberUIDs)
{
LogD(TEXT("UTeamSyncDataBase::OnRep_MemberUIDs, LastMemberUIDs=%s, %s"), *UStringUtils::ToString(LastMemberUIDs), *ToString());
}


#pragma endregion Data