Skip to content

Commit 271da93

Browse files
committed
Changed relevancy system to use PlayerId instead of UniqueNetId
- This has been made to make the culling and relevancy work in local splitscreen multiplayer game
1 parent 7c9f2c2 commit 271da93

File tree

8 files changed

+68
-68
lines changed

8 files changed

+68
-68
lines changed

Source/ProceduralDungeon/Private/DungeonGeneratorBase.cpp

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#include "Engine/Engine.h" // GEngine
1010
#include "GameFramework/Pawn.h"
1111
#include "GameFramework/PlayerState.h"
12+
#include "GameFramework/PlayerController.h"
13+
#include "Engine/LocalPLayer.h"
14+
#include "Engine/GameInstance.h"
1215
#include "NavigationSystem.h"
1316
#include "RoomData.h"
1417
#include "Room.h"
@@ -442,8 +445,8 @@ void ADungeonGeneratorBase::UpdatePlayerRooms()
442445
continue;
443446

444447
// Using their UniqueID to be reliable even when players join or leave
445-
const FUniqueNetIdRepl& UniqueID = Controller->PlayerState->GetUniqueId();
446-
if (!UniqueID.IsValid())
448+
int32 PlayerID = Controller->PlayerState->GetPlayerId();
449+
if (PlayerID <= 0)
447450
continue;
448451

449452
APawn* Player = GetVisibilityPawn(Controller);
@@ -453,17 +456,17 @@ void ADungeonGeneratorBase::UpdatePlayerRooms()
453456
const FTransform& Transform = UseGeneratorTransform() ? GetTransform() : FTransform::Identity;
454457
FBox WorldPlayerBox = ActorUtils::GetActorBoundingBoxForRooms(Player, Transform);
455458

456-
FPlayerRooms& PlayerRoom = PlayerRooms.FindOrAdd(UniqueID);
459+
FPlayerRooms& PlayerRoom = PlayerRooms.FindOrAdd(PlayerID);
457460
auto PreviousRoomList(PlayerRoom.CurrentRooms);
458461
PlayerRoom.Roll();
459-
FindElementsWithBoundsTest(*Octree, WorldPlayerBox, [this, &PlayerRoom, & UniqueID](const FDungeonOctreeElement& Element) {
462+
FindElementsWithBoundsTest(*Octree, WorldPlayerBox, [this, &PlayerRoom, &PlayerID](const FDungeonOctreeElement& Element) {
460463
PlayerRoom.AddCurrentRoom(Element.Room);
461-
Element.Room->SetPlayerInside(UniqueID, true);
464+
Element.Room->SetPlayerInside(PlayerID, true);
462465
});
463466

464467
for (URoom* Room : PlayerRoom.OldRooms)
465468
{
466-
Room->SetPlayerInside(UniqueID, false);
469+
Room->SetPlayerInside(PlayerID, false);
467470
}
468471

469472
// Both sets are equal if each set is included in the other
@@ -475,14 +478,6 @@ void ADungeonGeneratorBase::UpdatePlayerRooms()
475478

476479
void ADungeonGeneratorBase::UpdateRoomVisibility()
477480
{
478-
// Get local player controller
479-
// @TODO: Extend to support splitscreen / multiple local players?
480-
FUniqueNetIdRepl UniqueID = ActorUtils::GetPlayerUniqueId(GetWorld(), 0);
481-
482-
FPlayerRooms* PlayerRoom = PlayerRooms.Find(UniqueID);
483-
if (!PlayerRoom)
484-
return;
485-
486481
const bool bIsOcclusionEnabled = Dungeon::OcclusionCulling();
487482
const uint32 OcclusionDistance = Dungeon::OcclusionDistance();
488483
bool bForceUpdate = false;
@@ -504,13 +499,37 @@ void ADungeonGeneratorBase::UpdateRoomVisibility()
504499
if (!bIsOcclusionEnabled)
505500
return;
506501

502+
TSet<URoom*> OldRooms;
503+
TSet<URoom*> CurrentRooms;
504+
bool bHasChanged = false;
505+
506+
UGameInstance* GameInstance = GetWorld()->GetGameInstance();
507+
for (const ULocalPlayer* LocalPlayer : GameInstance->GetLocalPlayers())
508+
{
509+
APlayerController* Controller = LocalPlayer->PlayerController;
510+
if (!IsValid(Controller))
511+
continue;
512+
513+
APlayerState* State = Controller->PlayerState;
514+
if (!IsValid(State))
515+
continue;
516+
517+
FPlayerRooms* PlayerRoom = PlayerRooms.Find(State->GetPlayerId());
518+
if (!PlayerRoom)
519+
continue;
520+
521+
OldRooms.Append(PlayerRoom->OldRooms);
522+
CurrentRooms.Append(PlayerRoom->CurrentRooms);
523+
bHasChanged |= PlayerRoom->bHasChanged;
524+
}
525+
507526
// Save performance by not updating room visibilities if the player rooms haven't changed.
508-
if (!(bForceUpdate || PlayerRoom->bHasChanged))
527+
if (!(bForceUpdate || bHasChanged))
509528
return;
510529

511530
TSet<URoom*> VisibleRooms;
512-
UDungeonGraph::TraverseRooms(PlayerRoom->CurrentRooms, &VisibleRooms, OcclusionDistance, [](URoom* room, uint32 distance) { room->SetVisible(true); });
513-
UDungeonGraph::TraverseRooms(PlayerRoom->OldRooms, nullptr, OcclusionDistance, [&VisibleRooms](URoom* room, uint32 distance) { room->SetVisible(VisibleRooms.Contains(room)); });
531+
UDungeonGraph::TraverseRooms(CurrentRooms, &VisibleRooms, OcclusionDistance, [](URoom* room, uint32 distance) { room->SetVisible(true); });
532+
UDungeonGraph::TraverseRooms(OldRooms, nullptr, OcclusionDistance, [&VisibleRooms](URoom* room, uint32 distance) { room->SetVisible(VisibleRooms.Contains(room)); });
514533
}
515534

516535
void ADungeonGeneratorBase::UpdateRoomRelevancy()

Source/ProceduralDungeon/Private/ProceduralDungeonUtils.cpp

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -270,36 +270,21 @@ FBox ActorUtils::GetActorBoundingBoxForRooms(AActor* Actor, const FTransform& Du
270270
return ActorBox;
271271
}
272272

273-
FUniqueNetIdRepl ActorUtils::GetPlayerUniqueId(const UObject* WorldContextObject, int32 PlayerIndex)
273+
APlayerController* ActorUtils::GetPlayerControllerFromPlayerId(const UObject* WorldContextObject, int32 PlayerId)
274274
{
275-
APlayerController* Controller = UGameplayStatics::GetPlayerController(WorldContextObject, PlayerIndex);
276-
if (!IsValid(Controller) || !IsValid(Controller->PlayerState))
277-
return FUniqueNetIdRepl::Invalid();
278-
279-
return Controller->PlayerState->GetUniqueId();
280-
}
281-
282-
int32 ActorUtils::GetPlayerIndex(const UObject* WorldContextObject, const FUniqueNetIdRepl& PlayerUniqueId)
283-
{
284-
for (int32 Index = 0;; ++Index)
285-
{
286-
APlayerController* Controller = UGameplayStatics::GetPlayerController(WorldContextObject, Index);
287-
if (!IsValid(Controller))
288-
break;
275+
UWorld* World = WorldContextObject->GetWorld();
276+
if (!IsValid(World))
277+
return nullptr;
289278

290-
if (!IsValid(Controller->PlayerState))
291-
continue;
279+
AGameStateBase* GameState = World->GetGameState();
280+
if (!IsValid(GameState))
281+
return nullptr;
292282

293-
FUniqueNetIdRepl UniqueId = Controller->PlayerState->GetUniqueId();
294-
if (UniqueId == PlayerUniqueId)
295-
return Index;
296-
}
297-
return -1;
298-
}
283+
const auto* StatePtr = GameState->PlayerArray.FindByPredicate([PlayerId](const APlayerState* State) { return State->GetPlayerId() == PlayerId; });
284+
if (StatePtr == nullptr)
285+
return nullptr;
299286

300-
APlayerController* ActorUtils::GetPlayerControllerFromUniqueId(const UObject* WorldContextObject, const FUniqueNetIdRepl& PlayerUniqueId)
301-
{
302-
APlayerState* State = UGameplayStatics::GetPlayerStateFromUniqueNetId(WorldContextObject, PlayerUniqueId);
287+
const APlayerState* State = *StatePtr;
303288
if (!IsValid(State))
304289
return nullptr;
305290
return State->GetPlayerController();

Source/ProceduralDungeon/Private/Room.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ int32 URoom::GetRelevancyLevel(APlayerController* PlayerController) const
236236
if (!IsValid(PlayerController->PlayerState))
237237
return -1;
238238

239-
const int32* Level = RelevancyLevels.Find(PlayerController->PlayerState->GetUniqueId());
239+
const int32* Level = RelevancyLevels.Find(PlayerController->PlayerState->GetPlayerId());
240240
return (Level != nullptr) ? *Level : -1;
241241
}
242242

@@ -267,7 +267,7 @@ void URoom::GetAllRelevancyLevels(TMap<APlayerController*, int32>& OutRelevancyL
267267
OutRelevancyLevels.Empty();
268268
for (const auto& Pair : RelevancyLevels)
269269
{
270-
APlayerController* Controller = ActorUtils::GetPlayerControllerFromUniqueId(this, Pair.Key);
270+
APlayerController* Controller = ActorUtils::GetPlayerControllerFromPlayerId(this, Pair.Key);
271271
if (IsValid(Controller))
272272
OutRelevancyLevels.Add(Controller, Pair.Value);
273273
}
@@ -559,7 +559,7 @@ void URoom::SetVisible(bool Visible, bool bForceUpdate)
559559
UpdateVisibility();
560560
}
561561

562-
void URoom::SetRelevancyLevel(FUniqueNetIdRepl PlayerID, int32 Level)
562+
void URoom::SetRelevancyLevel(int32 PlayerID, int32 Level)
563563
{
564564
int32* FoundLevel = RelevancyLevels.Find(PlayerID);
565565
if (Level < 0)
@@ -574,12 +574,12 @@ void URoom::SetRelevancyLevel(FUniqueNetIdRepl PlayerID, int32 Level)
574574
return;
575575
RelevancyLevels.Add(PlayerID, Level);
576576
}
577-
APlayerController* Controller = ActorUtils::GetPlayerControllerFromUniqueId(GetWorld(), PlayerID);
578-
DungeonLog_Debug("Found player controller for id '%s': %s", *PlayerID.ToString(), *GetNameSafe(Controller));
577+
APlayerController* Controller = ActorUtils::GetPlayerControllerFromPlayerId(GetWorld(), PlayerID);
578+
DungeonLog_Debug("Found player controller for id %d: %s", PlayerID, *GetNameSafe(Controller));
579579
OnRelevancyChanged.Broadcast(this, Controller, Level);
580580
}
581581

582-
void URoom::SetPlayerInside(const FUniqueNetIdRepl& PlayerID, bool PlayerInside)
582+
void URoom::SetPlayerInside(int32 PlayerID, bool PlayerInside)
583583
{
584584
if (PlayerIDInside.Contains(PlayerID) != PlayerInside)
585585
return;
@@ -590,9 +590,11 @@ void URoom::SetPlayerInside(const FUniqueNetIdRepl& PlayerID, bool PlayerInside)
590590
PlayerIDInside.Remove(PlayerID);
591591
}
592592

593-
bool URoom::IsPlayerInside(int PlayerID) const
593+
bool URoom::IsPlayerInside(const APlayerController* PlayerController) const
594594
{
595-
FUniqueNetIdRepl UniqueID = ActorUtils::GetPlayerUniqueId(GetWorld(), PlayerID);
595+
if (!IsValid(PlayerController) || !IsValid(PlayerController->PlayerState))
596+
return !PlayerIDInside.IsEmpty();
597+
int32 UniqueID = PlayerController->PlayerState->GetPlayerId();
596598
return PlayerIDInside.Contains(UniqueID);
597599
}
598600

Source/ProceduralDungeon/Private/RoomLevel.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,9 @@ void ARoomLevel::Tick(float DeltaTime)
177177
#endif // ENABLE_DRAW_DEBUG
178178
}
179179

180-
bool ARoomLevel::IsPlayerInside()
180+
bool ARoomLevel::IsPlayerInside(const APlayerController* PlayerController)
181181
{
182-
return IsValid(Room) ? Room->IsPlayerInside() : false;
182+
return IsValid(Room) ? Room->IsPlayerInside(PlayerController) : false;
183183
}
184184

185185
bool ARoomLevel::IsVisible()

Source/ProceduralDungeon/Public/DungeonGeneratorBase.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ class PROCEDURALDUNGEON_API ADungeonGeneratorBase : public AActor
416416

417417
// Occlusion culling system
418418
TUniquePtr<FDungeonOctree> Octree;
419-
TMap<FUniqueNetIdRepl, FPlayerRooms> PlayerRooms;
419+
TMap<int32, FPlayerRooms> PlayerRooms;
420420

421421
// Transient. Only used to detect when occlusion setting is changed.
422422
bool bWasOcclusionEnabled {false};

Source/ProceduralDungeon/Public/ProceduralDungeonUtils.h

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,6 @@ namespace ActorUtils
158158
// Returns the bounding box of an actor considering only components that would interact with rooms (based on collision settings).
159159
FBox PROCEDURALDUNGEON_API GetActorBoundingBoxForRooms(AActor* Actor, const FTransform& DungeonTransform = FTransform::Identity);
160160

161-
// Returns the unique id associated with the player index.
162-
FUniqueNetIdRepl PROCEDURALDUNGEON_API GetPlayerUniqueId(const UObject* WorldContextObject, int32 PlayerIndex);
163-
164-
// Returns the index of the player with unique Id.
165-
int32 PROCEDURALDUNGEON_API GetPlayerIndex(const UObject* WorldContextObject, const FUniqueNetIdRepl& PlayerUniqueId);
166-
167-
// Returns the controller of the player with unique Id.
168-
class APlayerController* GetPlayerControllerFromUniqueId(const UObject* WorldContextObject, const FUniqueNetIdRepl& PlayerUniqueId);
161+
// Returns the player controller associated with the player state id.
162+
class APlayerController* GetPlayerControllerFromPlayerId(const UObject* WorldContextObject, int32 PlayerId);
169163
}

Source/ProceduralDungeon/Public/Room.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,16 @@ class PROCEDURALDUNGEON_API URoom : public UReplicableObject, public IReadOnlyRo
7878
//~ End IDungeonSaveInterface Interface
7979

8080
const ADungeonGeneratorBase* Generator() const { return GeneratorOwner.Get(); }
81-
void SetPlayerInside(const FUniqueNetIdRepl& PlayerID, bool PlayerInside);
81+
void SetPlayerInside(int32 PlayerID, bool PlayerInside);
8282
void SetVisible(bool Visible, bool bForceUpdate = false);
83-
void SetRelevancyLevel(FUniqueNetIdRepl PlayerID, int32 Level);
83+
void SetRelevancyLevel(int32 PlayerID, int32 Level);
8484
FORCEINLINE bool IsReady() const { return RoomData != nullptr; }
8585

8686
// Is the player currently inside the room?
8787
// A player can be in multiple rooms at once, for example when he stands at the door frame,
8888
// the player's capsule is in both rooms.
8989
UFUNCTION(BlueprintPure, Category = "Room")
90-
FORCEINLINE bool IsPlayerInside(int PlayerID = 0) const;
90+
FORCEINLINE bool IsPlayerInside(const APlayerController* PlayerController = nullptr) const;
9191

9292
// Is the room currently visible?
9393
UFUNCTION(BlueprintPure, Category = "Room", meta = (CompactNodeTitle = "Is Visible"))
@@ -217,10 +217,10 @@ class PROCEDURALDUNGEON_API URoom : public UReplicableObject, public IReadOnlyRo
217217
UPROPERTY(ReplicatedUsing = OnRep_Id, SaveGame)
218218
int64 Id {-1};
219219

220-
TSet<FUniqueNetIdRepl> PlayerIDInside {};
220+
TSet<int32> PlayerIDInside {};
221221
bool bIsVisible {true};
222222
bool bForceVisible {false};
223-
TMap<FUniqueNetIdRepl, int32> RelevancyLevels {};
223+
TMap<int32, int32> RelevancyLevels {};
224224

225225
UPROPERTY(ReplicatedUsing = OnRep_IsLocked, SaveGame)
226226
bool bIsLocked {false};

Source/ProceduralDungeon/Public/RoomLevel.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class PROCEDURALDUNGEON_API ARoomLevel : public ALevelScriptActor
5656
FORCEINLINE bool IsInit() const { return bIsInit; }
5757

5858
UFUNCTION(BlueprintPure, Category = "Procedural Dungeon", meta = (CompactNodeTitle = "Is Player Inside", DeprecatedFunction, DeprecationMessage = "Use GetRoom() instead to access directly the room functions."))
59-
bool IsPlayerInside();
59+
bool IsPlayerInside(const APlayerController* PlayerController = nullptr);
6060

6161
UFUNCTION(BlueprintPure, Category = "Procedural Dungeon", meta = (CompactNodeTitle = "Is Visible", DeprecatedFunction, DeprecationMessage = "Use GetRoom() instead to access directly the room functions."))
6262
bool IsVisible();

0 commit comments

Comments
 (0)