Hello again Jamsh,
Sorry to revive this thread like this, but I kind of put this in hold as I had my summer vacation and I postponed getting back at it for a few weeks after I started working again, but I’m hammering at it again.
I have since improved the spawning of the Grid from the Player like so:
void ATowerDefensePawn::BeginPlay()
{
Super::BeginPlay();
AttemptCreateGridAuth();
}
void ATowerDefensePawn::AttemptCreateGridAuth()
{
if (HasAuthority())
{
Server_CreateGrid();
}
}
.h:
UFUNCTION(Server, Reliable)
void Server_CreateGrid();
.cpp
void ATowerDefensePawn::Server_CreateGrid_Implementation()
{
UWorld* World = GetWorld();
if (World)
{
FActorSpawnParameters SpawnParameters;
SpawnParameters.Owner = this;
SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
Grid = Cast<AGrid>(World->SpawnActor(GridClassToSpawn, &FTransform::Identity, SpawnParameters));
}
}
I still have the exact same problem, hence why I’m reviving this thread, after changing the value on the server, the OnRep does not run on the client, only on the server.
The variable I’m trying to replicate and fire an OnRep from is my Selection, which is a USTRUCT I made that looks like this:
USTRUCT()
struct FGridSelection
{
GENERATED_BODY()
FGridSelection()
{
}
FGridSelection(const FIntPoint& BottomLeft, const FIntPoint& TopRight)
{
BottomLeftCell = BottomLeft;
TopRightCell = TopRight;
}
// Will be INT_MIN if we don't have a selection
FIntPoint BottomLeftCell;
FIntPoint TopRightCell;
};
Here is the declaration of it in the Grids’ .h:
private:
UPROPERTY(Transient, ReplicatedUsing=OnRep_Selection)
FGridSelection Selection;
UFUNCTION()
void OnRep_Selection() const;
Here are the two functions which are called from BP, which are not firing the OnRep on the Client, but on the Server only:
void AGrid::Server_ClearGridSelection_Implementation()
{
Selection = FGridSelection(INT_MIN, INT_MIN);
OnRep_Selection();
}
void AGrid::Server_SetGridSelection_Implementation(const FIntPoint& BottomLeftCell, const FIntPoint& TopRightCell)
{
if (!IsCellIdValid(BottomLeftCell) || !IsCellIdValid(TopRightCell))
{
return;
}
// If bottom left cell is further up or to the right of the top right cell.
if (BottomLeftCell.X > TopRightCell.X || BottomLeftCell.Y > TopRightCell.Y)
{
UE_LOG(LogTemp, Warning, TEXT("Trying to Set Grid Selection when BottomLeftCell param is above or to the right of TopRightCell param"));
return;
}
UE_LOG(LogTemp, Warning, TEXT("Server_SetGridSelection: %s"), *ULogHelper::GetActorNetRoleString(this));
Selection = FGridSelection(BottomLeftCell, TopRightCell);
OnRep_Selection();
}
Worth noting that the Id is correct, and the LeftCell is below the TopRight cell, so that is not colliding with anything.
Here is the actual OnRep function itself
void AGrid::OnRep_Selection() const
{
if (GridVisualizer)
{
UE_LOG(LogTemp, Warning, TEXT("OnRep_Selection: %s"), *ULogHelper::GetActorNetRoleString(this));
const FVector2D& GridBottomLeftPos = GetPointFromAnchor(EGridAnchor::GA_BottomLeft);
const FVector2D& GridTopRightPos = GetPointFromAnchor(EGridAnchor::GA_TopRight);
// This works even if the BottomLeft and TopRight cell are the same cell, will just get different anchors of the same cell.
const FVector2D& SelectionCellBottomLeftPos = GetPointFromCell(Selection.BottomLeftCell, EGridAnchor::GA_BottomLeft);
const FVector2D& SelectionCellTopRightPos = GetPointFromCell(Selection.TopRightCell, EGridAnchor::GA_TopRight);
GridVisualizer->SetSelection(GridBottomLeftPos, GridTopRightPos, SelectionCellBottomLeftPos, SelectionCellTopRightPos);
}
}
Finally here is the BP_Grid actor which, and this is the BeginPlay flow which is the only logic called from BP.
I’ve also used this log-help method to analyze if my code is running on server or client. You can see it used in the AGrid::Server_SetGridSelection_Implementation()
FString ULogHelper::GetActorNetRoleString(const AActor* Actor)
{
const ENetMode Mode = Actor->GetNetMode();
FString NetName;
switch (Mode)
{
case ENetMode::NM_Client:
NetName = TEXT("Client");
break;
case ENetMode::NM_Standalone:
NetName = TEXT("Standalone");
break;
case ENetMode::NM_ListenServer:
NetName = TEXT("Listen Server");
break;
case ENetMode::NM_DedicatedServer:
NetName = TEXT("Dedicated Server");
break;
default: ;
}
return NetName;
}
I realize this is quite the comment, and I apologize about that, I just really don’t understand this problem, and I’d love to understand it. Hopefully you or anyone else can shed some light on this problem that I’m having, thanks!