Skip to content

Plugin Development Guide PT

Lafko edited this page Apr 9, 2026 · 14 revisions

← Home


Guia de desenvolvimento de plugins

1. Primeiros passos

Pre-requisitos

  • MSVC v143 (Visual Studio 2022)
  • C++20 (/std:c++20)
  • Build x64 Release
  • Biblioteca de Runtime: /MD (DLL Multi-threaded) -- deve corresponder ao host

Configuracao do projeto

  1. Crie um novo projeto C++ DLL no Visual Studio
  2. Configure o caminho de inclusao para apontar ao diretorio fonte do POEFixer (para os headers do SDK e ImGui)
  3. Adicione os arquivos fonte do ImGui ao seu projeto: imgui.cpp, imgui_draw.cpp, imgui_tables.cpp, imgui_widgets.cpp
  4. Inclua os headers do SDK do plugin no seu codigo fonte:
    #include "plugin_sdk/PluginAPI.h"
    #include "plugin_sdk/PluginContext.h"
    #include "imgui/imgui.h"
    Ou use o header de conveniencia do ExamplePlugin:
    #include "sdk/PluginHelpers.h"  // Includes all SDK headers + MemoryReader + utilities
  5. Defina PLUGIN_EXPORTS e _CRT_SECURE_NO_WARNINGS nas Definicoes de Preprocessador do seu projeto

Estrutura de pastas

Plugins/
  YourPlugin/
    YourPlugin.dll      <-- O nome do DLL DEVE corresponder ao nome da pasta
    config/
      settings.txt      <-- Arquivo de configuracao opcional
    data/
      ...               <-- Diretorio de dados opcional (bancos de dados, caches, etc.)

Estrutura do projeto ExamplePlugin

O ExamplePlugin demonstra o layout recomendado do projeto:

Plugins/ExamplePlugin/
  ExamplePlugin.cpp        <-- Ponto de entrada principal do plugin + exportacoes de fabrica
  sdk/
    PluginHelpers.h        <-- MemoryReader, WideToNarrow, helpers de entidade/raridade
  examples/
    ExampleBuffs.h         <-- Lista de buffs com filtragem e barras de progresso
    ExampleEntities.h      <-- Lista de depuracao de entidades com mecanismo de observacao, arvores de componentes, dump JSON
    ExampleInventory.h     <-- ServerData, seletor de inventario, grade de slots, mods de itens com raridade
    ExampleMemory.h        <-- Visualizador hexadecimal, demo de Read<T>, scanner de padroes
    ExampleUiExplorer.h    <-- Explorador completo de elementos de UI com busca, navegacao, destaque

Estrutura do plugin KillCount

Um exemplo mais completo de plugin com SQLite3, atlas de icones e renderizacao de overlay:

Plugins/KillCount/
  KillCount.cpp            <-- Ponto de entrada principal do plugin, ciclo de vida IPlugin, UI de configuracao
  KillCount.h              <-- Declaracao da classe do plugin
  KillTracker.cpp/h        <-- Motor de contagem de kills/baus/mortes
  OverlayRenderer.cpp/h    <-- Overlay ImGui com padrao de arrastar para reposicionar
  IconAtlas.cpp/h           <-- Carregamento de folha de sprites de textura (D3D11 + stb_image)
  Database.cpp/h           <-- Wrapper SQLite3 para estatisticas persistentes
  DisplaySettings.h        <-- Struct de configuracao
  sdk/
    PluginHelpers.h        <-- Copiado do ExamplePlugin
  lib/
    sqlite3.c/h            <-- Amalgamacao SQLite3 (compilado como C)
    sqlite3-vcpkg-config.h <-- Override local para linkagem estatica

Convencao de nomes

O nome do arquivo DLL deve corresponder exatamente ao nome da pasta:

  • Pasta: Plugins/MyPlugin/ -> DLL: MyPlugin.dll
  • O host escaneia cada subpasta em Plugins/ e procura por <NomeDaPasta>.dll

2. Ciclo de vida do plugin

Load DLL (LoadLibrary)
  -> CreatePlugin()           -- Fabrica: instancia seu IPlugin
  -> SetContext(ctx)           -- Recebe os servicos do host
  -> SetPluginDirectory(dir)   -- Recebe o caminho da sua pasta
  -> GetSDKVersion()           -- Verificacao de compatibilidade
  -> GetName()                 -- Nome de exibicao para a UI
  -> [if enabled] OnEnable()  -- Inicializar recursos
  |
  Loop principal (cada frame):
    -> DrawUI()                -- Renderizar seu overlay (somente se habilitado)
    -> DrawSettings()          -- Renderizar configuracoes na aba Plugins
    -> WantsOverlay()          -- Host verifica se o plugin quer modo overlay
  |
  Periodicamente / ao encerrar:
    -> SaveSettings()          -- Persistir suas configuracoes
  |
  -> OnDisable()               -- Liberar recursos
  -> DestroyPlugin(plugin)     -- Fabrica: deletar seu IPlugin
  -> FreeLibrary               -- Descarregar DLL

Threading

  • Todos os metodos Draw* sao chamados na thread principal/de renderizacao
  • GetSnapshot() e outras funcoes do PluginContext sao thread-safe
  • NAO crie threads que chamem ImGui -- ImGui nao e thread-safe

3. Referencia da interface IPlugin

Todo plugin deve implementar a interface IPlugin (definida em plugin_sdk/PluginAPI.h):

void SetPluginDirectory(const char* dir)

  • Quando chamado: Uma vez, imediatamente apos a criacao
  • Parametro: Caminho relativo como "Plugins/YourPlugin"
  • Proposito: Armazene este caminho para carregar configuracoes/recursos

void SetContext(PluginContext* context)

  • Quando chamado: Uma vez, apos SetPluginDirectory
  • Parametro: Ponteiro para o PluginContext do host (valido durante a vida do plugin)
  • Proposito: Armazene este ponteiro -- e sua porta de acesso a todos os dados do jogo
  • Importante: Chame ImGui::SetCurrentContext(ctx->ImGuiContext) aqui

void OnEnable(bool isGameOpened)

  • Quando chamado: Quando o usuario habilita o plugin, ou na inicializacao se estava previamente habilitado
  • Parametro: true se o processo do jogo esta atualmente vinculado
  • Proposito: Carregar configuracoes, alocar recursos, inicializar estado

void OnDisable()

  • Quando chamado: Quando o usuario desabilita o plugin
  • Proposito: Liberar recursos, parar trabalho em segundo plano

void DrawUI()

  • Quando chamado: Cada frame, somente quando o plugin esta habilitado
  • Proposito: Renderizar seu overlay usando ImGui
  • Nota: Use IDs de janela unicos como "MyWindow##MyPlugin" para evitar conflitos

void DrawSettings()

  • Quando chamado: Cada frame, na aba de configuracoes de Plugins (somente quando habilitado)
  • Proposito: Renderizar a configuracao do plugin usando ImGui

void SaveSettings()

  • Quando chamado: Periodicamente e ao encerrar a aplicacao
  • Proposito: Salvar suas configuracoes em disco (ex., Plugins/YourPlugin/config/settings.txt)

const char* GetName()

  • Retorna: Nome de exibicao mostrado na aba Plugins (ex., "My Plugin")

int GetSDKVersion()

  • Retorna: PLUGIN_SDK_VERSION (atualmente 4)
  • Proposito: O host verifica isso para compatibilidade -- deve corresponder

bool WantsOverlay() (SDK v2)

  • Retorna: true se o plugin quer renderizar em modo overlay (overlay transparente sobre o jogo)
  • Padrao: false -- o plugin somente renderiza na janela de configuracoes normal
  • Proposito: Quando qualquer plugin retorna true, o host entra em modo overlay mesmo que nenhum recurso integrado o exija

Exportacoes de fabrica

Seu DLL deve exportar estas duas funcoes C:

extern "C" PLUGIN_API IPlugin* CreatePlugin() {
    return new MyPlugin();
}

extern "C" PLUGIN_API void DestroyPlugin(IPlugin* plugin) {
    delete plugin;
}

4. Referencia da API PluginContext

A struct PluginContext (definida em plugin_sdk/PluginContext.h) fornece ponteiros de funcao para acessar os dados do jogo. Todos os tipos estao no namespace PluginSDK.

Acesso a dados do jogo

GetSnapshot() -> shared_ptr<const PluginGameSnapshot>

Retorna um snapshot completo do estado do jogo. Atualizado uma vez por frame. Contem:

Campo Tipo Descricao
CurrentState GameStateTypes Estado atual do jogo
CurrentAreaName string Nome da area (ex., "The Riverways")
CurrentAreaHash string Hash unico da instancia da area
CurrentAreaLevel uint8_t Nivel de monstros da area atual
IsTown bool Verdadeiro se esta na cidade
IsHideout bool Verdadeiro se esta no esconderijo
IsPaused bool Verdadeiro se o jogo esta pausado
IsSkillTreeVisible bool Verdadeiro se o painel da arvore de habilidades esta aberto
WorldToGridConvertor float Fator de conversao mundo->grade
Player RadarEntity Dados da entidade do jogador local
Entities vector<RadarEntity> Todas as entidades proximas
LargeMap / MiniMap MapData Dados do overlay do mapa
Vitals PlayerVitals HP/ES/MP do jogador + buffs
ScreenWidth / ScreenHeight int Dimensoes da janela do jogo
ProcessId DWORD ID do processo do jogo
GameWindow HWND Handle da janela do jogo
GameWindowForeground bool Verdadeiro se o jogo esta em primeiro plano
IsAttached bool Verdadeiro se esta vinculado ao processo do jogo
IsWindowValid bool Verdadeiro se a janela do jogo e valida
LastUpdateTime uint64_t Timestamp da ultima atualizacao
AreaChangeCounter uint64_t Incrementa na mudanca de area
Inventories vector<InventoryInfo> Conteudo do inventario do jogador
CurrencyTotals map<string,int> Contagens de moeda por caminho
InventoryGrid InventoryGridInfo Info da grade do inventario
WorldToScreenMatrix XMFLOAT4X4 Matriz de projecao 3D->2D

Importante: Filtragem de entidades Entidades mortas (aquelas com EntityState == Useless) sao filtradas PARA FORA do snapshot antes que os plugins o recebam. Isso significa que voce nunca observara uma transicao de HP de vivo para morto. Se voce precisa detectar kills, use a deteccao baseada em desaparecimento -- rastreie IDs de entidades por zona e conte-os como mortos quando desaparecerem da lista de entidades enquanto estiverem na proximidade InnerCircle ou OuterCircle. Veja Secao 8: Receitas comuns para detalhes.

GetPlayerVitals() -> PlayerVitals

Atalho de conveniencia para os sinais vitais do jogador.

GetCurrentState() -> GameStateTypes

Retorna o enum do estado atual do jogo.

IsAttached() -> bool

Verdadeiro se o processo do jogo esta vinculado e legivel.

IsInGame() -> bool

Verdadeiro se esta atualmente no jogo (nao carregando, nao na tela de login).

IsGameForeground() -> bool

Verdadeiro se a janela do jogo e a janela em primeiro plano.

GetProcessId() -> DWORD

Retorna o ID do processo do jogo.

Acesso a dados de itens

ReadExtendedItemMods(entityAddress) -> ExtendedItemModInfo

Le todos os modificadores de uma entidade de item.

ReadItemRarity(entityAddress) -> int

Retorna: 0=Normal, 1=Magico, 2=Raro, 3=Unico

ReadItemStackCount(entityAddress) -> int

Retorna a contagem da pilha para itens de moeda/empilaveis.

ReadItemName(entityAddress) -> string

Retorna o nome do tipo base do item.

ReadItemPath(entityAddress) -> string

Retorna o caminho de metadados do item.

ReadItemBaseTypeName(entityAddress) -> string

Retorna o nome do tipo base do item (ex., "Divine Orb", "Chaos Orb"). Diferente de ReadItemName que retorna o caminho de metadados, este le o nome real do tipo base de BaseItemTypeData.BaseTypeName.

ReadItemUniqueName(entityAddress) -> string

Retorna o nome unico do item de Words.dat (ex., "Headhunter", "Brimstone Call"). Retorna string vazia para itens nao unicos.

Modo overlay (SDK v2)

IsOverlayMode() -> bool

Retorna verdadeiro se o host esta atualmente em modo overlay (overlay transparente sobre a janela do jogo). Use isso para ajustar sua renderizacao -- ex., desenhar no overlay do jogo vs. desenhar em uma janela de configuracoes.

Estado da UI (SDK v4)

IsMenuVisible() -> bool

Retorna verdadeiro quando o menu de configuracoes do host esta visivel (o overlay e interativo). Quando o menu esta oculto, a janela de overlay e transparente ao clique (WS_EX_TRANSPARENT), entao janelas ImGui nao podem receber entrada do mouse.

Use isso para implementar o padrao de overlay arrastavel:

  • Menu visivel: Mostra um indicador de arraste, permite interacao (abas, botoes)
  • Menu oculto: Remove o indicador de arraste, adiciona ImGuiWindowFlags_NoInputs para tornar a janela nao interativa

Veja Secao 6: Padrao de overlay arrastavel para a implementacao completa.

Leitura de memoria (SDK v2)

Acesso direto a memoria do processo do jogo. Todas as leituras sao seguras (retornam 0/vazio em caso de falha).

GetBaseAddress() -> uintptr_t

Retorna o endereco base do modulo executavel do jogo. Retorna 0 se nao esta vinculado.

GetModuleSize() -> uintptr_t

Retorna o tamanho do modulo do jogo em bytes. Retorna 0 se nao esta vinculado.

ReadProcessMemory(address, buffer, size) -> bool

Le um bloco de bytes brutos do processo do jogo. O buffer deve ter pelo menos size bytes alocados. Retorna verdadeiro em caso de sucesso.

// Example: Read a 4-byte integer from game memory
uint32_t value = 0;
m_Context->ReadProcessMemory(address, &value, sizeof(value));

// Example: Read a struct
MyStruct data{};
m_Context->ReadProcessMemory(structAddress, &data, sizeof(data));

ReadString(address) -> string

Le uma string ASCII terminada em nulo da memoria do jogo (max 128 caracteres).

ReadUnicodeString(address) -> wstring

Le uma string Unicode (wide) terminada em nulo da memoria do jogo (max 128 wchars).

GetPatternAddress(patternName) -> uintptr_t

Obtem um endereco resolvido de scan de padrao por nome. Retorna 0 se nao encontrado.

Padroes padrao:

Nome Descricao
"Game States" Raiz do vetor GameStates
"File Root" Registro de arquivos
"AreaChangeCounter" Contador de transicao de area
"Terrain Rotator Helper" Dados de rotacao
"Terrain Rotation Selector" Seletor de rotacao
"GameCullSize" Valor de recorte de tela

Projecao mundo-para-tela (SDK v2)

WorldToScreen(worldX, worldY, worldZ, outX, outY) -> bool

Converte uma posicao no espaco mundial para coordenadas de tela. Retorna verdadeiro se a posicao esta visivel na tela.

float screenX, screenY;
if (m_Context->WorldToScreen(entity.WorldX, entity.WorldY, entity.WorldZ, &screenX, &screenY)) {
    ImGui::GetBackgroundDrawList()->AddText(ImVec2(screenX, screenY), IM_COL32_WHITE, "Label");
}

Inventario (SDK v2)

RequestInventoryScan(inventoryId)

Solicita ao host que escaneie os inventarios. Passe -1 para escanear todos os inventarios, ou um ID de inventario especifico. Os dados do inventario no snapshot sao preenchidos apos o scan ser completado (proximo frame).

Nota: Os dados do inventario nao sao atualizados automaticamente -- voce deve chamar esta funcao para disparar um scan. Chame-a periodicamente (ex., a cada 2 segundos) se voce precisa de dados de inventario continuos.

Dados de terreno (SDK v2)

GetWalkableGrid(outWidth, outHeight) -> const uint8_t*

Retorna um ponteiro para os dados da grade transitavel. A grade e um array 2D onde 0 = nao transitavel, diferente de zero = transitavel. Retorna nullptr se os dados nao estao disponiveis.

GetTerrainHeight(gridX, gridY) -> float

Retorna a altura do terreno em uma posicao de grade. Retorna 0 se fora dos limites ou dados indisponiveis.

Leitura de containers nativos (SDK v3)

Estas funcoes leem containers da biblioteca padrao C++ diretamente da memoria do jogo, espelhando os metodos Core::Process do host.

ReadStdVector(containerAddress, elementSize, outCount) -> void*

Le um StdVector (struct de 24 bytes: {First, Last, End}) da memoria do jogo. Retorna um buffer alocado com malloc de elementos. O chamador deve fazer free() do ponteiro retornado. Retorna nullptr em caso de falha.

// Example: Read a vector of uint32_t
int count = 0;
void* data = m_Context->ReadStdVector(vectorAddr, sizeof(uint32_t), &count);
if (data && count > 0) {
    uint32_t* values = static_cast<uint32_t*>(data);
    for (int i = 0; i < count; i++) { /* values[i] */ }
    free(data);
}

ReadStdList(containerAddress, elementSize, outCount) -> void*

Le um StdList (struct de 16 bytes: {Head, Size}) da memoria do jogo. Percorre a lista encadeada e retorna um buffer contíguo. O chamador deve fazer free().

ReadStdBucket(containerAddress, elementSize, outCount) -> void*

Le um StdBucket da memoria do jogo (le o StdVector embutido). O chamador deve fazer free().

ReadStdMap(containerAddress, keySize, valueSize, callback, userData) -> int

Percorre um StdMap (struct de 16 bytes: {Head, Size}) e chama o callback para cada par chave-valor. Retorna o numero de nos visitados.

// Example: Read a map<uint32_t, float>
struct MapResult { std::vector<std::pair<uint32_t, float>> entries; };
MapResult result;
m_Context->ReadStdMap(mapAddr, sizeof(uint32_t), sizeof(float),
    [](const void* key, const void* value, void* userData) {
        auto* r = static_cast<MapResult*>(userData);
        uint32_t k; float v;
        memcpy(&k, key, sizeof(k));
        memcpy(&v, value, sizeof(v));
        r->entries.push_back({k, v});
    }, &result);

ReadStdWString(containerAddress) -> wstring

Le um StdWString (struct de 32 bytes com buffer inline/heap) da memoria do jogo.

GetInventoryName(inventoryId) -> const char*

Retorna o nome legivel de um ID de inventario (ex., 1 -> "MainInventory1", 3 -> "Weapon1", 64 -> "Currency1").

Acesso a dados de depuracao (SDK v4)

O SDK v4 fornece acesso direto aos dados de depuracao do host -- componentes de entidades, detalhes de inventario e arvore de elementos de UI -- correspondendo as abas de depuracao integradas.

Lista de depuracao de entidades

GetEntityDebugList() -> vector<DebugEntityInfo>

Retorna uma lista de todas as entidades com metadados de depuracao (Id, Address, Path, Type, SubType, State, Rarity, Zone). Isso espelha a aba Debug->Entity List.

WatchEntity(entityId)

Comeca a observar os componentes de uma entidade. A thread de trabalho do host lera os dados completos de componentes desta entidade a cada frame.

UnwatchEntity(entityId)

Para de observar os componentes de uma entidade. Chame isso quando o usuario colapsar o no da arvore da entidade para liberar recursos.

GetWatchedEntityData(entityId) -> DebugEntityComponents

Retorna os dados completos de componentes de uma entidade observada. Contem sub-structs para os 8 componentes reconhecidos (Life, Render, Positioned, Targetable, Animated, Stats, Actor, Buffs) mais a lista de todos os enderecos de componentes.

// Example: Watch entity on expand, read components
auto entities = m_Context->GetEntityDebugList();
for (auto& e : entities) {
    if (ImGui::TreeNode(e.Path.c_str())) {
        m_Context->WatchEntity(e.Id);
        auto data = m_Context->GetWatchedEntityData(e.Id);
        if (data.HasLife) {
            ImGui::Text("HP: %d / %d  ES: %d / %d",
                data.Life.Health.Current, data.Life.Health.Total,
                data.Life.EnergyShield.Current, data.Life.EnergyShield.Total);
        }
        ImGui::TreePop();
    } else {
        m_Context->UnwatchEntity(e.Id);
    }
}

Depuracao de inventario

GetServerDataAddress() -> uintptr_t

Retorna o endereco base do componente ServerData.

GetPlayerInventoryList() -> vector<pair<int, uintptr_t>>

Retorna todos os IDs de inventario do jogador e seus enderecos (do ServerData).

WatchInventory(inventoryId)

Comeca a observar um inventario para inspecao detalhada de depuracao. O host le a ocupacao de slots, detalhes de itens e modificadores.

GetWatchedInventoryData() -> DebugInventoryData

Retorna dados completos do inventario observado atualmente: dimensoes da grade, ocupacao de slots, itens com raridade e modificadores.

// Example: Inventory inspector
auto invList = m_Context->GetPlayerInventoryList();
m_Context->WatchInventory(invList[0].first);
auto inv = m_Context->GetWatchedInventoryData();
for (auto& item : inv.Items) {
    ImGui::Text("[%s] Rarity=%d Mods=%d", item.Path.c_str(), item.Rarity,
        (int)(item.ImplicitMods.size() + item.ExplicitMods.size()));
}

Arvore de elementos de UI

GetGameUiRootAddress() -> uintptr_t

Retorna o endereco do elemento raiz da UI do jogo (para navegacao da arvore de UI no jogo).

GetUiRootAddress() -> uintptr_t

Retorna o endereco raiz da UI de nivel superior.

GetGameCullValue() -> int

Retorna o valor atual do GameCullSize, utilizado para calculos de escala de UI. Combinado com as dimensoes da tela, isso permite calculo preciso de posicao/tamanho dos elementos de UI.

// Example: UI scale calculation (matching host logic)
int cullValue = m_Context->GetGameCullValue();
auto snapshot = m_Context->GetSnapshot();
// Scale for index 1 (width): screenWidth / (cullValue / baseWidth)
// Scale for index 2 (height): screenHeight / (cullValue / baseHeight)

PluginHelpers.h -- Wrapper de conveniencia (SDK v3)

O header sdk/PluginHelpers.h (incluido com o ExamplePlugin) fornece uma classe MemoryReader com tipagem segura que encapsula as funcoes brutas do PluginContext:

PluginSDK::MemoryReader mem(m_Context);

// Read a single struct
auto data = mem.Read<MyStruct>(address);

// Read an array
auto arr = mem.ReadArray<uint32_t>(address, count);

// Read native containers — returns std::vector<T>
auto vec = mem.ReadStdVector<uint32_t>(vectorAddr);
auto list = mem.ReadStdList<MyNode>(listAddr);
auto bucket = mem.ReadStdBucket<MyEntry>(bucketAddr);
auto map = mem.ReadStdMap<uint32_t, float>(mapAddr);
auto wstr = mem.ReadStdWString(wstringAddr);

O MemoryReader tambem fornece wrappers de conveniencia para ReadString(), ReadUnicodeString(), GetBaseAddress(), GetModuleSize(), e GetPatternAddress().

Utilitarios adicionais em PluginHelpers.h:

  • WideToNarrow(wstring) -- conversao segura de wstring para string (perda ASCII)
  • GetEntityTypeName(type) -- enum para nome de exibicao (incluindo ExpeditionMarker/ExpeditionRemnant)
  • GetNearbyZoneName(zone) -- zona para nome de exibicao
  • GetRarityName(rarity) / GetRarityColor(rarity) -- helpers de exibicao de raridade

Servicos do host

Log(level, message)

Escreve no sistema de log do host. Niveis: "Debug", "Info", "Warning", "Error"

ImGuiContext (void*)

O contexto ImGui do host. Chame ImGui::SetCurrentContext() com isso em SetContext().

D3DDevice (void*)

O ID3D11Device* do host. Faca cast e use para carregar texturas.


5. Referencia de estruturas de dados

Todos os tipos estao no namespace PluginSDK. Plugins tipicamente adicionam using namespace PluginSDK;.

RadarEntity

Dados por entidade disponiveis em snapshot->Entities:

Campo Tipo Descricao
Id uint32_t ID unico da entidade
Address uintptr_t Endereco de memoria (para chamadas de API de itens)
EntityDetailsAddress uintptr_t Endereco da struct de detalhes da entidade
RenderComponentAddress uintptr_t Endereco do componente Render (atalho)
IsValid bool Flag de validade da entidade
entityType EntityTypes Categoria da entidade
entitySubtype EntitySubtypes Subcategoria da entidade
entityState EntityStates Estado da entidade
Rarity int 0=Normal, 1=Magico, 2=Raro, 3=Unico
Reaction uint8_t 0=Hostil, 1=Neutro, 2=Amigavel
GridPositionX/Y float Posicao na grade do terreno
TerrainHeight float Altura do terreno na posicao da entidade
WorldX/Y/Z float Posicao no espaco mundial
ModelBoundsZ float Altura do modelo
Path wstring Caminho de metadados da entidade
PlayerName wstring Nome do jogador (se entidade jogador)
TgtPath string Caminho alvo (string estreita)
CurrentHP/MaxHP int Saude da entidade
CurrentES/MaxES int Escudo de energia da entidade
IsSleeping bool Flag de entidade distante
IsChestOpened bool Estado de bau aberto
Zone NearbyZone Proximidade ao jogador
ComponentCache EntityComponentCache Enderecos de componentes

Importante: Entidades mortas (EntityState::Useless) sao removidas do snapshot antes de chegar aos plugins. Voce nunca vera o HP de um monstro cair a zero -- ele simplesmente desaparece da lista. Use o campo Zone para distinguir kills (entidade desapareceu de InnerCircle/OuterCircle) de entidades saindo do alcance (entidade estava na zona Far).

Buff

Buff/debuff ativo:

Campo Tipo Descricao
Name string Nome interno do buff (ex., "flask_effect_life")
TimeLeft float Segundos restantes
Charges short Contagem de cargas
TotalTime float Duracao total

MapData

Estado do minimapa/mapa grande:

Campo Tipo Descricao
CenterX/Y float Centro do mapa
SizeX/Y float Dimensoes do mapa
ShiftX/Y float Deslocamento atual de pan
DefaultShiftX/Y float Valores de deslocamento padrao
Zoom float Nivel de zoom
Scale float Fator de escala do mapa
IsVisible bool O mapa esta atualmente visivel

InventoryInfo / InventoryItemInfo

Campo Tipo Descricao
Id int ID do inventario
TotalBoxesX/Y int Dimensoes da grade
Ptr uintptr_t Endereco de memoria do inventario
Items vector<InventoryItemInfo> Itens no inventario

Campos do item: Address, Name (caminho de metadados), Path (mesmo que Name), BaseTypeName (nome do tipo base, ex. "Divine Orb"), UniqueName (nome unico do item de Words.dat, ex. "Headhunter", vazio para nao unicos), SlotX/Y, Width/Height, StackCount, IsCurrency

ExtendedItemModInfo

Retornado por ReadExtendedItemMods():

Campo Tipo Descricao
ImplicitMods vector<ItemModData> Modificadores implicitos
ExplicitMods vector<ItemModData> Modificadores explicitos
EnchantMods vector<ItemModData> Modificadores de encantamento
HellscapeMods vector<ItemModData> Modificadores Hellscape
CrucibleMods vector<ItemModData> Modificadores Crucible
Rarity int 0=Normal, 1=Magico, 2=Raro, 3=Unico

ItemModData

Campo Tipo Descricao
Key string Chave de stat do mod
Values vector<float> Valores do mod

EntityComponentCache

Enderecos de componentes em cache por entidade (disponiveis via RadarEntity.ComponentCache):

Campo Tipo Metodo-Has
RenderAddr uintptr_t HasRender()
PositionedAddr uintptr_t HasPositioned()
ChestAddr uintptr_t HasChest()
PlayerAddr uintptr_t HasPlayer()
ShrineAddr uintptr_t HasShrine()
LifeAddr uintptr_t HasLife()
TargetableAddr uintptr_t HasTargetable()
OMPAddr uintptr_t HasOMP()
NPCAddr uintptr_t HasNPC()
TriggerableBlockageAddr uintptr_t HasTriggerableBlockage()
DiesAfterTimeAddr uintptr_t HasDiesAfterTime()
BuffsAddr uintptr_t HasBuffs()
WorldItemAddr uintptr_t HasWorldItem()
AreaTransitionAddr uintptr_t HasAreaTransition()
MinimapIconAddr uintptr_t HasMinimapIcon()
StatsAddr uintptr_t HasStats()

DebugEntityInfo (SDK v4)

Metadados de entidade de GetEntityDebugList():

Campo Tipo Descricao
Id uint32_t ID da entidade
Address uintptr_t Endereco de memoria
Path string Caminho de metadados
EntityType int Tipo de entidade (converter para EntityTypes)
EntitySubType int Subtipo de entidade (converter para EntitySubtypes)
EntityState int Estado da entidade (converter para EntityStates)
Rarity int 0=Normal, 1=Magico, 2=Raro, 3=Unico
Zone NearbyZone Proximidade ao jogador
ComponentAddresses vector<pair<string,uintptr_t>> Todos os pares nome->endereco de componentes

DebugEntityComponents (SDK v4)

Dados completos de componentes de GetWatchedEntityData():

Campo Tipo Descricao
EntityId uint32_t Para qual entidade sao estes dados
Valid bool Se os dados foram lidos com sucesso
HasLife / Life bool / DebugLifeComp Componente Life -- Life.Health, Life.EnergyShield, Life.Mana (cada um e um DebugVital com .Current, .Total, .Regeneration, .ReservedFlat, .ReservedPercent)
HasRender / Render bool / DebugRenderComp Posicao (WorldX/Y/Z, GridX/Y), TerrainHeight, ModelBounds (X/Y/Z)
HasPositioned / Positioned bool / DebugPositionedComp Valor de Reaction, flag IsFriendly
HasTargetable / Targetable bool / DebugTargetableComp IsTargetable, IsHighlightable, IsTargettedByPlayer, HiddenFromPlayer, MeetsQuestState, MeetsItemRequirements
HasAnimated / Animated bool / DebugAnimatedComp Path de animacao (string), Id (uint32)
HasStats / Stats bool / DebugStatsComp CurrentWeaponIndex, IsShapeshifted, StatsItems/StatsBuff (vetores de pares ID de stat->valor)
HasActor / Actor bool / DebugActorComp AnimationId, AnimationName, ActiveSkills (vetor de DebugActiveSkill), DeployedCounts[256]
HasBuffs / Buffs bool / vector<DebugBuff> Buffs ativos: Name, TotalTime, TimeLeft, Charges, FlaskSlot, Effectiveness, SourceEntityId

DebugInventoryData (SDK v4)

Detalhes de inventario de GetWatchedInventoryData():

Campo Tipo Descricao
InventoryId int ID do inventario (-1 se nenhum)
Address uintptr_t Endereco do inventario
TotalBoxesX/Y int Dimensoes da grade
ServerRequestCounter int Contador de sincronizacao do servidor
GridScreenX / GridScreenY float Posicao da grade na tela
CellSize float Tamanho da celula da grade em pixels
GridValid bool Se os dados da grade sao validos
SlotOccupied vector<bool> Ocupacao por slot
Items vector<DebugInventoryItem> Itens com caminho, raridade e modificadores

DebugInventoryItem (SDK v4)

Campo Tipo Descricao
Address uintptr_t Endereco da entidade do item
Path string Caminho de metadados do item
BaseTypeName string Nome do tipo base do item (ex. "Divine Orb")
UniqueName string Nome unico do item de Words.dat (vazio para nao unicos)
SlotX / SlotY int Posicao na grade do inventario
Rarity int 0=Normal, 1=Magico, 2=Raro, 3=Unico
ItemLevel int Nivel do item
RequiredLevel int Nivel requerido do personagem
IsIdentified bool Se o item esta identificado
IsCorrupted bool Se o item esta corrompido
CraftedModCount int Numero de mods criados
ImplicitMods vector<DebugModInfo> Modificadores implicitos
ExplicitMods vector<DebugModInfo> Modificadores explicitos
EnchantMods vector<DebugModInfo> Modificadores de encantamento
HellscapeMods vector<DebugModInfo> Modificadores Hellscape

DebugActiveSkill (SDK v4)

Campo Tipo Descricao
Name string Nome da habilidade
UseStage int Estagio de uso atual
CastType int Tipo de lancamento
TotalUses int Contagem total de usos
TotalCooldownTimeInMs int Cooldown em milissegundos
CanBeUsed bool Se a habilidade pode ser usada atualmente

DebugBuff (SDK v4)

Campo Tipo Descricao
Name string Nome interno do buff
TotalTime float Duracao total
TimeLeft float Segundos restantes
Charges short Contagem de cargas
FlaskSlot short Indice do slot de frasco
Effectiveness short Efetividade do buff
SourceEntityId uint32_t Entidade que aplicou este buff

DebugModInfo (SDK v4)

Campo Tipo Descricao
Name string Nome de exibicao do mod
StatKey string Identificador de chave de stat
AffixName string Nome do afixo
GenerationType int 1=Prefixo, 2=Sufixo, 3=Implicito
Value0 float Primeiro valor (NaN se nenhum)
Value1 float Segundo valor (NaN se nenhum)

Enumeracoes

EntityTypes: Unidentified(0), Chest(1), NPC(2), Player(3), Shrine(4), Monster(5), DeliriumBomb(6), DeliriumSpawner(7), OtherImportantObjects(8), Item(9), Renderable(10), AreaTransition(11), ExpeditionMarker(12), ExpeditionRemnant(13)

EntitySubtypes: _Unidentified(0), _None(1), PlayerSelf(2), PlayerOther(3), ChestWithMagicRarity(4), ChestWithRareRarity(5), ExpeditionChest(6), BreachChest(7), Strongbox(8), SpecialNPC(9), POIMonster(10), PinnacleBoss(11), WorldItem(12), InventoryItem(13)

EntityStates: None(0), Useless(1), PlayerLeader(2), MonsterFriendly(3), PinnacleBossHidden(4)

NearbyZone: None(0), InnerCircle(1, ~60 unidades de grade), OuterCircle(2, ~120 unidades de grade), Far(3)

GameStateTypes: AreaLoadingState(0), ChangePasswordState(1), CreditsState(2), EscapeState(3), InGameState(4), PreGameState(5), LoginState(6), WaitingState(7), CreateCharacterState(8), SelectCharacterState(9), DeleteCharacterState(10), LoadingState(11), GameNotLoaded(12)


6. Uso do ImGui em plugins

Contexto compartilhado

O host e o plugin compartilham o mesmo contexto ImGui. Voce deve chamar:

ImGui::SetCurrentContext(static_cast<ImGuiContext*>(m_Context->ImGuiContext));

no seu metodo SetContext().

IDs de janela

Sempre use IDs de janela unicos para evitar conflitos com o host ou outros plugins:

ImGui::Begin("My Window##MyPluginName", &showWindow);

Recursos disponiveis

  • Janelas, abas, arvores, tabelas, listas de desenho
  • Carregamento de texturas via dispositivo D3D11
  • Renderizacao de overlay via ImGui::GetBackgroundDrawList()
  • Icones FontAwesome 6 via #include "imgui/IconsFontAwesome6.h" (ex., ICON_FA_ARROWS_UP_DOWN_LEFT_RIGHT)

Renderizacao de overlay (SDK v2)

Use WorldToScreen() para desenhar rotulos/formas nas posicoes de entidades:

float sx, sy;
if (m_Context->WorldToScreen(entity.WorldX, entity.WorldY, entity.WorldZ, &sx, &sy)) {
    auto* drawList = ImGui::GetBackgroundDrawList();
    drawList->AddText(ImVec2(sx, sy - 20), IM_COL32(255, 255, 0, 255), "Monster");
    drawList->AddCircleFilled(ImVec2(sx, sy), 4.0f, IM_COL32(255, 0, 0, 255));
}

Modo overlay

Sobrescreva WantsOverlay() para retornar true para solicitar ao host que entre em modo overlay:

bool WantsOverlay() override { return m_OverlayEnabled; }

Quando em modo overlay, a janela do host e transparente e posicionada sobre o jogo. Suas chamadas DrawUI() renderizam diretamente na tela do jogo.

Padrao de overlay arrastavel (SDK v4)

O overlay do host usa WS_EX_TRANSPARENT para tornar a janela transparente ao clique quando o menu esta oculto. Isso significa que janelas ImGui nao podem receber entrada do mouse a menos que o menu esteja visivel. Para criar uma janela de overlay arrastavel (como o Vitals Overlay integrado), use este padrao de modo duplo:

#include "imgui/IconsFontAwesome6.h"

void MyPlugin::RenderOverlay() {
    bool menuVisible = m_Context->IsMenuVisible ? m_Context->IsMenuVisible() : false;

    if (menuVisible) {
        // === MODO ARRASTAVEL ===
        // Janela com fundo, indicador de arraste, controles interativos
        ImGui::SetNextWindowPos(ImVec2(m_PosX, m_PosY), ImGuiCond_Appearing);
        ImGui::SetNextWindowBgAlpha(m_Alpha);

        ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar |
            ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_AlwaysAutoResize |
            ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoCollapse |
            ImGuiWindowFlags_NoFocusOnAppearing;

        ImGui::Begin("##MyOverlay", nullptr, flags);

        // Drag hint (the entire window is draggable since there's no title bar)
        ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
            ICON_FA_ARROWS_UP_DOWN_LEFT_RIGHT " Drag to reposition");
        ImGui::Spacing();

        // ... your content here (tabs, text, icons, buttons — all interactive) ...

        // Persist position when user drags the window
        ImVec2 pos = ImGui::GetWindowPos();
        if (pos.x != m_PosX || pos.y != m_PosY) {
            m_PosX = pos.x;
            m_PosY = pos.y;
            // Save to settings on next SaveSettings() call
        }

        ImGui::End();
    }
    else {
        // === MODO NAO INTERATIVO ===
        // Overlay estatico — sem arraste, sem interacao de mouse
        ImGui::SetNextWindowPos(ImVec2(m_PosX, m_PosY));
        ImGui::SetNextWindowBgAlpha(m_Alpha);

        ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar |
            ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_AlwaysAutoResize |
            ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoCollapse |
            ImGuiWindowFlags_NoFocusOnAppearing |
            ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
            ImGuiWindowFlags_NoInputs;

        ImGui::Begin("##MyOverlay", nullptr, flags);
        // ... your content here (display-only, no interactive controls) ...
        ImGui::End();
    }
}

Pontos-chave:

  • ImGuiCond_Appearing define a posicao somente na primeira exibicao; ImGui entao rastreia a posicao de arraste
  • NoTitleBar + sem NoMove = a janela e arrastavel de qualquer area vazia (comportamento padrao do ImGui)
  • NoInputs no modo nao interativo evita que o overlay roube o foco atraves de WS_EX_TRANSPARENT
  • Sempre verifique nulo o ponteiro IsMenuVisible para compatibilidade retroativa: m_Context->IsMenuVisible ? m_Context->IsMenuVisible() : false
  • Salve a posicao no seu arquivo de configuracao para que persista entre sessoes

7. Persistencia de configuracao

Padrao recomendado

void OnEnable(bool isGameOpened) override {
    LoadSettings();  // Load from Plugins/YourPlugin/config/settings.txt
}

void SaveSettings() override {
    // Write to Plugins/YourPlugin/config/settings.txt
    // The host calls this periodically and on shutdown
}

Localizacao do arquivo

Armazene configuracoes em <PluginDirectory>/config/:

std::filesystem::path settingsPath =
    std::filesystem::path(m_Directory) / "config" / "settings.txt";

Formato simples chave-valor

Para plugins com muitas configuracoes, um formato de texto simples chave=valor funciona bem:

// Save
std::ofstream file(configDir / "settings.txt");
file << "ShowOverlay=" << (m_Settings.ShowOverlay ? 1 : 0) << "\n";
file << "WindowAlpha=" << m_Settings.WindowAlpha << "\n";
file << "PosX=" << m_Settings.PosX << "\n";
file << "PosY=" << m_Settings.PosY << "\n";

// Load
std::ifstream file(settingsPath);
std::string line;
while (std::getline(file, line)) {
    auto eq = line.find('=');
    if (eq == std::string::npos) continue;
    std::string key = line.substr(0, eq);
    std::string val = line.substr(eq + 1);
    if (key == "ShowOverlay") m_Settings.ShowOverlay = (val == "1");
    else if (key == "WindowAlpha") m_Settings.WindowAlpha = std::stof(val);
    else if (key == "PosX") m_Settings.PosX = std::stof(val);
    else if (key == "PosY") m_Settings.PosY = std::stof(val);
}

8. Receitas comuns

Obter a porcentagem de HP do jogador

auto vitals = m_Context->GetPlayerVitals();
int hpPercent = vitals.HPPercent; // 0-100

Listar todos os monstros dentro do circulo interno

auto snapshot = m_Context->GetSnapshot();
for (auto& e : snapshot->Entities) {
    if (e.entityType == EntityTypes::Monster &&
        e.Zone == NearbyZone::InnerCircle) {
        // e.CurrentHP, e.Path, e.WorldX/Y/Z...
    }
}

Verificar se o jogador tem um buff especifico

auto vitals = m_Context->GetPlayerVitals();
for (auto& buff : vitals.Buffs) {
    if (buff.Name == "flask_effect_life") {
        // buff.TimeLeft, buff.Charges...
    }
}

Obter informacoes da area atual

auto snapshot = m_Context->GetSnapshot();
std::string area = snapshot->CurrentAreaName;
bool isTown = snapshot->IsTown;
int level = snapshot->CurrentAreaLevel;

Desenhar texto na posicao mundial de uma entidade (SDK v2)

for (auto& e : snapshot->Entities) {
    if (e.entityType != EntityTypes::Monster) continue;
    float sx, sy;
    if (m_Context->WorldToScreen(e.WorldX, e.WorldY, e.WorldZ, &sx, &sy)) {
        auto* dl = ImGui::GetBackgroundDrawList();
        dl->AddText(ImVec2(sx, sy - 15), IM_COL32(255, 255, 0, 255), "Monster");
    }
}

Ler modificadores de itens do inventario

// First, request an inventory scan (call periodically, e.g. every 2s)
m_Context->RequestInventoryScan(-1);

// Then read from snapshot (next frame)
auto snapshot = m_Context->GetSnapshot();
for (auto& inv : snapshot->Inventories) {
    for (auto& item : inv.Items) {
        auto mods = m_Context->ReadExtendedItemMods(item.Address);
        // mods.ExplicitMods, mods.ImplicitMods...
    }
}

Contar entidades por tipo

auto snapshot = m_Context->GetSnapshot();
int monsters = 0, items = 0;
for (auto& e : snapshot->Entities) {
    if (e.entityType == EntityTypes::Monster) monsters++;
    if (e.entityType == EntityTypes::Item) items++;
}

Detectar mudanca de area

static uint64_t lastAreaChange = 0;
auto snapshot = m_Context->GetSnapshot();
if (snapshot->AreaChangeCounter != lastAreaChange) {
    lastAreaChange = snapshot->AreaChangeCounter;
    // Area changed! Reset state...
}

Verificar se esta na tela de carregamento

if (m_Context->GetCurrentState() == GameStateTypes::AreaLoadingState) {
    // Currently loading...
}

Detectar kills de monstros (baseado em desaparecimento)

Entidades mortas sao filtradas do snapshot (EntityState::Useless), entao voce nao pode detectar o HP caindo a 0. Em vez disso, rastreie entidades por ID e detecte quando elas desaparecem das zonas proximas:

// In your tracker class:
struct TrackedEntity {
    uint32_t Id;
    int Rarity;
    PluginSDK::NearbyZone Zone;
};

std::unordered_map<uint32_t, TrackedEntity> m_PrevEntities;

void DetectKills(const std::vector<PluginSDK::RadarEntity>& entities) {
    std::unordered_set<uint32_t> currentIds;

    // Update tracking map with current monsters
    for (const auto& e : entities) {
        if (e.entityType != EntityTypes::Monster) continue;
        if (e.entityState == EntityStates::MonsterFriendly) continue;
        currentIds.insert(e.Id);
        m_PrevEntities[e.Id] = { e.Id, e.Rarity, e.Zone };
    }

    // Disappeared from InnerCircle/OuterCircle = killed
    for (auto it = m_PrevEntities.begin(); it != m_PrevEntities.end(); ) {
        if (currentIds.count(it->first) == 0) {
            if (it->second.Zone == NearbyZone::InnerCircle ||
                it->second.Zone == NearbyZone::OuterCircle) {
                OnMonsterKilled(it->second.Rarity);
            }
            it = m_PrevEntities.erase(it);
        } else {
            ++it;
        }
    }
}

Por que funciona: Entidades dentro de ~120 unidades de grade que desaparecem repentinamente quase certamente foram mortas (nao apenas sairam do alcance). Entidades na zona Far naturalmente aparecem e desaparecem da lista de entidades -- nao conte essas.

Importante: Limpe m_PrevEntities na mudanca de area (AreaChangeCounter mudou) para evitar falsos positivos.

Ler memoria bruta do jogo (SDK v2)

// Read a struct from a known address
struct MyGameStruct { int field1; float field2; };
MyGameStruct data{};
if (m_Context->ReadProcessMemory(someAddress, &data, sizeof(data))) {
    // data.field1, data.field2 are now populated
}

// Read a string from memory
std::string str = m_Context->ReadString(stringAddress);

Usar resultados de scan de padroes (SDK v2)

uintptr_t gameStatesAddr = m_Context->GetPatternAddress("Game States");
if (gameStatesAddr != 0) {
    // Read data at the resolved pattern address
    uint64_t value = 0;
    m_Context->ReadProcessMemory(gameStatesAddr, &value, sizeof(value));
}

Verificar terreno transitavel (SDK v2)

int gridW = 0, gridH = 0;
const uint8_t* grid = m_Context->GetWalkableGrid(&gridW, &gridH);
if (grid && gridW > 0 && gridH > 0) {
    int x = (int)snapshot->Player.GridPositionX;
    int y = (int)snapshot->Player.GridPositionY;
    if (x >= 0 && x < gridW && y >= 0 && y < gridH) {
        bool walkable = grid[y * gridW + x] != 0;
    }
}

Ler memoria tipada com MemoryReader (SDK v3)

PluginSDK::MemoryReader mem(m_Context);

// Read a struct from a known address
struct GameData { int level; float health; };
auto data = mem.Read<GameData>(address);

// Read a StdVector of pointers
auto ptrs = mem.ReadStdVector<uintptr_t>(vectorAddr);
for (auto ptr : ptrs) { /* process each pointer */ }

// Read a StdMap<int, float>
auto entries = mem.ReadStdMap<int, float>(mapAddr);
for (auto& [key, value] : entries) { /* key, value */ }

Obter nome do inventario (SDK v3)

for (auto& inv : snapshot->Inventories) {
    const char* name = m_Context->GetInventoryName(inv.Id);
    // name is e.g. "MainInventory1", "Weapon1", "Currency1"
}

Ler enderecos de componentes de entidades

for (auto& e : snapshot->Entities) {
    auto& cc = e.ComponentCache;
    if (cc.HasLife()) {
        // cc.LifeAddr contains the Life component address
        // Use MemoryReader to read component structs
    }
    if (cc.HasRender()) {
        // cc.RenderAddr has the Render component address
    }
}

Inspecionar componentes de entidade via observacao de depuracao (SDK v4)

// Get all entities with debug info
auto entities = m_Context->GetEntityDebugList();
for (auto& e : entities) {
    bool open = ImGui::TreeNode(e.Path.c_str());
    if (open) {
        m_Context->WatchEntity(e.Id);
        auto comp = m_Context->GetWatchedEntityData(e.Id);
        if (comp.HasLife) {
            ImGui::Text("HP: %d/%d  ES: %d/%d  MP: %d/%d",
                comp.Life.Health.Current, comp.Life.Health.Total,
                comp.Life.EnergyShield.Current, comp.Life.EnergyShield.Total,
                comp.Life.Mana.Current, comp.Life.Mana.Total);
        }
        if (comp.HasActor) {
            ImGui::Text("Animation: %s (%d)  Skills: %d",
                comp.Actor.AnimationName.c_str(), comp.Actor.AnimationId,
                (int)comp.Actor.ActiveSkills.size());
        }
        ImGui::TreePop();
    } else {
        m_Context->UnwatchEntity(e.Id);
    }
}

Inspecionar inventario com grade de slots (SDK v4)

auto invList = m_Context->GetPlayerInventoryList();
if (!invList.empty()) {
    m_Context->WatchInventory(invList[0].first);
    auto inv = m_Context->GetWatchedInventoryData();
    if (inv.InventoryId >= 0) {
        ImGui::Text("Grid: %dx%d  Items: %d",
            inv.TotalBoxesX, inv.TotalBoxesY, (int)inv.Items.size());
        for (auto& item : inv.Items) {
            ImGui::Text("[R%d iLvl%d] %s (%s)  Mods: %d/%d/%d/%d",
                item.Rarity, item.ItemLevel,
                item.BaseTypeName.c_str(), item.Path.c_str(),
                (int)item.ImplicitMods.size(), (int)item.ExplicitMods.size(),
                (int)item.EnchantMods.size(), (int)item.HellscapeMods.size());
        }
    }
}

Navegar pela arvore de elementos de UI (SDK v4)

uintptr_t uiRoot = m_Context->GetGameUiRootAddress();
if (uiRoot) {
    PluginSDK::MemoryReader mem(m_Context);
    // Read children vector at offset 0x010
    auto children = mem.ReadStdVector<uintptr_t>(uiRoot + 0x010);
    for (auto childAddr : children) {
        // Read StringId at offset 0x448
        uintptr_t strPtr = mem.Read<uintptr_t>(childAddr + 0x448);
        if (strPtr) {
            std::string name = m_Context->ReadString(strPtr);
            ImGui::Text("Child: %s (0x%llX)", name.c_str(), childAddr);
        }
    }
}

Ler modificadores de itens com helpers de exibicao (SDK v3)

auto mods = m_Context->ReadExtendedItemMods(item.Address);
ImGui::TextColored(
    PluginSDK::GetRarityColor(mods.Rarity),
    "Rarity: %s", PluginSDK::GetRarityName(mods.Rarity));
for (auto& mod : mods.ExplicitMods) {
    ImGui::BulletText("%s", mod.Key.c_str());
}

9. Compilacao e implantacao

Configuracoes de build

Configuracao Valor
Configuration Release
Platform x64
C++ Standard /std:c++20
Runtime Library /MD (DLL Multi-threaded)
Configuration Type DLL

Arquivos necessarios no projeto do plugin

  • Seu(s) arquivo(s) .cpp do plugin
  • Arquivos fonte do ImGui: imgui.cpp, imgui_draw.cpp, imgui_tables.cpp, imgui_widgets.cpp
  • Caminho de inclusao para o diretorio raiz do POEFixer (para headers do SDK e ImGui)
  • Opcional: Copie Plugins/ExamplePlugin/sdk/PluginHelpers.h para o wrapper MemoryReader e funcoes utilitarias

Caminhos de inclusao

Seu .vcxproj deve ter estes diretorios de inclusao adicionais:

<AdditionalIncludeDirectories>$(SolutionDir)POEFixer;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

Se usar bibliotecas de terceiros locais (ex., SQLite3 em uma subpasta lib/), adicione $(ProjectDir)lib antes do caminho da solucao para que headers locais tenham prioridade:

<AdditionalIncludeDirectories>$(ProjectDir)lib;$(SolutionDir)POEFixer;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

Bibliotecas de terceiros

SQLite3 (linkagem estatica)

Para usar SQLite3 em um plugin, voce deve compilar o codigo fonte amalgamado diretamente no seu DLL -- Windows LoadLibrary nao busca no proprio diretorio do DLL por dependencias, entao linkar dinamicamente sqlite3.dll falhara com erro 126.

Passos:

  1. Copie sqlite3.c e sqlite3.h para o diretorio lib/ do seu plugin
  2. Crie lib/sqlite3-vcpkg-config.h para sobrescrever SQLITE_API (previne erros de __declspec(dllimport)):
    #ifndef SQLITE_API
    #define SQLITE_API
    #endif
    #define SQLITE_ENABLE_UNLOCK_NOTIFY 1
    #define SQLITE_OS_WIN 1
    #define SQLITE_ENABLE_COLUMN_METADATA 1
  3. Adicione sqlite3.c ao seu .vcxproj como arquivo C com avisos desabilitados:
    <ClCompile Include="lib\sqlite3.c">
      <CompileAs>CompileAsC</CompileAs>
      <WarningLevel>TurnOffAllWarnings</WarningLevel>
      <SDLCheck>false</SDLCheck>
      <PreprocessorDefinitions>SQLITE_THREADSAFE=1;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ClCompile>

stb_image (carregamento de texturas)

Para carregar texturas de arquivos de imagem (PNG, JPG), inclua stb_image em um arquivo .cpp:

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

Entao use o dispositivo D3D11 de m_Context->D3DDevice para criar texturas na GPU.

Saida

Configure seu diretorio de saida para:

$(SolutionDir)x64\Release\Plugins\YourPlugin\

Implantacao

Copie seu DLL compilado para Plugins/YourPlugin/YourPlugin.dll ao lado do executavel principal.

Depuracao

  1. Compile seu DLL do plugin em modo Debug
  2. Inicie a aplicacao host
  3. No Visual Studio: Depurar -> Anexar ao Processo -> selecione o .exe do host
  4. Defina breakpoints no codigo fonte do seu plugin
  5. O depurador parara quando seu codigo for chamado

10. Solucao de problemas

Problema Solucao
Plugin nao carrega Verifique se o nome do DLL corresponde exatamente ao nome da pasta
"SDK version mismatch" Recompile o plugin com os headers mais recentes do SDK (versao atual: 4)
Erro 126 do LoadLibrary O DLL tem dependencias nao resolvidas. Para bibliotecas de terceiros como SQLite3, compile-as estaticamente no DLL (veja Secao 9). Use dumpbin /dependents YourPlugin.dll para verificar.
Crash ao carregar Verifique incompatibilidade de CRT -- ambos devem usar /MD
ImGui nao renderiza Certifique-se de que ImGui::SetCurrentContext() e chamado em SetContext()
Dados estao vazios/zero Verifique IsAttached() e IsInGame() antes de ler dados
Inventario esta vazio Chame RequestInventoryScan(-1) -- dados de inventario sao sob demanda
Erro "Missing exports" Certifique-se de que CreatePlugin e DestroyPlugin sao exportados com extern "C"
Plugin causa crash no host Isso nao deveria acontecer -- todas as chamadas de plugin sao protegidas por SEH. Verifique os logs.
Dados obsoletos GetSnapshot() retorna os dados do frame mais recente. Nao armazene o ponteiro em cache.
Leitura de memoria retorna 0 Verifique se IsAttached() e verdadeiro e o endereco e valido
WorldToScreen retorna falso A posicao pode estar atras da camera ou fora da tela
Janela de overlay nao e clicavel O host usa WS_EX_TRANSPARENT quando o menu esta oculto. Use IsMenuVisible() para mostrar controles interativos somente quando o menu esta ativo. Veja o Padrao de Overlay Arrastavel na Secao 6.
Deteccao de kills nao funciona Entidades mortas sao removidas do snapshot. Use deteccao baseada em desaparecimento em vez de transicao de HP. Veja Secao 8.
Erros C2491 "dllimport function" Os headers da sua biblioteca de terceiros definem __declspec(dllimport). Crie um header de override local que defina a macro API como vazia (veja exemplo SQLite3 na Secao 9).

← Home

Clone this wiki locally