feat(char-spawn): Fase 4 — DT_Maps + pawn reposicionado na pos do DB
Cliente UE (foco da frente):
- FZMMOMapDef + FZMMOMapSpawn (Source/ZMMO/Data/World/MapDef.h) — row do
DT_Maps com MapId/DisplayName/ClientLevel/Spawns[] taggeados
- DT_Maps.uasset (Content/ZMMO/Data/World/) — 1 row "TestWorld" mapId=1
apontando pra L_TestWorld + spawn "default" em (0,0,200)
- L_TestWorld movido: /Game/ThirdPerson/TestWorld -> /Game/ZMMO/Maps/World/L_TestWorld
(75 ext actors WP duplicados, original deletado)
- ZMMOCharSummary.MapName (FString) -> MapId (int32)
- UIUserLobbyScreen_Base parseia uint16 mapId no S_CHAR_SELECT_OK + chama
Flow->TravelToMapById(mapId) + Flow->SetPendingSpawnPose(pos, yaw)
- UIFrontEndFlowSubsystem: MapsTableAsset (config), FindMapDef,
ResolveLevelPathByMapId, TravelToMapById, SetPendingSpawnPose/Consume
- AZMMOPlayerCharacter::BeginPlay: consome PendingSpawnPose do Flow e
SetActorLocationAndRotation no pawn local (substitui PlayerStart default)
- DefaultGame.ini: MapsTableAsset=/Game/ZMMO/Data/World/DT_Maps.DT_Maps
- .gitignore: + .vscode/
Outras mudancas no working tree (paralelas a esta frente):
- Content/AutoCreated/MMOWidget/Widgets/WBP_* (boot/login/charselect/etc.)
- Content/AutoCreated/MMOWidget/Fonts/Font_* + font_*_Face
- Refatoracao de fontes ZMMO/UI/Fonts (FF_* deletadas, F_* + Face)
- Content/__ExternalActors__/ZMMO/Debug/Maps/L_Test_UI/* (75 actors)
- Content/Editor/ZeusUMGForge/FontMap.uasset
- Content/ZMMO/UI/Shared/UI_CheckBox_Master.uasset
- Content/ExternalContent/{Fab,Marketplace,Megascans}/.gitkeep
- Content/ThirdPerson/Menu.umap
This commit is contained in:
92
Source/ZMMO/Data/World/MapDef.h
Normal file
92
Source/ZMMO/Data/World/MapDef.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataTable.h"
|
||||
#include "Engine/World.h"
|
||||
#include "ZoneRow.h"
|
||||
#include "MapDef.generated.h"
|
||||
|
||||
/**
|
||||
* FZMMOMapSpawn
|
||||
*
|
||||
* Ponto de spawn dentro de um mapa, identificado por `Tag`. Usado pelo
|
||||
* server pra decidir onde materializar um char quando ele entra:
|
||||
* - Char sem posicao salva: usa spawn com tag="default"
|
||||
* - Char saindo de raid: pode pedir tag="raid_return"
|
||||
* - Char morto: pode pedir tag="graveyard"
|
||||
*
|
||||
* ZeusEditorTools pode auto-popular o `Spawns[]` do FZMMOMapDef varrendo
|
||||
* todos APlayerStart (ou ator custom AZMMOMapSpawnPoint) presentes no
|
||||
* `.umap` do `ClientLevel` — botao "Sync from level".
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct ZMMO_API FZMMOMapSpawn
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Identificador do spawn (ex.: "default", "raid_return"). "default" e obrigatorio. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spawn")
|
||||
FName Tag = TEXT("default");
|
||||
|
||||
/** Posicao em cm (espaco do mundo). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spawn")
|
||||
FVector PositionCm = FVector::ZeroVector;
|
||||
|
||||
/** Yaw em graus. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spawn")
|
||||
float YawDeg = 0.f;
|
||||
};
|
||||
|
||||
/**
|
||||
* FZMMOMapDef
|
||||
*
|
||||
* Definicao canonica de um mapa do MMO. Linha da DT_Maps no cliente.
|
||||
* Exportada para `Server/ZeusServerEngine/Config/DataTables/maps_config.json`
|
||||
* pelo plugin ZeusEditorTools — o JSON e a fonte que o WorldServer consome.
|
||||
*
|
||||
* Identidade:
|
||||
* - `MapId` (uint16) e a chave estavel cross-cliente/server. 0 = invalido
|
||||
* (detecta bug de leitura de DB / migration esquecida).
|
||||
* - RowName na DataTable e o nome interno (ex.: "TestWorld") — usado no
|
||||
* exporter como `name` do JSON. Nao duplicamos em campo `Name`.
|
||||
*
|
||||
* Wire economy:
|
||||
* - `S_CHAR_SELECT_OK` envia `mapId` como uint16 (2 bytes) em vez de
|
||||
* `mapName` (string), evitando ~10-30 bytes por handoff.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct ZMMO_API FZMMOMapDef : public FTableRowBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** ID estavel uint16. 0 = invalido. int32 aqui pois UE nao expoe uint16 em UPROPERTY. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Map", meta = (ClampMin = "1", ClampMax = "65535"))
|
||||
int32 MapId = 0;
|
||||
|
||||
/** Nome de exibicao localizavel. Aparece em UI/loading screen. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Map")
|
||||
FText DisplayName;
|
||||
|
||||
/** Asset do level no cliente. Exporter converte pra string canonica no JSON do server. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Map|Level")
|
||||
TSoftObjectPtr<UWorld> ClientLevel;
|
||||
|
||||
/**
|
||||
* Pontos de spawn taggeados. Editavel inline na DataTable;
|
||||
* ZeusEditorTools pode auto-popular varrendo PlayerStart do .umap.
|
||||
* Pelo menos um com Tag="default" e esperado.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Map|Spawns")
|
||||
TArray<FZMMOMapSpawn> Spawns;
|
||||
|
||||
/** Regras PvP/Safe/Dungeon. Reusa enum de FZoneRow. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Map")
|
||||
EZoneRules Rules = EZoneRules::PvE;
|
||||
|
||||
/** Faixa de level recomendada — display only. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Map|Display")
|
||||
int32 RecommendedLevelMin = 1;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Map|Display")
|
||||
int32 RecommendedLevelMax = 99;
|
||||
};
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "ZMMO.h"
|
||||
#include "ZMMOWorldSubsystem.h"
|
||||
#include "ZeusNetworkSubsystem.h"
|
||||
#include "UI/FrontEnd/UIFrontEndFlowSubsystem.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogZMMOPlayer);
|
||||
|
||||
@@ -108,6 +109,31 @@ void AZMMOPlayerCharacter::BeginPlay()
|
||||
ResolveZeusNetworkSubsystem();
|
||||
BindZeusSpawnDelegate();
|
||||
TryRegisterLocalEntityFromCachedSpawn();
|
||||
|
||||
// Fase 4: reposiciona o pawn na pos salva no DB (vinda no S_CHAR_SELECT_OK
|
||||
// e memorizada no Flow). Substitui o PlayerStart default do level. Se nao
|
||||
// ha pose pendente (primeiro boot / debug PIE direto no level), mantem o
|
||||
// PlayerStart.
|
||||
if (const UGameInstance* GI = GetGameInstance())
|
||||
{
|
||||
if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem<UUIFrontEndFlowSubsystem>())
|
||||
{
|
||||
FVector PosCm = FVector::ZeroVector;
|
||||
float YawDeg = 0.0f;
|
||||
if (Flow->ConsumePendingSpawnPose(PosCm, YawDeg))
|
||||
{
|
||||
const FRotator NewRot(0.0f, YawDeg, 0.0f);
|
||||
SetActorLocationAndRotation(PosCm, NewRot, /*bSweep=*/false, nullptr, ETeleportType::TeleportPhysics);
|
||||
if (AController* C = GetController())
|
||||
{
|
||||
C->SetControlRotation(NewRot);
|
||||
}
|
||||
UE_LOG(LogZMMOPlayer, Log,
|
||||
TEXT("AZMMOPlayerCharacter: pawn reposicionado pra pos do DB (%s) yaw=%.1f"),
|
||||
*PosCm.ToString(), YawDeg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AZMMOPlayerCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "UIFrontEndFlowSubsystem.h"
|
||||
|
||||
#include "ZMMO.h"
|
||||
#include "Data/World/MapDef.h"
|
||||
#include "UIFrontEndScreenSet.h"
|
||||
#include "UIManagerSubsystem.h"
|
||||
#include "UIPrimaryGameLayout_Base.h"
|
||||
@@ -10,6 +11,7 @@
|
||||
#include "ZeusNetworkSubsystem.h"
|
||||
#include "ZeusCharServerSubsystem.h"
|
||||
#include "CommonActivatableWidget.h"
|
||||
#include "Engine/DataTable.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
@@ -398,3 +400,70 @@ void UUIFrontEndFlowSubsystem::HandleServerHelloTheme(FName ThemeId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const FZMMOMapDef* UUIFrontEndFlowSubsystem::FindMapDef(int32 MapId) const
|
||||
{
|
||||
if (MapId <= 0) return nullptr;
|
||||
if (MapsTableAsset.IsNull())
|
||||
{
|
||||
UE_LOG(LogZMMO, Warning, TEXT("FrontEndFlow: MapsTableAsset nao configurado — adicione DT_Maps em DefaultGame.ini"));
|
||||
return nullptr;
|
||||
}
|
||||
const UDataTable* Table = MapsTableAsset.LoadSynchronous();
|
||||
if (!Table) return nullptr;
|
||||
|
||||
for (const TPair<FName, uint8*>& Row : Table->GetRowMap())
|
||||
{
|
||||
const FZMMOMapDef* Def = reinterpret_cast<const FZMMOMapDef*>(Row.Value);
|
||||
if (Def && Def->MapId == MapId)
|
||||
{
|
||||
return Def;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FString UUIFrontEndFlowSubsystem::ResolveLevelPathByMapId(int32 MapId) const
|
||||
{
|
||||
const FZMMOMapDef* Def = FindMapDef(MapId);
|
||||
if (!Def) return FString();
|
||||
const FSoftObjectPath ObjPath = Def->ClientLevel.ToSoftObjectPath();
|
||||
if (!ObjPath.IsValid()) return FString();
|
||||
return ObjPath.GetLongPackageName();
|
||||
}
|
||||
|
||||
void UUIFrontEndFlowSubsystem::SetPendingSpawnPose(FVector PosCm, float YawDeg)
|
||||
{
|
||||
PendingSpawnPosCm = PosCm;
|
||||
PendingSpawnYawDeg = YawDeg;
|
||||
bHasPendingSpawnPose = true;
|
||||
UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: PendingSpawnPose set pos=(%.1f,%.1f,%.1f) yaw=%.1f"),
|
||||
PosCm.X, PosCm.Y, PosCm.Z, YawDeg);
|
||||
}
|
||||
|
||||
bool UUIFrontEndFlowSubsystem::ConsumePendingSpawnPose(FVector& OutPosCm, float& OutYawDeg)
|
||||
{
|
||||
if (!bHasPendingSpawnPose) return false;
|
||||
OutPosCm = PendingSpawnPosCm;
|
||||
OutYawDeg = PendingSpawnYawDeg;
|
||||
bHasPendingSpawnPose = false;
|
||||
UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: PendingSpawnPose consumido pos=(%.1f,%.1f,%.1f) yaw=%.1f"),
|
||||
OutPosCm.X, OutPosCm.Y, OutPosCm.Z, OutYawDeg);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UUIFrontEndFlowSubsystem::TravelToMapById(int32 MapId)
|
||||
{
|
||||
const FString MapPath = ResolveLevelPathByMapId(MapId);
|
||||
if (MapPath.IsEmpty())
|
||||
{
|
||||
UE_LOG(LogZMMO, Warning, TEXT("FrontEndFlow: TravelToMapById(%d) falhou — sem entrada no DT_Maps"), MapId);
|
||||
return false;
|
||||
}
|
||||
UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: TravelToMapById(%d) -> %s"), MapId, *MapPath);
|
||||
bTravelingToWorld = true;
|
||||
SetState(EZMMOFrontEndState::EnteringWorld);
|
||||
const FString Options = TEXT("game=/Script/ZMMO.ZMMOGameMode");
|
||||
UGameplayStatics::OpenLevel(GetGameInstance(), FName(*MapPath), /*bAbsolute=*/true, Options);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataTable.h"
|
||||
#include "Subsystems/GameInstanceSubsystem.h"
|
||||
#include "UI/FrontEndTypes.h"
|
||||
#include "UIFrontEndFlowSubsystem.generated.h"
|
||||
@@ -9,6 +10,7 @@ class UUIFrontEndScreenSet;
|
||||
class UUIManagerSubsystem;
|
||||
class UZeusNetworkSubsystem;
|
||||
class UZeusCharServerSubsystem;
|
||||
struct FZMMOMapDef;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnZMMOFrontEndStateChanged, EZMMOFrontEndState, NewState);
|
||||
|
||||
@@ -80,6 +82,42 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "FrontEnd|Worlds")
|
||||
void ClearSelectedWorld();
|
||||
|
||||
/**
|
||||
* Resolve `MapId` (uint16 recebido do CharServer) → row do DT_Maps.
|
||||
* Carrega o asset sync (PIE/dev OK; em prod considerar StreamableManager).
|
||||
* Retorna nullptr se MapId=0, DT_Maps nao configurado, ou mapId nao
|
||||
* encontrado.
|
||||
*/
|
||||
const FZMMOMapDef* FindMapDef(int32 MapId) const;
|
||||
|
||||
/**
|
||||
* Helper: resolve `MapId` no DT_Maps e retorna `ClientLevel.ToSoftObjectPath().GetLongPackageName()`
|
||||
* (ex.: `/Game/ThirdPerson/TestWorld`). String vazia se nao encontrado.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "FrontEnd|Maps")
|
||||
FString ResolveLevelPathByMapId(int32 MapId) const;
|
||||
|
||||
/**
|
||||
* Inicia transicao pra mundo dado um `MapId`: faz `OpenLevel` com
|
||||
* `?game=/Script/ZMMO.ZMMOGameMode`. No-op se `MapId` invalido ou
|
||||
* DT_Maps nao tem entrada.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FrontEnd|Maps")
|
||||
bool TravelToMapById(int32 MapId);
|
||||
|
||||
/**
|
||||
* Memoriza a pose autoritativa do char vinda no `S_CHAR_SELECT_OK`
|
||||
* (pos salva no DB + yaw). Sobrevive ao travel porque vivemos no
|
||||
* GameInstance. Lida pelo `AZMMOPlayerCharacter::BeginPlay` pra
|
||||
* reposicionar o pawn local em vez de spawnar no PlayerStart default
|
||||
* do level. Limpa apos consumo (a proxima sessao reenvia).
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FrontEnd|Spawn")
|
||||
void SetPendingSpawnPose(FVector PosCm, float YawDeg);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "FrontEnd|Spawn")
|
||||
bool ConsumePendingSpawnPose(FVector& OutPosCm, float& OutYawDeg);
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "FrontEnd")
|
||||
FOnZMMOFrontEndStateChanged OnStateChanged;
|
||||
|
||||
@@ -88,6 +126,15 @@ protected:
|
||||
UPROPERTY(Config, EditDefaultsOnly, Category = "FrontEnd")
|
||||
TSoftObjectPtr<UUIFrontEndScreenSet> ScreenSetAsset;
|
||||
|
||||
/**
|
||||
* DataTable com FZMMOMapDef. Source of truth client-side para
|
||||
* `MapId -> ClientLevel/spawns/displayName`. Configure em DefaultGame.ini:
|
||||
* [/Script/ZMMO.UIFrontEndFlowSubsystem]
|
||||
* MapsTableAsset=/Game/ZMMO/Data/World/DT_Maps.DT_Maps
|
||||
*/
|
||||
UPROPERTY(Config, EditDefaultsOnly, Category = "FrontEnd|Maps")
|
||||
TSoftObjectPtr<UDataTable> MapsTableAsset;
|
||||
|
||||
private:
|
||||
void BindNetwork();
|
||||
void UnbindNetwork();
|
||||
@@ -127,6 +174,16 @@ private:
|
||||
UPROPERTY(Transient)
|
||||
FString SelectedWorldId;
|
||||
|
||||
/** Pose autoritativa pendente (do S_CHAR_SELECT_OK). */
|
||||
UPROPERTY(Transient)
|
||||
FVector PendingSpawnPosCm = FVector::ZeroVector;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
float PendingSpawnYawDeg = 0.f;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
bool bHasPendingSpawnPose = false;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UUIFrontEndScreenSet> ScreenSet;
|
||||
|
||||
|
||||
@@ -153,6 +153,7 @@ void UUIUserLobbyScreen_Base::ParseCharList(const TArray<uint8>& Payload)
|
||||
uint32 baseLvl = 0, baseExp = 0, jobLvl = 0, jobExp = 0;
|
||||
uint16 s = 0, a = 0, v = 0, in_ = 0, d = 0, l = 0;
|
||||
uint32 hp = 0, maxHp = 0, sp = 0, maxSp = 0, money = 0;
|
||||
uint16 mapId16 = 0;
|
||||
float px = 0, py = 0, pz = 0, yaw = 0;
|
||||
uint32 hair = 0, hairColor = 0, skinColor = 0;
|
||||
uint8 bodyType = 0;
|
||||
@@ -172,7 +173,7 @@ void UUIUserLobbyScreen_Base::ParseCharList(const TArray<uint8>& Payload)
|
||||
|| !ReadU32(Payload, Pos, hp) || !ReadU32(Payload, Pos, maxHp)
|
||||
|| !ReadU32(Payload, Pos, sp) || !ReadU32(Payload, Pos, maxSp)
|
||||
|| !ReadU32(Payload, Pos, money)
|
||||
|| !ReadStringUtf8(Payload, Pos, E.MapName)
|
||||
|| !ReadU16(Payload, Pos, mapId16)
|
||||
|| !ReadFloat(Payload, Pos, px) || !ReadFloat(Payload, Pos, py) || !ReadFloat(Payload, Pos, pz)
|
||||
|| !ReadFloat(Payload, Pos, yaw)
|
||||
|| !ReadU32(Payload, Pos, hair) || !ReadU32(Payload, Pos, hairColor) || !ReadU32(Payload, Pos, skinColor)
|
||||
@@ -190,6 +191,7 @@ void UUIUserLobbyScreen_Base::ParseCharList(const TArray<uint8>& Payload)
|
||||
E.Stats.Int = in_; E.Stats.Dex = d; E.Stats.Luk = l;
|
||||
E.Hp = hp; E.MaxHp = maxHp; E.Sp = sp; E.MaxSp = maxSp;
|
||||
E.Money = static_cast<int32>(money);
|
||||
E.MapId = static_cast<int32>(mapId16);
|
||||
E.Position = FVector(px, py, pz); E.YawDeg = yaw;
|
||||
E.Appearance.Hair = hair; E.Appearance.HairColor = hairColor;
|
||||
E.Appearance.SkinColor = skinColor; E.Appearance.BodyType = bodyType;
|
||||
@@ -325,13 +327,14 @@ void UUIUserLobbyScreen_Base::HandleCharDeleteCancelAck(const TArray<uint8>& Pay
|
||||
|
||||
void UUIUserLobbyScreen_Base::HandleCharSelectOk(const TArray<uint8>& Payload)
|
||||
{
|
||||
// Wire: uint64 charId + string mapName + float x/y/z + float yaw +
|
||||
// Wire: uint64 charId + uint16 mapId + float x/y/z + float yaw +
|
||||
// string worldHost + uint16 worldPort + string handoffToken + string region
|
||||
int32 Pos = 0;
|
||||
uint64 CharId64 = 0; FString MapName, WorldHost, HandoffToken, Region;
|
||||
uint64 CharId64 = 0; FString WorldHost, HandoffToken, Region;
|
||||
uint16 MapId16 = 0;
|
||||
float Px=0, Py=0, Pz=0, Yaw=0; uint16 WorldPort = 0;
|
||||
if (!ReadU64(Payload, Pos, CharId64)
|
||||
|| !ReadStringUtf8(Payload, Pos, MapName)
|
||||
|| !ReadU16(Payload, Pos, MapId16)
|
||||
|| !ReadFloat(Payload, Pos, Px) || !ReadFloat(Payload, Pos, Py) || !ReadFloat(Payload, Pos, Pz)
|
||||
|| !ReadFloat(Payload, Pos, Yaw)
|
||||
|| !ReadStringUtf8(Payload, Pos, WorldHost)
|
||||
@@ -342,19 +345,25 @@ void UUIUserLobbyScreen_Base::HandleCharSelectOk(const TArray<uint8>& Payload)
|
||||
UE_LOG(LogZMMO, Warning, TEXT("Lobby: S_CHAR_SELECT_OK malformado"));
|
||||
return;
|
||||
}
|
||||
UE_LOG(LogZMMO, Log, TEXT("Lobby: handoff -> world=%s:%d map=%s token=%s..."),
|
||||
*WorldHost, WorldPort, *MapName, *HandoffToken.Left(8));
|
||||
UE_LOG(LogZMMO, Log, TEXT("Lobby: handoff -> world=%s:%d mapId=%u token=%s..."),
|
||||
*WorldHost, WorldPort, static_cast<uint32>(MapId16), *HandoffToken.Left(8));
|
||||
|
||||
UGameInstance* GI = GetGameInstance();
|
||||
if (!GI) return;
|
||||
if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem<UUIFrontEndFlowSubsystem>())
|
||||
UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem<UUIFrontEndFlowSubsystem>();
|
||||
if (Flow)
|
||||
{
|
||||
Flow->SetState(EZMMOFrontEndState::EnteringWorld);
|
||||
// Memoriza pose pra `AZMMOPlayerCharacter::BeginPlay` reposicionar
|
||||
// o pawn local na pos salva no DB (em vez do PlayerStart default).
|
||||
Flow->SetPendingSpawnPose(FVector(Px, Py, Pz), Yaw);
|
||||
}
|
||||
|
||||
// Fase 3: handoff UDP — apresenta o ticket no `C_CONNECT_REQUEST`. O
|
||||
// WorldServer valida via GETDEL no Valkey regional. WorldServer envia
|
||||
// `S_CONNECT_OK` (sucesso) ou `S_CONNECT_REJECT` (ticket invalido/expirado).
|
||||
// Disparamos PRIMEIRO o connect (nao-bloqueante), depois o OpenLevel —
|
||||
// ZeusNetworkSubsystem e per-GameInstance e persiste cross-travel.
|
||||
if (UZeusNetworkSubsystem* ZeusNet = GI->GetSubsystem<UZeusNetworkSubsystem>())
|
||||
{
|
||||
ZeusNet->ConnectToZeusServerWithTicket(WorldHost, static_cast<int32>(WorldPort), HandoffToken);
|
||||
@@ -363,6 +372,18 @@ void UUIUserLobbyScreen_Base::HandleCharSelectOk(const TArray<uint8>& Payload)
|
||||
{
|
||||
UE_LOG(LogZMMO, Error, TEXT("Lobby: ZeusNetworkSubsystem ausente — handoff abortado"));
|
||||
}
|
||||
|
||||
// Fase 4: lookup mapId no DT_Maps e dispara OpenLevel pro level certo.
|
||||
// Se DT_Maps nao tem entrada (ou ainda nao foi criada no editor),
|
||||
// caimos no fallback do S_TRAVEL_TO_MAP do server (mapName/mapPath string).
|
||||
if (Flow && MapId16 != 0)
|
||||
{
|
||||
if (!Flow->TravelToMapById(static_cast<int32>(MapId16)))
|
||||
{
|
||||
UE_LOG(LogZMMO, Warning, TEXT("Lobby: mapId=%u nao resolvido no DT_Maps — esperando S_TRAVEL_TO_MAP do server."),
|
||||
static_cast<uint32>(MapId16));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UUIUserLobbyScreen_Base::HandleCharSelectReject(const TArray<uint8>& Payload)
|
||||
|
||||
@@ -84,8 +84,9 @@ struct FZMMOCharSummary
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Char")
|
||||
int32 Money = 0;
|
||||
|
||||
/** ID do mapa (uint16). Resolvido via DT_Maps no cliente. 0 = invalido. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Char")
|
||||
FString MapName;
|
||||
int32 MapId = 0;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Char")
|
||||
FVector Position = FVector::ZeroVector;
|
||||
|
||||
Reference in New Issue
Block a user