Files
ZMMO/ARQUITETURA.md
2026-05-23 02:16:27 -03:00

37 KiB
Raw Blame History

Arquitectura do Cliente ZMMO

Regras de organização do projecto Unreal ZMMO (Content + Source) e tutorial de como adicionar gameplay novo seguindo o padrão. Este documento é normativo. Qualquer estrutura nova no projecto tem de respeitar estas regras, ou justificar a excepção por escrito.

Última actualização: 2026-05-11.

Extensões da arquitetura (documentos normativos paralelos, escopo específico):

  • ARQUITETURA_SERVER_SELECT.md — Server Select, lista de mundos, handoff CharServer↔WorldServer, multi-region, queue, Valkey. Roadmap em 7 fases.
  • ARQUITETURA_CHARACTER_MODEL.md — Stats primários (STR/AGI/VIT/INT/DEX/LUK), classes (Aprendiz → especializações), fórmulas de stats derivados (ATK/MATK/DEF/...), jobs.yml data-driven, stat allocation server-side. Espelha rathena.

Sumário de mudanças (2026-05-11): adicionada secção de temas sazonais de UI (UI/Themes/), com tutorial §4.7, regra de "no hard ref a textura em widget" em §5 e política de activação em §1.10.


1. Princípios fundamentais

Estas são as regras que não negociamos:

  1. Namespace do projecto. Todo o conteúdo do jogo vive sob Content/ZMMO/. Nada de assets soltos na raiz Content/.
  2. Organizar por feature, não por tipo de asset. Não criamos pastas Meshes/, Textures/, Materials/ no nível alto. O Content Browser já filtra por prefixo (SM_, T_, M_...). Pastas existem para refletir domínio de gameplay.
  3. Self-contained por entidade. Cada Mob, NPC ou Item vive numa pasta própria que contém todos os seus assets (malha, animações, materiais, texturas, áudio, VFX). Apagar a pasta = remover a entidade do jogo, sem referências órfãs espalhadas.
  4. Plano (máximo 3 níveis úteis). Se está a aninhar mais que isso, provavelmente está a organizar errado. Excepção: dentro de uma entidade self-contained, sub-pasta Animations/ é permitida quando o número de anims ultrapassa 56.
  5. DataTable manda em catálogo, Blueprint manda em comportamento. Dados tabulares (stats, drops, preços, descrições) vão em Content/ZMMO/Data/**/DT_*.uasset. Comportamento vai em Blueprints herdados das bases em Core/. Nunca misturar.
  6. Structs e Enums vivem em C++. Toda USTRUCT (row de DataTable inclusive) e toda UENUM é declarada em Source/ZMMO/Data/. Blueprint Structs estão proibidos — eles corrompem em refactor e não dão tipos fortes.
  7. Hierarquia de Entity. Tudo que vive no mundo herda da mesma raiz: BP_Entity_Base → (BP_Player_Base | BP_NPC_Base | BP_Mob_Base) → instâncias concretas. Espelha a classe C++ AZMMOEntity em Source/ZMMO/Game/Entity/.
  8. Lazy loading via TSoftObjectPtr. Linhas de DataTable e referências entre features usam soft pointers. Nada de hard ref para BPs de mob/item a partir da DT mestre — caso contrário carregamos o mundo inteiro ao abrir o inventário.
  9. Developers/ e ExternalContent/ fora do namespace ZMMO/. Não entram no cook (Developers/) e não poluem o nosso namespace (ExternalContent/ para Fab/Megascans/Marketplace).
  10. Temas de UI são data-driven, não hard-coded. Widgets não têm hard ref para textura/som sazonal — pedem ao UZMMOThemeSubsystem via uma chave declarada em EZMMOThemeKey (C++). Veja §1.10 abaixo para a política de activação.

1.10. Política de activação de temas

A UI suporta temas (Default, Christmas, Halloween, etc.). A escolha do tema activo segue esta ordem de precedência em runtime:

  1. Override de desenvolvimento (DefaultGame.ini, chave ActiveTheme=). Só lido fora de builds Shipping. Para QA testar Natal em Maio.
  2. Resposta autoritativa do servidor. Quando o servidor Zeus envia o opcode ServerHello com ThemeId, ele vence. É o único caminho válido em produção.
  3. Calendário local do cliente (DT_ThemeCalendar). Fallback antes do login (tela de login/character-select aparece antes do servidor falar) e se o servidor não enviar ThemeId.
  4. DA_Theme_Default. Último recurso. Sempre presente, sempre carregado.

Por que servidor vence: MMO precisa ligar Halloween fora da data canónica, fazer evento-surpresa, A/B test de tema sem patch do cliente.

Por que data local existe: sem ela, jogador que abre o jogo em 24 de Dezembro vê tema Default até autenticar — quebra a magia sazonal.

Por que .ini só fora de Shipping: evita que um jogador edite o ficheiro e force um tema que não devia ter acesso (cosméticos pagos, por exemplo).


2. Estrutura de pastas

2.1. Clients/ZMMO/Content/

Content/
│
├── ZMMO/                              ← namespace do projecto
│   │
│   ├── Core/                          ← bases que outros assets herdam
│   │   ├── Entity/
│   │   │   ├── BP_Entity_Base.uasset          ← herda AZMMOEntity (C++)
│   │   │   └── BPI_Entity.uasset              ← interface comum
│   │   ├── Player/
│   │   │   └── BP_Player_Base.uasset
│   │   ├── NPC/
│   │   │   └── BP_NPC_Base.uasset
│   │   ├── Mob/
│   │   │   ├── BP_Mob_Base.uasset
│   │   │   ├── BT_Mob_Default.uasset          ← Behavior Tree base
│   │   │   └── BTT_Mob_*.uasset               ← Tasks compartilhadas
│   │   ├── Item/
│   │   │   ├── BP_Item_Base.uasset
│   │   │   └── BPI_Item.uasset
│   │   └── Network/                           ← debug do UZMMOWorldSubsystem
│   │
│   ├── Entities/                      ← TODAS as instâncias concretas
│   │   ├── Players/
│   │   │   └── Classes/
│   │   │       ├── Warrior/
│   │   │       │   ├── BP_Player_Warrior.uasset
│   │   │       │   ├── SK_Warrior.uasset
│   │   │       │   ├── ABP_Warrior.uasset
│   │   │       │   ├── M_Warrior.uasset
│   │   │       │   ├── T_Warrior_*.uasset
│   │   │       │   └── Animations/
│   │   │       ├── Mage/
│   │   │       └── Archer/
│   │   │
│   │   ├── NPCs/
│   │   │   ├── Vendor_Blacksmith/
│   │   │   │   ├── BP_NPC_Blacksmith.uasset
│   │   │   │   ├── SK_Blacksmith.uasset
│   │   │   │   ├── ABP_Blacksmith.uasset
│   │   │   │   └── ...
│   │   │   └── Questgiver_Elder/
│   │   │
│   │   └── Mobs/
│   │       ├── Beasts/                        ← família (organiza, não herda)
│   │       │   ├── Wolf_Gray/                 ← pacote completo do mob
│   │       │   │   ├── BP_Mob_Wolf_Gray.uasset
│   │       │   │   ├── SK_Wolf.uasset
│   │       │   │   ├── PHY_Wolf.uasset
│   │       │   │   ├── M_Wolf.uasset
│   │       │   │   ├── MI_Wolf_Gray.uasset
│   │       │   │   ├── T_Wolf_*.uasset
│   │       │   │   ├── Animations/
│   │       │   │   │   ├── ABP_Wolf.uasset
│   │       │   │   │   ├── AS_Wolf_Skel.uasset
│   │       │   │   │   ├── AM_Wolf_AttackBite.uasset
│   │       │   │   │   └── A_Wolf_{Idle,Run,Hit,Death}.uasset
│   │       │   │   ├── Audio/
│   │       │   │   └── VFX/
│   │       │   ├── Bear_Brown/
│   │       │   └── Boar_Wild/
│   │       ├── Undead/
│   │       ├── Humanoids/
│   │       ├── Dragons/
│   │       └── Bosses/
│   │
│   ├── Data/                          ← apenas DataTables (.uasset)
│   │   ├── Items/
│   │   │   ├── DT_Items_Master.uasset
│   │   │   ├── DT_Items_Weapons.uasset
│   │   │   ├── DT_Items_Armor.uasset
│   │   │   ├── DT_Items_Consumables.uasset
│   │   │   └── DT_ItemDrops.uasset
│   │   ├── Mobs/
│   │   │   ├── DT_Mobs_Master.uasset
│   │   │   └── DT_MobSpawns.uasset
│   │   ├── Players/
│   │   │   ├── DT_Classes.uasset
│   │   │   └── DT_ExpCurve.uasset
│   │   ├── NPCs/
│   │   │   ├── DT_NPCs.uasset
│   │   │   └── DT_Dialogs.uasset
│   │   ├── Abilities/
│   │   │   ├── DT_Abilities.uasset
│   │   │   └── DT_StatusEffects.uasset
│   │   ├── Quests/
│   │   │   ├── DT_Quests.uasset
│   │   │   └── DT_QuestRewards.uasset
│   │   └── World/
│   │       └── DT_Zones.uasset
│   │
│   ├── Items/                         ← assets visuais (modelo/icon dos itens)
│   │   ├── Equipment/
│   │   │   ├── Weapons/
│   │   │   │   ├── Sword_Iron/
│   │   │   │   │   ├── SM_Sword_Iron.uasset
│   │   │   │   │   ├── M_Sword_Iron.uasset
│   │   │   │   │   ├── T_Sword_Iron_*.uasset
│   │   │   │   │   └── T_Icon_Sword_Iron.uasset
│   │   │   │   └── Bow_Hunter/
│   │   │   ├── Armor/
│   │   │   └── Accessories/
│   │   ├── Consumables/
│   │   │   ├── Potion_Health/
│   │   │   └── Potion_Mana/
│   │   ├── Materials/
│   │   └── Quest/
│   │
│   ├── Abilities/                     ← VFX/Cues/Montagens (data fica em Data/)
│   │   ├── Combat/  Buffs/  Heals/  Movement/
│   │   └── Shared/
│   │
│   ├── World/
│   │   ├── Maps/                      ← L_World_Open, L_City_*, L_Dungeon_*
│   │   ├── Instances/
│   │   ├── Environment/               ← Architecture/  Nature/  Props/
│   │   └── Interactables/             ← baús, portas, warps, recursos
│   │
│   ├── Maps/                          ← mapas que NÃO são de mundo
│   │   └── FrontEnd/                  ← L_FrontEnd (boot/menu; GameMode via
│   │                                     World Settings = AZMMOFrontEndGameMode)
│   │
│   ├── UI/                            ← HUD, Login, CharacterSelect, Inventory,
│   │   │                                Chat, Party, Guild, Trade, Quest, Map,
│   │   │                                Shared, Icons
│   │   ├── Shared/                      ← WBPs reutilizáveis (UI_Button_Master,
│   │   │                                   UI_Panel_Master, UI_Spinner_Master,
│   │   │                                   UI_CheckBox_Master, UI_Input_Master)
│   │   ├── FrontEnd/                     ← WBP_PrimaryGameLayout, DA_FrontEndScreenSet,
│   │   │                                   telas refinadas (Boot/Login/… vindas do Forge)
│   │   ├── Fonts/                       ← Font_*/FF_* (Cinzel, Rajdhani)
│   │   ├── CommonUI/Style/              ← estilos CommonUI (espelha o Hyper)
│   │   │   ├── Button/                  ← BSB_* (UCommonButtonStyle)
│   │   │   └── Text/                    ← BST_* (UCommonTextStyle)
│   │   └── Themes/                     ← temas sazonais (data-driven, ver §1.10)
│   │       ├── DA_Theme_Default.uasset         ← sempre carregado, fallback
│   │       ├── DA_Theme_Christmas.uasset
│   │       ├── DA_Theme_Halloween.uasset
│   │       ├── DT_ThemeCalendar.uasset         ← datas → ThemeId (fallback local)
│   │       ├── _Shared/                        ← assets reutilizados entre temas
│   │       ├── Default/                        ← assets do tema base
│   │       │   ├── HUD/   Login/   Inventory/
│   │       │   ├── Audio/ MS_MainMenu.uasset
│   │       │   └── VFX/
│   │       ├── Christmas/                      ← sazonal
│   │       │   ├── HUD/   Login/   Inventory/
│   │       │   ├── Audio/ MS_MainMenu_Christmas.uasset
│   │       │   └── VFX/   NS_Snow.uasset
│   │       └── Halloween/  ...                 ← mesma estrutura
│   ├── Effects/                       ← VFX globais (Niagara)
│   ├── Audio/                         ← Music, Ambient, SFX, Voice
│   ├── Input/                         ← Enhanced Input
│   ├── Cinematics/                    ← Sequencer
│   ├── Movement/                      ← Mounts, Transport
│   ├── Localization/                  ← StringTables
│   └── Debug/                         ← Maps de smoke test
│
├── Developers/                        ← rascunho por dev (não cooka)
│   └── <nick-do-dev>/
├── AutoCreated/                       ← saída CRUA do Zeus UMG Forge (HTML→WBP).
│   └── <batch>/                         Quarentena: refina-se e MOVE p/ ZMMO/UI/
│                                        FrontEnd/ antes de produção (não cooka cru)
├── ExternalContent/                   ← Fab / Megascans / Marketplace
│   ├── Fab/  Megascans/  Marketplace/
├── Collections/
└── __ExternalActors__/  __ExternalObjects__/   ← World Partition (não mexer)

2.2. Clients/ZMMO/Source/ZMMO/

Source/ZMMO/
├── ZMMO.Build.cs
├── ZMMO.h
├── ZMMO.cpp
│
├── Game/                              ← gameplay runtime (já existe)
│   ├── Entity/                        ← AZMMOEntity + derivados
│   ├── Controller/                    ← AZMMOPlayerController,
│   │                                    AZMMOFrontEndPlayerController
│   ├── Modes/                         ← GameMode + GameInstance,
│   │                                    AZMMOFrontEndGameMode
│   ├── Network/                       ← UZMMOWorldSubsystem
│   └── UI/                            ← runtime de UI (não é contrato de dados)
│       ├── ZMMOThemeSubsystem.*       ← resolução de tema
│       ├── Widgets/                   ← UUIButton_Base, UUIPanel_Base,
│       │                                UUISpinner_Base, UUICheckBox_Base,
│       │                                UUIInput_Base (variantes Box/Outline/
│       │                                Underline/Search), UUICommonText_Base
│       │                                (EUITextRole → FUIStyle.Text)
│       └── FrontEnd/                  ← infra de navegação CommonUI:
│                                        UUIActivatableScreen_Base,
│                                        UUIBootScreen_Base (1ª tela),
│                                        UUILoginScreen_Base (2ª tela),
│                                        CharServerOpcodes.h (faixa 2000-2099),
│                                        UUIPrimaryGameLayout_Base,
│                                        UUIManagerSubsystem (LocalPlayer),
│                                        UUIFrontEndFlowSubsystem (GameInstance),
│                                        UUIFrontEndScreenSet (UDataAsset)
│
└── Data/                              ← contrato de dados (USTRUCT + UENUM)
    ├── ZMMOData.h                     ← header agregador (opcional)
    ├── Items/
    │   ├── ItemRow.h                  ← FItemRow : public FTableRowBase
    │   └── ItemTypes.h                ← E_ItemType, E_ItemRarity, E_EquipSlot
    ├── Mobs/
    │   ├── MobRow.h                   ← FMobRow
    │   └── MobTypes.h                 ← E_MobFaction, E_MobBehavior
    ├── Players/
    │   ├── ClassRow.h                 ← FClassRow
    │   └── StatBlock.h                ← FStatBlock (HP/MP/Sta/Atk/Def…)
    ├── NPCs/
    │   ├── NPCRow.h
    │   └── DialogRow.h
    ├── Abilities/
    │   ├── AbilityRow.h
    │   └── StatusEffectRow.h
    ├── Quests/
    │   └── QuestRow.h
    ├── UI/
    │   ├── ThemeKeys.h                 ← EZMMOThemeKey (HUD_Frame, Login_BG…)
    │   ├── ThemeRow.h                  ← FThemeEntry (Key → SoftAsset)
    │   ├── ThemeCalendarRow.h          ← FThemeCalendarRow (data → ThemeId)
    │   ├── FrontEndTypes.h             ← EZMMOFrontEndState, EZMMOLobbyPage
    │   └── UILayerTags.h/.cpp          ← GameplayTags nativos UI.Layer.*
    └── World/
        └── ZoneRow.h

Regra: toda DT em Content/ZMMO/Data/X/ tem a Row Structure definida no .h correspondente em Source/ZMMO/Data/X/. Espelhamento 1-para-1 nos nomes das sub-pastas.

O subsystem do tema (UZMMOThemeSubsystem) vive em Source/ZMMO/Game/UI/ — é gameplay/runtime, não contrato de dados, por isso não fica em Data/.


3. Convenções de nomenclatura

3.1. Pastas

  • PascalCase, sem espaços, sem acentos.
  • Nome no singular para categoria conceitual (Mob, Item, Quest).
  • Nome no plural para colecção de instâncias (Mobs/, Items/, Quests/).
  • Família de mob/item usa nome temático: Beasts/, Undead/, Weapons/.
  • Instância individual usa Tipo_Variante: Wolf_Gray, Sword_Iron, Vendor_Blacksmith.

3.2. Assets (prefixo obrigatório)

Prefixo Tipo Exemplo
BP_ Blueprint Class BP_Mob_Wolf_Gray
BPI_ Blueprint Interface BPI_Entity
UI_ Widget Blueprint (estilo Hyper) UI_Button_Base, UI_Inventory_Slot
ABP_ Animation Blueprint ABP_Wolf
BT_ Behavior Tree BT_Mob_Default
BTT_ Behavior Tree Task BTT_Mob_ChasePlayer
BTS_ Behavior Tree Service BTS_Mob_FindTarget
BTD_ Behavior Tree Decorator BTD_Mob_CanSeeTarget
BB_ Blackboard BB_Mob_Default
SK_ Skeletal Mesh SK_Wolf
SKEL_ Skeleton SKEL_Wolf
SM_ Static Mesh SM_Sword_Iron
PHY_ Physics Asset PHY_Wolf
M_ Material M_Wolf
MI_ Material Instance MI_Wolf_Gray
MF_ Material Function MF_NormalBlend
T_ Texture T_Wolf_Diffuse
RT_ Render Target RT_MiniMap
Font_ Font (UFont, Composite) Font_Cinzel
FF_ Font Face (UFontFace, Inline) FF_Cinzel_Bold
BSB_ Common Button Style (UCommonButtonStyle) BSB_Button_Transparent
BST_ Common Text Style (UCommonTextStyle) BST_Button
NS_ Niagara System NS_Wolf_Death
NE_ Niagara Emitter NE_Sparks_Small
SC_ Sound Cue SC_Wolf_Growl
SW_ Sound Wave SW_Wolf_Growl_01
MS_ MetaSound Source MS_AmbientForest
MSA_ Mix MetaSound Asset MSA_CombatMix
A_ Animation Sequence A_Wolf_Idle
AM_ Animation Montage AM_Wolf_AttackBite
AS_ Anim State / Skeleton ref AS_Wolf_Skel
IA_ Input Action IA_Move
IMC_ Input Mapping Context IMC_Default
DA_ Data Asset (PrimaryDataAsset) DA_Quest_FirstSteps
DT_ Data Table DT_Items_Master
CT_ Curve Table CT_DamageFalloff
C_ Curve C_AttackPower
L_ Level / Map L_World_Open
LS_ Level Sequence LS_OpeningCinematic
GE_ Gameplay Effect (se GAS) GE_StatusBleed
GA_ Gameplay Ability (se GAS) GA_Fireball
GC_ Gameplay Cue (se GAS) GC_Hit_Blood

3.3. C++ (Source/ZMMO/Data/)

  • Struct: F<Nome>Row quando é linha de DataTable. Ex: FItemRow, FMobRow.
  • Struct genérica (não-DT): F<Nome>. Ex: FStatBlock.
  • Enum: E<Nome>. Sempre UENUM(BlueprintType) e tipado: enum class EItemType : uint8.
  • Header path espelha a pasta: Source/ZMMO/Data/Items/ItemRow.h#include "Data/Items/ItemRow.h".

Exceção — classes base de UI (widgets): a família de UI não leva o prefixo ZMMO (ao contrário de AZMMOEntity/UZMMOGameInstance). Classe C++ base de widget = UUI<Componente>_Base, sempre UCLASS(Abstract) (não instanciável direto; só os filhos derivam — padrão _Abstract do Hyper). Ex.: UUIButton_Base, UUIPanel_Base, UUIProgressBar_Base.

Sub-regra — infra de navegação de front-end: as classes que orquestram a navegação CommonUI seguem o mesmo prefixo UUI (sem ZMMO) por coerência com a família de UI, mesmo não sendo widgets. Assim: o layout raiz é UUIPrimaryGameLayout_Base (Abstract, widget), a base de tela é UUIActivatableScreen_Base (Abstract, widget), e os subsistemas de UI são UUIManagerSubsystem (ULocalPlayerSubsystem) e UUIFrontEndFlowSubsystem (UGameInstanceSubsystem). Atores de gameplay do front-end (GameMode, PlayerController) mantêm o prefixo AZMMO — não são UI, espelham AZMMOGameMode: AZMMOFrontEndGameMode, AZMMOFrontEndPlayerController.

Regra do UMG (importante): um Widget Blueprint não pode herdar de outro Widget Blueprint que tenha árvore de widgets (UMG aborta a compilação: "apenas um deles deve ter árvore"). Portanto a camada abstrata é só a classe C++ UUI<X>_Base. O WBP concreto (prefixo UI_, ver §3.2) herda direto da classe C++: UUIButton_Base (C++ Abstract) → UI_Button_Master (WBP, árvore própria). Variantes por feature são WBPs irmãos sobre a mesma classe C++ (UI_Button_<Feature> : UUIButton_Base), cada um com a própria árvore (montada via set_widget_tree) — nunca encadeando WBPs.


4. Tutoriais

4.1. Adicionar um Mob novo (ex: Wolf_Gray)

Pré-requisito: já existe FMobRow em Source/ZMMO/Data/Mobs/MobRow.h e DT_Mobs_Master em Content/ZMMO/Data/Mobs/.

  1. Criar a pasta Content/ZMMO/Entities/Mobs/Beasts/Wolf_Gray/.
  2. Importar todos os assets do lobo dentro dela:
    • SK_Wolf, SKEL_Wolf, PHY_Wolf.
    • T_Wolf_Diffuse, T_Wolf_Normal, T_Wolf_ORM.
    • M_Wolf e a variante MI_Wolf_Gray.
    • Animations/: ABP_Wolf, AS_Wolf_Skel, A_Wolf_*, AM_Wolf_*.
    • Audio/: SC_Wolf_Growl, SC_Wolf_Footstep.
    • VFX/: NS_Wolf_Death.
  3. Criar BP_Mob_Wolf_Gray herdando de Core/Mob/BP_Mob_Base.
  4. Atribuir no BP: SK_Wolf no Mesh, ABP_Wolf no AnimClass, BT_Mob_Default (ou um BT próprio se necessário) no AIController.
  5. Abrir Content/ZMMO/Data/Mobs/DT_Mobs_Master.uasset. Adicionar linha Wolf_Gray:
    • BlueprintRef = BP_Mob_Wolf_Gray (via TSoftObjectPtr — soft).
    • Stats = FStatBlock com HP/Atk/Def adequados.
    • Faction = EMobFaction::Hostile.
    • DropTableRow = WolfDrops (linha em DT_ItemDrops).
  6. Se for dropar loot novo, abrir DT_ItemDrops e adicionar a linha WolfDrops com array de FItemRow ids + chances.

Regra: nunca colocar um asset de Wolf_Gray fora de Entities/Mobs/Beasts/Wolf_Gray/. Apagar a pasta tem de remover o mob por inteiro.

4.2. Adicionar um Item novo (ex: Sword_Iron)

Pré-requisito: FItemRow em Source/ZMMO/Data/Items/ItemRow.h, DT_Items_Master em Content/ZMMO/Data/Items/.

  1. Criar Content/ZMMO/Items/Equipment/Weapons/Sword_Iron/.
  2. Importar SM_Sword_Iron, texturas T_Sword_Iron_*, material M_Sword_Iron, e o ícone T_Icon_Sword_Iron.
  3. Abrir DT_Items_Master. Adicionar linha Sword_Iron:
    • Type = EItemType::Weapon.
    • Rarity = EItemRarity::Common.
    • EquipSlot = EEquipSlot::MainHand.
    • WorldMesh = SM_Sword_Iron (soft).
    • Icon = T_Icon_Sword_Iron (soft).
    • DisplayName, Description via StringTable em Localization/.

Sem Blueprint por item. O item é "data only" — quando precisar de comportamento especial (ex: poção que cura), criar BP_Item_Potion_Health herdando de Core/Item/BP_Item_Base e apontar a coluna BehaviorClass da linha para esse BP. A maioria dos itens não tem BP próprio.

4.3. Adicionar uma Classe de Player (ex: Warrior)

  1. Criar Content/ZMMO/Entities/Players/Classes/Warrior/.
  2. Importar SK_Warrior, anims, materiais, etc. (mesmo padrão self-contained do mob).
  3. Criar BP_Player_Warrior herdando de Core/Player/BP_Player_Base.
  4. Adicionar linha Warrior em DT_Classes:
    • BlueprintRef = BP_Player_Warrior (soft).
    • BaseStats = FStatBlock.
    • LevelUpStats = FStatBlock (delta por nível).
    • StartingAbilities = TArray<FName> de ids em DT_Abilities.

4.4. Adicionar um NPC (ex: Vendor_Blacksmith)

  1. Criar Content/ZMMO/Entities/NPCs/Vendor_Blacksmith/ (self-contained).
  2. Criar BP_NPC_Blacksmith herdando de Core/NPC/BP_NPC_Base.
  3. Adicionar linha Vendor_Blacksmith em DT_NPCs:
    • BlueprintRef = BP_NPC_Blacksmith (soft).
    • Role = ENPCRole::Vendor.
    • ShopInventory = TArray<FName> de ids em DT_Items_Master.
    • DialogRoot = id em DT_Dialogs.

4.5. Adicionar uma Row Struct em C++

Exemplo: nova struct FCraftRecipeRow para receitas de crafting.

  1. Criar Source/ZMMO/Data/Crafting/CraftRecipeRow.h:
#pragma once

#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "CraftRecipeRow.generated.h"

USTRUCT(BlueprintType)
struct ZMMO_API FCraftRecipeRow : public FTableRowBase
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    FName OutputItem;

    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    int32 OutputQuantity = 1;

    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    TArray<FName> Ingredients;
};
  1. Recompilar o módulo ZMMO.
  2. No editor: Content/ZMMO/Data/Crafting/Add → Miscellaneous → Data Table → escolher CraftRecipeRow como Row Structure → salvar como DT_Crafting_Recipes.

4.6. Conectar uma DataTable a um Blueprint em runtime

// Em BP_Mob_Base (ou na sua classe C++ AZMMOMobBase):
//
// UPROPERTY(EditDefaultsOnly, Category="Data")
// TSoftObjectPtr<UDataTable> MobsTable;
//
// UPROPERTY(EditAnywhere, Category="Data")
// FName MobId;
//
// void AZMMOMobBase::BeginPlay()
// {
//     Super::BeginPlay();
//     if (UDataTable* Table = MobsTable.LoadSynchronous())
//     {
//         if (FMobRow* Row = Table->FindRow<FMobRow>(MobId, TEXT("Mob init")))
//         {
//             ApplyStats(Row->Stats);
//             Faction = Row->Faction;
//             DropTableRowId = Row->DropTableRow;
//         }
//     }
// }

Regra: o MobId é setado pelo UZMMOWorldSubsystem quando o servidor envia o opcode de spawn. O cliente nunca instancia mob "por nome" — sempre via id.

4.7. Adicionar um tema sazonal (ex: Christmas)

Pré-requisito: já existem EZMMOThemeKey em Source/ZMMO/Data/UI/ThemeKeys.h, FThemeEntry em ThemeRow.h, e o UZMMOThemeSubsystem em Source/ZMMO/Game/UI/.

  1. Criar a pasta Content/ZMMO/UI/Themes/Christmas/ com sub-pastas que espelham as features de UI a substituir: HUD/, Login/, Inventory/, Audio/, VFX/. Não precisa criar sub-pastas para features que o tema não altera.
  2. Importar os assets variantes lá dentro. Manter os mesmos nomes que os equivalentes em Themes/Default/ ajuda a comparar:
    • Themes/Default/HUD/T_HUD_Frame.uassetThemes/Christmas/HUD/T_HUD_Frame.uasset.
  3. Criar DA_Theme_Christmas.uasset em Content/ZMMO/UI/Themes/ como PrimaryDataAsset herdando da classe UZMMOThemeDataAsset (a classe vive em Source/ZMMO/Game/UI/).
  4. Preencher o TMap<EZMMOThemeKey, FThemeEntry> do data asset apenas com as chaves que o tema sobrescreve. As que ficam vazias caem automaticamente para DA_Theme_Default.
  5. Adicionar linha em DT_ThemeCalendar:
    • ThemeId = Christmas.
    • StartDate = 12-15.
    • EndDate = 01-05.
    • Priority = 10 (caso duas datas se sobreponham).
  6. No servidor Zeus, quando quiser activar o tema (autoritativo), enviar o opcode ServerHello com ThemeId = "Christmas". Em ausência disso, o cliente usa o calendário do passo 5.

Adicionar uma chave nova de tema (ex: tela de loading sazonal):

  1. Abrir Source/ZMMO/Data/UI/ThemeKeys.h e adicionar o valor ao enum:
    UENUM(BlueprintType)
    enum class EZMMOThemeKey : uint8
    {
        HUD_Frame,
        Login_Background,
        Login_Logo,
        Inventory_SlotEmpty,
        LoadingScreen,      // <-- nova chave
        MainMenu_Music,
    };
    
  2. Recompilar o módulo ZMMO.
  3. Abrir DA_Theme_Default no editor e preencher a nova chave obrigatoriamente (Default tem de ter todas).
  4. Preencher nos temas sazonais apenas se quiser substituir.

Regra de ouro do tema: widget consome via subsystem, nunca por hard ref.

// Em vez de:
// UPROPERTY(EditAnywhere) TObjectPtr<UTexture2D> HudFrameTex;
//
// Faz:
// const UZMMOThemeSubsystem* Theme = GetGameInstance()->GetSubsystem<UZMMOThemeSubsystem>();
// SetBrushFromSoftTexture(Theme->GetTexture(EZMMOThemeKey::HUD_Frame));

O subsystem aplica fallback internamente: pergunta ao tema activo; se a chave não estiver preenchida, devolve a do Default.

4.8. Adicionar uma tela de front-end (ex: Login)

Pré-requisito: já existem UUIActivatableScreen_Base e UUIFrontEndFlowSubsystem em Source/ZMMO/Game/UI/FrontEnd/, o WBP_PrimaryGameLayout e o DA_FrontEndScreenSet em Content/ZMMO/UI/FrontEnd/.

Fluxo geral: Boot → Connecting → Login → ServerSelect → Lobby → EnteringWorld → InWorld. O Lobby é o hub principal logado; suas páginas internas (CharacterSelect, CharacterCreate, Shop, Settings…) são EZMMOLobbyPage, navegadas por um stack CommonUI dentro da tela do Lobby — não são estados de topo do fluxo.

Rede do pré-login: o UUIFrontEndFlowSubsystem dirige a conexão ao UZeusCharServerSubsystem (WebSocket, CharServerUrl de Project Settings, default ws://127.0.0.1:7100/zeus) ao entrar em Boot — NÃO o UZeusNetworkSubsystem (UDP, world server, usado só no travel EnteringWorld→InWorld). A tela Boot (UUIBootScreen_Base) observa o CharServer: libera o botão "Iniciar" no OnConnected; o clique chama UUIFrontEndFlowSubsystem::RequestEnterLogin() → estado Login. Tela nunca segura hard-ref de asset de tema (cores via FUIStyle; §5).

Login: a tela UUILoginScreen_Base autentica pelo MESMO WebSocket do CharServer enviando C_CHAR_AUTH_REQUEST (opcode 2000, ver CharServerOpcodes.h) com um token. Escopo atual = dev: token dev:<n> aceito pelo StubTokenValidator (senha ignorada no stub). S_CHAR_AUTH_OKRequestEnterServerSelect() → estado ServerSelect; S_CHAR_AUTH_REJECT → mensagem de erro na própria tela. Auth real (HTTP /auth/login + JWT) fica para tarefa dedicada. "Voltar" → RequestBack() (Login→Boot, CharServer permanece conectado).

  1. Prototipar a tela em HTML/CSS em Tools/Templates/MMO_Widget/ (ou usar os templates já existentes).
  2. Rodar o Zeus UMG Forge (HTML→UMG). Saída crua: Content/AutoCreated/<batch>/.
  3. Abrir o WBP_* gerado e reparent a classe pai para UUIActivatableScreen_Base (a base é UCommonActivatableWidget; o WBP tem árvore própria — respeita §3.3, nunca encadeia WBP→WBP).
  4. Reconectar tema: o Forge gera hard ref de textura (proibido §5). Substituir por consumo via UZMMOThemeSubsystem por EZMMOThemeKey no override de RefreshUIStyle; remontar botões/painéis com os widgets compartilhados (UI_Button_Master, UI_Panel_Master).
  5. Mover o asset refinado de Content/AutoCreated/… para Content/ZMMO/UI/FrontEnd/ (o cru não cooka).
  6. Registrar no DA_FrontEndScreenSet: StateScreens[EZMMOFrontEndState::Login] = soft class do WBP refinado (páginas do Lobby vão no map de EZMMOLobbyPage da tela de Lobby). Sem registro, o flow faz no-op + log.

Regra: uma tela nunca segura hard ref a asset de tema (textura/som/VFX) — sempre via UZMMOThemeSubsystem por EZMMOThemeKey (igual §4.7 e §5).


5. O que NÃO fazer

  • Não criar pastas Meshes/, Textures/, Materials/ no nível alto.
  • Não criar Blueprint Struct no editor. Toda struct é C++.
  • Não referenciar BPs de mob/item com TObjectPtr<> (hard ref) a partir de DataTables ou Subsystems globais — usar TSoftObjectPtr<>.
  • Não misturar assets de uma entidade com assets de outra. Cada Wolf_Gray, Sword_Iron, etc. é uma pasta isolada.
  • Não colocar nada na raiz Content/. Tudo é Content/ZMMO/... ou Content/{Developers,AutoCreated,ExternalContent}/.... AutoCreated/ é quarentena do Zeus UMG Forge: refina-se e move para ZMMO/UI/FrontEnd/ antes de produção; o cru não cooka.
  • Não importar conteúdo do Fab/Megascans directo para dentro de ZMMO/. Vai sempre para ExternalContent/ e, se quiser usar, referencia dali.
  • Não colocar dados tabulares em Blueprint (lista hardcoded de itens, array de stats no ABP). Move para DataTable.
  • Não duplicar FStatBlock em C++ se já existe — importar de Source/ZMMO/Data/Players/StatBlock.h.
  • Não colocar hard ref de textura/som de tema dentro de um Widget (TObjectPtr<UTexture2D> ChristmasFrame é proibido). Widgets pedem ao UZMMOThemeSubsystem via EZMMOThemeKey. Hard ref a asset de tema bloqueia a troca em runtime e quebra a política de fallback. Vale também para telas de front-end (Boot/Login/ServerSelect/Lobby/…): a saída crua do Forge traz hard ref — tem de ser reconectada ao subsystem antes do merge (ver §4.8).
  • Não criar chave de tema "ad-hoc" como FName("HUD.Frame"). Toda chave é entrada do enum C++ EZMMOThemeKey. String literal é proibida — o compilador tem de quebrar quando alguém esquecer de adicionar a chave.
  • Não preencher um tema sazonal com todas as chaves só porque o Default preenche. Tema sazonal só lista o que substitui; o resto cai no Default automaticamente.

6. Excepções permitidas

  • Dentro de uma entidade self-contained, sub-pasta Animations/ é OK quando passa de 56 anims. O mesmo vale para Audio/ e VFX/ próprios da entidade.
  • Items/.../Equipment/Weapons/Sword_Iron/ mantém o ícone T_Icon_* junto do mesh — é a única excepção em que textura e mesh ficam na mesma pasta raiz da entidade (porque o ícone é parte do item, não um asset solto).
  • Skeleton compartilhado entre variantes (ex: Wolf_Gray, Wolf_Black) pode viver em Entities/Mobs/Beasts/_Shared/Wolf/ se as variantes reutilizarem o mesmo SK_ e Animations/. Pasta começa com _ para ordenar primeiro no Content Browser.

7. Referências externas


8. Como mudar este documento

Este .md é a fonte de verdade da arquitectura do cliente. Mudanças estruturais (nova pasta no nível alto, mudança de convenção de nome, novo tipo de asset) precisam de PR alterando este ficheiro primeiro. Sem PR neste documento, não se mexe na árvore real.