Arquitectura
Estos cinco diagramas documentan el monorepo @infernus/* de fuera hacia dentro: el grafo de paquetes y lo que hace cada envoltorio; el interior de @infernus/core; el interior del plugin C++ samp-node que embebe Node.js; el recorrido de un evento por core; y por ultimo el viaje completo entre core y el plugin.
Cada diagrama es un artefacto autocontenido y sigue el idioma en el que estás leyendo la documentación. La vista incrustada es un lienzo estático: abre el enlace bajo cada diagrama para desplazar y ampliar, buscar y enfocar nodos, rastrear relaciones, cambiar el tema y exportar a PNG/SVG.
Monorepo y detalle de paquetes
Los 25 paquetes compilables en packages/*, más types, shared, el host de ejecución y la cadena de compilación. La columna de dependencias va de izquierda a derecha: un gamemode sobre @infernus/core, a través del plugin samp-node, hasta el servidor open.mp. Las cuatro regiones agrupan los paquetes por dominio y cada nodo indica una implementación concreta: el nativo que envuelve o el mecanismo que usa.
Los nombres de paquetes y de funciones nativas se mantienen en inglés; las etiquetas de región y las tarjetas resumen están escritas en el idioma de esta página.
Dentro de @infernus/core
Los subsistemas internos: components (entidades, el cargador filterscript, el despachador CmdBus y el ciclo de vida de GameMode), utils (el motor de eventos bus.ts, la interceptacion de hook.ts y los registros de entidades de pools.ts) y wrapper (los enlaces nativos tipados y las tablas __inject__ intercambiables).
internalPlayerProps usa una clave Symbol, por eso un plugin puede extender Player sin chocar con los campos privados de core.
Dentro del plugin samp-node
@infernus/core no es una libreria autonoma: cada llamada nativa y cada evento pasan por el plugin C++ samp-node, que embebe Node.js como libreria compartida (libnode) dentro del proceso del servidor. Este diagrama cubre ese plugin: sus exportaciones PLUGIN_EXPORT (Load/Unload, OnPublicCall, AmxLoad, ProcessTick), el runtime V8 y uv_loop aislados que crea nodeimpl.cpp, el bootstrap de resource.cpp y las capas de marshalling events.cpp, natives.cpp y callbacks.cpp.
Dos detalles explican casi todo lo que se observa en core. No hay un hilo Node aparte: ProcessTick impulsa Tick en el hilo principal del servidor en cada frame. Y como un listener puede devolver legitimamente una Promise, handlePromiseReturnValue gira en Tick hasta que esa promise se resuelve, que es lo que permite a core tratar un listener async como un retorno sincrono 0/1.
Como se ejecuta defineEvent
La ruta de ejecución de todos los eventos, leída directamente de packages/core/src/utils/bus.ts. El registro es un efecto de importar el modulo: defineEvent llama a samp.registerEvent para declarar la firma del callback nativo y a samp.on para enganchar el trigger.
Despues trigger ejecuta la cadena de middleware: beforeEach convierte los argumentos nativos en un contexto, cada listener recibe { next, defaultValue, ...contexto }, y next(value) avanza la cadena ademas de fusionar value en ese contexto compartido. Un listener que nunca llama a next() corta la propagacion, y su valor de retorno pasa a ser el resultado del evento tras normalizarlo transformReturnValue a 0 o 1. afterEach se ejecuta una vez al final.
Eso es lo que permite componer cadenas: cualquier eslabon puede inspeccionar o reescribir lo que ven los siguientes, o detener el evento por completo.
core y el plugin, de extremo a extremo
Con las dos mitades juntas: un callback nativo que entra desde open.mp, pasa por la conversion AMX a V8 del plugin, llega a la cadena de middleware de core y vuelve a salir por callNative e InvokeNativeArray.
La mitad superior de la linea de tiempo es el arranque del plugin, incluido lo que hace realmente el script de bootstrap; la inferior es el viaje en estado estacionario. Conviene leer este diagrama despues de los dos anteriores: es la union entre ambos.