Skip to content

Plugin Development Guide ES

Lafko edited this page Apr 9, 2026 · 14 revisions

← Home


Guia de desarrollo de plugins

1. Primeros pasos

Requisitos previos

  • MSVC v143 (Visual Studio 2022)
  • C++20 (/std:c++20)
  • Compilacion x64 Release
  • Biblioteca en tiempo de ejecucion: /MD (DLL multiproceso) -- debe coincidir con el host

Configuracion del proyecto

  1. Crea un nuevo proyecto C++ DLL en Visual Studio
  2. Configura la ruta de inclusion para apuntar al directorio fuente de POEFixer (para los encabezados del SDK e ImGui)
  3. Agrega los archivos fuente de ImGui a tu proyecto: imgui.cpp, imgui_draw.cpp, imgui_tables.cpp, imgui_widgets.cpp
  4. Incluye los encabezados del SDK del plugin en tu codigo fuente:
    #include "plugin_sdk/PluginAPI.h"
    #include "plugin_sdk/PluginContext.h"
    #include "imgui/imgui.h"
    O usa el encabezado de conveniencia del ExamplePlugin:
    #include "sdk/PluginHelpers.h"  // Includes all SDK headers + MemoryReader + utilities
  5. Define PLUGIN_EXPORTS y _CRT_SECURE_NO_WARNINGS en las Definiciones del Preprocesador de tu proyecto

Estructura de carpetas

Plugins/
  YourPlugin/
    YourPlugin.dll      <-- El nombre del DLL DEBE coincidir con el nombre de la carpeta
    config/
      settings.txt      <-- Archivo de configuracion opcional
    data/
      ...               <-- Directorio de datos opcional (bases de datos, caches, etc.)

Estructura del proyecto ExamplePlugin

El ExamplePlugin demuestra la disposicion recomendada del proyecto:

Plugins/ExamplePlugin/
  ExamplePlugin.cpp        <-- Punto de entrada principal del plugin + exportaciones de fabrica
  sdk/
    PluginHelpers.h        <-- MemoryReader, WideToNarrow, helpers de entidad/rareza
  examples/
    ExampleBuffs.h         <-- Lista de buffs con filtrado y barras de progreso
    ExampleEntities.h      <-- Lista de depuracion de entidades con mecanismo de observacion, arboles de componentes, volcado JSON
    ExampleInventory.h     <-- ServerData, selector de inventario, cuadricula de ranuras, mods de items con rareza
    ExampleMemory.h        <-- Visor hexadecimal, demo de Read<T>, escaner de patrones
    ExampleUiExplorer.h    <-- Explorador completo de elementos de UI con busqueda, navegacion, resaltado

Estructura del plugin KillCount

Un ejemplo mas completo de plugin con SQLite3, atlas de iconos y renderizado de overlay:

Plugins/KillCount/
  KillCount.cpp            <-- Punto de entrada principal del plugin, ciclo de vida IPlugin, UI de configuracion
  KillCount.h              <-- Declaracion de la clase del plugin
  KillTracker.cpp/h        <-- Motor de conteo de muertes/cofres/muertes del jugador
  OverlayRenderer.cpp/h    <-- Overlay ImGui con patron de arrastrar para reposicionar
  IconAtlas.cpp/h           <-- Carga de hojas de sprites de textura (D3D11 + stb_image)
  Database.cpp/h           <-- Wrapper de SQLite3 para estadisticas persistentes
  DisplaySettings.h        <-- Estructura de configuracion
  sdk/
    PluginHelpers.h        <-- Copiado de ExamplePlugin
  lib/
    sqlite3.c/h            <-- Amalgamacion de SQLite3 (compilado como C)
    sqlite3-vcpkg-config.h <-- Sobreescritura local para enlace estatico

Convencion de nombres

El nombre del archivo DLL debe coincidir exactamente con el nombre de la carpeta:

  • Carpeta: Plugins/MyPlugin/ -> DLL: MyPlugin.dll
  • El host escanea cada subcarpeta en Plugins/ y busca <NombreDeCarpeta>.dll

2. Ciclo de vida del plugin

Load DLL (LoadLibrary)
  -> CreatePlugin()           -- Fabrica: instancia tu IPlugin
  -> SetContext(ctx)           -- Recibe los servicios del host
  -> SetPluginDirectory(dir)   -- Recibe la ruta de tu carpeta
  -> GetSDKVersion()           -- Verificacion de compatibilidad
  -> GetName()                 -- Nombre para mostrar en la UI
  -> [if enabled] OnEnable()  -- Inicializar recursos
  |
  Bucle principal (cada frame):
    -> DrawUI()                -- Renderizar tu overlay (solo si esta habilitado)
    -> DrawSettings()          -- Renderizar configuracion en la pestana Plugins
    -> WantsOverlay()          -- El host verifica si el plugin quiere modo overlay
  |
  Periodicamente / al cerrar:
    -> SaveSettings()          -- Persistir tu configuracion
  |
  -> OnDisable()               -- Liberar recursos
  -> DestroyPlugin(plugin)     -- Fabrica: eliminar tu IPlugin
  -> FreeLibrary               -- Descargar DLL

Hilos

  • Todos los metodos Draw* son llamados en el hilo principal/de renderizado
  • GetSnapshot() y otras funciones de PluginContext son seguras entre hilos
  • NO crear hilos que llamen a ImGui -- ImGui no es seguro entre hilos

3. Referencia de la interfaz IPlugin

Cada plugin debe implementar la interfaz IPlugin (definida en plugin_sdk/PluginAPI.h):

void SetPluginDirectory(const char* dir)

  • Cuando se llama: Una vez, inmediatamente despues de la creacion
  • Parametro: Ruta relativa como "Plugins/YourPlugin"
  • Proposito: Almacenar esta ruta para cargar configuracion/recursos

void SetContext(PluginContext* context)

  • Cuando se llama: Una vez, despues de SetPluginDirectory
  • Parametro: Puntero al PluginContext del host (valido durante la vida del plugin)
  • Proposito: Almacenar este puntero -- es tu puerta de acceso a todos los datos del juego
  • Importante: Llamar ImGui::SetCurrentContext(ctx->ImGuiContext) aqui

void OnEnable(bool isGameOpened)

  • Cuando se llama: Cuando el usuario habilita el plugin, o al inicio si estaba previamente habilitado
  • Parametro: true si el proceso del juego esta actualmente vinculado
  • Proposito: Cargar configuracion, asignar recursos, inicializar estado

void OnDisable()

  • Cuando se llama: Cuando el usuario deshabilita el plugin
  • Proposito: Liberar recursos, detener trabajo en segundo plano

void DrawUI()

  • Cuando se llama: Cada frame, solo cuando el plugin esta habilitado
  • Proposito: Renderizar tu overlay usando ImGui
  • Nota: Usar IDs de ventana unicos como "MyWindow##MyPlugin" para evitar conflictos

void DrawSettings()

  • Cuando se llama: Cada frame, en la pestana de configuracion de Plugins (solo cuando esta habilitado)
  • Proposito: Renderizar la configuracion del plugin usando ImGui

void SaveSettings()

  • Cuando se llama: Periodicamente y al cerrar la aplicacion
  • Proposito: Guardar tu configuracion en disco (ej., Plugins/YourPlugin/config/settings.txt)

const char* GetName()

  • Retorna: Nombre para mostrar en la pestana Plugins (ej., "My Plugin")

int GetSDKVersion()

  • Retorna: PLUGIN_SDK_VERSION (actualmente 4)
  • Proposito: El host verifica esto para compatibilidad -- debe coincidir

bool WantsOverlay() (SDK v2)

  • Retorna: true si el plugin quiere renderizar en modo overlay (overlay transparente sobre el juego)
  • Por defecto: false -- el plugin solo renderiza en la ventana de configuracion normal
  • Proposito: Cuando cualquier plugin retorna true, el host entra en modo overlay incluso si ninguna funcion integrada lo requiere

Exportaciones de fabrica

Tu DLL debe exportar estas dos funciones C:

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

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

4. Referencia de la API PluginContext

La estructura PluginContext (definida en plugin_sdk/PluginContext.h) proporciona punteros a funciones para acceder a los datos del juego. Todos los tipos estan en el espacio de nombres PluginSDK.

Acceso a datos del juego

GetSnapshot() -> shared_ptr<const PluginGameSnapshot>

Retorna una instantanea completa del estado del juego. Se actualiza una vez por frame. Contiene:

Campo Tipo Descripcion
CurrentState GameStateTypes Estado actual del juego
CurrentAreaName string Nombre del area (ej., "The Riverways")
CurrentAreaHash string Hash unico de la instancia del area
CurrentAreaLevel uint8_t Nivel de monstruos del area actual
IsTown bool Verdadero si esta en la ciudad
IsHideout bool Verdadero si esta en el refugio
IsPaused bool Verdadero si el juego esta en pausa
IsSkillTreeVisible bool Verdadero si el panel del arbol de habilidades esta abierto
WorldToGridConvertor float Factor de conversion mundo->cuadricula
Player RadarEntity Datos de la entidad del jugador local
Entities vector<RadarEntity> Todas las entidades cercanas
LargeMap / MiniMap MapData Datos del overlay del mapa
Vitals PlayerVitals HP/ES/MP del jugador + buffs
ScreenWidth / ScreenHeight int Dimensiones de la ventana del juego
ProcessId DWORD ID del proceso del juego
GameWindow HWND Handle de la ventana del juego
GameWindowForeground bool Verdadero si el juego esta en primer plano
IsAttached bool Verdadero si esta vinculado al proceso del juego
IsWindowValid bool Verdadero si la ventana del juego es valida
LastUpdateTime uint64_t Marca de tiempo de la ultima actualizacion
AreaChangeCounter uint64_t Se incrementa al cambiar de area
Inventories vector<InventoryInfo> Contenido del inventario del jugador
CurrencyTotals map<string,int> Cantidades de moneda por ruta
InventoryGrid InventoryGridInfo Informacion de la cuadricula del inventario
WorldToScreenMatrix XMFLOAT4X4 Matriz de proyeccion 3D->2D

Importante: Filtrado de entidades Las entidades muertas (aquellas con EntityState == Useless) son filtradas FUERA de la instantanea antes de que los plugins la reciban. Esto significa que nunca observaras una transicion de HP de vivo a muerto. Si necesitas detectar muertes, usa la deteccion basada en desaparicion en su lugar -- rastrea los IDs de entidades por zona y cuentalos como muertos cuando desaparezcan de la lista de entidades mientras estan en proximidad InnerCircle u OuterCircle. Ver Seccion 8: Recetas comunes para mas detalles.

GetPlayerVitals() -> PlayerVitals

Atajo de conveniencia para los signos vitales del jugador.

GetCurrentState() -> GameStateTypes

Retorna el enum del estado actual del juego.

IsAttached() -> bool

Verdadero si el proceso del juego esta vinculado y es legible.

IsInGame() -> bool

Verdadero si actualmente esta en el juego (no cargando, no en pantalla de inicio de sesion).

IsGameForeground() -> bool

Verdadero si la ventana del juego es la ventana en primer plano.

GetProcessId() -> DWORD

Retorna el ID del proceso del juego.

Acceso a datos de items

ReadExtendedItemMods(entityAddress) -> ExtendedItemModInfo

Lee todos los modificadores de una entidad de item.

ReadItemRarity(entityAddress) -> int

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

ReadItemStackCount(entityAddress) -> int

Retorna la cantidad de la pila para items de moneda/apilables.

ReadItemName(entityAddress) -> string

Retorna el nombre del tipo base del item.

ReadItemPath(entityAddress) -> string

Retorna la ruta de metadatos del item.

ReadItemBaseTypeName(entityAddress) -> string

Retorna el nombre del tipo base del item (ej., "Divine Orb", "Chaos Orb"). A diferencia de ReadItemName que retorna la ruta de metadatos, este lee el nombre real del tipo base desde BaseItemTypeData.BaseTypeName.

ReadItemUniqueName(entityAddress) -> string

Retorna el nombre unico del item desde Words.dat (ej., "Headhunter", "Brimstone Call"). Retorna cadena vacia para items no unicos.

Modo overlay (SDK v2)

IsOverlayMode() -> bool

Retorna verdadero si el host esta actualmente en modo overlay (overlay transparente sobre la ventana del juego). Usa esto para ajustar tu renderizado -- ej., dibujar sobre el overlay del juego vs. dibujar en una ventana de configuracion.

Estado de la UI (SDK v4)

IsMenuVisible() -> bool

Retorna verdadero cuando el menu de configuracion del host es visible (el overlay es interactivo). Cuando el menu esta oculto, la ventana de overlay es transparente al clic (WS_EX_TRANSPARENT), por lo que las ventanas de ImGui no pueden recibir entrada del raton.

Usa esto para implementar el patron de overlay arrastrable:

  • Menu visible: Muestra un indicador de arrastre, permite interaccion (pestanas, botones)
  • Menu oculto: Elimina el indicador de arrastre, agrega ImGuiWindowFlags_NoInputs para hacer la ventana no interactiva

Ver Seccion 6: Patron de overlay arrastrable para la implementacion completa.

Lectura de memoria (SDK v2)

Acceso directo a la memoria del proceso del juego. Todas las lecturas son seguras (retornan 0/vacio en caso de fallo).

GetBaseAddress() -> uintptr_t

Retorna la direccion base del modulo ejecutable del juego. Retorna 0 si no esta vinculado.

GetModuleSize() -> uintptr_t

Retorna el tamano del modulo del juego en bytes. Retorna 0 si no esta vinculado.

ReadProcessMemory(address, buffer, size) -> bool

Lee un bloque de bytes crudos del proceso del juego. El buffer debe tener al menos size bytes asignados. Retorna verdadero en caso de exito.

// 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

Lee una cadena ASCII terminada en nulo de la memoria del juego (max 128 caracteres).

ReadUnicodeString(address) -> wstring

Lee una cadena Unicode (ancha) terminada en nulo de la memoria del juego (max 128 wchars).

GetPatternAddress(patternName) -> uintptr_t

Obtiene una direccion resuelta de escaneo de patron por nombre. Retorna 0 si no se encuentra.

Patrones estandar:

Nombre Descripcion
"Game States" Raiz del vector GameStates
"File Root" Registro de archivos
"AreaChangeCounter" Contador de transicion de area
"Terrain Rotator Helper" Datos de rotacion
"Terrain Rotation Selector" Selector de rotacion
"GameCullSize" Valor de recorte de pantalla

Proyeccion mundo-a-pantalla (SDK v2)

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

Convierte una posicion en espacio mundial a coordenadas de pantalla. Retorna verdadero si la posicion es visible en pantalla.

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 al host que escanee los inventarios. Pasa -1 para escanear todos los inventarios, o un ID de inventario especifico. Los datos del inventario en la instantanea se llenan despues de que el escaneo se completa (siguiente frame).

Nota: Los datos del inventario no se actualizan automaticamente -- debes llamar a esta funcion para activar un escaneo. Llamala periodicamente (ej., cada 2 segundos) si necesitas datos de inventario continuos.

Datos de terreno (SDK v2)

GetWalkableGrid(outWidth, outHeight) -> const uint8_t*

Retorna un puntero a los datos de la cuadricula transitable. La cuadricula es un arreglo 2D donde 0 = no transitable, distinto de cero = transitable. Retorna nullptr si los datos no estan disponibles.

GetTerrainHeight(gridX, gridY) -> float

Retorna la altura del terreno en una posicion de cuadricula. Retorna 0 si esta fuera de limites o los datos no estan disponibles.

Lectura de contenedores nativos (SDK v3)

Estas funciones leen contenedores de la biblioteca estandar de C++ directamente de la memoria del juego, reflejando los metodos Core::Process del host.

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

Lee un StdVector (estructura de 24 bytes: {First, Last, End}) de la memoria del juego. Retorna un buffer asignado con malloc de elementos. El llamador debe hacer free() del puntero retornado. Retorna nullptr en caso de fallo.

// 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*

Lee un StdList (estructura de 16 bytes: {Head, Size}) de la memoria del juego. Recorre la lista enlazada y retorna un buffer contiguo. El llamador debe hacer free().

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

Lee un StdBucket de la memoria del juego (lee el StdVector embebido). El llamador debe hacer free().

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

Recorre un StdMap (estructura de 16 bytes: {Head, Size}) y llama al callback por cada par clave-valor. Retorna el numero de nodos 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

Lee un StdWString (estructura de 32 bytes con buffer inline/heap) de la memoria del juego.

GetInventoryName(inventoryId) -> const char*

Retorna el nombre legible de un ID de inventario (ej., 1 -> "MainInventory1", 3 -> "Weapon1", 64 -> "Currency1").

Acceso a datos de depuracion (SDK v4)

El SDK v4 proporciona acceso directo a los datos de depuracion del host -- componentes de entidades, detalles de inventario y arbol de elementos de UI -- coincidiendo con las pestanas de depuracion integradas.

Lista de depuracion de entidades

GetEntityDebugList() -> vector<DebugEntityInfo>

Retorna una lista de todas las entidades con metadatos de depuracion (Id, Address, Path, Type, SubType, State, Rarity, Zone). Esto refleja la pestana Debug->Entity List.

WatchEntity(entityId)

Comienza a observar los componentes de una entidad. El hilo de trabajo del host leera los datos completos de componentes de esta entidad cada frame.

UnwatchEntity(entityId)

Deja de observar los componentes de una entidad. Llama a esto cuando el usuario colapsa el nodo del arbol de la entidad para liberar recursos.

GetWatchedEntityData(entityId) -> DebugEntityComponents

Retorna los datos completos de componentes de una entidad observada. Contiene sub-estructuras para los 8 componentes reconocidos (Life, Render, Positioned, Targetable, Animated, Stats, Actor, Buffs) mas la lista de todas las direcciones 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);
    }
}

Depuracion de inventario

GetServerDataAddress() -> uintptr_t

Retorna la direccion base del componente ServerData.

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

Retorna todos los IDs de inventario del jugador y sus direcciones (desde ServerData).

WatchInventory(inventoryId)

Comienza a observar un inventario para inspeccion detallada de depuracion. El host lee la ocupacion de ranuras, detalles de items y modificadores.

GetWatchedInventoryData() -> DebugInventoryData

Retorna datos completos del inventario observado actualmente: dimensiones de cuadricula, ocupacion de ranuras, items con rareza y 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()));
}

Arbol de elementos de UI

GetGameUiRootAddress() -> uintptr_t

Retorna la direccion del elemento raiz de la UI del juego (para navegacion del arbol de UI en el juego).

GetUiRootAddress() -> uintptr_t

Retorna la direccion raiz de la UI de nivel superior.

GetGameCullValue() -> int

Retorna el valor actual de GameCullSize, utilizado para calculos de escala de UI. Combinado con las dimensiones de pantalla, esto permite calcular con precision la posicion/tamano de los 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)

El encabezado sdk/PluginHelpers.h (incluido con ExamplePlugin) proporciona una clase MemoryReader con tipos seguros que envuelve las funciones crudas de 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);

El MemoryReader tambien proporciona wrappers de conveniencia para ReadString(), ReadUnicodeString(), GetBaseAddress(), GetModuleSize(), y GetPatternAddress().

Utilidades adicionales en PluginHelpers.h:

  • WideToNarrow(wstring) -- conversion segura de wstring a string (perdida ASCII)
  • GetEntityTypeName(type) -- enum a nombre para mostrar (incluyendo ExpeditionMarker/ExpeditionRemnant)
  • GetNearbyZoneName(zone) -- zona a nombre para mostrar
  • GetRarityName(rarity) / GetRarityColor(rarity) -- helpers de visualizacion de rareza

Servicios del host

Log(level, message)

Escribe en el sistema de registro del host. Niveles: "Debug", "Info", "Warning", "Error"

ImGuiContext (void*)

El contexto ImGui del host. Llama a ImGui::SetCurrentContext() con esto en SetContext().

D3DDevice (void*)

El ID3D11Device* del host. Haz cast y usalo para cargar texturas.


5. Referencia de estructuras de datos

Todos los tipos estan en el espacio de nombres PluginSDK. Los plugins tipicamente agregan using namespace PluginSDK;.

RadarEntity

Datos por entidad disponibles en snapshot->Entities:

Campo Tipo Descripcion
Id uint32_t ID unico de la entidad
Address uintptr_t Direccion de memoria (para llamadas API de items)
EntityDetailsAddress uintptr_t Direccion de la estructura de detalles de entidad
RenderComponentAddress uintptr_t Direccion del componente Render (atajo)
IsValid bool Flag de validez de la entidad
entityType EntityTypes Categoria de la entidad
entitySubtype EntitySubtypes Subcategoria de la entidad
entityState EntityStates Estado de la entidad
Rarity int 0=Normal, 1=Magico, 2=Raro, 3=Unico
Reaction uint8_t 0=Hostil, 1=Neutral, 2=Amigable
GridPositionX/Y float Posicion en la cuadricula del terreno
TerrainHeight float Altura del terreno en la posicion de la entidad
WorldX/Y/Z float Posicion en espacio mundial
ModelBoundsZ float Altura del modelo
Path wstring Ruta de metadatos de la entidad
PlayerName wstring Nombre del jugador (si es entidad jugador)
TgtPath string Ruta de destino (cadena estrecha)
CurrentHP/MaxHP int Salud de la entidad
CurrentES/MaxES int Escudo de energia de la entidad
IsSleeping bool Flag de entidad lejana
IsChestOpened bool Estado de cofre abierto
Zone NearbyZone Proximidad al jugador
ComponentCache EntityComponentCache Direcciones de componentes

Importante: Las entidades muertas (EntityState::Useless) son eliminadas de la instantanea antes de que llegue a los plugins. Nunca veras que el HP de un monstruo caiga a cero -- simplemente desaparece de la lista. Usa el campo Zone para distinguir muertes (la entidad desaparecio de InnerCircle/OuterCircle) de entidades que salen del rango (la entidad estaba en la zona Far).

Buff

Buff/debuff activo:

Campo Tipo Descripcion
Name string Nombre interno del buff (ej., "flask_effect_life")
TimeLeft float Segundos restantes
Charges short Cantidad de cargas
TotalTime float Duracion total

MapData

Estado del minimapa/mapa grande:

Campo Tipo Descripcion
CenterX/Y float Centro del mapa
SizeX/Y float Dimensiones del mapa
ShiftX/Y float Desplazamiento actual de panoramizacion
DefaultShiftX/Y float Valores de desplazamiento predeterminados
Zoom float Nivel de zoom
Scale float Factor de escala del mapa
IsVisible bool El mapa esta actualmente visible

InventoryInfo / InventoryItemInfo

Campo Tipo Descripcion
Id int ID del inventario
TotalBoxesX/Y int Dimensiones de la cuadricula
Ptr uintptr_t Direccion de memoria del inventario
Items vector<InventoryItemInfo> Items en el inventario

Campos del item: Address, Name (ruta de metadatos), Path (igual que Name), BaseTypeName (nombre del tipo base, ej. "Divine Orb"), UniqueName (nombre unico del item desde Words.dat, ej. "Headhunter", vacio para no unicos), SlotX/Y, Width/Height, StackCount, IsCurrency

ExtendedItemModInfo

Retornado por ReadExtendedItemMods():

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

ItemModData

Campo Tipo Descripcion
Key string Clave del mod de estadistica
Values vector<float> Valores del mod

EntityComponentCache

Direcciones de componentes almacenadas en cache por entidad (disponibles 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)

Metadatos de entidad de GetEntityDebugList():

Campo Tipo Descripcion
Id uint32_t ID de la entidad
Address uintptr_t Direccion de memoria
Path string Ruta de metadatos
EntityType int Tipo de entidad (convertir a EntityTypes)
EntitySubType int Subtipo de entidad (convertir a EntitySubtypes)
EntityState int Estado de entidad (convertir a EntityStates)
Rarity int 0=Normal, 1=Magico, 2=Raro, 3=Unico
Zone NearbyZone Proximidad al jugador
ComponentAddresses vector<pair<string,uintptr_t>> Todos los pares nombre->direccion de componentes

DebugEntityComponents (SDK v4)

Datos completos de componentes de GetWatchedEntityData():

Campo Tipo Descripcion
EntityId uint32_t Para que entidad son estos datos
Valid bool Si los datos fueron leidos exitosamente
HasLife / Life bool / DebugLifeComp Componente Life -- Life.Health, Life.EnergyShield, Life.Mana (cada uno un DebugVital con .Current, .Total, .Regeneration, .ReservedFlat, .ReservedPercent)
HasRender / Render bool / DebugRenderComp Posicion (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 animacion (string), Id (uint32)
HasStats / Stats bool / DebugStatsComp CurrentWeaponIndex, IsShapeshifted, StatsItems/StatsBuff (vectores de pares ID de stat->valor)
HasActor / Actor bool / DebugActorComp AnimationId, AnimationName, ActiveSkills (vector de DebugActiveSkill), DeployedCounts[256]
HasBuffs / Buffs bool / vector<DebugBuff> Buffs activos: Name, TotalTime, TimeLeft, Charges, FlaskSlot, Effectiveness, SourceEntityId

DebugInventoryData (SDK v4)

Detalles de inventario de GetWatchedInventoryData():

Campo Tipo Descripcion
InventoryId int ID del inventario (-1 si ninguno)
Address uintptr_t Direccion del inventario
TotalBoxesX/Y int Dimensiones de la cuadricula
ServerRequestCounter int Contador de sincronizacion con el servidor
GridScreenX / GridScreenY float Posicion en pantalla de la cuadricula de UI
CellSize float Tamano de celda de la cuadricula en pixeles
GridValid bool Si los datos de la cuadricula de UI son validos
SlotOccupied vector<bool> Ocupacion por ranura
Items vector<DebugInventoryItem> Items con ruta, rareza y modificadores

DebugInventoryItem (SDK v4)

Campo Tipo Descripcion
Address uintptr_t Direccion de la entidad del item
Path string Ruta de metadatos del item
BaseTypeName string Nombre del tipo base (ej. "Divine Orb")
UniqueName string Nombre unico del item desde Words.dat (vacio para no unicos)
SlotX / SlotY int Posicion en la cuadricula del inventario
Rarity int 0=Normal, 1=Magico, 2=Raro, 3=Unico
ItemLevel int Nivel del item
RequiredLevel int Nivel requerido del personaje
IsIdentified bool Si el item esta identificado
IsCorrupted bool Si el item esta corrompido
CraftedModCount int Numero de mods creados
ImplicitMods vector<DebugModInfo> Modificadores implicitos
ExplicitMods vector<DebugModInfo> Modificadores explicitos
EnchantMods vector<DebugModInfo> Modificadores de encantamiento
HellscapeMods vector<DebugModInfo> Modificadores de Hellscape

DebugActiveSkill (SDK v4)

Campo Tipo Descripcion
Name string Nombre de la habilidad
UseStage int Etapa de uso actual
CastType int Tipo de lanzamiento
TotalUses int Conteo total de usos
TotalCooldownTimeInMs int Tiempo de enfriamiento en milisegundos
CanBeUsed bool Si la habilidad puede usarse actualmente

DebugBuff (SDK v4)

Campo Tipo Descripcion
Name string Nombre interno del buff
TotalTime float Duracion total
TimeLeft float Segundos restantes
Charges short Cantidad de cargas
FlaskSlot short Indice de ranura de frasco
Effectiveness short Efectividad del buff
SourceEntityId uint32_t Entidad que aplico este buff

DebugModInfo (SDK v4)

Campo Tipo Descripcion
Name string Nombre para mostrar del mod
StatKey string Identificador de clave de estadistica
AffixName string Nombre del afijo
GenerationType int 1=Prefijo, 2=Sufijo, 3=Implicito
Value0 float Primer valor (NaN si ninguno)
Value1 float Segundo valor (NaN si ninguno)

Enumeraciones

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 cuadricula), OuterCircle(2, ~120 unidades de cuadricula), 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 de ImGui en plugins

Contexto compartido

El host y el plugin comparten el mismo contexto de ImGui. Debes llamar:

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

en tu metodo SetContext().

IDs de ventana

Siempre usa IDs de ventana unicos para evitar conflictos con el host u otros plugins:

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

Caracteristicas disponibles

  • Ventanas, pestanas, arboles, tablas, listas de dibujo
  • Carga de texturas via dispositivo D3D11
  • Renderizado de overlay via ImGui::GetBackgroundDrawList()
  • Iconos FontAwesome 6 via #include "imgui/IconsFontAwesome6.h" (ej., ICON_FA_ARROWS_UP_DOWN_LEFT_RIGHT)

Renderizado de overlay (SDK v2)

Usa WorldToScreen() para dibujar etiquetas/formas en posiciones 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

Sobreescribe WantsOverlay() para retornar true para solicitar al host que entre en modo overlay:

bool WantsOverlay() override { return m_OverlayEnabled; }

Cuando esta en modo overlay, la ventana del host es transparente y posicionada sobre el juego. Tus llamadas a DrawUI() renderizan directamente sobre la pantalla del juego.

Patron de overlay arrastrable (SDK v4)

El overlay del host usa WS_EX_TRANSPARENT para hacer la ventana transparente al clic cuando el menu esta oculto. Esto significa que las ventanas de ImGui no pueden recibir entrada del raton a menos que el menu este visible. Para crear una ventana de overlay arrastrable (como el Vitals Overlay integrado), usa este patron de doble modo:

#include "imgui/IconsFontAwesome6.h"

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

    if (menuVisible) {
        // === MODO ARRASTRABLE ===
        // Ventana con fondo, indicador de arrastre, controles interactivos
        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 NO INTERACTIVO ===
        // Overlay estatico — sin arrastre, sin interaccion de raton
        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();
    }
}

Puntos clave:

  • ImGuiCond_Appearing establece la posicion solo en la primera aparicion; ImGui luego rastrea la posicion de arrastre
  • NoTitleBar + sin NoMove = la ventana es arrastrable desde cualquier area vacia (comportamiento predeterminado de ImGui)
  • NoInputs en modo no interactivo evita que el overlay robe el foco a traves de WS_EX_TRANSPARENT
  • Siempre verifica nulo el puntero IsMenuVisible para compatibilidad hacia atras: m_Context->IsMenuVisible ? m_Context->IsMenuVisible() : false
  • Guarda la posicion en tu archivo de configuracion para que persista entre sesiones

7. Persistencia de configuracion

Patron 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
}

Ubicacion del archivo

Almacena la configuracion en <PluginDirectory>/config/:

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

Formato simple clave-valor

Para plugins con muchas configuraciones, un formato de texto simple clave=valor funciona bien:

// 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. Recetas comunes

Obtener el porcentaje de HP del jugador

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

Listar todos los monstruos dentro del circulo interior

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 si el jugador tiene un buff especifico

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

Obtener informacion del area actual

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

Dibujar texto en la posicion mundial de una entidad (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");
    }
}

Leer modificadores de items del 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 cambio de area

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

Verificar si esta en pantalla de carga

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

Detectar muertes de monstruos (basado en desaparicion)

Las entidades muertas son filtradas de la instantanea (EntityState::Useless), por lo que no puedes detectar el HP cayendo a 0. En su lugar, rastrea las entidades por ID y detecta cuando desaparecen de las zonas cercanas:

// 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: Las entidades dentro de ~120 unidades de cuadricula que desaparecen repentinamente casi con certeza fueron eliminadas (no simplemente salieron del rango). Las entidades en la zona Far naturalmente aparecen y desaparecen de la lista de entidades -- no cuentes esas.

Importante: Limpia m_PrevEntities al cambiar de area (AreaChangeCounter cambio) para evitar falsos positivos.

Leer memoria cruda del juego (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 escaneo de patrones (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 transitable (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;
    }
}

Leer memoria con tipos usando 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 */ }

Obtener nombre del inventario (SDK v3)

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

Leer direcciones 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
    }
}

Inspeccionar componentes de entidad via observacion de depuracion (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);
    }
}

Inspeccionar inventario con cuadricula de ranuras (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 el arbol 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);
        }
    }
}

Leer modificadores de items con helpers de visualizacion (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. Compilacion y despliegue

Configuracion de compilacion

Configuracion Valor
Configuration Release
Platform x64
C++ Standard /std:c++20
Runtime Library /MD (DLL multiproceso)
Configuration Type DLL

Archivos requeridos en el proyecto del plugin

  • Tu(s) archivo(s) .cpp del plugin
  • Archivos fuente de ImGui: imgui.cpp, imgui_draw.cpp, imgui_tables.cpp, imgui_widgets.cpp
  • Ruta de inclusion al directorio raiz de POEFixer (para encabezados del SDK e ImGui)
  • Opcional: Copiar Plugins/ExamplePlugin/sdk/PluginHelpers.h para el wrapper MemoryReader y funciones de utilidad

Rutas de inclusion

Tu .vcxproj debe tener estos directorios de inclusion adicionales:

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

Si usas bibliotecas de terceros locales (ej., SQLite3 en una subcarpeta lib/), agrega $(ProjectDir)lib antes de la ruta de la solucion para que los encabezados locales tengan prioridad:

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

Bibliotecas de terceros

SQLite3 (enlace estatico)

Para usar SQLite3 en un plugin, debes compilar el codigo fuente de amalgamacion directamente en tu DLL -- Windows LoadLibrary no busca en el propio directorio del DLL las dependencias, por lo que enlazar dinamicamente sqlite3.dll fallara con error 126.

Pasos:

  1. Copia sqlite3.c y sqlite3.h en el directorio lib/ de tu plugin
  2. Crea lib/sqlite3-vcpkg-config.h para sobreescribir SQLITE_API (previene errores 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. Agrega sqlite3.c a tu .vcxproj como archivo C con advertencias deshabilitadas:
    <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 (carga de texturas)

Para cargar texturas desde archivos de imagen (PNG, JPG), incluye stb_image en un archivo .cpp:

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

Luego usa el dispositivo D3D11 de m_Context->D3DDevice para crear texturas en la GPU.

Salida

Configura tu directorio de salida a:

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

Despliegue

Copia tu DLL compilado a Plugins/YourPlugin/YourPlugin.dll junto al ejecutable principal.

Depuracion

  1. Compila tu DLL del plugin en modo Debug
  2. Inicia la aplicacion host
  3. En Visual Studio: Depurar -> Vincular a proceso -> selecciona el .exe del host
  4. Establece puntos de interrupcion en el codigo fuente de tu plugin
  5. El depurador se detendra cuando se llame tu codigo

10. Solucion de problemas

Problema Solucion
Plugin no carga Verifica que el nombre del DLL coincida exactamente con el nombre de la carpeta
"SDK version mismatch" Recompila el plugin con los ultimos encabezados del SDK (version actual: 4)
Error 126 de LoadLibrary El DLL tiene dependencias no resueltas. Para bibliotecas de terceros como SQLite3, compilalas estaticamente en el DLL (ver Seccion 9). Usa dumpbin /dependents YourPlugin.dll para verificar.
Crash al cargar Verifica incompatibilidad de CRT -- ambos deben usar /MD
ImGui no renderiza Asegurate de que ImGui::SetCurrentContext() se llama en SetContext()
Datos estan vacios/cero Verifica IsAttached() e IsInGame() antes de leer datos
Inventario esta vacio Llama a RequestInventoryScan(-1) -- los datos de inventario son bajo demanda
Error "Missing exports" Asegurate de que CreatePlugin y DestroyPlugin estan exportados con extern "C"
Plugin hace crash al host Esto no deberia pasar -- todas las llamadas de plugins estan protegidas por SEH. Revisa los registros.
Datos obsoletos GetSnapshot() retorna los datos del frame mas reciente. No almacenes el puntero en cache.
Lectura de memoria retorna 0 Verifica que IsAttached() es verdadero y la direccion es valida
WorldToScreen retorna falso La posicion puede estar detras de la camara o fuera de pantalla
Ventana de overlay no es clicable El host usa WS_EX_TRANSPARENT cuando el menu esta oculto. Usa IsMenuVisible() para mostrar controles interactivos solo cuando el menu esta activo. Ver el Patron de Overlay Arrastrable en la Seccion 6.
Deteccion de muertes no funciona Las entidades muertas son eliminadas de la instantanea. Usa deteccion basada en desaparicion en lugar de transicion de HP. Ver Seccion 8.
Errores C2491 "dllimport function" Los encabezados de tu biblioteca de terceros definen __declspec(dllimport). Crea un encabezado de sobreescritura local que establezca la macro API como vacia (ver ejemplo de SQLite3 en la Seccion 9).

← Home

Clone this wiki locally