refactor(charserver): World -> Realm + parse gatewayEndpoint + fix PendingLoadingSteps race
- ZMMOWorldEntry -> ZMMORealmEntry (RealmId + GatewayEndpoint single string)
- CharServerOpcodes: S_WORLD_* -> S_REALM_*
- UIServerSelectScreen: parse S_REALM_LIST com gatewayEndpoint
- UIUserLobbyScreen: parse S_CHAR_SELECT_OK, split gatewayEndpoint via FString::Split(":", FromEnd) — IPv6 tolerante
- UICharacterCreatePage: SubmitCreate envia realmId
- ZMMOCharSummary: WorldId -> RealmId
- UIFrontEndFlowSubsystem: GetSelectedRealmId; fix de race em PendingLoadingSteps_ (TSet snapshot via MoveTemp antes de iterar; evita access violation em TConstSetBitIterator durante async lambda)
Conferido E2E: 2 standalone instances entram simultaneamente em Provinces diferentes via Gateway.
This commit is contained in:
@@ -18,10 +18,10 @@ namespace ZMMOCharOp
|
||||
constexpr int32 S_CHAR_AUTH_REJECT = 2002; // uint16(reason)
|
||||
|
||||
// Listagem/criação/seleção de personagem
|
||||
constexpr int32 C_CHAR_LIST_REQUEST = 2010; // uint8 hasWorldFilter [+ uint8[16] worldId]
|
||||
constexpr int32 C_CHAR_LIST_REQUEST = 2010; // uint8 hasRealmFilter [+ uint8[16] realmId]
|
||||
constexpr int32 S_CHAR_LIST = 2011;
|
||||
|
||||
constexpr int32 C_CHAR_CREATE = 2020; // uint8[16] worldId + slot + ...
|
||||
constexpr int32 C_CHAR_CREATE = 2020; // uint8[16] realmId + slot + ...
|
||||
constexpr int32 S_CHAR_CREATE_OK = 2021;
|
||||
constexpr int32 S_CHAR_CREATE_REJECT= 2022;
|
||||
|
||||
@@ -37,14 +37,15 @@ namespace ZMMOCharOp
|
||||
constexpr int32 S_CHAR_SELECT_OK = 2041;
|
||||
constexpr int32 S_CHAR_SELECT_REJECT= 2042;
|
||||
|
||||
// Listagem de mundos (Fase 1 do ARQUITETURA_SERVER_SELECT)
|
||||
constexpr int32 C_WORLD_LIST_REQUEST = 2060; // payload vazio
|
||||
constexpr int32 S_WORLD_LIST = 2061; // uint16 count + entries
|
||||
// Listagem de Realms (renomeado de WORLD_LIST no refator R4 — Server
|
||||
// Meshing). Server-side: `Server/ZeusCharServer/src/services/realm-list.service.ts`.
|
||||
constexpr int32 C_REALM_LIST_REQUEST = 2060; // payload vazio
|
||||
constexpr int32 S_REALM_LIST = 2061; // uint16 count + entries
|
||||
/**
|
||||
* Push do CharServer com update de UM mundo (state/pop/queueLen).
|
||||
* Wire: string worldId + uint16 pop + uint16 queueLen + uint8 state
|
||||
* Push do CharServer com update de UM Realm (state/pop/queueLen).
|
||||
* Wire: string realmId + uint16 pop + uint16 queueLen + uint8 state
|
||||
*/
|
||||
constexpr int32 S_WORLD_STATUS_UPDATE = 2062;
|
||||
constexpr int32 S_REALM_STATUS_UPDATE = 2062;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,11 +69,13 @@ namespace ZMMOCharRejectReason
|
||||
constexpr uint16 AccountLocked = 5;
|
||||
constexpr uint16 AccountSuspended = 6;
|
||||
constexpr uint16 CredentialsAuthDisabled = 7;
|
||||
constexpr uint16 WorldOffline = 8;
|
||||
constexpr uint16 WorldMaintenance = 9;
|
||||
constexpr uint16 WorldFull = 10;
|
||||
constexpr uint16 WorldRegionMismatch = 11;
|
||||
constexpr uint16 WorldNotFound = 12;
|
||||
constexpr uint16 RealmOffline = 8;
|
||||
constexpr uint16 RealmMaintenance = 9;
|
||||
constexpr uint16 RealmFull = 10;
|
||||
constexpr uint16 RealmRegionMismatch = 11;
|
||||
constexpr uint16 RealmNotFound = 12;
|
||||
/** Posicao do char nao esta dentro de nenhuma cell do grid_layout do Realm (dead zone). */
|
||||
constexpr uint16 CharOutsideAnyCell = 13;
|
||||
constexpr uint16 NameInUse = 20;
|
||||
constexpr uint16 InvalidName = 21;
|
||||
constexpr uint16 SlotOccupied = 22;
|
||||
@@ -81,8 +84,8 @@ namespace ZMMOCharRejectReason
|
||||
constexpr uint16 DeleteNotScheduled = 25;
|
||||
}
|
||||
|
||||
/** Estado do mundo no wire (uint8). Espelha `WireWorldState` em CharOpcodes.ts. */
|
||||
namespace ZMMOWireWorldState
|
||||
/** Estado do Realm no wire (uint8). Espelha `WireRealmState` em CharOpcodes.ts. */
|
||||
namespace ZMMOWireRealmState
|
||||
{
|
||||
constexpr uint8 Offline = 0;
|
||||
constexpr uint8 Online = 1;
|
||||
|
||||
@@ -55,10 +55,10 @@ void UUICharacterCreatePage_Base::SubmitCreate()
|
||||
UZeusCharServerSubsystem* Char = GI->GetSubsystem<UZeusCharServerSubsystem>();
|
||||
if (!Flow || !Char) return;
|
||||
|
||||
const FString WorldId = Flow->GetSelectedWorldId();
|
||||
if (WorldId.IsEmpty())
|
||||
const FString RealmId = Flow->GetSelectedRealmId();
|
||||
if (RealmId.IsEmpty())
|
||||
{
|
||||
if (Text_Error) Text_Error->SetText(FText::FromString(TEXT("Mundo nao selecionado.")));
|
||||
if (Text_Error) Text_Error->SetText(FText::FromString(TEXT("Realm nao selecionado.")));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,9 +73,9 @@ void UUICharacterCreatePage_Base::SubmitCreate()
|
||||
const uint8 SlotIdx = 0;
|
||||
|
||||
TArray<uint8> Payload;
|
||||
if (!WriteUuid16(Payload, WorldId))
|
||||
if (!WriteUuid16(Payload, RealmId))
|
||||
{
|
||||
if (Text_Error) Text_Error->SetText(FText::FromString(TEXT("WorldId invalido.")));
|
||||
if (Text_Error) Text_Error->SetText(FText::FromString(TEXT("RealmId invalido.")));
|
||||
return;
|
||||
}
|
||||
WriteUInt8(Payload, SlotIdx);
|
||||
|
||||
@@ -19,7 +19,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FZMMOOnCharCreateCancelled);
|
||||
* S_CHAR_CREATE_OK/REJECT.
|
||||
*
|
||||
* Wire C_CHAR_CREATE (espelha char-create.service.ts):
|
||||
* uint8[16] worldId + uint8 slot + string name + uint16 classId
|
||||
* uint8[16] realmId + uint8 slot + string name + uint16 classId
|
||||
* + uint32 hair + uint32 hairColor + uint32 skinColor + uint8 bodyType
|
||||
*/
|
||||
UCLASS(Abstract, Blueprintable)
|
||||
|
||||
@@ -238,11 +238,18 @@ void UUIFrontEndFlowSubsystem::ResolveAndPushScreen(EZMMOFrontEndState State)
|
||||
// (HandlePostLoadMap/HandlePlayerSpawned podem ter
|
||||
// rodado durante o RequestAsyncLoad). Sem isso o
|
||||
// loading fica eterno se o servidor for rápido demais.
|
||||
for (const FName StepId : PendingLoadingSteps_)
|
||||
//
|
||||
// Snapshot via MoveTemp pra evitar invalidacao do iterator se
|
||||
// outro tick mexer no TSet durante o loop: CreateWeakLambda
|
||||
// garante `this` vivo, NAO garante que membros nao sejam
|
||||
// realocados entre captura e execucao (crash em
|
||||
// TConstSetBitIterator::FindFirstSetBit historico).
|
||||
TSet<FName> StepsSnapshot = MoveTemp(PendingLoadingSteps_);
|
||||
PendingLoadingSteps_.Reset();
|
||||
for (const FName StepId : StepsSnapshot)
|
||||
{
|
||||
Loading->MarkStepDone(StepId);
|
||||
}
|
||||
PendingLoadingSteps_.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -381,18 +388,18 @@ void UUIFrontEndFlowSubsystem::RequestEnterServerSelect()
|
||||
}
|
||||
}
|
||||
|
||||
void UUIFrontEndFlowSubsystem::SetSelectedWorldId(const FString& WorldId)
|
||||
void UUIFrontEndFlowSubsystem::SetSelectedRealmId(const FString& RealmId)
|
||||
{
|
||||
SelectedWorldId = WorldId;
|
||||
UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: mundo selecionado = %s"), *SelectedWorldId);
|
||||
SelectedRealmId = RealmId;
|
||||
UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: realm selecionado = %s"), *SelectedRealmId);
|
||||
}
|
||||
|
||||
void UUIFrontEndFlowSubsystem::ClearSelectedWorld()
|
||||
void UUIFrontEndFlowSubsystem::ClearSelectedRealm()
|
||||
{
|
||||
if (!SelectedWorldId.IsEmpty())
|
||||
if (!SelectedRealmId.IsEmpty())
|
||||
{
|
||||
UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: limpando mundo selecionado (%s)"), *SelectedWorldId);
|
||||
SelectedWorldId.Reset();
|
||||
UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: limpando realm selecionado (%s)"), *SelectedRealmId);
|
||||
SelectedRealmId.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,18 +83,18 @@ public:
|
||||
void RequestEnterServerSelect();
|
||||
|
||||
/**
|
||||
* Memoriza o mundo escolhido na ServerSelect (UUID v4 string). Lido pelo
|
||||
* Memoriza o Realm escolhido na ServerSelect (UUID v4 string). Lido pelo
|
||||
* CharSelect/CharCreate na hora de filtrar/criar personagens. Limpo no
|
||||
* logout/back para ServerSelect.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FrontEnd|Worlds")
|
||||
void SetSelectedWorldId(const FString& WorldId);
|
||||
UFUNCTION(BlueprintCallable, Category = "FrontEnd|Realms")
|
||||
void SetSelectedRealmId(const FString& RealmId);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "FrontEnd|Worlds")
|
||||
FString GetSelectedWorldId() const { return SelectedWorldId; }
|
||||
UFUNCTION(BlueprintPure, Category = "FrontEnd|Realms")
|
||||
FString GetSelectedRealmId() const { return SelectedRealmId; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "FrontEnd|Worlds")
|
||||
void ClearSelectedWorld();
|
||||
UFUNCTION(BlueprintCallable, Category = "FrontEnd|Realms")
|
||||
void ClearSelectedRealm();
|
||||
|
||||
/**
|
||||
* Resolve `MapId` (uint16 recebido do CharServer) → row do DT_Maps.
|
||||
@@ -222,7 +222,7 @@ private:
|
||||
EZMMOFrontEndState CurrentState = EZMMOFrontEndState::None;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FString SelectedWorldId;
|
||||
FString SelectedRealmId;
|
||||
|
||||
/** Pose autoritativa pendente (do S_CHAR_SELECT_OK). */
|
||||
UPROPERTY(Transient)
|
||||
|
||||
@@ -41,17 +41,17 @@ void UUIServerCard_Base::NativeDestruct()
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
|
||||
void UUIServerCard_Base::SetFromEntry(const FZMMOWorldEntry& InEntry)
|
||||
void UUIServerCard_Base::SetFromEntry(const FZMMORealmEntry& InEntry)
|
||||
{
|
||||
Entry = InEntry;
|
||||
|
||||
if (Text_Name)
|
||||
{
|
||||
Text_Name->SetText(FText::FromString(Entry.WorldName));
|
||||
Text_Name->SetText(FText::FromString(Entry.Name));
|
||||
}
|
||||
if (Text_Pop)
|
||||
{
|
||||
Text_Pop->SetText(FText::FromString(FString::Printf(TEXT("%d/%d"), Entry.Population, Entry.Capacity)));
|
||||
Text_Pop->SetText(FText::FromString(FString::Printf(TEXT("%d/%d"), Entry.Pop, Entry.Capacity)));
|
||||
}
|
||||
if (Text_Status)
|
||||
{
|
||||
@@ -60,15 +60,15 @@ void UUIServerCard_Base::SetFromEntry(const FZMMOWorldEntry& InEntry)
|
||||
if (Bar_Pop)
|
||||
{
|
||||
const float Ratio = (Entry.Capacity > 0)
|
||||
? FMath::Clamp(static_cast<float>(Entry.Population) / static_cast<float>(Entry.Capacity), 0.f, 1.f)
|
||||
? FMath::Clamp(static_cast<float>(Entry.Pop) / static_cast<float>(Entry.Capacity), 0.f, 1.f)
|
||||
: 0.f;
|
||||
Bar_Pop->SetPercent(Ratio);
|
||||
}
|
||||
|
||||
// Botao "Selecionar" sempre habilitado — mesmo com world offline,
|
||||
// Botao "Selecionar" sempre habilitado — mesmo com realm offline,
|
||||
// o usuario pode entrar no Lobby pra ver a lista de personagens, criar/
|
||||
// deletar. O gate de "entrar no mundo" fica no proprio Lobby (botao
|
||||
// "Entrar no Servidor"), que checa World.State antes do handoff.
|
||||
// deletar. O gate de "entrar no realm" fica no proprio Lobby (botao
|
||||
// "Entrar no Servidor"), que checa Realm.State antes do handoff.
|
||||
// TODO(ui-system): reativar quando UUIButton_Base voltar a ter RefreshUIStyle.
|
||||
// if (Btn_Enter)
|
||||
// {
|
||||
@@ -78,7 +78,7 @@ void UUIServerCard_Base::SetFromEntry(const FZMMOWorldEntry& InEntry)
|
||||
|
||||
void UUIServerCard_Base::HandleEnterClicked()
|
||||
{
|
||||
OnCardPressed.Broadcast(Entry.WorldId, Entry.State);
|
||||
OnCardPressed.Broadcast(Entry.RealmId, Entry.State);
|
||||
}
|
||||
|
||||
FText UUIServerCard_Base::FormatStatus_Implementation(uint8 State) const
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "ZMMOWorldEntry.h"
|
||||
#include "ZMMORealmEntry.h"
|
||||
#include "UIServerCard_Base.generated.h"
|
||||
|
||||
class UCommonTextBlock;
|
||||
@@ -11,10 +11,10 @@ class UUIButton_Base;
|
||||
class UProgressBar;
|
||||
|
||||
/**
|
||||
* Card de servidor (mundo) instanciado dinamicamente pela ServerSelect.
|
||||
* Card de servidor (realm) instanciado dinamicamente pela ServerSelect.
|
||||
*
|
||||
* Recebe um `FZMMOWorldEntry` e renderiza nome/pop/status/bar; ao clicar
|
||||
* em "Entrar" dispara `OnCardPressed.Broadcast(WorldId, State)`. A tela
|
||||
* Recebe um `FZMMORealmEntry` e renderiza nome/pop/status/bar; ao clicar
|
||||
* em "Entrar" dispara `OnCardPressed.Broadcast(RealmId, State)`. A tela
|
||||
* dona (UUIServerSelectScreen_Base) escuta esse delegate.
|
||||
*
|
||||
* WBP filho (`WBP_ServerCard`) precisa expor (via nomes de variavel):
|
||||
@@ -25,7 +25,7 @@ class UProgressBar;
|
||||
* - Btn_Enter : Button
|
||||
* - Bar_Pop : ProgressBar (opcional)
|
||||
*/
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FZMMOOnServerCardPressed, FString, WorldId, uint8, State);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FZMMOOnServerCardPressed, FString, RealmId, uint8, State);
|
||||
|
||||
UCLASS(Abstract, Blueprintable)
|
||||
class ZMMO_API UUIServerCard_Base : public UUserWidget
|
||||
@@ -34,14 +34,14 @@ class ZMMO_API UUIServerCard_Base : public UUserWidget
|
||||
|
||||
public:
|
||||
/**
|
||||
* Aplica os dados de um mundo no card.
|
||||
* Aplica os dados de um realm no card.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|ServerCard")
|
||||
void SetFromEntry(const FZMMOWorldEntry& Entry);
|
||||
void SetFromEntry(const FZMMORealmEntry& Entry);
|
||||
|
||||
/** Dados atuais (copia). */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|ServerCard")
|
||||
FZMMOWorldEntry Entry;
|
||||
FZMMORealmEntry Entry;
|
||||
|
||||
/**
|
||||
* Disparado quando o botao "Entrar" do card e clicado.
|
||||
|
||||
@@ -70,7 +70,7 @@ void UUIServerSelectScreen_Base::NativeOnActivated()
|
||||
}
|
||||
}
|
||||
|
||||
RequestWorldList();
|
||||
RequestRealmList();
|
||||
}
|
||||
|
||||
void UUIServerSelectScreen_Base::NativeOnDeactivated()
|
||||
@@ -110,23 +110,23 @@ void UUIServerSelectScreen_Base::RefreshUIStyle_Implementation()
|
||||
}
|
||||
}
|
||||
|
||||
void UUIServerSelectScreen_Base::RequestWorldList()
|
||||
void UUIServerSelectScreen_Base::RequestRealmList()
|
||||
{
|
||||
UZeusCharServerSubsystem* Char = GetCharServer();
|
||||
if (!Char || !Char->IsConnected())
|
||||
{
|
||||
UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: CharServer indisponivel — pulando RequestWorldList"));
|
||||
UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: CharServer indisponivel — pulando RequestRealmList"));
|
||||
return;
|
||||
}
|
||||
TArray<uint8> EmptyPayload;
|
||||
Char->SendCharRequest(ZMMOCharOp::C_WORLD_LIST_REQUEST, EmptyPayload);
|
||||
Char->SendCharRequest(ZMMOCharOp::C_REALM_LIST_REQUEST, EmptyPayload);
|
||||
}
|
||||
|
||||
void UUIServerSelectScreen_Base::SelectWorldAndProceed(const FString& WorldId)
|
||||
void UUIServerSelectScreen_Base::SelectRealmAndProceed(const FString& RealmId)
|
||||
{
|
||||
if (WorldId.IsEmpty())
|
||||
if (RealmId.IsEmpty())
|
||||
{
|
||||
UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: SelectWorldAndProceed com WorldId vazio."));
|
||||
UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: SelectRealmAndProceed com RealmId vazio."));
|
||||
return;
|
||||
}
|
||||
UGameInstance* GI = GetGameInstance();
|
||||
@@ -136,18 +136,18 @@ void UUIServerSelectScreen_Base::SelectWorldAndProceed(const FString& WorldId)
|
||||
}
|
||||
if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem<UUIFrontEndFlowSubsystem>())
|
||||
{
|
||||
Flow->SetSelectedWorldId(WorldId);
|
||||
Flow->SetSelectedRealmId(RealmId);
|
||||
Flow->SetState(EZMMOFrontEndState::Lobby);
|
||||
}
|
||||
}
|
||||
|
||||
void UUIServerSelectScreen_Base::HandleCharRawMessage(int32 Opcode, const TArray<uint8>& Payload)
|
||||
{
|
||||
if (Opcode == ZMMOCharOp::S_WORLD_LIST)
|
||||
if (Opcode == ZMMOCharOp::S_REALM_LIST)
|
||||
{
|
||||
ParseWorldList(Payload);
|
||||
ParseRealmList(Payload);
|
||||
}
|
||||
else if (Opcode == ZMMOCharOp::S_WORLD_STATUS_UPDATE)
|
||||
else if (Opcode == ZMMOCharOp::S_REALM_STATUS_UPDATE)
|
||||
{
|
||||
ApplyStatusUpdate(Payload);
|
||||
}
|
||||
@@ -155,24 +155,24 @@ void UUIServerSelectScreen_Base::HandleCharRawMessage(int32 Opcode, const TArray
|
||||
|
||||
void UUIServerSelectScreen_Base::ApplyStatusUpdate(const TArray<uint8>& Payload)
|
||||
{
|
||||
// Wire: string worldId + uint16 pop + uint16 queueLen + uint8 state
|
||||
// Wire: string realmId + uint16 pop + uint16 queueLen + uint8 state
|
||||
int32 Pos = 0;
|
||||
FString WorldId;
|
||||
FString RealmId;
|
||||
uint16 Pop = 0;
|
||||
uint16 QueueLen = 0;
|
||||
uint8 State = 0;
|
||||
if (!ReadStringUtf8(Payload, Pos, WorldId)
|
||||
if (!ReadStringUtf8(Payload, Pos, RealmId)
|
||||
|| !ReadU16(Payload, Pos, Pop)
|
||||
|| !ReadU16(Payload, Pos, QueueLen)
|
||||
|| !ReadU8(Payload, Pos, State))
|
||||
{
|
||||
UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_WORLD_STATUS_UPDATE malformado"));
|
||||
UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_REALM_STATUS_UPDATE malformado"));
|
||||
return;
|
||||
}
|
||||
int32 Idx = INDEX_NONE;
|
||||
for (int32 i = 0; i < Worlds.Num(); ++i)
|
||||
for (int32 i = 0; i < Realms.Num(); ++i)
|
||||
{
|
||||
if (Worlds[i].WorldId == WorldId)
|
||||
if (Realms[i].RealmId == RealmId)
|
||||
{
|
||||
Idx = i;
|
||||
break;
|
||||
@@ -180,76 +180,76 @@ void UUIServerSelectScreen_Base::ApplyStatusUpdate(const TArray<uint8>& Payload)
|
||||
}
|
||||
if (Idx == INDEX_NONE)
|
||||
{
|
||||
// Mundo desconhecido (ainda nao recebemos via S_WORLD_LIST). Pede lista
|
||||
// completa pra trazer dados estaticos (name/host/region/capacity).
|
||||
UE_LOG(LogZMMO, Log, TEXT("ServerSelect: STATUS_UPDATE de mundo desconhecido (%s) — pedindo lista"), *WorldId);
|
||||
RequestWorldList();
|
||||
// Realm desconhecido (ainda nao recebemos via S_REALM_LIST). Pede lista
|
||||
// completa pra trazer dados estaticos (name/gatewayEndpoint/region/capacity).
|
||||
UE_LOG(LogZMMO, Log, TEXT("ServerSelect: STATUS_UPDATE de realm desconhecido (%s) — pedindo lista"), *RealmId);
|
||||
RequestRealmList();
|
||||
return;
|
||||
}
|
||||
FZMMOWorldEntry& Entry = Worlds[Idx];
|
||||
Entry.Population = static_cast<int32>(Pop);
|
||||
FZMMORealmEntry& Entry = Realms[Idx];
|
||||
Entry.Pop = static_cast<int32>(Pop);
|
||||
Entry.QueueLen = static_cast<int32>(QueueLen);
|
||||
Entry.State = State;
|
||||
UE_LOG(LogZMMO, Log, TEXT("ServerSelect: world %s atualizado (state=%d pop=%d)"),
|
||||
*WorldId, State, Entry.Population);
|
||||
UE_LOG(LogZMMO, Log, TEXT("ServerSelect: realm %s atualizado (state=%d pop=%d)"),
|
||||
*RealmId, State, Entry.Pop);
|
||||
// Atualiza apenas o card afetado (preserva animacoes/scroll dos demais).
|
||||
if (SpawnedCards.IsValidIndex(Idx) && SpawnedCards[Idx])
|
||||
{
|
||||
SpawnedCards[Idx]->SetFromEntry(Entry);
|
||||
}
|
||||
OnWorldListReceived();
|
||||
OnRealmListReceived();
|
||||
}
|
||||
|
||||
void UUIServerSelectScreen_Base::ParseWorldList(const TArray<uint8>& Payload)
|
||||
void UUIServerSelectScreen_Base::ParseRealmList(const TArray<uint8>& Payload)
|
||||
{
|
||||
int32 Pos = 0;
|
||||
uint16 Count = 0;
|
||||
if (!ReadU16(Payload, Pos, Count))
|
||||
{
|
||||
UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_WORLD_LIST malformado (sem count)"));
|
||||
UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_REALM_LIST malformado (sem count)"));
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<FZMMOWorldEntry> NewWorlds;
|
||||
NewWorlds.Reserve(Count);
|
||||
TArray<FZMMORealmEntry> NewRealms;
|
||||
NewRealms.Reserve(Count);
|
||||
|
||||
for (uint16 i = 0; i < Count; ++i)
|
||||
{
|
||||
FZMMOWorldEntry Entry;
|
||||
FZMMORealmEntry Entry;
|
||||
FString RegionStr;
|
||||
uint16 Port = 0;
|
||||
uint16 Cap = 0;
|
||||
uint16 Pop = 0;
|
||||
uint16 QueueLen = 0;
|
||||
uint8 State = 0;
|
||||
|
||||
if (!ReadStringUtf8(Payload, Pos, Entry.WorldId)
|
||||
|| !ReadStringUtf8(Payload, Pos, Entry.WorldName)
|
||||
// Wire: string realmId + string name + string region + string gatewayEndpoint
|
||||
// + uint16 capacity + uint16 pop + uint16 queueLen + uint8 state
|
||||
// Server-side: Server/ZeusCharServer/src/services/realm-list.service.ts
|
||||
if (!ReadStringUtf8(Payload, Pos, Entry.RealmId)
|
||||
|| !ReadStringUtf8(Payload, Pos, Entry.Name)
|
||||
|| !ReadStringUtf8(Payload, Pos, RegionStr)
|
||||
|| !ReadStringUtf8(Payload, Pos, Entry.Host)
|
||||
|| !ReadU16(Payload, Pos, Port)
|
||||
|| !ReadStringUtf8(Payload, Pos, Entry.GatewayEndpoint)
|
||||
|| !ReadU16(Payload, Pos, Cap)
|
||||
|| !ReadU16(Payload, Pos, Pop)
|
||||
|| !ReadU16(Payload, Pos, QueueLen)
|
||||
|| !ReadU8(Payload, Pos, State))
|
||||
{
|
||||
UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_WORLD_LIST malformado (entry %d)"), i);
|
||||
UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_REALM_LIST malformado (entry %d)"), i);
|
||||
return;
|
||||
}
|
||||
Entry.Region = RegionStr;
|
||||
Entry.Port = static_cast<int32>(Port);
|
||||
Entry.Capacity = static_cast<int32>(Cap);
|
||||
Entry.Population = static_cast<int32>(Pop);
|
||||
Entry.Pop = static_cast<int32>(Pop);
|
||||
Entry.QueueLen = static_cast<int32>(QueueLen);
|
||||
Entry.State = State;
|
||||
NewWorlds.Add(MoveTemp(Entry));
|
||||
NewRealms.Add(MoveTemp(Entry));
|
||||
}
|
||||
|
||||
Worlds = MoveTemp(NewWorlds);
|
||||
UE_LOG(LogZMMO, Log, TEXT("ServerSelect: recebidos %d mundos"), Worlds.Num());
|
||||
Realms = MoveTemp(NewRealms);
|
||||
UE_LOG(LogZMMO, Log, TEXT("ServerSelect: recebidos %d realms"), Realms.Num());
|
||||
|
||||
RebuildCards();
|
||||
OnWorldListReceived();
|
||||
OnRealmListReceived();
|
||||
}
|
||||
|
||||
void UUIServerSelectScreen_Base::RebuildCards()
|
||||
@@ -280,15 +280,15 @@ void UUIServerSelectScreen_Base::RebuildCards()
|
||||
const int32 GridColumns = 2; // bate com o mock (ColumnFill[0]/[1])
|
||||
|
||||
int32 Index = 0;
|
||||
for (const FZMMOWorldEntry& W : Worlds)
|
||||
for (const FZMMORealmEntry& R : Realms)
|
||||
{
|
||||
UUIServerCard_Base* Card = CreateWidget<UUIServerCard_Base>(this, CardClass);
|
||||
if (!Card)
|
||||
{
|
||||
UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: falha ao criar card para %s"), *W.WorldName);
|
||||
UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: falha ao criar card para %s"), *R.Name);
|
||||
continue;
|
||||
}
|
||||
Card->SetFromEntry(W);
|
||||
Card->SetFromEntry(R);
|
||||
Card->OnCardPressed.AddDynamic(this, &UUIServerSelectScreen_Base::HandleCardPressed);
|
||||
CardContainer->AddChild(Card);
|
||||
|
||||
@@ -311,14 +311,14 @@ void UUIServerSelectScreen_Base::RebuildCards()
|
||||
}
|
||||
}
|
||||
|
||||
void UUIServerSelectScreen_Base::HandleCardPressed(FString WorldId, uint8 State)
|
||||
void UUIServerSelectScreen_Base::HandleCardPressed(FString RealmId, uint8 State)
|
||||
{
|
||||
// O ServerSelect permite escolher qualquer mundo (online/maintenance/
|
||||
// offline) — o usuario entra no Lobby pra gerenciar chars desse mundo.
|
||||
// O bloqueio real (handoff -> world) acontece no botao "Entrar no
|
||||
// Servidor" do Lobby, que checa World.State antes do C_CHAR_SELECT.
|
||||
UE_LOG(LogZMMO, Log, TEXT("ServerSelect: mundo %s selecionado (state=%d)"), *WorldId, State);
|
||||
SelectWorldAndProceed(WorldId);
|
||||
// O ServerSelect permite escolher qualquer realm (online/maintenance/
|
||||
// offline) — o usuario entra no Lobby pra gerenciar chars desse realm.
|
||||
// O bloqueio real (handoff -> gateway) acontece no botao "Entrar no
|
||||
// Servidor" do Lobby, que checa Realm.State antes do C_CHAR_SELECT.
|
||||
UE_LOG(LogZMMO, Log, TEXT("ServerSelect: realm %s selecionado (state=%d)"), *RealmId, State);
|
||||
SelectRealmAndProceed(RealmId);
|
||||
}
|
||||
|
||||
UZeusCharServerSubsystem* UUIServerSelectScreen_Base::GetCharServer() const
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UIActivatableScreen_Base.h"
|
||||
#include "ZMMOWorldEntry.h"
|
||||
#include "ZMMORealmEntry.h"
|
||||
#include "UIServerSelectScreen_Base.generated.h"
|
||||
|
||||
class UCommonTextBlock;
|
||||
@@ -12,15 +12,15 @@ class UUIServerCard_Base;
|
||||
class UZeusCharServerSubsystem;
|
||||
|
||||
/**
|
||||
* Tela de Server Select — Fase 1 do ARQUITETURA_SERVER_SELECT.
|
||||
* Tela de Realm Select — Fase 1 do ARQUITETURA_SERVER_SELECT.
|
||||
*
|
||||
* Fluxo:
|
||||
* 1. NativeOnActivated subscreve OnRawMessage do UZeusCharServerSubsystem
|
||||
* 2. Envia C_WORLD_LIST_REQUEST (payload vazio)
|
||||
* 3. ParseWorldList monta `Worlds`
|
||||
* 2. Envia C_REALM_LIST_REQUEST (payload vazio)
|
||||
* 3. ParseRealmList monta `Realms`
|
||||
* 4. RebuildCards apaga filhos de `CardContainer` e instancia 1 WBP_ServerCard
|
||||
* por mundo, bindando `OnCardPressed -> SelectWorldAndProceed`
|
||||
* 5. Dispara `OnWorldListReceived` (BIE) pra extensoes em BP
|
||||
* por realm, bindando `OnCardPressed -> SelectRealmAndProceed`
|
||||
* 5. Dispara `OnRealmListReceived` (BIE) pra extensoes em BP
|
||||
*/
|
||||
UCLASS(Abstract, Blueprintable)
|
||||
class ZMMO_API UUIServerSelectScreen_Base : public UUIActivatableScreen_Base
|
||||
@@ -29,16 +29,16 @@ class ZMMO_API UUIServerSelectScreen_Base : public UUIActivatableScreen_Base
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|ServerSelect")
|
||||
void RequestWorldList();
|
||||
void RequestRealmList();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|ServerSelect")
|
||||
void SelectWorldAndProceed(const FString& WorldId);
|
||||
void SelectRealmAndProceed(const FString& RealmId);
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|ServerSelect")
|
||||
TArray<FZMMOWorldEntry> Worlds;
|
||||
TArray<FZMMORealmEntry> Realms;
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Zeus|ServerSelect")
|
||||
void OnWorldListReceived();
|
||||
void OnRealmListReceived();
|
||||
|
||||
protected:
|
||||
virtual void NativePreConstruct() override;
|
||||
@@ -50,10 +50,10 @@ protected:
|
||||
void HandleCharRawMessage(int32 Opcode, const TArray<uint8>& Payload);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleCardPressed(FString WorldId, uint8 State);
|
||||
void HandleCardPressed(FString RealmId, uint8 State);
|
||||
|
||||
UZeusCharServerSubsystem* GetCharServer() const;
|
||||
void ParseWorldList(const TArray<uint8>& Payload);
|
||||
void ParseRealmList(const TArray<uint8>& Payload);
|
||||
void ApplyStatusUpdate(const TArray<uint8>& Payload);
|
||||
void RebuildCards();
|
||||
|
||||
|
||||
@@ -66,27 +66,27 @@ void UUIUserLobbyScreen_Base::RequestCharList()
|
||||
UZeusCharServerSubsystem* Char = GetCharServer();
|
||||
if (!Char || !Char->IsConnected()) return;
|
||||
|
||||
FString WorldId;
|
||||
FString RealmId;
|
||||
if (UGameInstance* GI = GetGameInstance())
|
||||
{
|
||||
if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem<UUIFrontEndFlowSubsystem>())
|
||||
{
|
||||
WorldId = Flow->GetSelectedWorldId();
|
||||
RealmId = Flow->GetSelectedRealmId();
|
||||
}
|
||||
}
|
||||
|
||||
// Wire: uint8 hasFilter + (uint8[16] worldId se hasFilter==1)
|
||||
// Wire: uint8 hasFilter + (uint8[16] realmId se hasFilter==1)
|
||||
TArray<uint8> Payload;
|
||||
if (WorldId.IsEmpty())
|
||||
if (RealmId.IsEmpty())
|
||||
{
|
||||
Payload.Add(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Payload.Add(1);
|
||||
if (!WriteUuid16(Payload, WorldId))
|
||||
if (!WriteUuid16(Payload, RealmId))
|
||||
{
|
||||
UE_LOG(LogZMMO, Warning, TEXT("Lobby: SelectedWorldId invalido (%s) — pedindo sem filtro"), *WorldId);
|
||||
UE_LOG(LogZMMO, Warning, TEXT("Lobby: SelectedRealmId invalido (%s) — pedindo sem filtro"), *RealmId);
|
||||
Payload.Reset();
|
||||
Payload.Add(0);
|
||||
}
|
||||
@@ -110,7 +110,7 @@ void UUIUserLobbyScreen_Base::BackToServerSelect()
|
||||
if (!GI) return;
|
||||
if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem<UUIFrontEndFlowSubsystem>())
|
||||
{
|
||||
Flow->ClearSelectedWorld();
|
||||
Flow->ClearSelectedRealm();
|
||||
Flow->SetState(EZMMOFrontEndState::ServerSelect);
|
||||
}
|
||||
}
|
||||
@@ -160,7 +160,7 @@ void UUIUserLobbyScreen_Base::ParseCharList(const TArray<uint8>& Payload)
|
||||
uint64 deleteAt = 0;
|
||||
|
||||
if (!ReadU64(Payload, Pos, charId64)
|
||||
|| !ReadUuid16(Payload, Pos, E.WorldId)
|
||||
|| !ReadUuid16(Payload, Pos, E.RealmId)
|
||||
|| !ReadU8(Payload, Pos, slot)
|
||||
|| !ReadStringUtf8(Payload, Pos, E.Name)
|
||||
|| !ReadU16(Payload, Pos, classId)
|
||||
@@ -328,25 +328,45 @@ void UUIUserLobbyScreen_Base::HandleCharDeleteCancelAck(const TArray<uint8>& Pay
|
||||
void UUIUserLobbyScreen_Base::HandleCharSelectOk(const TArray<uint8>& Payload)
|
||||
{
|
||||
// Wire: uint64 charId + uint16 mapId + float x/y/z + float yaw +
|
||||
// string worldHost + uint16 worldPort + string handoffToken + string region
|
||||
// string gatewayEndpoint + string handoffToken + string region
|
||||
// gatewayEndpoint = "host:port" (UMA single string — refator R4 unificou
|
||||
// host+port). Server-side: char-select.service.ts.
|
||||
int32 Pos = 0;
|
||||
uint64 CharId64 = 0; FString WorldHost, HandoffToken, Region;
|
||||
uint64 CharId64 = 0; FString GatewayEndpoint, HandoffToken, Region;
|
||||
uint16 MapId16 = 0;
|
||||
float Px=0, Py=0, Pz=0, Yaw=0; uint16 WorldPort = 0;
|
||||
float Px=0, Py=0, Pz=0, Yaw=0;
|
||||
if (!ReadU64(Payload, Pos, CharId64)
|
||||
|| !ReadU16(Payload, Pos, MapId16)
|
||||
|| !ReadFloat(Payload, Pos, Px) || !ReadFloat(Payload, Pos, Py) || !ReadFloat(Payload, Pos, Pz)
|
||||
|| !ReadFloat(Payload, Pos, Yaw)
|
||||
|| !ReadStringUtf8(Payload, Pos, WorldHost)
|
||||
|| !ReadU16(Payload, Pos, WorldPort)
|
||||
|| !ReadStringUtf8(Payload, Pos, GatewayEndpoint)
|
||||
|| !ReadStringUtf8(Payload, Pos, HandoffToken)
|
||||
|| !ReadStringUtf8(Payload, Pos, Region))
|
||||
{
|
||||
UE_LOG(LogZMMO, Warning, TEXT("Lobby: S_CHAR_SELECT_OK malformado"));
|
||||
return;
|
||||
}
|
||||
UE_LOG(LogZMMO, Log, TEXT("Lobby: handoff -> world=%s:%d mapId=%u token=%s..."),
|
||||
*WorldHost, WorldPort, static_cast<uint32>(MapId16), *HandoffToken.Left(8));
|
||||
UE_LOG(LogZMMO, Log, TEXT("Lobby: handoff -> realm gateway=%s mapId=%u token=%s..."),
|
||||
*GatewayEndpoint, static_cast<uint32>(MapId16), *HandoffToken.Left(8));
|
||||
|
||||
// Split "host:port" para a API legada do ZeusNetworkSubsystem que ainda
|
||||
// recebe os dois separados. Defensivo: se o server mandar string vazia
|
||||
// ou sem ':' a gente loga e aborta o handoff em vez de tentar conectar
|
||||
// em endereco invalido.
|
||||
FString GwHost;
|
||||
FString GwPortStr;
|
||||
if (!GatewayEndpoint.Split(TEXT(":"), &GwHost, &GwPortStr, ESearchCase::CaseSensitive, ESearchDir::FromEnd))
|
||||
{
|
||||
UE_LOG(LogZMMO, Error, TEXT("Lobby: gatewayEndpoint malformado (sem ':') = %s"), *GatewayEndpoint);
|
||||
return;
|
||||
}
|
||||
int32 GwPort = 0;
|
||||
LexFromString(GwPort, *GwPortStr);
|
||||
if (GwHost.IsEmpty() || GwPort <= 0)
|
||||
{
|
||||
UE_LOG(LogZMMO, Error, TEXT("Lobby: gatewayEndpoint invalido host=%s port=%d"), *GwHost, GwPort);
|
||||
return;
|
||||
}
|
||||
|
||||
UGameInstance* GI = GetGameInstance();
|
||||
if (!GI) return;
|
||||
@@ -360,13 +380,13 @@ void UUIUserLobbyScreen_Base::HandleCharSelectOk(const TArray<uint8>& Payload)
|
||||
}
|
||||
|
||||
// Fase 3: handoff UDP — apresenta o ticket no `C_CONNECT_REQUEST`. O
|
||||
// WorldServer valida via GETDEL no Valkey regional. WorldServer envia
|
||||
// Gateway/ZeusServer valida via GETDEL no Valkey regional. Server 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);
|
||||
ZeusNet->ConnectToZeusServerWithTicket(GwHost, GwPort, HandoffToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -18,13 +18,13 @@ class UZeusCharServerSubsystem;
|
||||
* Tela Lobby (host) — Fase 1.5 do ARQUITETURA_SERVER_SELECT.
|
||||
*
|
||||
* Fluxo:
|
||||
* 1. NativeOnActivated: pega `SelectedWorldId` do FlowSubsystem, envia
|
||||
* C_CHAR_LIST_REQUEST filtrado por esse mundo
|
||||
* 1. NativeOnActivated: pega `SelectedRealmId` do FlowSubsystem, envia
|
||||
* C_CHAR_LIST_REQUEST filtrado por esse realm
|
||||
* 2. Parse S_CHAR_LIST -> Chars[]; instancia 1 WBP_CharCard por entry
|
||||
* 3. Card "Selecionar" -> C_CHAR_SELECT(charId) -> S_CHAR_SELECT_OK ->
|
||||
* handoff UDP pro WorldServer
|
||||
* handoff UDP pro Gateway do Realm
|
||||
* 4. Botao "Criar Personagem" -> mostra page interna `CharacterCreate`
|
||||
* 5. Botao "Voltar" -> ClearSelectedWorld + Flow.SetState(ServerSelect)
|
||||
* 5. Botao "Voltar" -> ClearSelectedRealm + Flow.SetState(ServerSelect)
|
||||
*
|
||||
* Pages internas via UWidgetSwitcher:
|
||||
* - PageIndex 0: Lista de chars (CardContainer)
|
||||
@@ -96,6 +96,9 @@ protected:
|
||||
UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr<UUICharacterCreatePage_Base> CharCreatePage;
|
||||
|
||||
UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr<UCommonTextBlock> Text_Title;
|
||||
// TODO(R4): renomear o widget Text_WorldName -> Text_RealmName no WBP_UserLobby.uasset
|
||||
// tambem; aqui mantivemos o nome antigo pra nao quebrar o BindWidgetOptional ate
|
||||
// alguem reabrir o WBP no editor.
|
||||
UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr<UCommonTextBlock> Text_WorldName;
|
||||
UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr<UUIButton_Base> Btn_Back;
|
||||
UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr<UUIButton_Base> Btn_Create;
|
||||
|
||||
@@ -43,7 +43,7 @@ struct FZMMOCharSummary
|
||||
FString CharId;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Char")
|
||||
FString WorldId;
|
||||
FString RealmId;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Char")
|
||||
int32 Slot = 0;
|
||||
|
||||
48
Source/ZMMO/Game/UI/FrontEnd/ZMMORealmEntry.h
Normal file
48
Source/ZMMO/Game/UI/FrontEnd/ZMMORealmEntry.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ZMMORealmEntry.generated.h"
|
||||
|
||||
/**
|
||||
* Entrada de Realm (cluster Gateway+Provinces) recebida do CharServer via
|
||||
* S_REALM_LIST. Espelha `RealmRecord` em
|
||||
* `Server/ZeusCharServer/src/types/realm.types.ts`.
|
||||
*
|
||||
* `GatewayEndpoint` e' o endereco "host:port" do Gateway do Realm (UMA
|
||||
* single string vinda do server — substitui o par host+port da versao
|
||||
* anterior). Cliente faz split por `:` quando precisa abrir UDP.
|
||||
*
|
||||
* State usa o wire raw (0=Offline, 1=Online, 2=Maintenance — ver
|
||||
* ZMMOWireRealmState em CharServerOpcodes.h).
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FZMMORealmEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm")
|
||||
FString RealmId;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm")
|
||||
FString Name;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm")
|
||||
FString Region;
|
||||
|
||||
/** Endpoint do Gateway no formato "host:port" (ex: "127.0.0.1:7777"). */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm")
|
||||
FString GatewayEndpoint;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm")
|
||||
int32 Capacity = 0;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm")
|
||||
int32 Pop = 0;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm")
|
||||
int32 QueueLen = 0;
|
||||
|
||||
/** 0=Offline, 1=Online, 2=Maintenance (vide ZMMOWireRealmState). */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm")
|
||||
uint8 State = 0;
|
||||
};
|
||||
@@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ZMMOWorldEntry.generated.h"
|
||||
|
||||
/**
|
||||
* Entrada de mundo (server) recebida do CharServer via S_WORLD_LIST.
|
||||
* Espelha `WorldRecord` em `Server/ZeusCharServer/src/types/world.types.ts`.
|
||||
*
|
||||
* State usa o wire raw (0=Offline, 1=Online, 2=Maintenance — ver
|
||||
* ZMMOWireWorldState em CharServerOpcodes.h).
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FZMMOWorldEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|World")
|
||||
FString WorldId;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|World")
|
||||
FString WorldName;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|World")
|
||||
FString Region;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|World")
|
||||
FString Host;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|World")
|
||||
int32 Port = 0;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|World")
|
||||
int32 Capacity = 0;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|World")
|
||||
int32 Population = 0;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|World")
|
||||
int32 QueueLen = 0;
|
||||
|
||||
/** 0=Offline, 1=Online, 2=Maintenance (vide ZMMOWireWorldState). */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|World")
|
||||
uint8 State = 0;
|
||||
};
|
||||
Reference in New Issue
Block a user