feat(player): CharName do DB no PlayerState + header do Status Window
- PlayerState ganha CharName (Replicated) + SetCharInfo(CharName, GuildName) - ZMMOPlayerCharacter assina FZeusOnCharInfoReceived; TryApplyCachedCharInfo resolve race se S_CHAR_INFO chega antes do pawn possuir - UIPlayerStatus_Window header le PlayerState->CharName (fallback p/ GetPlayerName) em vez do "Mateuus-61E9B19A4FB2" generico Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -18,6 +18,7 @@
|
||||
#include "UObject/ConstructorHelpers.h"
|
||||
#include "ZMMO.h"
|
||||
#include "ZMMOAttributeComponent.h"
|
||||
#include "ZMMOPlayerState.h"
|
||||
#include "ZMMOWorldSubsystem.h"
|
||||
#include "ZeusNetworkSubsystem.h"
|
||||
#include "UI/FrontEnd/UIFrontEndFlowSubsystem.h"
|
||||
@@ -278,6 +279,7 @@ void AZMMOPlayerCharacter::BindZeusSpawnDelegate()
|
||||
}
|
||||
|
||||
ZeusNetwork->OnPlayerSpawned.AddDynamic(this, &AZMMOPlayerCharacter::HandleZeusPlayerSpawned);
|
||||
ZeusNetwork->OnCharInfoReceived.AddDynamic(this, &AZMMOPlayerCharacter::HandleZeusCharInfo);
|
||||
bSpawnDelegateBound = true;
|
||||
}
|
||||
|
||||
@@ -289,6 +291,7 @@ void AZMMOPlayerCharacter::UnbindZeusSpawnDelegate()
|
||||
return;
|
||||
}
|
||||
ZeusNetwork->OnPlayerSpawned.RemoveDynamic(this, &AZMMOPlayerCharacter::HandleZeusPlayerSpawned);
|
||||
ZeusNetwork->OnCharInfoReceived.RemoveDynamic(this, &AZMMOPlayerCharacter::HandleZeusCharInfo);
|
||||
bSpawnDelegateBound = false;
|
||||
}
|
||||
|
||||
@@ -307,6 +310,10 @@ void AZMMOPlayerCharacter::TryRegisterLocalEntityFromCachedSpawn()
|
||||
{
|
||||
HandleLocalSpawnReady(CachedEntityId, CachedPosCm, CachedYawDeg, CachedServerTimeMs);
|
||||
}
|
||||
|
||||
// Race fix: o S_CHAR_INFO chega ANTES de S_SPAWN_PLAYER mas o pawn pode
|
||||
// nao existir ainda (OpenLevel em andamento). Aplica o ultimo cacheado.
|
||||
TryApplyCachedCharInfo();
|
||||
}
|
||||
|
||||
void AZMMOPlayerCharacter::HandleZeusPlayerSpawned(const int32 InEntityId, const bool bIsLocal,
|
||||
@@ -349,25 +356,74 @@ void AZMMOPlayerCharacter::HandleLocalSpawnReady(const int32 InEntityId, const F
|
||||
}
|
||||
}
|
||||
|
||||
// Seed do EntityId no AttributeComponent do PlayerState. O componente
|
||||
// vive no AZMMOPlayerState via Component Registry — busca via PlayerState
|
||||
// (sobrevive ao despawn do Pawn). S_ATTRIBUTE_SNAPSHOT_FULL chega via
|
||||
// UDP e e' rouado pelo ZMMOAttributeNetworkHandler via lookup por
|
||||
// EntityId no PlayerArray do GameState.
|
||||
// Identidade publica (EntityId) + seed no AttributeComponent. CharName/Guild
|
||||
// vem separado via S_CHAR_INFO -> HandleZeusCharInfo. AttributeComponent vive
|
||||
// como subobject e recebe seed pra que o ZMMOAttributeNetworkHandler consiga
|
||||
// rotear o snapshot por EntityId desde o primeiro pacote.
|
||||
if (APlayerState* PS = GetPlayerState())
|
||||
{
|
||||
if (AZMMOPlayerState* ZMMOPs = Cast<AZMMOPlayerState>(PS))
|
||||
{
|
||||
ZMMOPs->SetPublicIdentity(EntityIdAsInt64, /*CharId*/ FString(),
|
||||
/*BaseLevel*/ 1, /*ClassId*/ 0);
|
||||
}
|
||||
if (UZMMOAttributeComponent* AttrComp = PS->FindComponentByClass<UZMMOAttributeComponent>())
|
||||
{
|
||||
AttrComp->SeedEntityId(InEntityId);
|
||||
}
|
||||
}
|
||||
|
||||
// Aplica char info cacheada (caso S_CHAR_INFO tenha chegado antes do PS existir).
|
||||
TryApplyCachedCharInfo();
|
||||
|
||||
// UI in-game e' responsabilidade do AZMMOHUD (GameMode.HUDClass), nao do
|
||||
// Pawn. AHUD::BeginPlay chama UUIInGameFlowSubsystem::StartInGame, que
|
||||
// pushea WBP_HUD em UI.Layer.Game. UZMMOHudWidget::NativeOnActivated auto-binda
|
||||
// no UZMMOAttributeComponent via PlayerState.
|
||||
}
|
||||
|
||||
void AZMMOPlayerCharacter::HandleZeusCharInfo(const int32 InEntityId, const FString& CharName, const FString& GuildName)
|
||||
{
|
||||
UE_LOG(LogZMMOPlayer, Log,
|
||||
TEXT("AZMMOPlayerCharacter: S_CHAR_INFO recebido EntityId=%d name=%s guild=%s"),
|
||||
InEntityId, *CharName, *GuildName);
|
||||
|
||||
APlayerState* PS = GetPlayerState();
|
||||
if (!PS)
|
||||
{
|
||||
// Race fix: pawn pode nao ter PlayerState ainda. Subsystem ja cacheou
|
||||
// (LastLocalCharInfoCache); TryApplyCachedCharInfo aplica em
|
||||
// HandleLocalSpawnReady ou em outra oportunidade.
|
||||
return;
|
||||
}
|
||||
if (AZMMOPlayerState* ZMMOPs = Cast<AZMMOPlayerState>(PS))
|
||||
{
|
||||
ZMMOPs->SetCharInfo(CharName, GuildName);
|
||||
}
|
||||
}
|
||||
|
||||
void AZMMOPlayerCharacter::TryApplyCachedCharInfo()
|
||||
{
|
||||
if (!ZeusNetwork)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int32 CachedEntityId = 0;
|
||||
FString CachedCharName;
|
||||
FString CachedGuildName;
|
||||
if (!ZeusNetwork->TryGetLastLocalCharInfo(CachedEntityId, CachedCharName, CachedGuildName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (APlayerState* PS = GetPlayerState())
|
||||
{
|
||||
if (AZMMOPlayerState* ZMMOPs = Cast<AZMMOPlayerState>(PS))
|
||||
{
|
||||
ZMMOPs->SetCharInfo(CachedCharName, CachedGuildName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AZMMOPlayerCharacter::FlushInputAxisToServer(const float DeltaSeconds)
|
||||
{
|
||||
ResolveZeusNetworkSubsystem();
|
||||
|
||||
@@ -133,12 +133,19 @@ private:
|
||||
/**
|
||||
* Memoriza `EntityId` autoritativo e regista o pawn no `UZMMOWorldSubsystem`
|
||||
* para o registry partilhado por proxies remotos (filtra snapshots locais).
|
||||
* Metadados publicos do char (Name/Guild) chegam via S_CHAR_INFO em
|
||||
* HandleZeusCharInfo (servidor envia ANTES de S_SPAWN_PLAYER).
|
||||
*/
|
||||
void HandleLocalSpawnReady(int32 InEntityId, FVector PosCm, float YawDeg, int64 ServerTimeMs);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleZeusPlayerSpawned(int32 InEntityId, bool bIsLocal, FVector PosCm, float YawDeg, int64 ServerTimeMs);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleZeusCharInfo(int32 InEntityId, const FString& CharName, const FString& GuildName);
|
||||
|
||||
void TryApplyCachedCharInfo();
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UZeusNetworkSubsystem> ZeusNetwork;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "Misc/ConfigCacheIni.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "ZMMO.h"
|
||||
|
||||
AZMMOPlayerState::AZMMOPlayerState()
|
||||
@@ -72,6 +73,17 @@ AZMMOPlayerState::AZMMOPlayerState()
|
||||
}
|
||||
}
|
||||
|
||||
void AZMMOPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(AZMMOPlayerState, ZMMOEntityId);
|
||||
DOREPLIFETIME(AZMMOPlayerState, CharId);
|
||||
DOREPLIFETIME(AZMMOPlayerState, CharName);
|
||||
DOREPLIFETIME(AZMMOPlayerState, BaseLevel);
|
||||
DOREPLIFETIME(AZMMOPlayerState, ClassId);
|
||||
DOREPLIFETIME(AZMMOPlayerState, GuildName);
|
||||
}
|
||||
|
||||
void AZMMOPlayerState::SetPublicIdentity(int64 InEntityId, const FString& InCharId,
|
||||
int32 InBaseLevel, int32 InClassId)
|
||||
{
|
||||
@@ -80,3 +92,9 @@ void AZMMOPlayerState::SetPublicIdentity(int64 InEntityId, const FString& InChar
|
||||
BaseLevel = InBaseLevel;
|
||||
ClassId = InClassId;
|
||||
}
|
||||
|
||||
void AZMMOPlayerState::SetCharInfo(const FString& InCharName, const FString& InGuildName)
|
||||
{
|
||||
CharName = InCharName;
|
||||
GuildName = InGuildName;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Templates/SubclassOf.h"
|
||||
#include "ZMMOPlayerState.generated.h"
|
||||
|
||||
class FLifetimeProperty;
|
||||
class UActorComponent;
|
||||
|
||||
/**
|
||||
@@ -52,36 +53,55 @@ public:
|
||||
AZMMOPlayerState();
|
||||
|
||||
// === Identidade publica ===
|
||||
//
|
||||
// Todos os campos publicos sao Replicated — hoje o cliente UE5 roda em
|
||||
// Standalone (servidor C++ standalone, sem replicacao UE5 ativa), entao
|
||||
// a marca nao tem efeito em runtime; deixa a classe pronta caso o projeto
|
||||
// passe a usar dedicated UE5 no futuro.
|
||||
|
||||
/** EntityId autoritativo do server. 0 ate o spawn local confirmar. */
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity")
|
||||
UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity")
|
||||
int64 ZMMOEntityId = 0;
|
||||
|
||||
/** CharId (BIGINT no DB; FString pra preservar 64 bits em Blueprint). */
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity")
|
||||
UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity")
|
||||
FString CharId;
|
||||
|
||||
/** Nome do char vindo do DB (`characters.name`). Setado por
|
||||
* AZMMOPlayerCharacter::HandleLocalSpawnReady com o payload do
|
||||
* S_SPAWN_PLAYER. UI prefere isto a APlayerState::GetPlayerName(). */
|
||||
UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity")
|
||||
FString CharName;
|
||||
|
||||
// === Progressao publica ===
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Progress")
|
||||
UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Progress")
|
||||
int32 BaseLevel = 1;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Progress")
|
||||
UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Progress")
|
||||
int32 ClassId = 0;
|
||||
|
||||
// === Guild (futuro) ===
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Guild")
|
||||
UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Guild")
|
||||
FString GuildName;
|
||||
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
/// Atualizado quando S_ATTRIBUTE_SNAPSHOT_FULL chega — UZMMOHudWidget chama
|
||||
/// isso pra refletir os dados publicos do snapshot no PlayerState.
|
||||
/// Setado por AZMMOPlayerCharacter::HandleLocalSpawnReady (pose/EntityId)
|
||||
/// e atualizado por handlers de snapshot/level up para refletir dados
|
||||
/// publicos do char no PlayerState.
|
||||
UFUNCTION(BlueprintCallable, Category = "ZMMO|Identity")
|
||||
void SetPublicIdentity(int64 InEntityId, const FString& InCharId,
|
||||
int32 InBaseLevel, int32 InClassId);
|
||||
|
||||
/// Setado pelo handler do S_CHAR_INFO (chega antes de S_SPAWN_PLAYER) —
|
||||
/// metadados publicos do char vindos do servidor.
|
||||
UFUNCTION(BlueprintCallable, Category = "ZMMO|Identity")
|
||||
void SetCharInfo(const FString& InCharName, const FString& InGuildName);
|
||||
|
||||
/// Template helper: PS->GetZMMOComponent<UZMMOAttributeComponent>()
|
||||
template <typename T>
|
||||
T* GetZMMOComponent() const
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "ZMMOAttributeComponent.h"
|
||||
#include "ZMMOJobDataAsset.h"
|
||||
#include "ZMMOJobsSubsystem.h"
|
||||
#include "ZMMOPlayerState.h"
|
||||
#include "UI/InGame/UIInGameFlowSubsystem.h"
|
||||
#include "UI/InGameTypes.h"
|
||||
#include "UI/Widgets/UIPlayerStatus_BonusRow.h"
|
||||
@@ -87,10 +88,20 @@ void UUIPlayerStatus_Window::BindToLocalPlayer()
|
||||
APlayerController* PC = World->GetFirstPlayerController();
|
||||
if (!PC || !PC->PlayerState) return;
|
||||
|
||||
// CharName vem do PlayerState (estável por sessão) — popula uma vez.
|
||||
// CharName vem do AZMMOPlayerState (setado pelo S_SPAWN_PLAYER no spawn
|
||||
// local). Fallback pra APlayerState::GetPlayerName() se o cast falhar ou
|
||||
// se CharName ainda nao chegou (race com handler que dispara o widget).
|
||||
if (Text_CharName)
|
||||
{
|
||||
Text_CharName->SetText(FText::FromString(PC->PlayerState->GetPlayerName()));
|
||||
FString DisplayName = PC->PlayerState->GetPlayerName();
|
||||
if (const AZMMOPlayerState* ZMMOPs = Cast<AZMMOPlayerState>(PC->PlayerState))
|
||||
{
|
||||
if (!ZMMOPs->CharName.IsEmpty())
|
||||
{
|
||||
DisplayName = ZMMOPs->CharName;
|
||||
}
|
||||
}
|
||||
Text_CharName->SetText(FText::FromString(DisplayName));
|
||||
}
|
||||
|
||||
UZMMOAttributeComponent* Comp = PC->PlayerState->FindComponentByClass<UZMMOAttributeComponent>();
|
||||
|
||||
Reference in New Issue
Block a user