DA_Job_Novice DisplayName e docs de arquitetura (3 ARQUITETURA*.md) sincronizados com a nova terminologia PT-BR. Server (Novice.json + JobsDatabase.cpp) sera commitado separado no repo do ZeusServerEngine. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
37 KiB
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 (Novato → especializações), fórmulas de stats derivados (ATK/MATK/DEF/...),jobs.ymldata-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:
- Namespace do projecto. Todo o conteúdo do jogo vive sob
Content/ZMMO/. Nada de assets soltos na raizContent/. - 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. - 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.
- 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 5–6. - 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 emCore/. Nunca misturar. - Structs e Enums vivem em C++. Toda
USTRUCT(row de DataTable inclusive) e todaUENUMé declarada emSource/ZMMO/Data/. Blueprint Structs estão proibidos — eles corrompem em refactor e não dão tipos fortes. - 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++AZMMOEntityemSource/ZMMO/Game/Entity/. - 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. Developers/eExternalContent/fora do namespaceZMMO/. Não entram no cook (Developers/) e não poluem o nosso namespace (ExternalContent/para Fab/Megascans/Marketplace).- Temas de UI são data-driven, não hard-coded. Widgets não têm hard
ref para textura/som sazonal — pedem ao
UZMMOThemeSubsystemvia uma chave declarada emEZMMOThemeKey(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:
- Override de desenvolvimento (
DefaultGame.ini, chaveActiveTheme=). Só lido fora de builds Shipping. Para QA testar Natal em Maio. - Resposta autoritativa do servidor. Quando o servidor Zeus envia o
opcode
ServerHellocomThemeId, ele vence. É o único caminho válido em produção. - 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 enviarThemeId. 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>Rowquando é linha de DataTable. Ex:FItemRow,FMobRow. - Struct genérica (não-DT):
F<Nome>. Ex:FStatBlock. - Enum:
E<Nome>. SempreUENUM(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/.
- Criar a pasta
Content/ZMMO/Entities/Mobs/Beasts/Wolf_Gray/. - Importar todos os assets do lobo dentro dela:
SK_Wolf,SKEL_Wolf,PHY_Wolf.T_Wolf_Diffuse,T_Wolf_Normal,T_Wolf_ORM.M_Wolfe a varianteMI_Wolf_Gray.Animations/:ABP_Wolf,AS_Wolf_Skel,A_Wolf_*,AM_Wolf_*.Audio/:SC_Wolf_Growl,SC_Wolf_Footstep.VFX/:NS_Wolf_Death.
- Criar
BP_Mob_Wolf_Grayherdando deCore/Mob/BP_Mob_Base. - Atribuir no BP:
SK_Wolfno Mesh,ABP_Wolfno AnimClass,BT_Mob_Default(ou um BT próprio se necessário) no AIController. - Abrir
Content/ZMMO/Data/Mobs/DT_Mobs_Master.uasset. Adicionar linhaWolf_Gray:BlueprintRef=BP_Mob_Wolf_Gray(viaTSoftObjectPtr— soft).Stats=FStatBlockcom HP/Atk/Def adequados.Faction=EMobFaction::Hostile.DropTableRow=WolfDrops(linha emDT_ItemDrops).
- Se for dropar loot novo, abrir
DT_ItemDropse adicionar a linhaWolfDropscom array deFItemRowids + 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/.
- Criar
Content/ZMMO/Items/Equipment/Weapons/Sword_Iron/. - Importar
SM_Sword_Iron, texturasT_Sword_Iron_*, materialM_Sword_Iron, e o íconeT_Icon_Sword_Iron. - Abrir
DT_Items_Master. Adicionar linhaSword_Iron:Type=EItemType::Weapon.Rarity=EItemRarity::Common.EquipSlot=EEquipSlot::MainHand.WorldMesh=SM_Sword_Iron(soft).Icon=T_Icon_Sword_Iron(soft).DisplayName,DescriptionviaStringTableemLocalization/.
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)
- Criar
Content/ZMMO/Entities/Players/Classes/Warrior/. - Importar
SK_Warrior, anims, materiais, etc. (mesmo padrão self-contained do mob). - Criar
BP_Player_Warriorherdando deCore/Player/BP_Player_Base. - Adicionar linha
WarrioremDT_Classes:BlueprintRef=BP_Player_Warrior(soft).BaseStats=FStatBlock.LevelUpStats=FStatBlock(delta por nível).StartingAbilities=TArray<FName>de ids emDT_Abilities.
4.4. Adicionar um NPC (ex: Vendor_Blacksmith)
- Criar
Content/ZMMO/Entities/NPCs/Vendor_Blacksmith/(self-contained). - Criar
BP_NPC_Blacksmithherdando deCore/NPC/BP_NPC_Base. - Adicionar linha
Vendor_BlacksmithemDT_NPCs:BlueprintRef=BP_NPC_Blacksmith(soft).Role=ENPCRole::Vendor.ShopInventory=TArray<FName>de ids emDT_Items_Master.DialogRoot= id emDT_Dialogs.
4.5. Adicionar uma Row Struct em C++
Exemplo: nova struct FCraftRecipeRow para receitas de crafting.
- 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;
};
- Recompilar o módulo
ZMMO. - No editor:
Content/ZMMO/Data/Crafting/→Add → Miscellaneous → Data Table→ escolherCraftRecipeRowcomo Row Structure → salvar comoDT_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/.
- 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. - 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.uasset↔Themes/Christmas/HUD/T_HUD_Frame.uasset.
- Criar
DA_Theme_Christmas.uassetemContent/ZMMO/UI/Themes/comoPrimaryDataAssetherdando da classeUZMMOThemeDataAsset(a classe vive emSource/ZMMO/Game/UI/). - Preencher o
TMap<EZMMOThemeKey, FThemeEntry>do data asset apenas com as chaves que o tema sobrescreve. As que ficam vazias caem automaticamente paraDA_Theme_Default. - Adicionar linha em
DT_ThemeCalendar:ThemeId=Christmas.StartDate=12-15.EndDate=01-05.Priority=10(caso duas datas se sobreponham).
- No servidor Zeus, quando quiser activar o tema (autoritativo), enviar o
opcode
ServerHellocomThemeId = "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):
- Abrir
Source/ZMMO/Data/UI/ThemeKeys.he adicionar o valor ao enum:UENUM(BlueprintType) enum class EZMMOThemeKey : uint8 { HUD_Frame, Login_Background, Login_Logo, Inventory_SlotEmpty, LoadingScreen, // <-- nova chave MainMenu_Music, }; - Recompilar o módulo
ZMMO. - Abrir
DA_Theme_Defaultno editor e preencher a nova chave obrigatoriamente (Default tem de ter todas). - 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_OK
→ RequestEnterServerSelect() → 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).
- Prototipar a tela em HTML/CSS em
Tools/Templates/MMO_Widget/(ou usar os templates já existentes). - Rodar o Zeus UMG Forge (HTML→UMG). Saída crua:
Content/AutoCreated/<batch>/. - Abrir o
WBP_*gerado e reparent a classe pai paraUUIActivatableScreen_Base(a base éUCommonActivatableWidget; o WBP tem árvore própria — respeita §3.3, nunca encadeia WBP→WBP). - Reconectar tema: o Forge gera hard ref de textura (proibido §5).
Substituir por consumo via
UZMMOThemeSubsystemporEZMMOThemeKeyno override deRefreshUIStyle; remontar botões/painéis com os widgets compartilhados (UI_Button_Master,UI_Panel_Master). - Mover o asset refinado de
Content/AutoCreated/…paraContent/ZMMO/UI/FrontEnd/(o cru não cooka). - Registrar no
DA_FrontEndScreenSet:StateScreens[EZMMOFrontEndState::Login]= soft class do WBP refinado (páginas do Lobby vão no map deEZMMOLobbyPageda 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 Structno editor. Toda struct é C++. - Não referenciar BPs de mob/item com
TObjectPtr<>(hard ref) a partir de DataTables ou Subsystems globais — usarTSoftObjectPtr<>. - 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/...ouContent/{Developers,AutoCreated,ExternalContent}/....AutoCreated/é quarentena do Zeus UMG Forge: refina-se e move paraZMMO/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 paraExternalContent/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
FStatBlockem C++ se já existe — importar deSource/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 aoUZMMOThemeSubsystemviaEZMMOThemeKey. 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 5–6 anims. O mesmo vale paraAudio/eVFX/próprios da entidade. Items/.../Equipment/Weapons/Sword_Iron/mantém o íconeT_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 emEntities/Mobs/Beasts/_Shared/Wolf/se as variantes reutilizarem o mesmoSK_eAnimations/. Pasta começa com_para ordenar primeiro no Content Browser.
7. Referências externas
- Allar UE5 Style Guide — base das nossas convenções de nomenclatura e estrutura.
- Epic – Unreal Engine Directory Structure.
- Lyra Project Structure — modelo Epic para projecto multiplayer escalável.
- Hyperdense – UE5 folder structure best practices.
- Epic Forum – Using DataTables, Structs and DataAssets for Inventory.
- Epic Forum – Item system: how to structure/store data.
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.