feat(client): Fase 3 — handoff UDP no Lobby + entry no mundo de gameplay

Lobby:
- HandleCharSelectOk invoca UZeusNetworkSubsystem->ConnectToZeusServerWithTicket
  (host, port, token) recebido do CharServer no S_CHAR_SELECT_OK.

Server travel:
- HandleServerTravelRequested força ZMMOGameMode via URL option
  (`?game=/Script/ZMMO.ZMMOGameMode`) no OpenLevel, independente do
  GameModeOverride do .umap. Evita herdar ZMMOFrontEndGameMode em mapas
  copiados/baseados em L_FrontEnd.

PlayerController:
- ZMMOPlayerController::BeginPlay força FInputModeGameOnly + cursor off +
  FlushPressedKeys ao tomar posse. Necessário pq UIManagerSubsystem é
  LocalPlayerSubsystem (sobrevive ao travel) e o controller anterior
  (AZMMOFrontEndPlayerController) tinha setado FInputModeUIOnly — sem este
  reset, WASD/Look não chegavam ao pawn no novo mundo.

Assets:
- TestWorld: PlayerStart adicionado, World Settings com GameModeOverride=
  ZMMOGameMode + Default Pawn=ZMMOPlayerCharacter + Controller=
  ZMMOPlayerController.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-22 20:58:48 -03:00
parent 166650671f
commit 99aee52317
8 changed files with 36 additions and 3 deletions

View File

@@ -34,6 +34,20 @@ void AZMMOPlayerController::BeginPlay()
{
Super::BeginPlay();
if (IsLocalPlayerController())
{
// Limpa estado de input herdado da UI do FrontEnd (UIManagerSubsystem
// e LocalPlayerSubsystem — sobrevive ao travel, e o controller anterior
// (AZMMOFrontEndPlayerController) tinha setado FInputModeUIOnly +
// bShowMouseCursor=true). Sem este reset, WASD/Look podem nao chegar
// ao pawn porque o Slate ainda esta com foco no widget antigo.
FInputModeGameOnly InputMode;
InputMode.SetConsumeCaptureMouseDown(true);
SetInputMode(InputMode);
bShowMouseCursor = false;
FlushPressedKeys();
}
if (ShouldUseTouchControls() && IsLocalPlayerController())
{
MobileControlsWidget = CreateWidget<UUserWidget>(this, MobileControlsWidgetClass);

View File

@@ -365,7 +365,15 @@ void UUIFrontEndFlowSubsystem::HandleServerTravelRequested(const FString& MapNam
if (!MapPath.IsEmpty())
{
UGameplayStatics::OpenLevel(GetGameInstance(), FName(*MapPath));
// Forca ZMMOGameMode em qualquer mapa que venha do WorldServer.
// World Settings do .umap podem ter override herdado de L_FrontEnd
// (GameModeOverride=ZMMOFrontEndGameMode) — sobrescrevemos via URL
// option pra nao depender de cada artista configurar.
// Evolucao natural: quando o WorldServer enviar GameMode no
// S_TRAVEL_TO_MAP (wire estendido), trocar este literal por
// `?game=` + valor recebido do server.
const FString Options = TEXT("game=/Script/ZMMO.ZMMOGameMode");
UGameplayStatics::OpenLevel(GetGameInstance(), FName(*MapPath), /*bAbsolute=*/true, Options);
}
}

View File

@@ -6,6 +6,7 @@
#include "UICharCard_Base.h"
#include "UICharacterCreatePage_Base.h"
#include "ZeusCharServerSubsystem.h"
#include "ZeusNetworkSubsystem.h"
#include "CommonTextBlock.h"
#include "Components/PanelWidget.h"
#include "Components/WidgetSwitcher.h"
@@ -431,8 +432,18 @@ void UUIUserLobbyScreen_Base::HandleCharSelectOk(const TArray<uint8>& Payload)
{
Flow->SetState(EZMMOFrontEndState::EnteringWorld);
}
// TODO Fase 1.5C: invocar UZeusNetworkSubsystem->ConnectToWorld(host,port,token)
// quando o WorldServer aceitar tickets do CharServer (handshake estendido).
// 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).
if (UZeusNetworkSubsystem* ZeusNet = GI->GetSubsystem<UZeusNetworkSubsystem>())
{
ZeusNet->ConnectToZeusServerWithTicket(WorldHost, static_cast<int32>(WorldPort), HandoffToken);
}
else
{
UE_LOG(LogZMMO, Error, TEXT("Lobby: ZeusNetworkSubsystem ausente — handoff abortado"));
}
}
void UUIUserLobbyScreen_Base::HandleCharSelectReject(const TArray<uint8>& Payload)